diff --git a/hostapd/Makefile b/hostapd/Makefile index 1e7390620..6ba505d71 100644 --- a/hostapd/Makefile +++ b/hostapd/Makefile @@ -95,6 +95,10 @@ OBJS += wpa_ft.o NEED_SHA256=y endif +ifdef CONFIG_IEEE80211N +CFLAGS += -DCONFIG_IEEE80211N +endif + ifdef CONFIG_DRIVER_HOSTAP CFLAGS += -DCONFIG_DRIVER_HOSTAP OBJS += driver_hostap.o diff --git a/hostapd/ap.h b/hostapd/ap.h index 383ecaf14..5db4f25c7 100644 --- a/hostapd/ap.h +++ b/hostapd/ap.h @@ -15,6 +15,10 @@ #ifndef AP_H #define AP_H +#ifdef CONFIG_IEEE80211N +#include "ieee802_11_defs.h" +#endif /* CONFIG_IEEE80211N */ + /* STA flags */ #define WLAN_STA_AUTH BIT(0) #define WLAN_STA_ASSOC BIT(1) @@ -27,6 +31,7 @@ #define WLAN_STA_PREAUTH BIT(8) #define WLAN_STA_WME BIT(9) #define WLAN_STA_MFP BIT(10) +#define WLAN_STA_HT BIT(11) #define WLAN_STA_NONERP BIT(31) /* Maximum number of supported rates (from both Supported Rates and Extended @@ -83,6 +88,10 @@ struct sta_info { struct hostapd_ssid *ssid_probe; /* SSID selection based on ProbeReq */ int vlan_id; + +#ifdef CONFIG_IEEE80211N + struct ht_cap_ie ht_capabilities; /* IEEE 802.11n capabilities */ +#endif /* CONFIG_IEEE80211N */ }; diff --git a/hostapd/ap_list.c b/hostapd/ap_list.c index 5f5b5d4f5..b7082d525 100644 --- a/hostapd/ap_list.c +++ b/hostapd/ap_list.c @@ -107,6 +107,15 @@ static int ap_list_beacon_olbc(struct hostapd_iface *iface, struct ap_info *ap) } +#ifdef CONFIG_IEEE80211N +static int ap_list_beacon_olbc_ht(struct hostapd_iface *iface, + struct ap_info *ap) +{ + return !ap->ht_support; +} +#endif /* CONFIG_IEEE80211N */ + + struct ap_info * ap_get_ap(struct hostapd_iface *iface, u8 *ap) { struct ap_info *s; @@ -282,6 +291,7 @@ void ap_list_process_beacon(struct hostapd_iface *iface, struct ap_info *ap; int new_ap = 0; size_t len; + int set_beacon = 0; if (iface->conf->ap_table_max_size < 1) return; @@ -338,6 +348,11 @@ void ap_list_process_beacon(struct hostapd_iface *iface, else if (fi) ap->channel = fi->channel; + if (elems->ht_capabilities) + ap->ht_support = 1; + else + ap->ht_support = 0; + ap->num_beacons++; time(&ap->last_beacon); if (fi) { @@ -358,12 +373,24 @@ void ap_list_process_beacon(struct hostapd_iface *iface, if (!iface->olbc && ap_list_beacon_olbc(iface, ap)) { - struct hostapd_data *hapd = iface->bss[0]; iface->olbc = 1; wpa_printf(MSG_DEBUG, "OLBC AP detected: " MACSTR " - enable " "protection", MAC2STR(ap->addr)); - ieee802_11_set_beacons(hapd->iface); + set_beacon++; } + +#ifdef CONFIG_IEEE80211N + if (!iface->olbc_ht && ap_list_beacon_olbc_ht(iface, ap)) { + iface->olbc_ht = 1; + hostapd_ht_operation_update(iface); + wpa_printf(MSG_DEBUG, "OLBC HT AP detected: " MACSTR + " - enable protection", MAC2STR(ap->addr)); + set_beacon++; + } +#endif /* CONFIG_IEEE80211N */ + + if (set_beacon) + ieee802_11_set_beacons(iface); } @@ -372,6 +399,7 @@ static void ap_list_timer(void *eloop_ctx, void *timeout_ctx) struct hostapd_iface *iface = eloop_ctx; time_t now; struct ap_info *ap; + int set_beacon = 0; eloop_register_timeout(10, 0, ap_list_timer, iface, NULL); @@ -395,23 +423,37 @@ static void ap_list_timer(void *eloop_ctx, void *timeout_ctx) ap_free_ap(iface, ap); } - if (iface->olbc) { + if (iface->olbc || iface->olbc_ht) { int olbc = 0; + int olbc_ht = 0; + ap = iface->ap_list; - while (ap) { - if (ap_list_beacon_olbc(iface, ap)) { + while (ap && (olbc == 0 || olbc_ht == 0)) { + if (ap_list_beacon_olbc(iface, ap)) olbc = 1; - break; - } +#ifdef CONFIG_IEEE80211N + if (ap_list_beacon_olbc_ht(iface, ap)) + olbc_ht = 1; +#endif /* CONFIG_IEEE80211N */ ap = ap->next; } - if (!olbc) { - struct hostapd_data *hapd = iface->bss[0]; + if (!olbc && iface->olbc) { wpa_printf(MSG_DEBUG, "OLBC not detected anymore"); iface->olbc = 0; - ieee802_11_set_beacons(hapd->iface); + set_beacon++; } +#ifdef CONFIG_IEEE80211N + if (!olbc_ht && iface->olbc_ht) { + wpa_printf(MSG_DEBUG, "OLBC HT not detected anymore"); + iface->olbc_ht = 0; + hostapd_ht_operation_update(iface); + set_beacon++; + } +#endif /* CONFIG_IEEE80211N */ } + + if (set_beacon) + ieee802_11_set_beacons(iface); } diff --git a/hostapd/ap_list.h b/hostapd/ap_list.h index 668d90963..c5db8b866 100644 --- a/hostapd/ap_list.h +++ b/hostapd/ap_list.h @@ -43,6 +43,8 @@ struct ap_info { int datarate; /* in 100 kbps */ int ssi_signal; + int ht_support; + unsigned int num_beacons; /* number of beacon frames received */ time_t last_beacon; diff --git a/hostapd/beacon.c b/hostapd/beacon.c index d00555980..1a221d730 100644 --- a/hostapd/beacon.c +++ b/hostapd/beacon.c @@ -285,6 +285,13 @@ void handle_probe_req(struct hostapd_data *hapd, struct ieee80211_mgmt *mgmt, if (hapd->conf->wme_enabled) pos = hostapd_eid_wme(hapd, pos); +#ifdef CONFIG_IEEE80211N + if (hapd->conf->ieee80211n) { + pos = hostapd_eid_ht_capabilities_info(hapd, pos); + pos = hostapd_eid_ht_operation(hapd, pos); + } +#endif /* CONFIG_IEEE80211N */ + if (hostapd_send_mgmt_frame(hapd, resp, pos - (u8 *) resp, 0) < 0) perror("handle_probe_req: send"); @@ -376,6 +383,25 @@ void ieee802_11_set_beacon(struct hostapd_data *hapd) if (hapd->conf->wme_enabled) tailpos = hostapd_eid_wme(hapd, tailpos); +#ifdef CONFIG_IEEE80211N + if (hapd->conf->ieee80211n) { + tailpos = hostapd_eid_ht_capabilities_info(hapd, tailpos); + tailpos = hostapd_eid_ht_operation(hapd, tailpos); + + if (hostapd_set_ht_capability( + hapd->conf->iface, hapd, + &hapd->conf->ht_capabilities.data)) { + wpa_printf(MSG_ERROR, "Could not set HT capabilities " + "for kernel driver"); + } + if (hostapd_set_ht_operation( + hapd->conf->iface, hapd, + &hapd->conf->ht_operation.data)) + wpa_printf(MSG_ERROR, "Could not set HT operation for " + "kernel driver"); + } +#endif /* CONFIG_IEEE80211N */ + tail_len = tailpos > tail ? tailpos - tail : 0; if (hostapd_set_beacon(hapd->conf->iface, hapd, (u8 *) head, head_len, diff --git a/hostapd/config.c b/hostapd/config.c index 58fdaefa0..72461c0d5 100644 --- a/hostapd/config.c +++ b/hostapd/config.c @@ -183,6 +183,54 @@ static void hostapd_config_defaults_bss(struct hostapd_bss_config *bss) } +#ifdef CONFIG_IEEE80211N +static int hostapd_config_defaults_bss_80211n(struct hostapd_bss_config *bss) +{ + u16 capabilities_info = 0; + u16 operation_mode = 0; + + if (bss == NULL) + return -1; + + /* add default values to HT capabilities parameters */ + os_memset(&bss->ht_capabilities, 0, sizeof(struct ht_cap_ie)); + bss->ht_capabilities.id = WLAN_EID_HT_CAP; + bss->ht_capabilities.length = HT_CAPABILITIES_LEN; + +#if 0 /* FIX: remove? was commented out */ + bss->ht_capabilities.mac_ht_param_info.max_rx_ampdu_factor = + MAX_RX_AMPDU_FACTOR_64KB; +#endif + SET_2BIT_U8(&bss->ht_capabilities.data.mac_ht_params_info, + MAC_HT_PARAM_INFO_MAX_RX_AMPDU_FACTOR_OFFSET, + MAX_RX_AMPDU_FACTOR_64KB); + + SET_2BIT_LE16(&capabilities_info, + HT_CAP_INFO_MIMO_PWR_SAVE_OFFSET, + MIMO_PWR_NO_LIMIT_ON_MIMO_SEQS); + + capabilities_info |= HT_CAP_INFO_GREEN_FIELD; + + bss->ht_capabilities.data.capabilities_info = + host_to_le16(capabilities_info); + + bss->ht_capabilities.data.supported_mcs_set[0] = 0xff; + bss->ht_capabilities.data.supported_mcs_set[1] = 0xff; + + /* add default values to HT operation parameters */ + os_memset(&bss->ht_operation, 0, sizeof(struct ht_operation_ie)); + bss->ht_operation.id = WLAN_EID_HT_OPERATION; + bss->ht_operation.length = HT_OPERATION_LEN; + SET_2BIT_LE16(&operation_mode, + HT_INFO_OPERATION_MODE_OP_MODE_OFFSET, + OP_MODE_PURE); + bss->ht_operation.data.operation_mode = host_to_le16(operation_mode); + + return 0; +} +#endif /* CONFIG_IEEE80211N */ + + static struct hostapd_config * hostapd_config_defaults(void) { struct hostapd_config *conf; @@ -244,6 +292,10 @@ static struct hostapd_config * hostapd_config_defaults(void) conf->wme_ac_params[2] = ac_vi; conf->wme_ac_params[3] = ac_vo; +#ifdef CONFIG_IEEE80211N + hostapd_config_defaults_bss_80211n(bss); +#endif /* CONFIG_IEEE80211N */ + return conf; } @@ -1933,6 +1985,10 @@ struct hostapd_config * hostapd_config_read(const char *fname) } else if (os_strcmp(buf, "ieee80211w") == 0) { bss->ieee80211w = atoi(pos); #endif /* CONFIG_IEEE80211W */ +#ifdef CONFIG_IEEE80211N + } else if (os_strcmp(buf, "ieee80211n") == 0) { + bss->ieee80211n = atoi(pos); +#endif /* CONFIG_IEEE80211N */ } else if (os_strcmp(buf, "max_listen_interval") == 0) { bss->max_listen_interval = atoi(pos); } else if (os_strcmp(buf, "okc") == 0) { diff --git a/hostapd/config.h b/hostapd/config.h index 3f740bdd0..d1d15df8e 100644 --- a/hostapd/config.h +++ b/hostapd/config.h @@ -18,6 +18,9 @@ #include "defs.h" #include "ip_addr.h" #include "wpa_common.h" +#ifdef CONFIG_IEEE80211N +#include "ieee802_11_defs.h" +#endif /* CONFIG_IEEE80211N */ #ifndef IFNAMSIZ #define IFNAMSIZ 16 @@ -272,6 +275,16 @@ struct hostapd_bss_config { u16 max_listen_interval; int okc; /* Opportunistic Key Caching */ + +#ifdef CONFIG_IEEE80211N + int ieee80211n; + /* TODO: these structures should not really be used here; move to + * struct hostapd_data or struct hostapd_iface and just include the + * needed values here for generating IEs elsewhere */ + struct ht_cap_ie ht_capabilities; + struct ht_operation_ie ht_operation; + int ht_op_mode_fixed; +#endif /* CONFIG_IEEE80211N */ }; diff --git a/hostapd/driver.h b/hostapd/driver.h index f76d2d432..2b323a181 100644 --- a/hostapd/driver.h +++ b/hostapd/driver.h @@ -15,6 +15,17 @@ #ifndef DRIVER_H #define DRIVER_H +struct hostapd_sta_add_params { + const u8 *addr; + u16 aid; + u16 capability; + const u8 *supp_rates; + size_t supp_rates_len; + int flags; + u16 listen_interval; + const struct ht_cap_ie *ht_capabilities; +}; + enum hostapd_driver_if_type { HOSTAPD_IF_VLAN, HOSTAPD_IF_WDS }; @@ -78,9 +89,12 @@ struct wpa_driver_ops { int (*send_mgmt_frame)(void *priv, const void *msg, size_t len, int flags); int (*set_assoc_ap)(void *priv, const u8 *addr); + /* note: sta_add() is deprecated; use sta_add2() instead */ int (*sta_add)(const char *ifname, void *priv, const u8 *addr, u16 aid, u16 capability, u8 *supp_rates, size_t supp_rates_len, int flags, u16 listen_interval); + int (*sta_add2)(const char *ifname, void *priv, + struct hostapd_sta_add_params *params); int (*get_inact_sec)(void *priv, const u8 *addr); int (*sta_clear_stats)(void *priv, const u8 *addr); @@ -163,6 +177,11 @@ struct wpa_driver_ops { int (*set_radius_acl_auth)(void *priv, const u8 *mac, int accepted, u32 session_timeout); int (*set_radius_acl_expire)(void *priv, const u8 *mac); + + int (*set_ht_capability)(const char *ifname, void *priv, + const u8 *data, size_t data_len); + int (*set_ht_operation)(const char *ifname, void *priv, + const u8 *data, size_t data_len); }; static inline void * @@ -362,13 +381,32 @@ hostapd_set_countermeasures(struct hostapd_data *hapd, int enabled) static inline int hostapd_sta_add(const char *ifname, struct hostapd_data *hapd, const u8 *addr, - u16 aid, u16 capability, u8 *supp_rates, size_t supp_rates_len, - int flags, u16 listen_interval) + u16 aid, u16 capability, const u8 *supp_rates, + size_t supp_rates_len, int flags, u16 listen_interval, + const struct ht_cap_ie *ht_capabilities) { - if (hapd->driver == NULL || hapd->driver->sta_add == NULL) + if (hapd->driver == NULL) + return 0; + + if (hapd->driver->sta_add2) { + struct hostapd_sta_add_params params; + os_memset(¶ms, 0, sizeof(params)); + params.addr = addr; + params.aid = aid; + params.capability = capability; + params.supp_rates = supp_rates; + params.supp_rates_len = supp_rates_len; + params.flags = flags; + params.listen_interval = listen_interval; + params.ht_capabilities = ht_capabilities; + return hapd->driver->sta_add2(ifname, hapd->drv_priv, ¶ms); + } + + if (hapd->driver->sta_add == NULL) return 0; return hapd->driver->sta_add(ifname, hapd->drv_priv, addr, aid, - capability, supp_rates, supp_rates_len, + capability, (u8 *) supp_rates, + supp_rates_len, flags, listen_interval); } @@ -701,4 +739,30 @@ hostapd_set_radius_acl_expire(struct hostapd_data *hapd, const u8 *mac) return hapd->driver->set_radius_acl_expire(hapd->drv_priv, mac); } +#ifdef CONFIG_IEEE80211N +static inline int +hostapd_set_ht_capability(const char *ifname, struct hostapd_data *hapd, + const struct ieee80211_ht_capability *ht_cap) +{ + if (hapd->driver == NULL || hapd->driver->set_ht_capability == NULL || + ht_cap == NULL) + return 0; + return hapd->driver->set_ht_capability( + ifname, hapd->drv_priv, (const u8 *) ht_cap, + sizeof(struct ieee80211_ht_capability)); +} + +static inline int +hostapd_set_ht_operation(const char *ifname, struct hostapd_data *hapd, + const struct ieee80211_ht_operation *ht_operation) +{ + if (hapd->driver == NULL || hapd->driver->set_ht_operation == NULL || + ht_operation == NULL) + return 0; + return hapd->driver->set_ht_operation( + ifname, hapd->drv_priv, (const u8 *) ht_operation, + sizeof(struct ieee80211_ht_operation)); +} +#endif /* CONFIG_IEEE80211N */ + #endif /* DRIVER_H */ diff --git a/hostapd/hostapd.conf b/hostapd/hostapd.conf index 755d190e4..55987dc6a 100644 --- a/hostapd/hostapd.conf +++ b/hostapd/hostapd.conf @@ -377,6 +377,13 @@ wme_ac_vo_acm=0 # remain asleep). Default: 65535 (no limit apart from field size) #max_listen_interval=100 +##### IEEE 802.11n related configuration ###################################### + +# ieee80211n: Whether IEEE 802.11n (HT) is enabled +# 0 = disabled (default) +# 1 = enabled +#ieee80211n=1 + ##### IEEE 802.1X-2004 related configuration ################################## # Require IEEE 802.1X authorization diff --git a/hostapd/hostapd.h b/hostapd/hostapd.h index 10afd5ec1..d98982019 100644 --- a/hostapd/hostapd.h +++ b/hostapd/hostapd.h @@ -221,6 +221,18 @@ struct hostapd_iface { int olbc; /* Overlapping Legacy BSS Condition */ + /* Number of HT associated stations that do not support greenfield */ + int num_sta_ht_no_gf; + + /* Number of associated non-HT stations */ + int num_sta_no_ht; + + /* Number of HT associated stations 20 MHz */ + int num_sta_ht_20mhz; + + /* Overlapping BSS information */ + int olbc_ht; + int dfs_enable; u8 pwr_const; unsigned int tx_power; diff --git a/hostapd/ieee802_11.c b/hostapd/ieee802_11.c index 1ebf98594..b3a9f3d13 100644 --- a/hostapd/ieee802_11.c +++ b/hostapd/ieee802_11.c @@ -100,6 +100,113 @@ u8 * hostapd_eid_ext_supp_rates(struct hostapd_data *hapd, u8 *eid) } +#ifdef CONFIG_IEEE80211N + +u8 * hostapd_eid_ht_capabilities_info(struct hostapd_data *hapd, u8 *eid) +{ + u8 *pos = eid; + os_memcpy(pos, &hapd->conf->ht_capabilities, sizeof(struct ht_cap_ie)); + pos += sizeof(struct ht_cap_ie); + return pos; +} + + +u8 * hostapd_eid_ht_operation(struct hostapd_data *hapd, u8 *eid) +{ + u8 *pos = eid; + os_memcpy(pos, &hapd->conf->ht_operation, + sizeof(struct ht_operation_ie)); + pos += sizeof(struct ht_operation_ie); + return pos; +} + + +/* +op_mode +Set to 0 (HT pure) under the followign conditions + - all STAs in the BSS are 20/40 MHz HT in 20/40 MHz BSS or + - all STAs in the BSS are 20 MHz HT in 20 MHz BSS +Set to 1 (HT non-member protection) if there may be non-HT STAs + in both the primary and the secondary channel +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) +{ + struct ht_operation_ie *ht_operation; + u16 operation_mode = 0; + u16 cur_op_mode, new_op_mode; + int op_mode_changes = 0; + struct hostapd_data *hapd = iface->bss[0]; + + /* TODO: should hapd pointer really be used here? This should most + * likely be per radio, not per BSS.. */ + + if (!hapd->conf->ieee80211n || hapd->conf->ht_op_mode_fixed) + return 0; + + ht_operation = &hapd->conf->ht_operation; + operation_mode = le_to_host16(ht_operation->data.operation_mode); + wpa_printf(MSG_DEBUG, "%s current operation mode=0x%X", + __func__, operation_mode); + + if ((operation_mode & HT_INFO_OPERATION_MODE_NON_GF_DEVS_PRESENT) == 0 + && iface->num_sta_ht_no_gf) { + operation_mode |= HT_INFO_OPERATION_MODE_NON_GF_DEVS_PRESENT; + op_mode_changes++; + } else if ((operation_mode & + HT_INFO_OPERATION_MODE_NON_GF_DEVS_PRESENT) && + iface->num_sta_ht_no_gf == 0) { + operation_mode &= ~HT_INFO_OPERATION_MODE_NON_GF_DEVS_PRESENT; + op_mode_changes++; + } + + if ((operation_mode & HT_INFO_OPERATION_MODE_NON_HT_STA_PRESENT) == 0 + && (iface->num_sta_no_ht || iface->olbc_ht)) { + operation_mode |= HT_INFO_OPERATION_MODE_NON_HT_STA_PRESENT; + op_mode_changes++; + } else if ((operation_mode & HT_INFO_OPERATION_MODE_NON_HT_STA_PRESENT) + && (iface->num_sta_no_ht == 0 && iface->olbc_ht == 0)) { + operation_mode &= ~HT_INFO_OPERATION_MODE_NON_HT_STA_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 || + (operation_mode & HT_INFO_OPERATION_MODE_NON_GF_DEVS_PRESENT)) + new_op_mode = OP_MODE_MIXED; + else if ((hapd->conf->ht_capabilities.data.capabilities_info & + HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET) && + iface->num_sta_ht_20mhz) + new_op_mode = OP_MODE_20MHZ_HT_STA_ASSOCED; + else if (iface->olbc_ht) + new_op_mode = OP_MODE_MAY_BE_LEGACY_STAS; + else + new_op_mode = OP_MODE_PURE; + + cur_op_mode = operation_mode & HT_INFO_OPERATION_MODE_OP_MODE_MASK; + if (cur_op_mode != new_op_mode) { + operation_mode &= ~HT_INFO_OPERATION_MODE_OP_MODE_MASK; + operation_mode |= new_op_mode; + op_mode_changes++; + } + + wpa_printf(MSG_DEBUG, "%s new operation mode=0x%X changes=%d", + __func__, operation_mode, op_mode_changes); + ht_operation->data.operation_mode = host_to_le16(operation_mode); + + return op_mode_changes; +} + +#endif /* CONFIG_IEEE80211N */ + + u16 hostapd_own_capab_info(struct hostapd_data *hapd, struct sta_info *sta, int probe) { @@ -326,6 +433,14 @@ ParseRes ieee802_11_parse_elems(struct hostapd_data *hapd, u8 *start, elems->ftie = pos; elems->ftie_len = elen; break; + case WLAN_EID_HT_CAP: + elems->ht_capabilities = pos; + elems->ht_capabilities_len = elen; + break; + case WLAN_EID_HT_OPERATION: + elems->ht_operation = pos; + elems->ht_operation_len = elen; + break; default: unknown++; if (!show_errors) @@ -871,7 +986,8 @@ static void handle_assoc(struct hostapd_data *hapd, sta->capability = capab_info; - /* followed by SSID and Supported rates */ + /* followed by SSID and Supported rates; and HT capabilities if 802.11n + * is used */ if (ieee802_11_parse_elems(hapd, pos, left, &elems, 1) == ParseFailed || !elems.ssid) { printf("STA " MACSTR " sent invalid association request\n", @@ -943,6 +1059,24 @@ static void handle_assoc(struct hostapd_data *hapd, sta->supported_rates_len += elems.ext_supp_rates_len; } +#ifdef CONFIG_IEEE80211N + /* save HT capabilities in the sta object */ + os_memset(&sta->ht_capabilities, 0, sizeof(sta->ht_capabilities)); + if (elems.ht_capabilities && + elems.ht_capabilities_len >= sizeof(struct ieee80211_ht_capability) + && (sta->flags & WLAN_STA_WME)) { + /* note: without WMM capability, treat the sta as non-HT */ + sta->flags |= WLAN_STA_HT; + sta->ht_capabilities.id = WLAN_EID_HT_CAP; + sta->ht_capabilities.length = + sizeof(struct ieee80211_ht_capability); + os_memcpy(&sta->ht_capabilities.data, + elems.ht_capabilities, + sizeof(struct ieee80211_ht_capability)); + } else + sta->flags &= ~WLAN_STA_HT; +#endif /* CONFIG_IEEE80211N */ + if ((hapd->conf->wpa & WPA_PROTO_RSN) && elems.rsn_ie) { wpa_ie = elems.rsn_ie; wpa_ie_len = elems.rsn_ie_len; @@ -1074,6 +1208,38 @@ static void handle_assoc(struct hostapd_data *hapd, ieee802_11_set_beacons(hapd->iface); } +#ifdef CONFIG_IEEE80211N + if (sta->flags & WLAN_STA_HT) { + if ((sta->ht_capabilities.data.capabilities_info & + HT_CAP_INFO_GREEN_FIELD) == 0) { + hapd->iface->num_sta_ht_no_gf++; + wpa_printf(MSG_DEBUG, "%s STA " MACSTR " - no " + "greenfield, num of non-gf stations %d", + __func__, MAC2STR(sta->addr), + hapd->iface->num_sta_ht_no_gf); + } + if ((sta->ht_capabilities.data.capabilities_info & + HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET) == 0) { + hapd->iface->num_sta_ht_20mhz++; + wpa_printf(MSG_DEBUG, "%s STA " MACSTR " - 20 MHz HT, " + "num of 20MHz HT STAs %d", + __func__, MAC2STR(sta->addr), + hapd->iface->num_sta_ht_20mhz); + } + } else { + hapd->iface->num_sta_no_ht++; + if (hapd->conf->ieee80211n) { + wpa_printf(MSG_DEBUG, "%s STA " MACSTR + " - no HT, num of non-HT stations %d", + __func__, MAC2STR(sta->addr), + hapd->iface->num_sta_no_ht); + } + } + + if (hostapd_ht_operation_update(hapd->iface) > 0) + ieee802_11_set_beacons(hapd->iface); +#endif /* CONFIG_IEEE80211N */ + /* get a unique AID */ if (sta->aid > 0) { wpa_printf(MSG_DEBUG, " old AID %d", sta->aid); @@ -1139,6 +1305,13 @@ static void handle_assoc(struct hostapd_data *hapd, if (sta->flags & WLAN_STA_WME) p = hostapd_eid_wme(hapd, p); +#ifdef CONFIG_IEEE80211N + if (hapd->conf->ieee80211n) { + p = hostapd_eid_ht_capabilities_info(hapd, p); + p = hostapd_eid_ht_operation(hapd, p); + } +#endif /* CONFIG_IEEE80211N */ + #ifdef CONFIG_IEEE80211R if (resp == WLAN_STATUS_SUCCESS) { /* IEEE 802.11r: Mobility Domain Information, Fast BSS @@ -1552,6 +1725,7 @@ static void handle_assoc_cb(struct hostapd_data *hapd, u16 status; struct sta_info *sta; int new_assoc = 1; + struct ht_cap_ie *ht_cap = NULL; if (!ok) { hostapd_logger(hapd, mgmt->da, HOSTAPD_MODULE_IEEE80211, @@ -1602,9 +1776,15 @@ static void handle_assoc_cb(struct hostapd_data *hapd, else mlme_associate_indication(hapd, sta); +#ifdef CONFIG_IEEE80211N + if (sta->flags & WLAN_STA_HT) + ht_cap = &sta->ht_capabilities; +#endif /* CONFIG_IEEE80211N */ + if (hostapd_sta_add(hapd->conf->iface, hapd, sta->addr, sta->aid, sta->capability, sta->supported_rates, - sta->supported_rates_len, 0, sta->listen_interval)) + sta->supported_rates_len, 0, sta->listen_interval, + ht_cap)) { hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_NOTICE, diff --git a/hostapd/ieee802_11.h b/hostapd/ieee802_11.h index 9f2a4d82d..186c0d366 100644 --- a/hostapd/ieee802_11.h +++ b/hostapd/ieee802_11.h @@ -55,6 +55,10 @@ struct ieee802_11_elems { u8 mdie_len; u8 *ftie; u8 ftie_len; + u8 *ht_capabilities; + u8 ht_capabilities_len; + u8 *ht_operation; + u8 ht_operation_len; }; typedef enum { ParseOK = 0, ParseUnknown = 1, ParseFailed = -1 } ParseRes; @@ -69,6 +73,7 @@ struct hostapd_frame_info { unsigned int passive_scan:1; }; +struct hostapd_iface; struct hostapd_data; struct sta_info; @@ -91,5 +96,8 @@ u16 hostapd_own_capab_info(struct hostapd_data *hapd, struct sta_info *sta, int probe); u8 * hostapd_eid_supp_rates(struct hostapd_data *hapd, u8 *eid); u8 * hostapd_eid_ext_supp_rates(struct hostapd_data *hapd, u8 *eid); +u8 * hostapd_eid_ht_capabilities_info(struct hostapd_data *hapd, u8 *eid); +u8 * hostapd_eid_ht_operation(struct hostapd_data *hapd, u8 *eid); +int hostapd_ht_operation_update(struct hostapd_iface *iface); #endif /* IEEE802_11_H */ diff --git a/hostapd/sta_info.c b/hostapd/sta_info.c index 635e018c9..7bc7faed5 100644 --- a/hostapd/sta_info.c +++ b/hostapd/sta_info.c @@ -150,6 +150,21 @@ void ap_free_sta(struct hostapd_data *hapd, struct sta_info *sta) set_beacon++; } +#ifdef CONFIG_IEEE80211N + if (sta->flags & WLAN_STA_HT) { + if ((sta->ht_capabilities.data.capabilities_info & + HT_CAP_INFO_GREEN_FIELD) == 0) + hapd->iface->num_sta_ht_no_gf--; + if ((sta->ht_capabilities.data.capabilities_info & + HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET) == 0) + hapd->iface->num_sta_ht_20mhz--; + } else + hapd->iface->num_sta_no_ht--; + + if (hostapd_ht_operation_update(hapd->iface) > 0) + set_beacon++; +#endif /* CONFIG_IEEE80211N */ + if (set_beacon) ieee802_11_set_beacons(hapd->iface); diff --git a/src/common/ieee802_11_defs.h b/src/common/ieee802_11_defs.h index f7c47d982..47fe0fb31 100644 --- a/src/common/ieee802_11_defs.h +++ b/src/common/ieee802_11_defs.h @@ -186,12 +186,18 @@ #define WLAN_EID_IBSS_DFS 41 /* EIDs defined by IEEE 802.11h - END */ #define WLAN_EID_ERP_INFO 42 +#define WLAN_EID_HT_CAP 45 #define WLAN_EID_RSN 48 #define WLAN_EID_EXT_SUPP_RATES 50 #define WLAN_EID_MOBILITY_DOMAIN 54 #define WLAN_EID_FAST_BSS_TRANSITION 55 #define WLAN_EID_TIMEOUT_INTERVAL 56 #define WLAN_EID_RIC_DATA 57 +#define WLAN_EID_HT_OPERATION 61 +#define WLAN_EID_SECONDARY_CHANNEL_OFFSET 62 +#define WLAN_EID_20_40_BSS_COEXISTENCE 72 +#define WLAN_EID_20_40_BSS_INTOLERANT 73 +#define WLAN_EID_OVERLAPPING_BSS_SCAN_PARAMS 74 #define WLAN_EID_VENDOR_SPECIFIC 221 @@ -312,4 +318,227 @@ struct ieee80211_mgmt { #define ERP_INFO_USE_PROTECTION BIT(1) #define ERP_INFO_BARKER_PREAMBLE_MODE BIT(2) + +/* HT Capability element */ + +#define MIMO_PWR_DONT_SEND_MIMO_SEQS 0 +#define MIMO_PWR_NEED2PRECEDE_MIMO_SEQS_BY_RTS 1 +#define MIMO_PWR_NO_LIMIT_ON_MIMO_SEQS 3 + +enum { + MAX_RX_AMPDU_FACTOR_8KB = 0, + MAX_RX_AMPDU_FACTOR_16KB, + MAX_RX_AMPDU_FACTOR_32KB, + MAX_RX_AMPDU_FACTOR_64KB +}; + +enum { + CALIBRATION_NOT_SUPPORTED = 0, + CALIBRATION_CANNOT_INIT, + CALIBRATION_CAN_INIT, + CALIBRATION_FULL_SUPPORT +}; + +enum { + MCS_FEEDBACK_NOT_PROVIDED = 0, + MCS_FEEDBACK_UNSOLICITED, + MCS_FEEDBACK_MRQ_RESPONSE +}; + + +struct ieee80211_ht_capability { + le16 capabilities_info; + u8 mac_ht_params_info; + u8 supported_mcs_set[16]; + le16 extended_ht_capability_info; + le32 tx_BF_capability_info; + u8 antenna_selection_info; +} STRUCT_PACKED; + + +struct ieee80211_ht_operation { + u8 control_chan; + u8 ht_param; + le16 operation_mode; + le16 stbc_param; + u8 basic_set[16]; +} STRUCT_PACKED; + +/* auxiliary bit manipulation macros FIXME: move it to common later... */ +#define SET_2BIT_U8(_ptr_, _shift_, _val_) \ + ((*(_ptr_) &= ~(3 << (_shift_))), \ + (*(_ptr_) |= (*(_ptr_) & (((u8)3) << (_shift_))) | \ + (((u8)(_val_) & 3) << _shift_))) + +#define GET_2BIT_U8(_var_, _shift_) \ + (((_var_) & (((u8)3) << (_shift_))) >> (_shift_)) + +#define SET_2BIT_LE16(_u16ptr_, _shift_, _val_) \ + ((*(_u16ptr_) &= ~(3 << (_shift_))), \ + (*(_u16ptr_) |= \ + (((*(_u16ptr_)) & (((u16)3) << ((u16)_shift_))) | \ + (((u16)(_val_) & (u16)3) << (u16)(_shift_))))) + +#define GET_2BIT_LE16(_var_, _shift_) \ + (((_var_) & (((u16)3) << (_shift_))) >> (_shift_)) + +#define SET_2BIT_LE32(_u32ptr_, _shift_, _val_) \ + ((*(_u32ptr_) &= ~(3 << (_shift_))), \ + (*(_u32ptr_) |= (((*(_u32ptr_)) & (((u32)3) << (_shift_))) | \ + (((u32)(_val_) & 3) << _shift_)))) + +#define GET_2BIT_LE32(_var_, _shift_) \ + (((_var_) & (((u32)3) << (_shift_))) >> (_shift_)) + +#define SET_3BIT_LE16(_u16ptr_, _shift_, _val_) \ + ((*(_u16ptr_) &= ~(7 << (_shift_))), \ + (*(_u16ptr_) |= (((*(_u16ptr_)) & (((u16)7) << (_shift_))) | \ + (((u16)(_val_) & 7) << _shift_)))) + +#define GET_3BIT_LE16(_var_, _shift_) \ + (((_var_) & (((u16)7) << (_shift_))) >> (_shift_)) + +#define SET_3BIT_LE32(_u32ptr_, _shift_, _val_) \ + ((*(_u32ptr_) &= ~(7 << (_shift_))), \ + (*(_u32ptr_) |= (((*(_u32ptr_)) & (((u32)7) << (_shift_))) | \ + (((u32)(_val_) & 7) << _shift_)))) + +#define GET_3BIT_LE32(_var_, _shift_) \ + (((_var_) & (((u32)7) << (_shift_))) >> (_shift_)) + + +#define HT_CAP_INFO_ADVANCED_CODDING_CAP ((u16) BIT(0)) +#define HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET ((u16) BIT(1)) +#define HT_CAP_INFO_MIMO_PWR_SAVE_OFFSET 2 +#define HT_CAP_INFO_GREEN_FIELD ((u16) BIT(4)) +#define HT_CAP_INFO_SHORT_GI20MHZ ((u16) BIT(5)) +#define HT_CAP_INFO_SHORT_GI40MHZ ((u16) BIT(6)) +#define HT_CAP_INFO_TX_STBC ((u16) BIT(7)) +#define HT_CAP_INFO_RX_STBC_OFFSET 8 +#define HT_CAP_INFO_DELAYED_BA ((u16) BIT(10)) +#define HT_CAP_INFO_MAX_AMSDU_SIZE ((u16) BIT(11)) +#define HT_CAP_INFO_DSSS_CCK40MHZ ((u16) BIT(12)) +#define HT_CAP_INFO_PSMP_SUPP ((u16) BIT(13)) +#define HT_CAP_INFO_STBC_CTRL_FRAME_SUPP ((u16) BIT(14)) +#define HT_CAP_INFO_LSIG_TXOP_PROTECT_SUPPORT ((u16) BIT(15)) + + +#define MAC_HT_PARAM_INFO_MAX_RX_AMPDU_FACTOR_OFFSET 0 +#define MAC_HT_PARAM_INFO_MAX_MPDU_DENSITY_OFFSET 2 + +#define EXT_HT_CAP_INFO_PCO ((u16) BIT(0)) +#define EXT_HT_CAP_INFO_TRANS_TIME_OFFSET 1 +#define EXT_HT_CAP_INFO_MCS_FEEDBACK_OFFSET 8 +#define EXT_HT_CAP_INFO_HTC_SUPPORTED ((u16) BIT(10)) +#define EXT_HT_CAP_INFO_RD_RESPONDER ((u16) BIT(11)) + + +#define TX_BEAMFORM_CAP_TXBF_CAP ((u32) BIT(0)) +#define TX_BEAMFORM_CAP_RX_STAGGERED_SOUNDING_CAP ((u32) BIT(1)) +#define TX_BEAMFORM_CAP_TX_STAGGERED_SOUNDING_CAP ((u32) BIT(2)) +#define TX_BEAMFORM_CAP_RX_ZLF_CAP ((u32) BIT(3)) +#define TX_BEAMFORM_CAP_TX_ZLF_CAP ((u32) BIT(4)) +#define TX_BEAMFORM_CAP_IMPLICIT_ZLF_CAP ((u32) BIT(5)) +#define TX_BEAMFORM_CAP_CALIB_OFFSET 6 +#define TX_BEAMFORM_CAP_EXPLICIT_CSI_TXBF_CAP ((u32) BIT(8)) +#define TX_BEAMFORM_CAP_EXPLICIT_UNCOMPR_STEERING_MATRIX_CAP ((u32) BIT(9)) +#define TX_BEAMFORM_CAP_EXPLICIT_BF_CSI_FEEDBACK_CAP ((u32) BIT(10)) +#define TX_BEAMFORM_CAP_EXPLICIT_BF_CSI_FEEDBACK_OFFSET 11 +#define TX_BEAMFORM_CAP_EXPLICIT_UNCOMPR_STEERING_MATRIX_FEEDBACK_OFFSET 13 +#define TX_BEAMFORM_CAP_EXPLICIT_COMPRESSED_STEERING_MATRIX_FEEDBACK_OFFSET 15 +#define TX_BEAMFORM_CAP_MINIMAL_GROUPING_OFFSET 17 +#define TX_BEAMFORM_CAP_CSI_NUM_BEAMFORMER_ANT_OFFSET 19 +#define TX_BEAMFORM_CAP_UNCOMPRESSED_STEERING_MATRIX_BEAMFORMER_ANT_OFFSET 21 +#define TX_BEAMFORM_CAP_COMPRESSED_STEERING_MATRIX_BEAMFORMER_ANT_OFFSET 23 +#define TX_BEAMFORM_CAP_SCI_MAX_OF_ROWS_BEANFORMER_SUPPORTED_OFFSET 25 + + +#define ASEL_CAPABILITY_ASEL_CAPABLE ((u8) BIT(0)) +#define ASEL_CAPABILITY_EXPLICIT_CSI_FEEDBACK_BASED_TX_AS_CAP ((u8) BIT(1)) +#define ASEL_CAPABILITY_ANT_INDICES_FEEDBACK_BASED_TX_AS_CAP ((u8) BIT(2)) +#define ASEL_CAPABILITY_EXPLICIT_CSI_FEEDBACK_CAP ((u8) BIT(3)) +#define ASEL_CAPABILITY_ANT_INDICES_FEEDBACK_CAP ((u8) BIT(4)) +#define ASEL_CAPABILITY_RX_AS_CAP ((u8) BIT(5)) +#define ASEL_CAPABILITY_TX_SOUND_PPDUS_CAP ((u8) BIT(6)) + + +struct ht_cap_ie { + u8 id; + u8 length; + struct ieee80211_ht_capability data; +} STRUCT_PACKED; + + +#define EXT_CHNL_OFF_NONE 0 +#define EXT_CHNL_OFF_ABOVE 1 +#define EXT_CHNL_OFF_BELOW 3 + +#define REC_TRANS_CHNL_WIDTH_20 0 +#define REC_TRANS_CHNL_WIDTH_ANY 1 + +#define OP_MODE_PURE 0 +#define OP_MODE_MAY_BE_LEGACY_STAS 1 +#define OP_MODE_20MHZ_HT_STA_ASSOCED 2 +#define OP_MODE_MIXED 3 + +#define HT_INFO_HT_PARAM_EXT_CHNL_OFF_OFFSET 0 +#define HT_INFO_HT_PARAM_REC_TRANS_CHNL_WIDTH ((u8) BIT(2)) +#define HT_INFO_HT_PARAM_RIFS_MODE ((u8) BIT(3)) +#define HT_INFO_HT_PARAM_CTRL_ACCESS_ONLY ((u8) BIT(4)) +#define HT_INFO_HT_PARAM_SRV_INTERVAL_GRANULARITY ((u8) BIT(5)) + +#define HT_INFO_OPERATION_MODE_OP_MODE_MASK \ + ((le16) (0x0001 | 0x0002)) +#define HT_INFO_OPERATION_MODE_OP_MODE_OFFSET 0 +#define HT_INFO_OPERATION_MODE_NON_GF_DEVS_PRESENT ((u8) BIT(2)) +#define HT_INFO_OPERATION_MODE_TRANSMIT_BURST_LIMIT ((u8) BIT(3)) +#define HT_INFO_OPERATION_MODE_NON_HT_STA_PRESENT ((u8) BIT(4)) + +#define HT_INFO_STBC_PARAM_DUAL_BEACON ((u16) BIT(6)) +#define HT_INFO_STBC_PARAM_DUAL_STBC_PROTECT ((u16) BIT(7)) +#define HT_INFO_STBC_PARAM_SECONDARY_BCN ((u16) BIT(8)) +#define HT_INFO_STBC_PARAM_LSIG_TXOP_PROTECT_ALLOWED ((u16) BIT(9)) +#define HT_INFO_STBC_PARAM_PCO_ACTIVE ((u16) BIT(10)) +#define HT_INFO_STBC_PARAM_PCO_PHASE ((u16) BIT(11)) + +struct ht_operation_ie { + u8 id; + u8 length; + struct ieee80211_ht_operation data; +} STRUCT_PACKED; + + +#define HT_CAPABILITIES_LEN (sizeof(struct ht_cap_ie) - 2) +#define HT_OPERATION_LEN (sizeof(struct ht_operation_ie) - 2) + +/* Secondary channel offset element */ +#define SECONDARY_CHANNEL_OFFSET_NONE 0 +#define SECONDARY_CHANNEL_OFFSET_ABOVE 1 +#define SECONDARY_CHANNEL_OFFSET_BELOW 3 +struct secondary_channel_offset_ie { + u8 id; + u8 length; + u8 secondary_offset_offset; +} STRUCT_PACKED; + + +/* body of Recommended Transmit Channel Width action frame */ +#define CHANNEL_WIDTH_20 0 +#define CHANNEL_WIDTH_ANY 1 +struct recommended_tx_channel_width_action { + u8 category; + u8 action; + u8 channel_width; +} STRUCT_PACKED; + +/* body of MIMO Power Save action frame */ +#define PWR_SAVE_MODE_STATIC 0 +#define PWR_SAVE_MODE_DYNAMIC 1 +struct mimo_pwr_save_action { + u8 category; + u8 action; + u8 enable; + u8 mode; +} STRUCT_PACKED; + #endif /* IEEE802_11_DEFS_H */