1252190Srpaulo/* 2252190Srpaulo * hostapd / IEEE 802.11 Management 3252190Srpaulo * Copyright (c) 2002-2012, Jouni Malinen <j@w1.fi> 4252190Srpaulo * 5252190Srpaulo * This software may be distributed under the terms of the BSD license. 6252190Srpaulo * See README for more details. 7252190Srpaulo */ 8252190Srpaulo 9252190Srpaulo#include "utils/includes.h" 10252190Srpaulo 11252190Srpaulo#include "utils/common.h" 12252190Srpaulo#include "common/ieee802_11_defs.h" 13346981Scy#include "common/ocv.h" 14252190Srpaulo#include "hostapd.h" 15252190Srpaulo#include "sta_info.h" 16252190Srpaulo#include "ap_config.h" 17252190Srpaulo#include "ap_drv_ops.h" 18346981Scy#include "wpa_auth.h" 19252190Srpaulo#include "ieee802_11.h" 20252190Srpaulo 21252190Srpaulo 22252190Srpaulo#ifdef CONFIG_IEEE80211W 23252190Srpaulo 24252190Srpaulou8 * hostapd_eid_assoc_comeback_time(struct hostapd_data *hapd, 25252190Srpaulo struct sta_info *sta, u8 *eid) 26252190Srpaulo{ 27252190Srpaulo u8 *pos = eid; 28252190Srpaulo u32 timeout, tu; 29281806Srpaulo struct os_reltime now, passed; 30252190Srpaulo 31252190Srpaulo *pos++ = WLAN_EID_TIMEOUT_INTERVAL; 32252190Srpaulo *pos++ = 5; 33252190Srpaulo *pos++ = WLAN_TIMEOUT_ASSOC_COMEBACK; 34281806Srpaulo os_get_reltime(&now); 35281806Srpaulo os_reltime_sub(&now, &sta->sa_query_start, &passed); 36252190Srpaulo tu = (passed.sec * 1000000 + passed.usec) / 1024; 37252190Srpaulo if (hapd->conf->assoc_sa_query_max_timeout > tu) 38252190Srpaulo timeout = hapd->conf->assoc_sa_query_max_timeout - tu; 39252190Srpaulo else 40252190Srpaulo timeout = 0; 41252190Srpaulo if (timeout < hapd->conf->assoc_sa_query_max_timeout) 42252190Srpaulo timeout++; /* add some extra time for local timers */ 43252190Srpaulo WPA_PUT_LE32(pos, timeout); 44252190Srpaulo pos += 4; 45252190Srpaulo 46252190Srpaulo return pos; 47252190Srpaulo} 48252190Srpaulo 49252190Srpaulo 50252190Srpaulo/* MLME-SAQuery.request */ 51252190Srpaulovoid ieee802_11_send_sa_query_req(struct hostapd_data *hapd, 52252190Srpaulo const u8 *addr, const u8 *trans_id) 53252190Srpaulo{ 54346981Scy#ifdef CONFIG_OCV 55346981Scy struct sta_info *sta; 56346981Scy#endif /* CONFIG_OCV */ 57346981Scy struct ieee80211_mgmt *mgmt; 58346981Scy u8 *oci_ie = NULL; 59346981Scy u8 oci_ie_len = 0; 60252190Srpaulo u8 *end; 61252190Srpaulo 62252190Srpaulo wpa_printf(MSG_DEBUG, "IEEE 802.11: Sending SA Query Request to " 63252190Srpaulo MACSTR, MAC2STR(addr)); 64252190Srpaulo wpa_hexdump(MSG_DEBUG, "IEEE 802.11: SA Query Transaction ID", 65252190Srpaulo trans_id, WLAN_SA_QUERY_TR_ID_LEN); 66252190Srpaulo 67346981Scy#ifdef CONFIG_OCV 68346981Scy sta = ap_get_sta(hapd, addr); 69346981Scy if (sta && wpa_auth_uses_ocv(sta->wpa_sm)) { 70346981Scy struct wpa_channel_info ci; 71346981Scy 72346981Scy if (hostapd_drv_channel_info(hapd, &ci) != 0) { 73346981Scy wpa_printf(MSG_WARNING, 74346981Scy "Failed to get channel info for OCI element in SA Query Request"); 75346981Scy return; 76346981Scy } 77346981Scy 78346981Scy oci_ie_len = OCV_OCI_EXTENDED_LEN; 79346981Scy oci_ie = os_zalloc(oci_ie_len); 80346981Scy if (!oci_ie) { 81346981Scy wpa_printf(MSG_WARNING, 82346981Scy "Failed to allocate buffer for OCI element in SA Query Request"); 83346981Scy return; 84346981Scy } 85346981Scy 86346981Scy if (ocv_insert_extended_oci(&ci, oci_ie) < 0) { 87346981Scy os_free(oci_ie); 88346981Scy return; 89346981Scy } 90346981Scy } 91346981Scy#endif /* CONFIG_OCV */ 92346981Scy 93346981Scy mgmt = os_zalloc(sizeof(*mgmt) + oci_ie_len); 94346981Scy if (!mgmt) { 95346981Scy wpa_printf(MSG_DEBUG, 96346981Scy "Failed to allocate buffer for SA Query Response frame"); 97346981Scy os_free(oci_ie); 98346981Scy return; 99346981Scy } 100346981Scy 101346981Scy mgmt->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT, 102346981Scy WLAN_FC_STYPE_ACTION); 103346981Scy os_memcpy(mgmt->da, addr, ETH_ALEN); 104346981Scy os_memcpy(mgmt->sa, hapd->own_addr, ETH_ALEN); 105346981Scy os_memcpy(mgmt->bssid, hapd->own_addr, ETH_ALEN); 106346981Scy mgmt->u.action.category = WLAN_ACTION_SA_QUERY; 107346981Scy mgmt->u.action.u.sa_query_req.action = WLAN_SA_QUERY_REQUEST; 108346981Scy os_memcpy(mgmt->u.action.u.sa_query_req.trans_id, trans_id, 109252190Srpaulo WLAN_SA_QUERY_TR_ID_LEN); 110346981Scy end = mgmt->u.action.u.sa_query_req.variable; 111346981Scy#ifdef CONFIG_OCV 112346981Scy if (oci_ie_len > 0) { 113346981Scy os_memcpy(end, oci_ie, oci_ie_len); 114346981Scy end += oci_ie_len; 115346981Scy } 116346981Scy#endif /* CONFIG_OCV */ 117346981Scy if (hostapd_drv_send_mlme(hapd, mgmt, end - (u8 *) mgmt, 0) < 0) 118281806Srpaulo wpa_printf(MSG_INFO, "ieee802_11_send_sa_query_req: send failed"); 119346981Scy 120346981Scy os_free(mgmt); 121346981Scy os_free(oci_ie); 122252190Srpaulo} 123252190Srpaulo 124252190Srpaulo 125252190Srpaulostatic void ieee802_11_send_sa_query_resp(struct hostapd_data *hapd, 126252190Srpaulo const u8 *sa, const u8 *trans_id) 127252190Srpaulo{ 128252190Srpaulo struct sta_info *sta; 129346981Scy struct ieee80211_mgmt *resp; 130346981Scy u8 *oci_ie = NULL; 131346981Scy u8 oci_ie_len = 0; 132252190Srpaulo u8 *end; 133252190Srpaulo 134252190Srpaulo wpa_printf(MSG_DEBUG, "IEEE 802.11: Received SA Query Request from " 135252190Srpaulo MACSTR, MAC2STR(sa)); 136252190Srpaulo wpa_hexdump(MSG_DEBUG, "IEEE 802.11: SA Query Transaction ID", 137252190Srpaulo trans_id, WLAN_SA_QUERY_TR_ID_LEN); 138252190Srpaulo 139252190Srpaulo sta = ap_get_sta(hapd, sa); 140252190Srpaulo if (sta == NULL || !(sta->flags & WLAN_STA_ASSOC)) { 141252190Srpaulo wpa_printf(MSG_DEBUG, "IEEE 802.11: Ignore SA Query Request " 142252190Srpaulo "from unassociated STA " MACSTR, MAC2STR(sa)); 143252190Srpaulo return; 144252190Srpaulo } 145252190Srpaulo 146346981Scy#ifdef CONFIG_OCV 147346981Scy if (wpa_auth_uses_ocv(sta->wpa_sm)) { 148346981Scy struct wpa_channel_info ci; 149346981Scy 150346981Scy if (hostapd_drv_channel_info(hapd, &ci) != 0) { 151346981Scy wpa_printf(MSG_WARNING, 152346981Scy "Failed to get channel info for OCI element in SA Query Response"); 153346981Scy return; 154346981Scy } 155346981Scy 156346981Scy oci_ie_len = OCV_OCI_EXTENDED_LEN; 157346981Scy oci_ie = os_zalloc(oci_ie_len); 158346981Scy if (!oci_ie) { 159346981Scy wpa_printf(MSG_WARNING, 160346981Scy "Failed to allocate buffer for for OCI element in SA Query Response"); 161346981Scy return; 162346981Scy } 163346981Scy 164346981Scy if (ocv_insert_extended_oci(&ci, oci_ie) < 0) { 165346981Scy os_free(oci_ie); 166346981Scy return; 167346981Scy } 168346981Scy } 169346981Scy#endif /* CONFIG_OCV */ 170346981Scy 171346981Scy resp = os_zalloc(sizeof(*resp) + oci_ie_len); 172346981Scy if (!resp) { 173346981Scy wpa_printf(MSG_DEBUG, 174346981Scy "Failed to allocate buffer for SA Query Response frame"); 175346981Scy os_free(oci_ie); 176346981Scy return; 177346981Scy } 178346981Scy 179252190Srpaulo wpa_printf(MSG_DEBUG, "IEEE 802.11: Sending SA Query Response to " 180252190Srpaulo MACSTR, MAC2STR(sa)); 181252190Srpaulo 182346981Scy resp->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT, 183346981Scy WLAN_FC_STYPE_ACTION); 184346981Scy os_memcpy(resp->da, sa, ETH_ALEN); 185346981Scy os_memcpy(resp->sa, hapd->own_addr, ETH_ALEN); 186346981Scy os_memcpy(resp->bssid, hapd->own_addr, ETH_ALEN); 187346981Scy resp->u.action.category = WLAN_ACTION_SA_QUERY; 188346981Scy resp->u.action.u.sa_query_req.action = WLAN_SA_QUERY_RESPONSE; 189346981Scy os_memcpy(resp->u.action.u.sa_query_req.trans_id, trans_id, 190252190Srpaulo WLAN_SA_QUERY_TR_ID_LEN); 191346981Scy end = resp->u.action.u.sa_query_req.variable; 192346981Scy#ifdef CONFIG_OCV 193346981Scy if (oci_ie_len > 0) { 194346981Scy os_memcpy(end, oci_ie, oci_ie_len); 195346981Scy end += oci_ie_len; 196346981Scy } 197346981Scy#endif /* CONFIG_OCV */ 198346981Scy if (hostapd_drv_send_mlme(hapd, resp, end - (u8 *) resp, 0) < 0) 199281806Srpaulo wpa_printf(MSG_INFO, "ieee80211_mgmt_sa_query_request: send failed"); 200346981Scy 201346981Scy os_free(resp); 202346981Scy os_free(oci_ie); 203252190Srpaulo} 204252190Srpaulo 205252190Srpaulo 206346981Scyvoid ieee802_11_sa_query_action(struct hostapd_data *hapd, 207346981Scy const struct ieee80211_mgmt *mgmt, 208346981Scy size_t len) 209252190Srpaulo{ 210252190Srpaulo struct sta_info *sta; 211252190Srpaulo int i; 212346981Scy const u8 *sa = mgmt->sa; 213346981Scy const u8 action_type = mgmt->u.action.u.sa_query_resp.action; 214346981Scy const u8 *trans_id = mgmt->u.action.u.sa_query_resp.trans_id; 215252190Srpaulo 216346981Scy if (((const u8 *) mgmt) + len < 217346981Scy mgmt->u.action.u.sa_query_resp.variable) { 218346981Scy wpa_printf(MSG_DEBUG, 219346981Scy "IEEE 802.11: Too short SA Query Action frame (len=%lu)", 220346981Scy (unsigned long) len); 221346981Scy return; 222346981Scy } 223346981Scy 224346981Scy sta = ap_get_sta(hapd, sa); 225346981Scy 226346981Scy#ifdef CONFIG_OCV 227346981Scy if (sta && wpa_auth_uses_ocv(sta->wpa_sm)) { 228346981Scy struct ieee802_11_elems elems; 229346981Scy struct wpa_channel_info ci; 230346981Scy int tx_chanwidth; 231346981Scy int tx_seg1_idx; 232346981Scy size_t ies_len; 233346981Scy const u8 *ies; 234346981Scy 235346981Scy ies = mgmt->u.action.u.sa_query_resp.variable; 236346981Scy ies_len = len - (ies - (u8 *) mgmt); 237346981Scy if (ieee802_11_parse_elems(ies, ies_len, &elems, 1) == 238346981Scy ParseFailed) { 239346981Scy wpa_printf(MSG_DEBUG, 240346981Scy "SA Query: Failed to parse elements"); 241346981Scy return; 242346981Scy } 243346981Scy 244346981Scy if (hostapd_drv_channel_info(hapd, &ci) != 0) { 245346981Scy wpa_printf(MSG_WARNING, 246346981Scy "Failed to get channel info to validate received OCI in SA Query Action frame"); 247346981Scy return; 248346981Scy } 249346981Scy 250346981Scy if (get_sta_tx_parameters(sta->wpa_sm, 251346981Scy channel_width_to_int(ci.chanwidth), 252346981Scy ci.seg1_idx, &tx_chanwidth, 253346981Scy &tx_seg1_idx) < 0) 254346981Scy return; 255346981Scy 256346981Scy if (ocv_verify_tx_params(elems.oci, elems.oci_len, &ci, 257346981Scy tx_chanwidth, tx_seg1_idx) != 0) { 258346981Scy wpa_printf(MSG_WARNING, "%s", ocv_errorstr); 259346981Scy return; 260346981Scy } 261346981Scy } 262346981Scy#endif /* CONFIG_OCV */ 263346981Scy 264252190Srpaulo if (action_type == WLAN_SA_QUERY_REQUEST) { 265252190Srpaulo ieee802_11_send_sa_query_resp(hapd, sa, trans_id); 266252190Srpaulo return; 267252190Srpaulo } 268252190Srpaulo 269252190Srpaulo if (action_type != WLAN_SA_QUERY_RESPONSE) { 270252190Srpaulo wpa_printf(MSG_DEBUG, "IEEE 802.11: Unexpected SA Query " 271252190Srpaulo "Action %d", action_type); 272252190Srpaulo return; 273252190Srpaulo } 274252190Srpaulo 275252190Srpaulo wpa_printf(MSG_DEBUG, "IEEE 802.11: Received SA Query Response from " 276252190Srpaulo MACSTR, MAC2STR(sa)); 277252190Srpaulo wpa_hexdump(MSG_DEBUG, "IEEE 802.11: SA Query Transaction ID", 278252190Srpaulo trans_id, WLAN_SA_QUERY_TR_ID_LEN); 279252190Srpaulo 280252190Srpaulo /* MLME-SAQuery.confirm */ 281252190Srpaulo 282252190Srpaulo if (sta == NULL || sta->sa_query_trans_id == NULL) { 283252190Srpaulo wpa_printf(MSG_DEBUG, "IEEE 802.11: No matching STA with " 284252190Srpaulo "pending SA Query request found"); 285252190Srpaulo return; 286252190Srpaulo } 287252190Srpaulo 288252190Srpaulo for (i = 0; i < sta->sa_query_count; i++) { 289252190Srpaulo if (os_memcmp(sta->sa_query_trans_id + 290252190Srpaulo i * WLAN_SA_QUERY_TR_ID_LEN, 291252190Srpaulo trans_id, WLAN_SA_QUERY_TR_ID_LEN) == 0) 292252190Srpaulo break; 293252190Srpaulo } 294252190Srpaulo 295252190Srpaulo if (i >= sta->sa_query_count) { 296252190Srpaulo wpa_printf(MSG_DEBUG, "IEEE 802.11: No matching SA Query " 297252190Srpaulo "transaction identifier found"); 298252190Srpaulo return; 299252190Srpaulo } 300252190Srpaulo 301252190Srpaulo hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, 302252190Srpaulo HOSTAPD_LEVEL_DEBUG, 303252190Srpaulo "Reply to pending SA Query received"); 304252190Srpaulo ap_sta_stop_sa_query(hapd, sta); 305252190Srpaulo} 306252190Srpaulo 307252190Srpaulo#endif /* CONFIG_IEEE80211W */ 308252190Srpaulo 309252190Srpaulo 310281806Srpaulostatic void hostapd_ext_capab_byte(struct hostapd_data *hapd, u8 *pos, int idx) 311281806Srpaulo{ 312281806Srpaulo *pos = 0x00; 313281806Srpaulo 314281806Srpaulo switch (idx) { 315281806Srpaulo case 0: /* Bits 0-7 */ 316281806Srpaulo if (hapd->iconf->obss_interval) 317281806Srpaulo *pos |= 0x01; /* Bit 0 - Coexistence management */ 318337817Scy if (hapd->iface->drv_flags & WPA_DRIVER_FLAGS_AP_CSA) 319337817Scy *pos |= 0x04; /* Bit 2 - Extended Channel Switching */ 320281806Srpaulo break; 321281806Srpaulo case 1: /* Bits 8-15 */ 322281806Srpaulo if (hapd->conf->proxy_arp) 323281806Srpaulo *pos |= 0x10; /* Bit 12 - Proxy ARP */ 324346981Scy if (hapd->conf->coloc_intf_reporting) { 325346981Scy /* Bit 13 - Collocated Interference Reporting */ 326346981Scy *pos |= 0x20; 327346981Scy } 328281806Srpaulo break; 329281806Srpaulo case 2: /* Bits 16-23 */ 330281806Srpaulo if (hapd->conf->wnm_sleep_mode) 331281806Srpaulo *pos |= 0x02; /* Bit 17 - WNM-Sleep Mode */ 332281806Srpaulo if (hapd->conf->bss_transition) 333281806Srpaulo *pos |= 0x08; /* Bit 19 - BSS Transition */ 334281806Srpaulo break; 335281806Srpaulo case 3: /* Bits 24-31 */ 336346981Scy#ifdef CONFIG_WNM_AP 337281806Srpaulo *pos |= 0x02; /* Bit 25 - SSID List */ 338346981Scy#endif /* CONFIG_WNM_AP */ 339281806Srpaulo if (hapd->conf->time_advertisement == 2) 340281806Srpaulo *pos |= 0x08; /* Bit 27 - UTC TSF Offset */ 341281806Srpaulo if (hapd->conf->interworking) 342281806Srpaulo *pos |= 0x80; /* Bit 31 - Interworking */ 343281806Srpaulo break; 344281806Srpaulo case 4: /* Bits 32-39 */ 345281806Srpaulo if (hapd->conf->qos_map_set_len) 346281806Srpaulo *pos |= 0x01; /* Bit 32 - QoS Map */ 347281806Srpaulo if (hapd->conf->tdls & TDLS_PROHIBIT) 348281806Srpaulo *pos |= 0x40; /* Bit 38 - TDLS Prohibited */ 349281806Srpaulo if (hapd->conf->tdls & TDLS_PROHIBIT_CHAN_SWITCH) { 350281806Srpaulo /* Bit 39 - TDLS Channel Switching Prohibited */ 351281806Srpaulo *pos |= 0x80; 352281806Srpaulo } 353281806Srpaulo break; 354281806Srpaulo case 5: /* Bits 40-47 */ 355281806Srpaulo#ifdef CONFIG_HS20 356281806Srpaulo if (hapd->conf->hs20) 357281806Srpaulo *pos |= 0x40; /* Bit 46 - WNM-Notification */ 358281806Srpaulo#endif /* CONFIG_HS20 */ 359337817Scy#ifdef CONFIG_MBO 360337817Scy if (hapd->conf->mbo_enabled) 361337817Scy *pos |= 0x40; /* Bit 46 - WNM-Notification */ 362337817Scy#endif /* CONFIG_MBO */ 363281806Srpaulo break; 364281806Srpaulo case 6: /* Bits 48-55 */ 365281806Srpaulo if (hapd->conf->ssid.utf8_ssid) 366281806Srpaulo *pos |= 0x01; /* Bit 48 - UTF-8 SSID */ 367281806Srpaulo break; 368346981Scy case 7: /* Bits 56-63 */ 369346981Scy break; 370337817Scy case 8: /* Bits 64-71 */ 371337817Scy if (hapd->conf->ftm_responder) 372337817Scy *pos |= 0x40; /* Bit 70 - FTM responder */ 373337817Scy if (hapd->conf->ftm_initiator) 374337817Scy *pos |= 0x80; /* Bit 71 - FTM initiator */ 375337817Scy break; 376346981Scy case 9: /* Bits 72-79 */ 377346981Scy#ifdef CONFIG_FILS 378346981Scy if ((hapd->conf->wpa & WPA_PROTO_RSN) && 379346981Scy wpa_key_mgmt_fils(hapd->conf->wpa_key_mgmt)) 380346981Scy *pos |= 0x01; 381346981Scy#endif /* CONFIG_FILS */ 382346981Scy break; 383346981Scy case 10: /* Bits 80-87 */ 384346981Scy#ifdef CONFIG_SAE 385346981Scy if (hapd->conf->wpa && 386346981Scy wpa_key_mgmt_sae(hapd->conf->wpa_key_mgmt)) { 387346981Scy int in_use = hostapd_sae_pw_id_in_use(hapd->conf); 388346981Scy 389346981Scy if (in_use) 390346981Scy *pos |= 0x02; /* Bit 81 - SAE Password 391346981Scy * Identifiers In Use */ 392346981Scy if (in_use == 2) 393346981Scy *pos |= 0x04; /* Bit 82 - SAE Password 394346981Scy * Identifiers Used Exclusively */ 395346981Scy } 396346981Scy#endif /* CONFIG_SAE */ 397346981Scy break; 398281806Srpaulo } 399281806Srpaulo} 400281806Srpaulo 401281806Srpaulo 402252190Srpaulou8 * hostapd_eid_ext_capab(struct hostapd_data *hapd, u8 *eid) 403252190Srpaulo{ 404252190Srpaulo u8 *pos = eid; 405281806Srpaulo u8 len = 0, i; 406252190Srpaulo 407252190Srpaulo if (hapd->conf->tdls & (TDLS_PROHIBIT | TDLS_PROHIBIT_CHAN_SWITCH)) 408252190Srpaulo len = 5; 409252190Srpaulo if (len < 4 && hapd->conf->interworking) 410252190Srpaulo len = 4; 411252190Srpaulo if (len < 3 && hapd->conf->wnm_sleep_mode) 412252190Srpaulo len = 3; 413281806Srpaulo if (len < 1 && hapd->iconf->obss_interval) 414281806Srpaulo len = 1; 415252190Srpaulo if (len < 7 && hapd->conf->ssid.utf8_ssid) 416252190Srpaulo len = 7; 417337817Scy if (len < 9 && 418337817Scy (hapd->conf->ftm_initiator || hapd->conf->ftm_responder)) 419337817Scy len = 9; 420346981Scy#ifdef CONFIG_WNM_AP 421252190Srpaulo if (len < 4) 422252190Srpaulo len = 4; 423346981Scy#endif /* CONFIG_WNM_AP */ 424281806Srpaulo#ifdef CONFIG_HS20 425281806Srpaulo if (hapd->conf->hs20 && len < 6) 426281806Srpaulo len = 6; 427281806Srpaulo#endif /* CONFIG_HS20 */ 428337817Scy#ifdef CONFIG_MBO 429337817Scy if (hapd->conf->mbo_enabled && len < 6) 430337817Scy len = 6; 431337817Scy#endif /* CONFIG_MBO */ 432346981Scy#ifdef CONFIG_FILS 433346981Scy if ((!(hapd->conf->wpa & WPA_PROTO_RSN) || 434346981Scy !wpa_key_mgmt_fils(hapd->conf->wpa_key_mgmt)) && len < 10) 435346981Scy len = 10; 436346981Scy#endif /* CONFIG_FILS */ 437346981Scy#ifdef CONFIG_SAE 438346981Scy if (len < 11 && hapd->conf->wpa && 439346981Scy wpa_key_mgmt_sae(hapd->conf->wpa_key_mgmt) && 440346981Scy hostapd_sae_pw_id_in_use(hapd->conf)) 441346981Scy len = 11; 442346981Scy#endif /* CONFIG_SAE */ 443281806Srpaulo if (len < hapd->iface->extended_capa_len) 444281806Srpaulo len = hapd->iface->extended_capa_len; 445252190Srpaulo if (len == 0) 446252190Srpaulo return eid; 447252190Srpaulo 448252190Srpaulo *pos++ = WLAN_EID_EXT_CAPAB; 449252190Srpaulo *pos++ = len; 450281806Srpaulo for (i = 0; i < len; i++, pos++) { 451281806Srpaulo hostapd_ext_capab_byte(hapd, pos, i); 452252190Srpaulo 453281806Srpaulo if (i < hapd->iface->extended_capa_len) { 454281806Srpaulo *pos &= ~hapd->iface->extended_capa_mask[i]; 455281806Srpaulo *pos |= hapd->iface->extended_capa[i]; 456281806Srpaulo } 457281806Srpaulo } 458252190Srpaulo 459281806Srpaulo while (len > 0 && eid[1 + len] == 0) { 460281806Srpaulo len--; 461281806Srpaulo eid[1] = len; 462281806Srpaulo } 463281806Srpaulo if (len == 0) 464281806Srpaulo return eid; 465252190Srpaulo 466281806Srpaulo return eid + 2 + len; 467281806Srpaulo} 468252190Srpaulo 469252190Srpaulo 470281806Srpaulou8 * hostapd_eid_qos_map_set(struct hostapd_data *hapd, u8 *eid) 471281806Srpaulo{ 472281806Srpaulo u8 *pos = eid; 473281806Srpaulo u8 len = hapd->conf->qos_map_set_len; 474252190Srpaulo 475281806Srpaulo if (!len) 476281806Srpaulo return eid; 477281806Srpaulo 478281806Srpaulo *pos++ = WLAN_EID_QOS_MAP_SET; 479281806Srpaulo *pos++ = len; 480281806Srpaulo os_memcpy(pos, hapd->conf->qos_map_set, len); 481281806Srpaulo pos += len; 482281806Srpaulo 483252190Srpaulo return pos; 484252190Srpaulo} 485252190Srpaulo 486252190Srpaulo 487252190Srpaulou8 * hostapd_eid_interworking(struct hostapd_data *hapd, u8 *eid) 488252190Srpaulo{ 489252190Srpaulo u8 *pos = eid; 490252190Srpaulo#ifdef CONFIG_INTERWORKING 491252190Srpaulo u8 *len; 492252190Srpaulo 493252190Srpaulo if (!hapd->conf->interworking) 494252190Srpaulo return eid; 495252190Srpaulo 496252190Srpaulo *pos++ = WLAN_EID_INTERWORKING; 497252190Srpaulo len = pos++; 498252190Srpaulo 499252190Srpaulo *pos = hapd->conf->access_network_type; 500252190Srpaulo if (hapd->conf->internet) 501252190Srpaulo *pos |= INTERWORKING_ANO_INTERNET; 502252190Srpaulo if (hapd->conf->asra) 503252190Srpaulo *pos |= INTERWORKING_ANO_ASRA; 504252190Srpaulo if (hapd->conf->esr) 505252190Srpaulo *pos |= INTERWORKING_ANO_ESR; 506252190Srpaulo if (hapd->conf->uesa) 507252190Srpaulo *pos |= INTERWORKING_ANO_UESA; 508252190Srpaulo pos++; 509252190Srpaulo 510252190Srpaulo if (hapd->conf->venue_info_set) { 511252190Srpaulo *pos++ = hapd->conf->venue_group; 512252190Srpaulo *pos++ = hapd->conf->venue_type; 513252190Srpaulo } 514252190Srpaulo 515252190Srpaulo if (!is_zero_ether_addr(hapd->conf->hessid)) { 516252190Srpaulo os_memcpy(pos, hapd->conf->hessid, ETH_ALEN); 517252190Srpaulo pos += ETH_ALEN; 518252190Srpaulo } 519252190Srpaulo 520252190Srpaulo *len = pos - len - 1; 521252190Srpaulo#endif /* CONFIG_INTERWORKING */ 522252190Srpaulo 523252190Srpaulo return pos; 524252190Srpaulo} 525252190Srpaulo 526252190Srpaulo 527252190Srpaulou8 * hostapd_eid_adv_proto(struct hostapd_data *hapd, u8 *eid) 528252190Srpaulo{ 529252190Srpaulo u8 *pos = eid; 530252190Srpaulo#ifdef CONFIG_INTERWORKING 531252190Srpaulo 532252190Srpaulo /* TODO: Separate configuration for ANQP? */ 533252190Srpaulo if (!hapd->conf->interworking) 534252190Srpaulo return eid; 535252190Srpaulo 536252190Srpaulo *pos++ = WLAN_EID_ADV_PROTO; 537252190Srpaulo *pos++ = 2; 538252190Srpaulo *pos++ = 0x7F; /* Query Response Length Limit | PAME-BI */ 539252190Srpaulo *pos++ = ACCESS_NETWORK_QUERY_PROTOCOL; 540252190Srpaulo#endif /* CONFIG_INTERWORKING */ 541252190Srpaulo 542252190Srpaulo return pos; 543252190Srpaulo} 544252190Srpaulo 545252190Srpaulo 546252190Srpaulou8 * hostapd_eid_roaming_consortium(struct hostapd_data *hapd, u8 *eid) 547252190Srpaulo{ 548252190Srpaulo u8 *pos = eid; 549252190Srpaulo#ifdef CONFIG_INTERWORKING 550252190Srpaulo u8 *len; 551252190Srpaulo unsigned int i, count; 552252190Srpaulo 553252190Srpaulo if (!hapd->conf->interworking || 554252190Srpaulo hapd->conf->roaming_consortium == NULL || 555252190Srpaulo hapd->conf->roaming_consortium_count == 0) 556252190Srpaulo return eid; 557252190Srpaulo 558252190Srpaulo *pos++ = WLAN_EID_ROAMING_CONSORTIUM; 559252190Srpaulo len = pos++; 560252190Srpaulo 561252190Srpaulo /* Number of ANQP OIs (in addition to the max 3 listed here) */ 562252190Srpaulo if (hapd->conf->roaming_consortium_count > 3 + 255) 563252190Srpaulo *pos++ = 255; 564252190Srpaulo else if (hapd->conf->roaming_consortium_count > 3) 565252190Srpaulo *pos++ = hapd->conf->roaming_consortium_count - 3; 566252190Srpaulo else 567252190Srpaulo *pos++ = 0; 568252190Srpaulo 569252190Srpaulo /* OU #1 and #2 Lengths */ 570252190Srpaulo *pos = hapd->conf->roaming_consortium[0].len; 571252190Srpaulo if (hapd->conf->roaming_consortium_count > 1) 572252190Srpaulo *pos |= hapd->conf->roaming_consortium[1].len << 4; 573252190Srpaulo pos++; 574252190Srpaulo 575252190Srpaulo if (hapd->conf->roaming_consortium_count > 3) 576252190Srpaulo count = 3; 577252190Srpaulo else 578252190Srpaulo count = hapd->conf->roaming_consortium_count; 579252190Srpaulo 580252190Srpaulo for (i = 0; i < count; i++) { 581252190Srpaulo os_memcpy(pos, hapd->conf->roaming_consortium[i].oi, 582252190Srpaulo hapd->conf->roaming_consortium[i].len); 583252190Srpaulo pos += hapd->conf->roaming_consortium[i].len; 584252190Srpaulo } 585252190Srpaulo 586252190Srpaulo *len = pos - len - 1; 587252190Srpaulo#endif /* CONFIG_INTERWORKING */ 588252190Srpaulo 589252190Srpaulo return pos; 590252190Srpaulo} 591252190Srpaulo 592252190Srpaulo 593252190Srpaulou8 * hostapd_eid_time_adv(struct hostapd_data *hapd, u8 *eid) 594252190Srpaulo{ 595252190Srpaulo if (hapd->conf->time_advertisement != 2) 596252190Srpaulo return eid; 597252190Srpaulo 598252190Srpaulo if (hapd->time_adv == NULL && 599252190Srpaulo hostapd_update_time_adv(hapd) < 0) 600252190Srpaulo return eid; 601252190Srpaulo 602252190Srpaulo if (hapd->time_adv == NULL) 603252190Srpaulo return eid; 604252190Srpaulo 605252190Srpaulo os_memcpy(eid, wpabuf_head(hapd->time_adv), 606252190Srpaulo wpabuf_len(hapd->time_adv)); 607252190Srpaulo eid += wpabuf_len(hapd->time_adv); 608252190Srpaulo 609252190Srpaulo return eid; 610252190Srpaulo} 611252190Srpaulo 612252190Srpaulo 613252190Srpaulou8 * hostapd_eid_time_zone(struct hostapd_data *hapd, u8 *eid) 614252190Srpaulo{ 615252190Srpaulo size_t len; 616252190Srpaulo 617346981Scy if (hapd->conf->time_advertisement != 2 || !hapd->conf->time_zone) 618252190Srpaulo return eid; 619252190Srpaulo 620252190Srpaulo len = os_strlen(hapd->conf->time_zone); 621252190Srpaulo 622252190Srpaulo *eid++ = WLAN_EID_TIME_ZONE; 623252190Srpaulo *eid++ = len; 624252190Srpaulo os_memcpy(eid, hapd->conf->time_zone, len); 625252190Srpaulo eid += len; 626252190Srpaulo 627252190Srpaulo return eid; 628252190Srpaulo} 629252190Srpaulo 630252190Srpaulo 631252190Srpauloint hostapd_update_time_adv(struct hostapd_data *hapd) 632252190Srpaulo{ 633252190Srpaulo const int elen = 2 + 1 + 10 + 5 + 1; 634252190Srpaulo struct os_time t; 635252190Srpaulo struct os_tm tm; 636252190Srpaulo u8 *pos; 637252190Srpaulo 638252190Srpaulo if (hapd->conf->time_advertisement != 2) 639252190Srpaulo return 0; 640252190Srpaulo 641252190Srpaulo if (os_get_time(&t) < 0 || os_gmtime(t.sec, &tm) < 0) 642252190Srpaulo return -1; 643252190Srpaulo 644252190Srpaulo if (!hapd->time_adv) { 645252190Srpaulo hapd->time_adv = wpabuf_alloc(elen); 646252190Srpaulo if (hapd->time_adv == NULL) 647252190Srpaulo return -1; 648252190Srpaulo pos = wpabuf_put(hapd->time_adv, elen); 649252190Srpaulo } else 650252190Srpaulo pos = wpabuf_mhead_u8(hapd->time_adv); 651252190Srpaulo 652252190Srpaulo *pos++ = WLAN_EID_TIME_ADVERTISEMENT; 653252190Srpaulo *pos++ = 1 + 10 + 5 + 1; 654252190Srpaulo 655252190Srpaulo *pos++ = 2; /* UTC time at which the TSF timer is 0 */ 656252190Srpaulo 657252190Srpaulo /* Time Value at TSF 0 */ 658252190Srpaulo /* FIX: need to calculate this based on the current TSF value */ 659252190Srpaulo WPA_PUT_LE16(pos, tm.year); /* Year */ 660252190Srpaulo pos += 2; 661252190Srpaulo *pos++ = tm.month; /* Month */ 662252190Srpaulo *pos++ = tm.day; /* Day of month */ 663252190Srpaulo *pos++ = tm.hour; /* Hours */ 664252190Srpaulo *pos++ = tm.min; /* Minutes */ 665252190Srpaulo *pos++ = tm.sec; /* Seconds */ 666252190Srpaulo WPA_PUT_LE16(pos, 0); /* Milliseconds (not used) */ 667252190Srpaulo pos += 2; 668252190Srpaulo *pos++ = 0; /* Reserved */ 669252190Srpaulo 670252190Srpaulo /* Time Error */ 671252190Srpaulo /* TODO: fill in an estimate on the error */ 672252190Srpaulo *pos++ = 0; 673252190Srpaulo *pos++ = 0; 674252190Srpaulo *pos++ = 0; 675252190Srpaulo *pos++ = 0; 676252190Srpaulo *pos++ = 0; 677252190Srpaulo 678252190Srpaulo *pos++ = hapd->time_update_counter++; 679252190Srpaulo 680252190Srpaulo return 0; 681252190Srpaulo} 682252190Srpaulo 683252190Srpaulo 684252190Srpaulou8 * hostapd_eid_bss_max_idle_period(struct hostapd_data *hapd, u8 *eid) 685252190Srpaulo{ 686252190Srpaulo u8 *pos = eid; 687252190Srpaulo 688346981Scy#ifdef CONFIG_WNM_AP 689252190Srpaulo if (hapd->conf->ap_max_inactivity > 0) { 690252190Srpaulo unsigned int val; 691252190Srpaulo *pos++ = WLAN_EID_BSS_MAX_IDLE_PERIOD; 692252190Srpaulo *pos++ = 3; 693252190Srpaulo val = hapd->conf->ap_max_inactivity; 694252190Srpaulo if (val > 68000) 695252190Srpaulo val = 68000; 696252190Srpaulo val *= 1000; 697252190Srpaulo val /= 1024; 698252190Srpaulo if (val == 0) 699252190Srpaulo val = 1; 700252190Srpaulo if (val > 65535) 701252190Srpaulo val = 65535; 702252190Srpaulo WPA_PUT_LE16(pos, val); 703252190Srpaulo pos += 2; 704252190Srpaulo *pos++ = 0x00; /* TODO: Protected Keep-Alive Required */ 705252190Srpaulo } 706346981Scy#endif /* CONFIG_WNM_AP */ 707252190Srpaulo 708252190Srpaulo return pos; 709252190Srpaulo} 710337817Scy 711337817Scy 712337817Scy#ifdef CONFIG_MBO 713337817Scy 714346981Scyu8 * hostapd_eid_mbo_rssi_assoc_rej(struct hostapd_data *hapd, u8 *eid, 715346981Scy size_t len, int delta) 716346981Scy{ 717346981Scy u8 mbo[4]; 718346981Scy 719346981Scy mbo[0] = OCE_ATTR_ID_RSSI_BASED_ASSOC_REJECT; 720346981Scy mbo[1] = 2; 721346981Scy /* Delta RSSI */ 722346981Scy mbo[2] = delta; 723346981Scy /* Retry delay */ 724346981Scy mbo[3] = hapd->iconf->rssi_reject_assoc_timeout; 725346981Scy 726346981Scy return eid + mbo_add_ie(eid, len, mbo, 4); 727346981Scy} 728346981Scy 729346981Scy 730337817Scyu8 * hostapd_eid_mbo(struct hostapd_data *hapd, u8 *eid, size_t len) 731337817Scy{ 732346981Scy u8 mbo[9], *mbo_pos = mbo; 733337817Scy u8 *pos = eid; 734337817Scy 735346981Scy if (!hapd->conf->mbo_enabled && 736346981Scy !OCE_STA_CFON_ENABLED(hapd) && !OCE_AP_ENABLED(hapd)) 737337817Scy return eid; 738337817Scy 739346981Scy if (hapd->conf->mbo_enabled) { 740346981Scy *mbo_pos++ = MBO_ATTR_ID_AP_CAPA_IND; 741346981Scy *mbo_pos++ = 1; 742346981Scy /* Not Cellular aware */ 743346981Scy *mbo_pos++ = 0; 744346981Scy } 745337817Scy 746346981Scy if (hapd->conf->mbo_enabled && hapd->mbo_assoc_disallow) { 747337817Scy *mbo_pos++ = MBO_ATTR_ID_ASSOC_DISALLOW; 748337817Scy *mbo_pos++ = 1; 749337817Scy *mbo_pos++ = hapd->mbo_assoc_disallow; 750337817Scy } 751337817Scy 752346981Scy if (OCE_STA_CFON_ENABLED(hapd) || OCE_AP_ENABLED(hapd)) { 753346981Scy u8 ctrl; 754346981Scy 755346981Scy ctrl = OCE_RELEASE; 756346981Scy if (OCE_STA_CFON_ENABLED(hapd) && !OCE_AP_ENABLED(hapd)) 757346981Scy ctrl |= OCE_IS_STA_CFON; 758346981Scy 759346981Scy *mbo_pos++ = OCE_ATTR_ID_CAPA_IND; 760346981Scy *mbo_pos++ = 1; 761346981Scy *mbo_pos++ = ctrl; 762346981Scy } 763346981Scy 764337817Scy pos += mbo_add_ie(pos, len, mbo, mbo_pos - mbo); 765337817Scy 766337817Scy return pos; 767337817Scy} 768337817Scy 769337817Scy 770337817Scyu8 hostapd_mbo_ie_len(struct hostapd_data *hapd) 771337817Scy{ 772346981Scy u8 len; 773346981Scy 774346981Scy if (!hapd->conf->mbo_enabled && 775346981Scy !OCE_STA_CFON_ENABLED(hapd) && !OCE_AP_ENABLED(hapd)) 776337817Scy return 0; 777337817Scy 778337817Scy /* 779337817Scy * MBO IE header (6) + Capability Indication attribute (3) + 780337817Scy * Association Disallowed attribute (3) = 12 781337817Scy */ 782346981Scy len = 6; 783346981Scy if (hapd->conf->mbo_enabled) 784346981Scy len += 3 + (hapd->mbo_assoc_disallow ? 3 : 0); 785346981Scy 786346981Scy /* OCE capability indication attribute (3) */ 787346981Scy if (OCE_STA_CFON_ENABLED(hapd) || OCE_AP_ENABLED(hapd)) 788346981Scy len += 3; 789346981Scy 790346981Scy return len; 791337817Scy} 792337817Scy 793337817Scy#endif /* CONFIG_MBO */ 794337817Scy 795337817Scy 796346981Scy#ifdef CONFIG_OWE 797346981Scystatic int hostapd_eid_owe_trans_enabled(struct hostapd_data *hapd) 798346981Scy{ 799346981Scy return hapd->conf->owe_transition_ssid_len > 0 && 800346981Scy !is_zero_ether_addr(hapd->conf->owe_transition_bssid); 801346981Scy} 802346981Scy#endif /* CONFIG_OWE */ 803346981Scy 804346981Scy 805346981Scysize_t hostapd_eid_owe_trans_len(struct hostapd_data *hapd) 806346981Scy{ 807346981Scy#ifdef CONFIG_OWE 808346981Scy if (!hostapd_eid_owe_trans_enabled(hapd)) 809346981Scy return 0; 810346981Scy return 6 + ETH_ALEN + 1 + hapd->conf->owe_transition_ssid_len; 811346981Scy#else /* CONFIG_OWE */ 812346981Scy return 0; 813346981Scy#endif /* CONFIG_OWE */ 814346981Scy} 815346981Scy 816346981Scy 817346981Scyu8 * hostapd_eid_owe_trans(struct hostapd_data *hapd, u8 *eid, 818346981Scy size_t len) 819346981Scy{ 820346981Scy#ifdef CONFIG_OWE 821346981Scy u8 *pos = eid; 822346981Scy size_t elen; 823346981Scy 824346981Scy if (hapd->conf->owe_transition_ifname[0] && 825346981Scy !hostapd_eid_owe_trans_enabled(hapd)) 826346981Scy hostapd_owe_trans_get_info(hapd); 827346981Scy 828346981Scy if (!hostapd_eid_owe_trans_enabled(hapd)) 829346981Scy return pos; 830346981Scy 831346981Scy elen = hostapd_eid_owe_trans_len(hapd); 832346981Scy if (len < elen) { 833346981Scy wpa_printf(MSG_DEBUG, 834346981Scy "OWE: Not enough room in the buffer for OWE IE"); 835346981Scy return pos; 836346981Scy } 837346981Scy 838346981Scy *pos++ = WLAN_EID_VENDOR_SPECIFIC; 839346981Scy *pos++ = elen - 2; 840346981Scy WPA_PUT_BE24(pos, OUI_WFA); 841346981Scy pos += 3; 842346981Scy *pos++ = OWE_OUI_TYPE; 843346981Scy os_memcpy(pos, hapd->conf->owe_transition_bssid, ETH_ALEN); 844346981Scy pos += ETH_ALEN; 845346981Scy *pos++ = hapd->conf->owe_transition_ssid_len; 846346981Scy os_memcpy(pos, hapd->conf->owe_transition_ssid, 847346981Scy hapd->conf->owe_transition_ssid_len); 848346981Scy pos += hapd->conf->owe_transition_ssid_len; 849346981Scy 850346981Scy return pos; 851346981Scy#else /* CONFIG_OWE */ 852346981Scy return eid; 853346981Scy#endif /* CONFIG_OWE */ 854346981Scy} 855346981Scy 856346981Scy 857337817Scyvoid ap_copy_sta_supp_op_classes(struct sta_info *sta, 858337817Scy const u8 *supp_op_classes, 859337817Scy size_t supp_op_classes_len) 860337817Scy{ 861337817Scy if (!supp_op_classes) 862337817Scy return; 863337817Scy os_free(sta->supp_op_classes); 864337817Scy sta->supp_op_classes = os_malloc(1 + supp_op_classes_len); 865337817Scy if (!sta->supp_op_classes) 866337817Scy return; 867337817Scy 868337817Scy sta->supp_op_classes[0] = supp_op_classes_len; 869337817Scy os_memcpy(sta->supp_op_classes + 1, supp_op_classes, 870337817Scy supp_op_classes_len); 871337817Scy} 872346981Scy 873346981Scy 874346981Scyu8 * hostapd_eid_fils_indic(struct hostapd_data *hapd, u8 *eid, int hessid) 875346981Scy{ 876346981Scy u8 *pos = eid; 877346981Scy#ifdef CONFIG_FILS 878346981Scy u8 *len; 879346981Scy u16 fils_info = 0; 880346981Scy size_t realms; 881346981Scy struct fils_realm *realm; 882346981Scy 883346981Scy if (!(hapd->conf->wpa & WPA_PROTO_RSN) || 884346981Scy !wpa_key_mgmt_fils(hapd->conf->wpa_key_mgmt)) 885346981Scy return pos; 886346981Scy 887346981Scy realms = dl_list_len(&hapd->conf->fils_realms); 888346981Scy if (realms > 7) 889346981Scy realms = 7; /* 3 bit count field limits this to max 7 */ 890346981Scy 891346981Scy *pos++ = WLAN_EID_FILS_INDICATION; 892346981Scy len = pos++; 893346981Scy /* TODO: B0..B2: Number of Public Key Identifiers */ 894346981Scy if (hapd->conf->erp_domain) { 895346981Scy /* B3..B5: Number of Realm Identifiers */ 896346981Scy fils_info |= realms << 3; 897346981Scy } 898346981Scy /* TODO: B6: FILS IP Address Configuration */ 899346981Scy if (hapd->conf->fils_cache_id_set) 900346981Scy fils_info |= BIT(7); 901346981Scy if (hessid && !is_zero_ether_addr(hapd->conf->hessid)) 902346981Scy fils_info |= BIT(8); /* HESSID Included */ 903346981Scy /* FILS Shared Key Authentication without PFS Supported */ 904346981Scy fils_info |= BIT(9); 905346981Scy if (hapd->conf->fils_dh_group) { 906346981Scy /* FILS Shared Key Authentication with PFS Supported */ 907346981Scy fils_info |= BIT(10); 908346981Scy } 909346981Scy /* TODO: B11: FILS Public Key Authentication Supported */ 910346981Scy /* B12..B15: Reserved */ 911346981Scy WPA_PUT_LE16(pos, fils_info); 912346981Scy pos += 2; 913346981Scy if (hapd->conf->fils_cache_id_set) { 914346981Scy os_memcpy(pos, hapd->conf->fils_cache_id, FILS_CACHE_ID_LEN); 915346981Scy pos += FILS_CACHE_ID_LEN; 916346981Scy } 917346981Scy if (hessid && !is_zero_ether_addr(hapd->conf->hessid)) { 918346981Scy os_memcpy(pos, hapd->conf->hessid, ETH_ALEN); 919346981Scy pos += ETH_ALEN; 920346981Scy } 921346981Scy 922346981Scy dl_list_for_each(realm, &hapd->conf->fils_realms, struct fils_realm, 923346981Scy list) { 924346981Scy if (realms == 0) 925346981Scy break; 926346981Scy realms--; 927346981Scy os_memcpy(pos, realm->hash, 2); 928346981Scy pos += 2; 929346981Scy } 930346981Scy *len = pos - len - 1; 931346981Scy#endif /* CONFIG_FILS */ 932346981Scy 933346981Scy return pos; 934346981Scy} 935346981Scy 936346981Scy 937346981Scy#ifdef CONFIG_OCV 938346981Scyint get_tx_parameters(struct sta_info *sta, int ap_max_chanwidth, 939346981Scy int ap_seg1_idx, int *bandwidth, int *seg1_idx) 940346981Scy{ 941346981Scy int ht_40mhz = 0; 942346981Scy int vht_80p80 = 0; 943346981Scy int requested_bw; 944346981Scy 945346981Scy if (sta->ht_capabilities) 946346981Scy ht_40mhz = !!(sta->ht_capabilities->ht_capabilities_info & 947346981Scy HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET); 948346981Scy 949346981Scy if (sta->vht_operation) { 950346981Scy struct ieee80211_vht_operation *oper = sta->vht_operation; 951346981Scy 952346981Scy /* 953346981Scy * If a VHT Operation element was present, use it to determine 954346981Scy * the supported channel bandwidth. 955346981Scy */ 956346981Scy if (oper->vht_op_info_chwidth == 0) { 957346981Scy requested_bw = ht_40mhz ? 40 : 20; 958346981Scy } else if (oper->vht_op_info_chan_center_freq_seg1_idx == 0) { 959346981Scy requested_bw = 80; 960346981Scy } else { 961346981Scy int diff; 962346981Scy 963346981Scy requested_bw = 160; 964346981Scy diff = abs((int) 965346981Scy oper->vht_op_info_chan_center_freq_seg0_idx - 966346981Scy (int) 967346981Scy oper->vht_op_info_chan_center_freq_seg1_idx); 968346981Scy vht_80p80 = oper->vht_op_info_chan_center_freq_seg1_idx 969346981Scy != 0 && diff > 16; 970346981Scy } 971346981Scy } else if (sta->vht_capabilities) { 972346981Scy struct ieee80211_vht_capabilities *capab; 973346981Scy int vht_chanwidth; 974346981Scy 975346981Scy capab = sta->vht_capabilities; 976346981Scy 977346981Scy /* 978346981Scy * If only the VHT Capabilities element is present (e.g., for 979346981Scy * normal clients), use it to determine the supported channel 980346981Scy * bandwidth. 981346981Scy */ 982346981Scy vht_chanwidth = capab->vht_capabilities_info & 983346981Scy VHT_CAP_SUPP_CHAN_WIDTH_MASK; 984346981Scy vht_80p80 = capab->vht_capabilities_info & 985346981Scy VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ; 986346981Scy 987346981Scy /* TODO: Also take into account Extended NSS BW Support field */ 988346981Scy requested_bw = vht_chanwidth ? 160 : 80; 989346981Scy } else { 990346981Scy requested_bw = ht_40mhz ? 40 : 20; 991346981Scy } 992346981Scy 993346981Scy *bandwidth = requested_bw < ap_max_chanwidth ? 994346981Scy requested_bw : ap_max_chanwidth; 995346981Scy 996346981Scy *seg1_idx = 0; 997346981Scy if (ap_seg1_idx && vht_80p80) 998346981Scy *seg1_idx = ap_seg1_idx; 999346981Scy 1000346981Scy return 0; 1001346981Scy} 1002346981Scy#endif /* CONFIG_OCV */ 1003