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(¶ms, 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], ¶ms) < 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的路径流程。