X-Git-Url: http://www.project-moonshot.org/gitweb/?a=blobdiff_plain;ds=sidebyside;f=libeap%2Fsrc%2Fap%2Fieee802_11_ht.c;h=11fde2a26394607e32f9d6f02eb7750474f0a9ff;hb=4f319dde67a76fe0aaf33f6d2788968012584ada;hp=7541b83816d029d3e5ef56ad424c55d7f48bd216;hpb=ed09b5e64dd485851310307979d5eed14678087b;p=mech_eap.git diff --git a/libeap/src/ap/ieee802_11_ht.c b/libeap/src/ap/ieee802_11_ht.c index 7541b83..11fde2a 100644 --- a/libeap/src/ap/ieee802_11_ht.c +++ b/libeap/src/ap/ieee802_11_ht.c @@ -3,26 +3,22 @@ * Copyright (c) 2002-2009, Jouni Malinen * Copyright (c) 2007-2008, Intel Corporation * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "utils/includes.h" #include "utils/common.h" +#include "utils/eloop.h" #include "common/ieee802_11_defs.h" -#include "drivers/driver.h" #include "hostapd.h" #include "ap_config.h" #include "sta_info.h" #include "beacon.h" #include "ieee802_11.h" +#include "hw_features.h" +#include "ap_drv_ops.h" u8 * hostapd_eid_ht_capabilities(struct hostapd_data *hapd, u8 *eid) @@ -30,7 +26,8 @@ u8 * hostapd_eid_ht_capabilities(struct hostapd_data *hapd, u8 *eid) struct ieee80211_ht_capabilities *cap; u8 *pos = eid; - if (!hapd->iconf->ieee80211n || !hapd->iface->current_mode) + if (!hapd->iconf->ieee80211n || !hapd->iface->current_mode || + hapd->conf->disable_11n) return eid; *pos++ = WLAN_EID_HT_CAP; @@ -49,6 +46,35 @@ u8 * hostapd_eid_ht_capabilities(struct hostapd_data *hapd, u8 *eid) pos += sizeof(*cap); + if (hapd->iconf->obss_interval) { + struct ieee80211_obss_scan_parameters *scan_params; + + *pos++ = WLAN_EID_OVERLAPPING_BSS_SCAN_PARAMS; + *pos++ = sizeof(*scan_params); + + scan_params = (struct ieee80211_obss_scan_parameters *) pos; + os_memset(scan_params, 0, sizeof(*scan_params)); + scan_params->width_trigger_scan_interval = + host_to_le16(hapd->iconf->obss_interval); + + /* Fill in default values for remaining parameters + * (IEEE Std 802.11-2012, 8.4.2.61 and MIB defval) */ + scan_params->scan_passive_dwell = + host_to_le16(20); + scan_params->scan_active_dwell = + host_to_le16(10); + scan_params->scan_passive_total_per_channel = + host_to_le16(200); + scan_params->scan_active_total_per_channel = + host_to_le16(20); + scan_params->channel_transition_delay_factor = + host_to_le16(5); + scan_params->scan_activity_threshold = + host_to_le16(25); + + pos += sizeof(*scan_params); + } + return pos; } @@ -58,7 +84,7 @@ u8 * hostapd_eid_ht_operation(struct hostapd_data *hapd, u8 *eid) struct ieee80211_ht_operation *oper; u8 *pos = eid; - if (!hapd->iconf->ieee80211n) + if (!hapd->iconf->ieee80211n || hapd->conf->disable_11n) return eid; *pos++ = WLAN_EID_HT_OPERATION; @@ -67,14 +93,14 @@ u8 * hostapd_eid_ht_operation(struct hostapd_data *hapd, u8 *eid) oper = (struct ieee80211_ht_operation *) pos; os_memset(oper, 0, sizeof(*oper)); - oper->control_chan = hapd->iconf->channel; + oper->primary_chan = hapd->iconf->channel; oper->operation_mode = host_to_le16(hapd->iface->ht_op_mode); if (hapd->iconf->secondary_channel == 1) oper->ht_param |= HT_INFO_HT_PARAM_SECONDARY_CHNL_ABOVE | - HT_INFO_HT_PARAM_REC_TRANS_CHNL_WIDTH; + HT_INFO_HT_PARAM_STA_CHNL_WIDTH; if (hapd->iconf->secondary_channel == -1) oper->ht_param |= HT_INFO_HT_PARAM_SECONDARY_CHNL_BELOW | - HT_INFO_HT_PARAM_REC_TRANS_CHNL_WIDTH; + HT_INFO_HT_PARAM_STA_CHNL_WIDTH; pos += sizeof(*oper); @@ -92,7 +118,6 @@ Set to 1 (HT non-member protection) if there may be non-HT STAs Set to 2 if only HT STAs are associated in BSS, however and at least one 20 MHz HT STA is associated Set to 3 (HT mixed mode) when one or more non-HT STAs are associated - (currently non-GF HT station is considered as non-HT STA also) */ int hostapd_ht_operation_update(struct hostapd_iface *iface) { @@ -105,50 +130,40 @@ int hostapd_ht_operation_update(struct hostapd_iface *iface) wpa_printf(MSG_DEBUG, "%s current operation mode=0x%X", __func__, iface->ht_op_mode); - if (!(iface->ht_op_mode & HT_INFO_OPERATION_MODE_NON_GF_DEVS_PRESENT) + if (!(iface->ht_op_mode & HT_OPER_OP_MODE_NON_GF_HT_STAS_PRESENT) && iface->num_sta_ht_no_gf) { - iface->ht_op_mode |= - HT_INFO_OPERATION_MODE_NON_GF_DEVS_PRESENT; + iface->ht_op_mode |= HT_OPER_OP_MODE_NON_GF_HT_STAS_PRESENT; op_mode_changes++; } else if ((iface->ht_op_mode & - HT_INFO_OPERATION_MODE_NON_GF_DEVS_PRESENT) && + HT_OPER_OP_MODE_NON_GF_HT_STAS_PRESENT) && iface->num_sta_ht_no_gf == 0) { - iface->ht_op_mode &= - ~HT_INFO_OPERATION_MODE_NON_GF_DEVS_PRESENT; + iface->ht_op_mode &= ~HT_OPER_OP_MODE_NON_GF_HT_STAS_PRESENT; op_mode_changes++; } - if (!(iface->ht_op_mode & HT_INFO_OPERATION_MODE_NON_HT_STA_PRESENT) && + if (!(iface->ht_op_mode & HT_OPER_OP_MODE_OBSS_NON_HT_STAS_PRESENT) && (iface->num_sta_no_ht || iface->olbc_ht)) { - iface->ht_op_mode |= HT_INFO_OPERATION_MODE_NON_HT_STA_PRESENT; + iface->ht_op_mode |= HT_OPER_OP_MODE_OBSS_NON_HT_STAS_PRESENT; op_mode_changes++; } else if ((iface->ht_op_mode & - HT_INFO_OPERATION_MODE_NON_HT_STA_PRESENT) && + HT_OPER_OP_MODE_OBSS_NON_HT_STAS_PRESENT) && (iface->num_sta_no_ht == 0 && !iface->olbc_ht)) { - iface->ht_op_mode &= - ~HT_INFO_OPERATION_MODE_NON_HT_STA_PRESENT; + iface->ht_op_mode &= ~HT_OPER_OP_MODE_OBSS_NON_HT_STAS_PRESENT; op_mode_changes++; } - /* Note: currently we switch to the MIXED op mode if HT non-greenfield - * station is associated. Probably it's a theoretical case, since - * it looks like all known HT STAs support greenfield. - */ - new_op_mode = 0; - if (iface->num_sta_no_ht || - (iface->ht_op_mode & HT_INFO_OPERATION_MODE_NON_GF_DEVS_PRESENT)) - new_op_mode = OP_MODE_MIXED; - else if ((iface->conf->ht_capab & HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET) - && iface->num_sta_ht_20mhz) - new_op_mode = OP_MODE_20MHZ_HT_STA_ASSOCED; + if (iface->num_sta_no_ht) + new_op_mode = HT_PROT_NON_HT_MIXED; + else if (iface->conf->secondary_channel && iface->num_sta_ht_20mhz) + new_op_mode = HT_PROT_20MHZ_PROTECTION; else if (iface->olbc_ht) - new_op_mode = OP_MODE_MAY_BE_LEGACY_STAS; + new_op_mode = HT_PROT_NONMEMBER_PROTECTION; else - new_op_mode = OP_MODE_PURE; + new_op_mode = HT_PROT_NO_PROTECTION; - cur_op_mode = iface->ht_op_mode & HT_INFO_OPERATION_MODE_OP_MODE_MASK; + cur_op_mode = iface->ht_op_mode & HT_OPER_OP_MODE_HT_PROT_MASK; if (cur_op_mode != new_op_mode) { - iface->ht_op_mode &= ~HT_INFO_OPERATION_MODE_OP_MODE_MASK; + iface->ht_op_mode &= ~HT_OPER_OP_MODE_HT_PROT_MASK; iface->ht_op_mode |= new_op_mode; op_mode_changes++; } @@ -160,11 +175,150 @@ int hostapd_ht_operation_update(struct hostapd_iface *iface) } -u16 copy_sta_ht_capab(struct sta_info *sta, const u8 *ht_capab, - size_t ht_capab_len) +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_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); + } + } +} + + +u16 copy_sta_ht_capab(struct hostapd_data *hapd, struct sta_info *sta, + const u8 *ht_capab) +{ + /* + * Disable HT caps for STAs associated to no-HT BSSes, or for stations + * that did not specify a valid WMM IE in the (Re)Association Request + * frame. + */ if (!ht_capab || - ht_capab_len < sizeof(struct ieee80211_ht_capabilities)) { + !(sta->flags & WLAN_STA_WMM) || hapd->conf->disable_11n) { sta->flags &= ~WLAN_STA_HT; os_free(sta->ht_capabilities); sta->ht_capabilities = NULL; @@ -186,6 +340,52 @@ u16 copy_sta_ht_capab(struct sta_info *sta, const u8 *ht_capab, } +void ht40_intolerant_add(struct hostapd_iface *iface, struct sta_info *sta) +{ + if (iface->current_mode->mode != HOSTAPD_MODE_IEEE80211G) + return; + + wpa_printf(MSG_INFO, "HT: Forty MHz Intolerant is set by STA " MACSTR + " in Association Request", MAC2STR(sta->addr)); + + if (sta->ht40_intolerant_set) + return; + + sta->ht40_intolerant_set = 1; + iface->num_sta_ht40_intolerant++; + eloop_cancel_timeout(ap_ht2040_timeout, iface, NULL); + + if (iface->conf->secondary_channel && + (iface->drv_flags & WPA_DRIVER_FLAGS_HT_2040_COEX)) { + iface->conf->secondary_channel = 0; + ieee802_11_set_beacons(iface); + } +} + + +void ht40_intolerant_remove(struct hostapd_iface *iface, struct sta_info *sta) +{ + if (!sta->ht40_intolerant_set) + return; + + sta->ht40_intolerant_set = 0; + iface->num_sta_ht40_intolerant--; + + if (iface->num_sta_ht40_intolerant == 0 && + (iface->conf->ht_capab & HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET) && + (iface->drv_flags & WPA_DRIVER_FLAGS_HT_2040_COEX)) { + unsigned int delay_time = OVERLAPPING_BSS_TRANS_DELAY_FACTOR * + iface->conf->obss_interval; + wpa_printf(MSG_DEBUG, + "HT: Start 20->40 MHz transition timer (%d seconds)", + delay_time); + eloop_cancel_timeout(ap_ht2040_timeout, iface, NULL); + eloop_register_timeout(delay_time, 0, ap_ht2040_timeout, + iface, NULL); + } +} + + static void update_sta_ht(struct hostapd_data *hapd, struct sta_info *sta) { u16 ht_capab; @@ -213,6 +413,9 @@ static void update_sta_ht(struct hostapd_data *hapd, struct sta_info *sta) __func__, MAC2STR(sta->addr), hapd->iface->num_sta_ht_20mhz); } + + if (ht_capab & HT_CAP_INFO_40MHZ_INTOLERANT) + ht40_intolerant_add(hapd->iface, sta); } @@ -253,8 +456,14 @@ void hostapd_get_ht_capab(struct hostapd_data *hapd, return; os_memcpy(neg_ht_cap, ht_cap, sizeof(*neg_ht_cap)); cap = le_to_host16(neg_ht_cap->ht_capabilities_info); - cap &= hapd->iconf->ht_capab; - cap |= (hapd->iconf->ht_capab & HT_CAP_INFO_SMPS_DISABLED); + + /* + * Mask out HT features we don't support, but don't overwrite + * non-symmetric features like STBC and SMPS. Just because + * we're not in dynamic SMPS mode the STA might still be. + */ + cap &= (hapd->iconf->ht_capab | HT_CAP_INFO_RX_STBC_MASK | + HT_CAP_INFO_TX_STBC | HT_CAP_INFO_SMPS_MASK); /* * STBC needs to be handled specially @@ -268,3 +477,14 @@ void hostapd_get_ht_capab(struct hostapd_data *hapd, neg_ht_cap->ht_capabilities_info = host_to_le16(cap); } + + +void ap_ht2040_timeout(void *eloop_data, void *user_data) +{ + struct hostapd_iface *iface = eloop_data; + + wpa_printf(MSG_INFO, "Switching to 40 MHz operation"); + + iface->conf->secondary_channel = iface->secondary_ch; + ieee802_11_set_beacons(iface); +}