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_ht40_allowed = 1; int i; const u8 *start = (const u8 *) mgmt; const u8 *data = start + IEEE80211_HDRLEN + 2; 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; if (len < IEEE80211_HDRLEN + 2 + sizeof(*bc_ie)) return; bc_ie = (struct ieee80211_2040_bss_coex_ie *) data; if (bc_ie->element_id != WLAN_EID_20_40_BSS_COEXISTENCE || bc_ie->length < 1) { wpa_printf(MSG_DEBUG, "Unexpected IE (%u,%u) in coex report", bc_ie->element_id, bc_ie->length); return; } if (len < IEEE80211_HDRLEN + 2 + 2 + bc_ie->length) return; data += 2 + bc_ie->length; wpa_printf(MSG_DEBUG, "20/40 BSS Coexistence Information field: 0x%x", bc_ie->coex_param); 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_ht40_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_ht40_allowed = 0; } if (start + len - data >= 3 && data[0] == WLAN_EID_20_40_BSS_INTOLERANT && data[1] >= 1) { u8 ielen = data[1]; if (ielen > start + len - data - 2) return; ic_report = (struct ieee80211_2040_intol_chan_report *) data; wpa_printf(MSG_DEBUG, "20/40 BSS Intolerant Channel Report: Operating Class %u", ic_report->op_class); /* Go through the channel report to find any BSS there in the * affected channel range */ for (i = 0; i < ielen - 1; i++) { u8 chan = ic_report->variable[i]; if (is_40_allowed(iface, chan)) continue; hostapd_logger(hapd, mgmt->sa, HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_DEBUG, "20_40_INTOLERANT channel %d reported", chan); is_ht40_allowed = 0; } } wpa_printf(MSG_DEBUG, "is_ht40_allowed=%d num_sta_ht40_intolerant=%d", is_ht40_allowed, iface->num_sta_ht40_intolerant); if (!is_ht40_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 && iface->conf->obss_interval) { 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); wpa_printf(MSG_DEBUG, "Reschedule HT 20/40 timeout to occur in %u seconds", delay_time); } } }
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_ht40_allowed = 1; int i; const u8 *start = (const u8 *) mgmt; const u8 *data = start + IEEE80211_HDRLEN + 2; struct sta_info *sta; wpa_printf(MSG_DEBUG, "HT: Received 20/40 BSS Coexistence Management frame from " MACSTR, MAC2STR(mgmt->sa)); 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)) { wpa_printf(MSG_DEBUG, "Ignore 20/40 BSS Coexistence Management frame since 40 MHz capability is not enabled"); return; } if (len < IEEE80211_HDRLEN + 2 + sizeof(*bc_ie)) { wpa_printf(MSG_DEBUG, "Ignore too short 20/40 BSS Coexistence Management frame"); return; } /* 20/40 BSS Coexistence element */ bc_ie = (struct ieee80211_2040_bss_coex_ie *) data; if (bc_ie->element_id != WLAN_EID_20_40_BSS_COEXISTENCE || bc_ie->length < 1) { wpa_printf(MSG_DEBUG, "Unexpected IE (%u,%u) in coex report", bc_ie->element_id, bc_ie->length); return; } if (len < IEEE80211_HDRLEN + 2 + 2 + bc_ie->length) { wpa_printf(MSG_DEBUG, "Truncated 20/40 BSS Coexistence element"); return; } data += 2 + bc_ie->length; wpa_printf(MSG_DEBUG, "20/40 BSS Coexistence Information field: 0x%x (%s%s%s%s%s%s)", bc_ie->coex_param, (bc_ie->coex_param & BIT(0)) ? "[InfoReq]" : "", (bc_ie->coex_param & BIT(1)) ? "[40MHzIntolerant]" : "", (bc_ie->coex_param & BIT(2)) ? "[20MHzBSSWidthReq]" : "", (bc_ie->coex_param & BIT(3)) ? "[OBSSScanExemptionReq]" : "", (bc_ie->coex_param & BIT(4)) ? "[OBSSScanExemptionGrant]" : "", (bc_ie->coex_param & (BIT(5) | BIT(6) | BIT(7))) ? "[Reserved]" : ""); if (bc_ie->coex_param & WLAN_20_40_BSS_COEX_20MHZ_WIDTH_REQ) { /* Intra-BSS communication prohibiting 20/40 MHz BSS operation */ sta = ap_get_sta(hapd, mgmt->sa); if (!sta || !(sta->flags & WLAN_STA_ASSOC)) { wpa_printf(MSG_DEBUG, "Ignore intra-BSS 20/40 BSS Coexistence Management frame from not-associated STA"); return; } 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_ht40_allowed = 0; } if (bc_ie->coex_param & WLAN_20_40_BSS_COEX_40MHZ_INTOL) { /* Inter-BSS communication prohibiting 20/40 MHz BSS operation */ hostapd_logger(hapd, mgmt->sa, HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_DEBUG, "40 MHz intolerant bit is set in BSS coexistence information field"); is_ht40_allowed = 0; } /* 20/40 BSS Intolerant Channel Report element (zero or more times) */ while (start + len - data >= 3 && data[0] == WLAN_EID_20_40_BSS_INTOLERANT && data[1] >= 1) { u8 ielen = data[1]; if (ielen > start + len - data - 2) { wpa_printf(MSG_DEBUG, "Truncated 20/40 BSS Intolerant Channel Report element"); return; } ic_report = (struct ieee80211_2040_intol_chan_report *) data; wpa_printf(MSG_DEBUG, "20/40 BSS Intolerant Channel Report: Operating Class %u", ic_report->op_class); /* Go through the channel report to find any BSS there in the * affected channel range */ for (i = 0; i < ielen - 1; i++) { u8 chan = ic_report->variable[i]; if (chan == iface->conf->channel) continue; /* matching own primary channel */ if (is_40_allowed(iface, chan)) continue; /* not within affected channels */ hostapd_logger(hapd, mgmt->sa, HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_DEBUG, "20_40_INTOLERANT channel %d reported", chan); is_ht40_allowed = 0; } data += 2 + ielen; } wpa_printf(MSG_DEBUG, "is_ht40_allowed=%d num_sta_ht40_intolerant=%d", is_ht40_allowed, iface->num_sta_ht40_intolerant); if (!is_ht40_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 && iface->conf->obss_interval) { 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); wpa_printf(MSG_DEBUG, "Reschedule HT 20/40 timeout to occur in %u seconds", delay_time); } } }