diff --git a/src/ap/ieee802_11.c b/src/ap/ieee802_11.c index 14fb56758..0430c701d 100644 --- a/src/ap/ieee802_11.c +++ b/src/ap/ieee802_11.c @@ -1647,6 +1647,15 @@ static int handle_action(struct hostapd_data *hapd, #endif /* CONFIG_WNM */ case WLAN_ACTION_PUBLIC: case WLAN_ACTION_PROTECTED_DUAL: +#ifdef CONFIG_IEEE80211N + if (mgmt->u.action.u.public_action.action == + WLAN_PA_20_40_BSS_COEX) { + wpa_printf(MSG_DEBUG, + "HT20/40 coex mgmt frame received from STA " + MACSTR, MAC2STR(mgmt->sa)); + hostapd_2040_coex_action(hapd, mgmt, len); + } +#endif /* CONFIG_IEEE80211N */ if (hapd->public_action_cb) { hapd->public_action_cb(hapd->public_action_cb_ctx, (u8 *) mgmt, len, diff --git a/src/ap/ieee802_11.h b/src/ap/ieee802_11.h index 5b9437151..cf0d3f2bf 100644 --- a/src/ap/ieee802_11.h +++ b/src/ap/ieee802_11.h @@ -14,11 +14,14 @@ struct hostapd_data; struct sta_info; struct hostapd_frame_info; struct ieee80211_ht_capabilities; +struct ieee80211_mgmt; int ieee802_11_mgmt(struct hostapd_data *hapd, const u8 *buf, size_t len, struct hostapd_frame_info *fi); void ieee802_11_mgmt_cb(struct hostapd_data *hapd, const u8 *buf, size_t len, u16 stype, int ok); +void hostapd_2040_coex_action(struct hostapd_data *hapd, + const struct ieee80211_mgmt *mgmt, size_t len); #ifdef NEED_AP_MLME int ieee802_11_get_mib(struct hostapd_data *hapd, char *buf, size_t buflen); int ieee802_11_get_mib_sta(struct hostapd_data *hapd, struct sta_info *sta, diff --git a/src/ap/ieee802_11_ht.c b/src/ap/ieee802_11_ht.c index 38caaed27..c0a7cd414 100644 --- a/src/ap/ieee802_11_ht.c +++ b/src/ap/ieee802_11_ht.c @@ -175,6 +175,117 @@ int hostapd_ht_operation_update(struct hostapd_iface *iface) } +static int is_40_allowed(struct hostapd_iface *iface, int channel) +{ + int pri_freq, sec_freq; + int affected_start, affected_end; + int pri = 2407 + 5 * channel; + + if (iface->current_mode->mode != HOSTAPD_MODE_IEEE80211G) + return 1; + + pri_freq = hostapd_hw_get_freq(iface->bss[0], iface->conf->channel); + + if (iface->conf->secondary_channel > 0) + sec_freq = pri_freq + 20; + else + sec_freq = pri_freq - 20; + + affected_start = (pri_freq + sec_freq) / 2 - 25; + affected_end = (pri_freq + sec_freq) / 2 + 25; + if ((pri < affected_start || pri > affected_end)) + return 1; /* not within affected channel range */ + + wpa_printf(MSG_ERROR, "40 MHz affected channel range: [%d,%d] MHz", + affected_start, affected_end); + wpa_printf(MSG_ERROR, "Neighboring BSS: freq=%d", pri); + return 0; +} + + +void hostapd_2040_coex_action(struct hostapd_data *hapd, + const struct ieee80211_mgmt *mgmt, size_t len) +{ + struct hostapd_iface *iface = hapd->iface; + struct ieee80211_2040_bss_coex_ie *bc_ie; + struct ieee80211_2040_intol_chan_report *ic_report; + int is_ht_allowed = 1; + int i; + const u8 *data = (const u8 *) &mgmt->u.action.u.public_action.action; + size_t hdr_len; + + hostapd_logger(hapd, mgmt->sa, HOSTAPD_MODULE_IEEE80211, + HOSTAPD_LEVEL_DEBUG, "hostapd_public_action - action=%d", + mgmt->u.action.u.public_action.action); + + if (!(iface->conf->ht_capab & HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET)) + return; + + hdr_len = data - (u8 *) mgmt; + if (hdr_len > len) + return; + data++; + + bc_ie = (struct ieee80211_2040_bss_coex_ie *) &data[0]; + ic_report = (struct ieee80211_2040_intol_chan_report *) + (&data[0] + sizeof(*bc_ie)); + + if (bc_ie->coex_param & WLAN_20_40_BSS_COEX_20MHZ_WIDTH_REQ) { + hostapd_logger(hapd, mgmt->sa, + HOSTAPD_MODULE_IEEE80211, + HOSTAPD_LEVEL_DEBUG, + "20 MHz BSS width request bit is set in BSS coexistence information field"); + is_ht_allowed = 0; + } + + if (bc_ie->coex_param & WLAN_20_40_BSS_COEX_40MHZ_INTOL) { + hostapd_logger(hapd, mgmt->sa, + HOSTAPD_MODULE_IEEE80211, + HOSTAPD_LEVEL_DEBUG, + "40 MHz intolerant bit is set in BSS coexistence information field"); + is_ht_allowed = 0; + } + + if (ic_report && + (ic_report->element_id == WLAN_EID_20_40_BSS_INTOLERANT)) { + /* Go through the channel report to find any BSS there in the + * affected channel range */ + for (i = 0; i < ic_report->length - 1; i++) { + if (is_40_allowed(iface, ic_report->variable[i])) + continue; + hostapd_logger(hapd, mgmt->sa, + HOSTAPD_MODULE_IEEE80211, + HOSTAPD_LEVEL_DEBUG, + "20_40_INTOLERANT channel %d reported", + ic_report->variable[i]); + is_ht_allowed = 0; + break; + } + } + + if (!is_ht_allowed && + (iface->drv_flags & WPA_DRIVER_FLAGS_HT_2040_COEX)) { + if (iface->conf->secondary_channel) { + hostapd_logger(hapd, mgmt->sa, + HOSTAPD_MODULE_IEEE80211, + HOSTAPD_LEVEL_INFO, + "Switching to 20 MHz operation"); + iface->conf->secondary_channel = 0; + ieee802_11_set_beacons(iface); + } + if (!iface->num_sta_ht40_intolerant) { + unsigned int delay_time; + delay_time = OVERLAPPING_BSS_TRANS_DELAY_FACTOR * + iface->conf->obss_interval; + eloop_cancel_timeout(ap_ht2040_timeout, hapd->iface, + NULL); + eloop_register_timeout(delay_time, 0, ap_ht2040_timeout, + hapd->iface, NULL); + } + } +} + + u16 copy_sta_ht_capab(struct hostapd_data *hapd, struct sta_info *sta, const u8 *ht_capab, size_t ht_capab_len) {