diff --git a/hostapd/config_file.c b/hostapd/config_file.c index e30efbe31..99cd05286 100644 --- a/hostapd/config_file.c +++ b/hostapd/config_file.c @@ -2690,6 +2690,8 @@ static int hostapd_config_fill(struct hostapd_config *conf, conf->vht_oper_centr_freq_seg0_idx = atoi(pos); } else if (os_strcmp(buf, "vht_oper_centr_freq_seg1_idx") == 0) { conf->vht_oper_centr_freq_seg1_idx = atoi(pos); + } else if (os_strcmp(buf, "vendor_vht") == 0) { + bss->vendor_vht = atoi(pos); #endif /* CONFIG_IEEE80211AC */ } else if (os_strcmp(buf, "max_listen_interval") == 0) { bss->max_listen_interval = atoi(pos); diff --git a/src/ap/ap_config.h b/src/ap/ap_config.h index 58af6cb19..e5215c529 100644 --- a/src/ap/ap_config.h +++ b/src/ap/ap_config.h @@ -551,6 +551,8 @@ struct hostapd_bss_config { int mesh; int radio_measurements; + + int vendor_vht; }; diff --git a/src/ap/beacon.c b/src/ap/beacon.c index 4a8703acc..b0a74e01d 100644 --- a/src/ap/beacon.c +++ b/src/ap/beacon.c @@ -379,6 +379,10 @@ static u8 * hostapd_gen_probe_resp(struct hostapd_data *hapd, #endif /* CONFIG_P2P */ if (hapd->conf->vendor_elements) buflen += wpabuf_len(hapd->conf->vendor_elements); + if (hapd->conf->vendor_vht) { + buflen += 5 + 2 + sizeof(struct ieee80211_vht_capabilities) + + 2 + sizeof(struct ieee80211_vht_operation); + } resp = os_zalloc(buflen); if (resp == NULL) return NULL; @@ -446,8 +450,12 @@ static u8 * hostapd_gen_probe_resp(struct hostapd_data *hapd, pos = hostapd_add_csa_elems(hapd, pos, (u8 *)resp, &hapd->cs_c_off_proberesp); #ifdef CONFIG_IEEE80211AC - pos = hostapd_eid_vht_capabilities(hapd, pos); - pos = hostapd_eid_vht_operation(hapd, pos); + if (hapd->iconf->ieee80211ac && !hapd->conf->disable_11ac) { + pos = hostapd_eid_vht_capabilities(hapd, pos); + pos = hostapd_eid_vht_operation(hapd, pos); + } + if (hapd->conf->vendor_vht) + pos = hostapd_eid_vendor_vht(hapd, pos); #endif /* CONFIG_IEEE80211AC */ /* Wi-Fi Alliance WMM */ @@ -776,6 +784,14 @@ int ieee802_11_build_ap_params(struct hostapd_data *hapd, #endif /* CONFIG_P2P */ if (hapd->conf->vendor_elements) tail_len += wpabuf_len(hapd->conf->vendor_elements); + +#ifdef CONFIG_IEEE80211AC + if (hapd->conf->vendor_vht) { + tail_len += 5 + 2 + sizeof(struct ieee80211_vht_capabilities) + + 2 + sizeof(struct ieee80211_vht_operation); + } +#endif /* CONFIG_IEEE80211AC */ + tailpos = tail = os_malloc(tail_len); if (head == NULL || tail == NULL) { wpa_printf(MSG_ERROR, "Failed to set beacon data"); @@ -865,8 +881,12 @@ int ieee802_11_build_ap_params(struct hostapd_data *hapd, tailpos = hostapd_add_csa_elems(hapd, tailpos, tail, &hapd->cs_c_off_beacon); #ifdef CONFIG_IEEE80211AC - tailpos = hostapd_eid_vht_capabilities(hapd, tailpos); - tailpos = hostapd_eid_vht_operation(hapd, tailpos); + if (hapd->iconf->ieee80211ac && !hapd->conf->disable_11ac) { + tailpos = hostapd_eid_vht_capabilities(hapd, tailpos); + tailpos = hostapd_eid_vht_operation(hapd, tailpos); + } + if (hapd->conf->vendor_vht) + tailpos = hostapd_eid_vendor_vht(hapd, tailpos); #endif /* CONFIG_IEEE80211AC */ /* Wi-Fi Alliance WMM */ diff --git a/src/ap/hw_features.c b/src/ap/hw_features.c index 6b0a72d83..f6d79ea6b 100644 --- a/src/ap/hw_features.c +++ b/src/ap/hw_features.c @@ -641,12 +641,31 @@ static int ieee80211ac_cap_check_max(u32 hw, u32 conf, u32 mask, static int ieee80211ac_supported_vht_capab(struct hostapd_iface *iface) { - u32 hw = iface->current_mode->vht_capab; + struct hostapd_hw_modes *mode = iface->current_mode; + u32 hw = mode->vht_capab; u32 conf = iface->conf->vht_capab; wpa_printf(MSG_DEBUG, "hw vht capab: 0x%x, conf vht capab: 0x%x", hw, conf); + if (mode->mode == HOSTAPD_MODE_IEEE80211G && + iface->conf->bss[0]->vendor_vht && + mode->vht_capab == 0 && iface->hw_features) { + int i; + + for (i = 0; i < iface->num_hw_features; i++) { + if (iface->hw_features[i].mode == + HOSTAPD_MODE_IEEE80211A) { + mode = &iface->hw_features[i]; + hw = mode->vht_capab; + wpa_printf(MSG_DEBUG, + "update hw vht capab based on 5 GHz band: 0x%x", + hw); + break; + } + } + } + #define VHT_CAP_CHECK(cap) \ do { \ if (!ieee80211ac_cap_check(hw, conf, cap, #cap)) \ diff --git a/src/ap/ieee802_11.c b/src/ap/ieee802_11.c index 3d4488a1c..89911b1fd 100644 --- a/src/ap/ieee802_11.c +++ b/src/ap/ieee802_11.c @@ -1327,6 +1327,13 @@ static u16 check_assoc_ies(struct hostapd_data *hapd, struct sta_info *sta, "mandatory VHT PHY - reject association"); return WLAN_STATUS_ASSOC_DENIED_NO_VHT; } + + if (hapd->conf->vendor_vht && !elems.vht_capabilities) { + resp = copy_sta_vendor_vht(hapd, sta, elems.vendor_vht, + elems.vendor_vht_len); + if (resp != WLAN_STATUS_SUCCESS) + return resp; + } #endif /* CONFIG_IEEE80211AC */ #ifdef CONFIG_P2P @@ -1616,8 +1623,10 @@ static void send_assoc_resp(struct hostapd_data *hapd, struct sta_info *sta, #endif /* CONFIG_IEEE80211N */ #ifdef CONFIG_IEEE80211AC - p = hostapd_eid_vht_capabilities(hapd, p); - p = hostapd_eid_vht_operation(hapd, p); + if (hapd->iconf->ieee80211ac && !hapd->conf->disable_11ac) { + p = hostapd_eid_vht_capabilities(hapd, p); + p = hostapd_eid_vht_operation(hapd, p); + } #endif /* CONFIG_IEEE80211AC */ p = hostapd_eid_ext_capab(hapd, p); @@ -1625,6 +1634,11 @@ static void send_assoc_resp(struct hostapd_data *hapd, struct sta_info *sta, if (sta->qos_map_enabled) p = hostapd_eid_qos_map_set(hapd, p); +#ifdef CONFIG_IEEE80211AC + if (hapd->conf->vendor_vht && (sta->flags & WLAN_STA_VENDOR_VHT)) + p = hostapd_eid_vendor_vht(hapd, p); +#endif /* CONFIG_IEEE80211AC */ + if (sta->flags & WLAN_STA_WMM) p = hostapd_eid_wmm(hapd, p); diff --git a/src/ap/ieee802_11.h b/src/ap/ieee802_11.h index beaeac500..41c27d906 100644 --- a/src/ap/ieee802_11.h +++ b/src/ap/ieee802_11.h @@ -51,6 +51,7 @@ u8 * hostapd_eid_ht_capabilities(struct hostapd_data *hapd, u8 *eid); u8 * hostapd_eid_ht_operation(struct hostapd_data *hapd, u8 *eid); u8 * hostapd_eid_vht_capabilities(struct hostapd_data *hapd, u8 *eid); u8 * hostapd_eid_vht_operation(struct hostapd_data *hapd, u8 *eid); +u8 * hostapd_eid_vendor_vht(struct hostapd_data *hapd, u8 *eid); int hostapd_ht_operation_update(struct hostapd_iface *iface); void ieee802_11_send_sa_query_req(struct hostapd_data *hapd, const u8 *addr, const u8 *trans_id); @@ -62,6 +63,9 @@ void hostapd_get_vht_capab(struct hostapd_data *hapd, struct ieee80211_vht_capabilities *neg_vht_cap); u16 copy_sta_ht_capab(struct hostapd_data *hapd, struct sta_info *sta, const u8 *ht_capab, size_t ht_capab_len); +u16 copy_sta_vendor_vht(struct hostapd_data *hapd, struct sta_info *sta, + const u8 *ie, size_t len); + void update_ht_state(struct hostapd_data *hapd, struct sta_info *sta); void ht40_intolerant_add(struct hostapd_iface *iface, struct sta_info *sta); void ht40_intolerant_remove(struct hostapd_iface *iface, struct sta_info *sta); diff --git a/src/ap/ieee802_11_vht.c b/src/ap/ieee802_11_vht.c index 437cf5031..89c14ec87 100644 --- a/src/ap/ieee802_11_vht.c +++ b/src/ap/ieee802_11_vht.c @@ -22,12 +22,25 @@ u8 * hostapd_eid_vht_capabilities(struct hostapd_data *hapd, u8 *eid) { struct ieee80211_vht_capabilities *cap; + struct hostapd_hw_modes *mode = hapd->iface->current_mode; u8 *pos = eid; - if (!hapd->iconf->ieee80211ac || !hapd->iface->current_mode || - hapd->conf->disable_11ac) + if (!mode) return eid; + if (mode->mode == HOSTAPD_MODE_IEEE80211G && hapd->conf->vendor_vht && + mode->vht_capab == 0 && hapd->iface->hw_features) { + int i; + + for (i = 0; i < hapd->iface->num_hw_features; i++) { + if (hapd->iface->hw_features[i].mode == + HOSTAPD_MODE_IEEE80211A) { + mode = &hapd->iface->hw_features[i]; + break; + } + } + } + *pos++ = WLAN_EID_VHT_CAP; *pos++ = sizeof(*cap); @@ -37,8 +50,7 @@ u8 * hostapd_eid_vht_capabilities(struct hostapd_data *hapd, u8 *eid) hapd->iface->conf->vht_capab); /* Supported MCS set comes from hw */ - os_memcpy(&cap->vht_supported_mcs_set, - hapd->iface->current_mode->vht_mcs_set, 8); + os_memcpy(&cap->vht_supported_mcs_set, mode->vht_mcs_set, 8); pos += sizeof(*cap); @@ -51,9 +63,6 @@ u8 * hostapd_eid_vht_operation(struct hostapd_data *hapd, u8 *eid) struct ieee80211_vht_operation *oper; u8 *pos = eid; - if (!hapd->iconf->ieee80211ac || hapd->conf->disable_11ac) - return eid; - *pos++ = WLAN_EID_VHT_OPERATION; *pos++ = sizeof(*oper); @@ -109,6 +118,66 @@ u16 copy_sta_vht_capab(struct hostapd_data *hapd, struct sta_info *sta, } +u16 copy_sta_vendor_vht(struct hostapd_data *hapd, struct sta_info *sta, + const u8 *ie, size_t len) +{ + const u8 *vht_capab; + unsigned int vht_capab_len; + + if (!ie || len < 5 + 2 + sizeof(struct ieee80211_vht_capabilities) || + hapd->conf->disable_11ac) + goto no_capab; + + /* The VHT Capabilities element embedded in vendor VHT */ + vht_capab = ie + 5; + if (vht_capab[0] != WLAN_EID_VHT_CAP) + goto no_capab; + vht_capab_len = vht_capab[1]; + if (vht_capab_len < sizeof(struct ieee80211_vht_capabilities) || + vht_capab_len > ie + len - vht_capab - 2) + goto no_capab; + vht_capab += 2; + + if (sta->vht_capabilities == NULL) { + sta->vht_capabilities = + os_zalloc(sizeof(struct ieee80211_vht_capabilities)); + if (sta->vht_capabilities == NULL) + return WLAN_STATUS_UNSPECIFIED_FAILURE; + } + + sta->flags |= WLAN_STA_VHT | WLAN_STA_VENDOR_VHT; + os_memcpy(sta->vht_capabilities, vht_capab, + sizeof(struct ieee80211_vht_capabilities)); + return WLAN_STATUS_SUCCESS; + +no_capab: + sta->flags &= ~WLAN_STA_VENDOR_VHT; + return WLAN_STATUS_SUCCESS; +} + + +u8 * hostapd_eid_vendor_vht(struct hostapd_data *hapd, u8 *eid) +{ + u8 *pos = eid; + + if (!hapd->iface->current_mode) + return eid; + + *pos++ = WLAN_EID_VENDOR_SPECIFIC; + *pos++ = (5 + /* The Vendor OUI, type and subtype */ + 2 + sizeof(struct ieee80211_vht_capabilities) + + 2 + sizeof(struct ieee80211_vht_operation)); + + WPA_PUT_BE32(pos, (OUI_BROADCOM << 8) | VENDOR_VHT_TYPE); + pos += 4; + *pos++ = VENDOR_VHT_SUBTYPE; + pos = hostapd_eid_vht_capabilities(hapd, pos); + pos = hostapd_eid_vht_operation(hapd, pos); + + return pos; +} + + u16 set_sta_vht_opmode(struct hostapd_data *hapd, struct sta_info *sta, const u8 *vht_oper_notif) { diff --git a/src/ap/sta_info.c b/src/ap/sta_info.c index 1c2197a42..f4de14093 100644 --- a/src/ap/sta_info.c +++ b/src/ap/sta_info.c @@ -1101,7 +1101,7 @@ int ap_sta_flags_txt(u32 flags, char *buf, size_t buflen) int res; buf[0] = '\0'; - res = os_snprintf(buf, buflen, "%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s", + res = os_snprintf(buf, buflen, "%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s", (flags & WLAN_STA_AUTH ? "[AUTH]" : ""), (flags & WLAN_STA_ASSOC ? "[ASSOC]" : ""), (flags & WLAN_STA_AUTHORIZED ? "[AUTHORIZED]" : ""), @@ -1119,6 +1119,7 @@ int ap_sta_flags_txt(u32 flags, char *buf, size_t buflen) (flags & WLAN_STA_WPS2 ? "[WPS2]" : ""), (flags & WLAN_STA_GAS ? "[GAS]" : ""), (flags & WLAN_STA_VHT ? "[VHT]" : ""), + (flags & WLAN_STA_VENDOR_VHT ? "[VENDOR_VHT]" : ""), (flags & WLAN_STA_WNM_SLEEP_MODE ? "[WNM_SLEEP_MODE]" : "")); if (os_snprintf_error(buflen, res)) diff --git a/src/ap/sta_info.h b/src/ap/sta_info.h index 588a9e2f2..59d0e2915 100644 --- a/src/ap/sta_info.h +++ b/src/ap/sta_info.h @@ -35,6 +35,7 @@ #define WLAN_STA_VHT BIT(18) #define WLAN_STA_WNM_SLEEP_MODE BIT(19) #define WLAN_STA_VHT_OPMODE_ENABLED BIT(20) +#define WLAN_STA_VENDOR_VHT BIT(21) #define WLAN_STA_PENDING_DISASSOC_CB BIT(29) #define WLAN_STA_PENDING_DEAUTH_CB BIT(30) #define WLAN_STA_NONERP BIT(31) diff --git a/src/common/ieee802_11_common.c b/src/common/ieee802_11_common.c index e1d45cf96..ed8d46619 100644 --- a/src/common/ieee802_11_common.c +++ b/src/common/ieee802_11_common.c @@ -128,6 +128,15 @@ static int ieee802_11_parse_vendor_specific(const u8 *pos, size_t elen, elems->vendor_ht_cap = pos; elems->vendor_ht_cap_len = elen; break; + case VENDOR_VHT_TYPE: + if (elen > 4 && + (pos[4] == VENDOR_VHT_SUBTYPE || + pos[4] == VENDOR_VHT_SUBTYPE2)) { + elems->vendor_vht = pos; + elems->vendor_vht_len = elen; + } else + return -1; + break; default: wpa_printf(MSG_EXCESSIVE, "Unknown Broadcom " "information element ignored " diff --git a/src/common/ieee802_11_common.h b/src/common/ieee802_11_common.h index 2357afc5c..05fe32b45 100644 --- a/src/common/ieee802_11_common.h +++ b/src/common/ieee802_11_common.h @@ -35,6 +35,7 @@ struct ieee802_11_elems { const u8 *vht_operation; const u8 *vht_opmode_notif; const u8 *vendor_ht_cap; + const u8 *vendor_vht; const u8 *p2p; const u8 *wfd; const u8 *link_id; @@ -71,6 +72,7 @@ struct ieee802_11_elems { u8 vht_capabilities_len; u8 vht_operation_len; u8 vendor_ht_cap_len; + u8 vendor_vht_len; u8 p2p_len; u8 wfd_len; u8 interworking_len; diff --git a/src/common/ieee802_11_defs.h b/src/common/ieee802_11_defs.h index dfe0fafd3..803b8ccc8 100644 --- a/src/common/ieee802_11_defs.h +++ b/src/common/ieee802_11_defs.h @@ -1155,6 +1155,9 @@ enum plink_action_field { }; #define OUI_BROADCOM 0x00904c /* Broadcom (Epigram) */ +#define VENDOR_VHT_TYPE 0x04 +#define VENDOR_VHT_SUBTYPE 0x08 +#define VENDOR_VHT_SUBTYPE2 0x00 #define VENDOR_HT_CAPAB_OUI_TYPE 0x33 /* 00-90-4c:0x33 */