hostapd启动流程(二)

继续上篇:https://blog.csdn.net/krokodil98/article/details/116601818。上篇写了不需要acs扫描,固定信道启动的流程,本篇分析另一条路径,即acs扫描信道流程。

hostapd_setup_interface -》
	setup_interface-》
		setup_interface2-1. hostapd_select_hw_mode
				-》hostapd_check_chans
				-》acs_init,本篇从这里开始
... ... ... ...  ... ... ... ...

第二部分:
这部分从acs_init开始。

acs_init

enum hostapd_chan_status acs_init(struct hostapd_iface *iface)
{
	int err;
	wpa_printf(MSG_INFO, "ACS: Automatic channel selection started, this may take a bit");
	if (iface->drv_flags & WPA_DRIVER_FLAGS_ACS_OFFLOAD) {
		... ...
	}
	if (!iface->current_mode)
		return HOSTAPD_CHAN_INVALID;
	acs_cleanup(iface);//清除掉iface里的acs扫描流程用到的变量,如acs_num_completed_scans
	err = acs_request_scan(iface);//开启acs扫描流程
	if (err < 0)
		return HOSTAPD_CHAN_INVALID;
	hostapd_set_state(iface, HAPD_IFACE_ACS);//设置接口状态
	wpa_msg(iface->bss[0]->msg_ctx, MSG_INFO, ACS_EVENT_STARTED);
	return HOSTAPD_CHAN_ACS;//让上层函数知道需要等到acs扫描流程
}

真正实现扫描操作的是acs_request_scan函数。

acs_request_scan

static int acs_request_scan(struct hostapd_iface *iface)
{
	struct wpa_driver_scan_params params;
	struct hostapd_channel_data *chan;
	int i, *freq;
	os_memset(&params, 0, sizeof(params));//创建存储channel信息用的变量
	params.freqs = os_calloc(iface->current_mode->num_channels + 1, sizeof(params.freqs[0]));
	if (params.freqs == NULL)
		return -1;
	freq = params.freqs;
	for (i = 0; i < iface->current_mode->num_channels; i++) {
   //将iface支持的信道号转成对应的信道频率存储到params.freqs里
		chan = &iface->current_mode->channels[i];
		if (chan->flag & HOSTAPD_CHAN_DISABLED)
			continue;
		if (!is_in_chanlist(iface, chan))
			continue;
		*freq++ = chan->freq;
	}
	*freq = 0;
	iface->scan_cb = acs_scan_complete;//将scan函数赋值成acs_scan_complete,之后执行scan_cb的时候就会调acs_scan_complete
	wpa_printf(MSG_DEBUG, "ACS: Scanning %d / %d", iface->acs_num_completed_scans + 1, iface->conf->acs_num_scans);
   //这里输出当前执行到第几次acs扫描,指定次数通过在hostapd.conf里设置acs_num_scans = 6实现
	if (hostapd_driver_scan(iface->bss[0], &params) < 0) {
		wpa_printf(MSG_ERROR, "ACS: Failed to request initial scan");
		acs_cleanup(iface);
		os_free(params.freqs);
		return -1;
	}
	os_free(params.freqs);
	return 0;
}

其中,每次扫描完后,acs_scan_complete最终会被调用到。在acs_scan_complete里,会判断是否已完成了acs_num_completed_scans 次扫描。若次数不够,则再次调用acs_request_scan。acs_scan_complete中的这部分代码如下:

if (++iface->acs_num_completed_scans < iface->conf->acs_num_scans) {
		err = acs_request_scan(iface);
		if (err) {
			wpa_printf(MSG_ERROR, "ACS: Failed to request scan");
			goto fail;
		}
		return;
  }

开始单次acs扫描,进入hostapd_driver_scan函数。

hostapd_driver_scan
int hostapd_driver_scan(struct hostapd_data *hapd,
			struct wpa_driver_scan_params *params)
{
	if (hapd->driver && hapd->driver->scan2)//如果scan2注册了,则调用该函数
		return hapd->driver->scan2(hapd->drv_priv, params);
	return -1;
}

这里的scan2注册赋值在:driver_nl80211.c

const struct wpa_driver_ops wpa_driver_nl80211_ops = {
	.name = "nl80211",
	.desc = "Linux nl80211/cfg80211",
    ... ...
    .scan2 = driver_nl80211_scan2,
	... ...
	.get_scan_results2 = wpa_driver_nl80211_get_scan_results,
 ... ...
 };

知这一句scan2实际调用到的driver_nl80211_scan2函数。

driver_nl80211_scan2
static int driver_nl80211_scan2(void *priv,
				struct wpa_driver_scan_params *params)
{
	struct i802_bss *bss = priv;
#ifdef CONFIG_DRIVER_NL80211_QCA
	... ...
#endif /* CONFIG_DRIVER_NL80211_QCA */
	return wpa_driver_nl80211_scan(bss, params);
}

driver_nl80211_scan2调到下一层函数wpa_driver_nl80211_scan

wpa_driver_nl80211_scan
int wpa_driver_nl80211_scan(struct i802_bss *bss,
			    struct wpa_driver_scan_params *params)
{
	struct wpa_driver_nl80211_data *drv = bss->drv;
	int ret = -1, timeout;
	struct nl_msg *msg = NULL;
	wpa_dbg(drv->ctx, MSG_DEBUG, "nl80211: scan request");
	drv->scan_for_auth = 0;
	if (TEST_FAIL())
		return -1;
	msg = nl80211_scan_common(bss, NL80211_CMD_TRIGGER_SCAN, params);
   //调用IEEE 802.11 的被动扫描。除被动扫描外,函数还支持主动扫描,即向特定目的地SSID发送probe request
   #if 0 
   其中,
       struct wpa_driver_scan_params {
    	/* ssids - SSIDs to scan for */
    	struct wpa_driver_scan_ssid ssids[WPAS_MAX_SCAN_SSIDS];
    	/* num_ssids - Number of entries in ssids array
    	 * Zero indicates a request for a passive scan.*/
    	size_t num_ssids;
             ... ...
       }
   #endif
   //在nl80211_scan_common里,通过判断params->num_ssids是否为0来确定是主动还是被动扫描。默认不配置的话是0
	if (!msg)
		return -1;
	... ...
	ret = send_and_recv_msgs(drv, msg, NULL, NULL);
	msg = NULL;
	if (ret) {//异常情况处理
		wpa_printf(MSG_DEBUG, "nl80211: Scan trigger failed: ret=%d "
			   "(%s)", ret, strerror(-ret));
		... ...
	}
	drv->scan_state = SCAN_REQUESTED;
	/* Not all drivers generate "scan completed" wireless event, so try to
	 * read results after a timeout. */
	timeout = 10;
	if (drv->scan_complete_events) {
		/*
		 * The driver seems to deliver events to notify when scan is
		 * complete, so use longer timeout to avoid race conditions
		 * with scanning and following association request.
		 */
		timeout = 30;
	}
	wpa_printf(MSG_DEBUG, "Scan requested (ret=%d) - scan timeout %d "
		   "seconds", ret, timeout);
	eloop_cancel_timeout(wpa_driver_nl80211_scan_timeout, drv, drv->ctx);
	eloop_register_timeout(timeout, 0, wpa_driver_nl80211_scan_timeout,
			       drv, drv->ctx);
	drv->last_scan_cmd = NL80211_CMD_TRIGGER_SCAN;
fail:
	nlmsg_free(msg);
	return ret;//正常情况下返回0
}

因为函数wpa_driver_nl80211_scan返回0
=》driver_nl80211_scan2返回0
=》hostapd_driver_scan返回0
=》acs_request_scan里没有进入异常条件,属于正常情况返回0

而之前在函数acs_request_scan里调用的hostapd_driver_scan,最终会触发事件NL80211_CMD_TRIGGER_SCAN

事件NL80211_CMD_TRIGGER_SCAN

函数do_process_drv_event收到了事件NL80211_CMD_TRIGGER_SCAN,会进入switch的NL80211_CMD_TRIGGER_SCAN事件分支,会在函数里输出“nl80211: Drv Event … … … …”。进而会触发进入wpa_supplicant_event函数的EVENT_SCAN_STARTED事件流程。
函数do_process_drv_event:

static void do_process_drv_event(struct i802_bss *bss, int cmd,
				 struct nlattr **tb)
{
	struct wpa_driver_nl80211_data *drv = bss->drv;
	union wpa_event_data data;
	int external_scan_event = 0;

	wpa_printf(MSG_DEBUG, "nl80211: Drv Event %d (%s) received for %s",
		   cmd, nl80211_command_to_string(cmd), bss->ifname);//输出事件信息
    ... ... ... ...
	switch (cmd) {
	case NL80211_CMD_TRIGGER_SCAN://开始扫描
		wpa_dbg(drv->ctx, MSG_DEBUG, "nl80211: Scan trigger");
		drv->scan_state = SCAN_STARTED;//设置状态
		if (drv->scan_for_auth) {
			/*
			 * Cannot indicate EVENT_SCAN_STARTED here since we skip
			 * EVENT_SCAN_RESULTS in scan_for_auth case and the
			 * upper layer implementation could get confused about
			 * scanning state.
			 */
			wpa_printf(MSG_DEBUG, "nl80211: Do not indicate scan-start event due to internal scan_for_auth");
			break;
		}
		wpa_supplicant_event(drv->ctx, EVENT_SCAN_STARTED, NULL);//进入wpa_supplicant_event的EVENT_SCAN_STARTED事件流程
		break;
	... ... ... ...
	case NL80211_CMD_NEW_SCAN_RESULTS://扫描结束
		wpa_dbg(drv->ctx, MSG_DEBUG,
			"nl80211: New scan results available");
		drv->scan_complete_events = 1;
		if (drv->last_scan_cmd == NL80211_CMD_TRIGGER_SCAN) {
			drv->scan_state = SCAN_COMPLETED;
			eloop_cancel_timeout(wpa_driver_nl80211_scan_timeout,
					     drv, drv->ctx);
			drv->last_scan_cmd = 0;
		} else {
			external_scan_event = 1;
		}
		send_scan_event(drv, 0, tb, external_scan_event);//调用send_scan_event函数输出本次扫描相关信息
    ... ... ... ...
	}
}

在wpa_supplicant_event函数里,先会输出“wlan0: Event SCAN_STARTED (47) received”。而事件SCAN_STARTED 不等于函数内switch (event)的任何一个case分支的值,故进入了default的分支,会输出“Unknown event 47”。

事件NL80211_CMD_NEW_SCAN_RESULTS

一段扫描流程结束后,do_process_drv_event收到了事件NL80211_CMD_NEW_SCAN_RESULTS,会在函数中输出“nl80211: Drv Event … …”。

进入switch的NL80211_CMD_NEW_SCAN_RESULTS事件分支,输出“nl80211: New scan results available”,并调用send_scan_event函数。

在函数send_scan_event里输出了本次扫描的信道频率,"nl80211: Scan included frequencies: … … ",并调用 wpa_supplicant_event(drv->ctx, EVENT_SCAN_RESULTS, &event)。

wpa_supplicant_event会收到SCAN_RESULTS 事件,此时iface->scan_cb的函数已在ieee80211n_check_40mhz里被赋值为ieee80211n_check_scan,即wpa_supplicant_event会调用ieee80211n_check_scan函数来检查扫描结果。

ieee80211n_check_scan函数,检查信道

static void ieee80211n_check_scan(struct hostapd_iface *iface)
{
	struct wpa_scan_results *scan_res;
	int oper40;
	int res;
	/* Check list of neighboring BSSes (from scan) to see whether 40 MHz is
	 * allowed per IEEE Std 802.11-2012, 10.15.3.2 */
	iface->scan_cb = NULL;
	scan_res = hostapd_driver_get_scan_results(iface->bss[0]);
	if (scan_res == NULL) {//结束接口初始化
		hostapd_setup_interface_complete(iface, 1);
		return;
	}
	if (iface->current_mode->mode == HOSTAPD_MODE_IEEE80211A)//判断当前起的接口是2.4G还是5G
		oper40 = ieee80211n_check_40mhz_5g(iface, scan_res);
	else
		oper40 = ieee80211n_check_40mhz_2g4(iface, scan_res);//检查2.4G
	wpa_scan_results_free(scan_res);//释放扫描结果
	iface->secondary_ch = iface->conf->secondary_channel;

	if (!oper40) {//如果返回值oper40是0,则说明不支持40M
		wpa_printf(MSG_INFO, "20/40 MHz operation not permitted on "
			   "channel pri=%d sec=%d based on overlapping BSSes",
			   iface->conf->channel,
			   iface->conf->channel +
			   iface->conf->secondary_channel * 4);
		iface->conf->secondary_channel = 0;
		if (iface->drv_flags & WPA_DRIVER_FLAGS_HT_2040_COEX) {
			/*
			 * TODO: Could consider scheduling another scan to check
			 * if channel width can be changed if no coex reports
			 * are received from associating stations.
			 */
		}
	}
	res = ieee80211n_allowed_ht40_channel_pair(iface);
	if (!res) {
		iface->conf->secondary_channel = 0;
		res = 1;
		wpa_printf(MSG_INFO, "Fallback to 20 MHz");
	}
	hostapd_setup_interface_complete(iface, !res);//结束接口初始化
}

ieee80211n_check_scan先调用hostapd_driver_get_scan_results

hostapd_driver_get_scan_results

struct wpa_scan_results * hostapd_driver_get_scan_results(
	struct hostapd_data *hapd)
{
	if (hapd->driver && hapd->driver->get_scan_results2)
		return hapd->driver->get_scan_results2(hapd->drv_priv);
	return NULL;
}

这里的get_scan_results2指的是,wpa_driver_nl80211_get_scan_results函数。具体赋值同样是在:之前提到的wpa_driver_nl80211_ops 里。

const struct wpa_driver_ops wpa_driver_nl80211_ops = {
	.name = "nl80211",
	.desc = "Linux nl80211/cfg80211",
    ... ...
    .scan2 = driver_nl80211_scan2,
	... ...
	.get_scan_results2 = wpa_driver_nl80211_get_scan_results,
 ... ...
 };

实际被调用的函数是wpa_driver_nl80211_get_scan_results

wpa_driver_nl80211_get_scan_results
struct wpa_scan_results * wpa_driver_nl80211_get_scan_results(void *priv)
{
	struct i802_bss *bss = priv;
	struct wpa_driver_nl80211_data *drv = bss->drv;
	struct wpa_scan_results *res;
	res = nl80211_get_scan_results(drv);//若正常执行完毕,应返回非NULL的res
	if (res)
		wpa_driver_nl80211_check_bss_status(drv, res);
	return res;
}

而wpa_driver_nl80211_get_scan_results函数会调用nl80211_get_scan_results

函数nl80211_get_scan_results里,正常情况下会输出"nl80211: Received scan results … (BSSes)",并调用nl80211_get_noise_for_scan_results去进一步获取信道noise信息到res里。

wpa_driver_nl80211_get_scan_results返回后,返回上层函数hostapd_driver_get_scan_results,再返回到函数ieee80211n_check_scan。

如果返回值res是NULL,则异常,调用hostapd_setup_interface_complete结束。
接着判断当前接口是2.4G接口还是5G接口,分别调用检查函数。若2.4G,就调用ieee80211n_check_40mhz_2g4,检查是否能支持2.4G上运行40MHZ热点。

ieee80211n_check_40mhz_2g4

static int ieee80211n_check_40mhz_2g4(struct hostapd_iface *iface,
				      struct wpa_scan_results *scan_res)
{
	int pri_chan, sec_chan;
	pri_chan = iface->conf->channel;
	sec_chan = pri_chan + iface->conf->secondary_channel * 4;//secondary_channel为正,则说明是primary向上加4的channel。为负,则向下减4。
	return check_40mhz_2g4(iface->current_mode, scan_res, pri_chan,
			       sec_chan);//调用check_40mhz_2g4
}

函数内,算出了primary和secondary channel值,传进函数check_40mhz_2g4

check_40mhz_2g4
int check_40mhz_2g4(struct hostapd_hw_modes *mode,
		    struct wpa_scan_results *scan_res, int pri_chan,
		    int sec_chan)
{
	int pri_freq, sec_freq;
	int affected_start, affected_end;
	size_t i;
	if (!mode || !scan_res || !pri_chan || !sec_chan ||
	    pri_chan == sec_chan)
     //若是20M,则iface->conf->secondary_channel为0,sec_chan==pri_chan,此处退出
		return 0;
	pri_freq = hw_get_freq(mode, pri_chan);//根据channel值,算出对应信道的频率值
	sec_freq = hw_get_freq(mode, sec_chan);
	affected_start = (pri_freq + sec_freq) / 2 - 25;
	affected_end = (pri_freq + sec_freq) / 2 + 25;
	wpa_printf(MSG_DEBUG, "40 MHz affected channel range: [%d,%d] MHz",
		   affected_start, affected_end);
	for (i = 0; i < scan_res->num; i++) {//遍历扫描到的BSS信息
		struct wpa_scan_res *bss = scan_res->res[i];
		int pri = bss->freq;
		int sec = pri;
		struct ieee802_11_elems elems;
		/* Check for overlapping 20 MHz BSS */
		if (check_20mhz_bss(bss, pri_freq, affected_start,
				    affected_end)) {
			wpa_printf(MSG_DEBUG,
				   "Overlapping 20 MHz BSS is found");
			return 0;//如果和20M的其他热点有重叠
		}
		get_pri_sec_chan(bss, &pri_chan, &sec_chan);//获得primary和secondary的信道号
		if (sec_chan) {//算出当前遍历到的BSS的secondary channel的信道频率
			if (sec_chan < pri_chan)
				sec = pri - 20;
			else
				sec = pri + 20;
		}
		if ((pri < affected_start || pri > affected_end) &&
		    (sec < affected_start || sec > affected_end))
			continue; /* not within affected channel range */
		wpa_printf(MSG_DEBUG, "Neighboring BSS: " MACSTR
			   " freq=%d pri=%d sec=%d",
			   MAC2STR(bss->bssid), bss->freq, pri_chan, sec_chan);
		if (sec_chan) {
			if (pri_freq != pri || sec_freq != sec) {//如果扫描到的BSS的primary和secondary不完全一致
				wpa_printf(MSG_DEBUG,
					   "40 MHz pri/sec mismatch with BSS "
					   MACSTR
					   " <%d,%d> (chan=%d%c) vs. <%d,%d>",
					   MAC2STR(bss->bssid),
					   pri, sec, pri_chan,
					   sec > pri ? '+' : '-',
					   pri_freq, sec_freq);
				return 0;//报错
			}
		}
		ieee802_11_parse_elems((u8 *) (bss + 1), bss->ie_len, &elems,
				       0);
		if (elems.ht_capabilities) {
			struct ieee80211_ht_capabilities *ht_cap =
				(struct ieee80211_ht_capabilities *)
				elems.ht_capabilities;

			if (le_to_host16(ht_cap->ht_capabilities_info) &
			    HT_CAP_INFO_40MHZ_INTOLERANT) {
				wpa_printf(MSG_DEBUG,
					   "40 MHz Intolerant is set on channel %d in BSS "
					   MACSTR, pri, MAC2STR(bss->bssid));
				return 0;
			}
		}
	}
	return 1;
}

其中,( pri-freq + sec-freq ) / 2 ± 30是不受影响的。故而受影响的信道频率为,这个值加减5。如果信道频率小于affected_start ,则说明它与这个40M的BSS的覆盖范围并不冲突。affected_end方向同理,若信道频率大于affected_end值,则说明它与这个40M的BSS的覆盖范围并不冲突。
在这里插入图片描述

调用check_20mhz_bss检查是否与某个20M BSS重叠

check_20mhz_bss
static int check_20mhz_bss(struct wpa_scan_res *bss, int pri_freq, int start,
			   int end)
{
	struct ieee802_11_elems elems;
	struct ieee80211_ht_operation *oper;
	if (bss->freq < start || bss->freq > end || bss->freq == pri_freq)
		return 0;//当前BSS并非重叠,返回0
	ieee802_11_parse_elems((u8 *) (bss + 1), bss->ie_len, &elems, 0);//将wpa_scan_res里的信息分析截取到elems理
	if (!elems.ht_capabilities) {//如果当前BSS没有开启HT Capability Info一项
		wpa_printf(MSG_DEBUG, "Found overlapping legacy BSS: "
			   MACSTR " freq=%d", MAC2STR(bss->bssid), bss->freq);
		return 1;//返回1就是说明有冲突
	}
	if (elems.ht_operation) {//如果BSS开启了HT Capability Info选项,并且获取到了它的HT Operation Information
		oper = (struct ieee80211_ht_operation *) elems.ht_operation;
		if (oper->ht_param & HT_INFO_HT_PARAM_SECONDARY_CHNL_OFF_MASK)
			return 0;//这里判定为无冲突是因为这个函数只判断与周围20M热点是否信道重叠,如果与掩码按位与不为0,说明这是个40M热点,退出函数
   /*其中,HT_INFO_HT_PARAM_SECONDARY_CHNL_OFF_MASK及相关定义如下:这个与beacon包中的内容可以对应上,详见下文抓包截图
    #define HT_INFO_HT_PARAM_SECONDARY_CHNL_OFF_MASK	((u8) BIT(0) | BIT(1))
    #define HT_INFO_HT_PARAM_SECONDARY_CHNL_ABOVE		((u8) BIT(0))
    #define HT_INFO_HT_PARAM_SECONDARY_CHNL_BELOW		((u8) BIT(0) | BIT(1))
   */
		wpa_printf(MSG_DEBUG, "Found overlapping 20 MHz HT BSS: "
			   MACSTR " freq=%d", MAC2STR(bss->bssid), bss->freq);
		return 1;//返回1说明有冲突
	}
	return 0;
}

ieee802_11_parse_elems函数中,各项info的解析代码基本类似于以下结构。

switch (id) {
    ... ...
    case WLAN_EID_HT_CAP:   //#define WLAN_EID_HT_CAP 45
			if (elen < sizeof(struct ieee80211_ht_capabilities))
				break;
			elems->ht_capabilities = pos;
			break;
    case WLAN_EID_HT_OPERATION:   //#define WLAN_EID_HT_OPERATION 61
			if (elen < sizeof(struct ieee80211_ht_operation))
				break;
			elems->ht_operation = pos;
			break;
   ... ...
 }

其中,id就对应着抓包中的Element ID。
以omnipeek抓到的某BEACON包为例:
ht capability 部分:
在这里插入图片描述
ht operation 部分:
在这里插入图片描述
这部分对应到代码里,就是elems.ht_operation->ht_param & HT_INFO_HT_PARAM_SECONDARY_CHNL_BELOW == 1(也等同于按位与上HT_INFO_HT_PARAM_SECONDARY_CHNL_OFF_MASK),这个值对应到beacon包里对应Byte后就是它的bit 0和bit 1位置1。07 == 0000 0111。

check_20mhz_bss返回到check_40mhz_2g4里,如果返回值是1说明与其他20M热点有重叠,直接返回。否则,就继续判断。

判断primary和secondary的信道频率是否处于受影响范围内。若与当前遍历到的BSS不影响,则continue进行与下一个BSS的范围比较。
若处于影响范围,则输出当前这个相邻BSS的信息“Neighboring BSS … … "。

接着判断,如果当前遍历的BSS是40M的,并且当前BSS与我们的热点的primary channel和secondary channel不完全一致,则说明范围是有重叠冲突的,输出mismatch信息,返回。
如果没有冲突,解析出当前BSS的ht capabilities info。判断ht_capabilities_info里是否置上了HT_CAP_INFO_40MHZ_INTOLERANT,如果有,则说明当前BSS不支持周围BSS有40M传输。

#define HT_CAP_INFO_40MHZ_INTOLERANT		((u16) BIT(14))

对照着上文的抓包截图,可以看到,第14bit值为0时,omnipeek对应解析是” AP does Not allow use of 40MHz Transmissions In Neighboring BSSs “。

check_40mhz_2g4返回到ieee80211n_check_40mhz_2g4里,再将返回值返回到ieee80211n_check_scan里,允许40M就为1,反之为0。这也即是oper40的值。

函数ieee80211n_check_scan里会调用ieee80211n_allowed_ht40_channel_pair

ieee80211n_allowed_ht40_channel_pair

static int ieee80211n_allowed_ht40_channel_pair(struct hostapd_iface *iface)
{
	int pri_chan, sec_chan;
	if (!iface->conf->secondary_channel)//如果是20M热点则不需检查
		return 1; /* HT40 not used */
	pri_chan = iface->conf->channel;//算出主次channel值
	sec_chan = pri_chan + iface->conf->secondary_channel * 4;
	return allowed_ht40_channel_pair(iface->current_mode, pri_chan,sec_chan);
}

调用下层函数allowed_ht40_channel_pair

allowed_ht40_channel_pair
int allowed_ht40_channel_pair(struct hostapd_hw_modes *mode, int pri_chan,
			      int sec_chan)
{
	int ok, j, first;
	int allowed[] = { 36, 44, 52, 60, 100, 108, 116, 124, 132, 140,
			  149, 157, 184, 192 };//这802.11A模式下需要用到的变量
	size_t k;
	if (pri_chan == sec_chan || !sec_chan)//如果是20M则不需检查
		return 1; /* HT40 not used */
	wpa_printf(MSG_DEBUG,"HT40: control channel: %d  secondary channel: %d",pri_chan, sec_chan);
	/* Verify that HT40 secondary channel is an allowed 20 MHz channel */
	ok = 0;
	for (j = 0; j < mode->num_channels; j++) {//遍历当前接口支持的channel list
		struct hostapd_channel_data *chan = &mode->channels[j];
		if (!(chan->flag & HOSTAPD_CHAN_DISABLED) &&
		    chan->chan == sec_chan) {//若secondary channel在接口支持的列表里,并且该信道并未DISABLE,将OK置1
			ok = 1;//反之,若secondary channel不被当前接口支持或状态DISABLE,则不置1,默认值是0
			break;
		}
	}
	if (!ok) {//如果OK是0,则说明secondary不可用
		wpa_printf(MSG_ERROR, "HT40 secondary channel %d not allowed",
			   sec_chan);
		return 0;
	}
	/*
	 * Verify that HT40 primary,secondary channel pair is allowed per
	 * IEEE 802.11n Annex J. This is only needed for 5 GHz band since
	 * 2.4 GHz rules allow all cases where the secondary channel fits into
	 * the list of allowed channels (already checked above).
	 */
	if (mode->mode != HOSTAPD_MODE_IEEE80211A)//如果当前模式非802.11A,则检查完毕,40M正常返回1
		return 1;
	... ... ... ...//802.11A下的一些检查
	return 1;
}

最终返回到ieee80211n_check_scan里,若返回值是1,说明可以启动40M热点,反之启动20M热点,并调用hostapd_setup_interface_complete结束接口初始化。

本文到此,终于结束了acs的路径流程。

  • 6
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值