1189251Ssam/* 2189251Ssam * IEEE 802.11 Common routines 3289549Srpaulo * Copyright (c) 2002-2015, Jouni Malinen <j@w1.fi> 4189251Ssam * 5252726Srpaulo * This software may be distributed under the terms of the BSD license. 6252726Srpaulo * See README for more details. 7189251Ssam */ 8189251Ssam 9189251Ssam#include "includes.h" 10189251Ssam 11189251Ssam#include "common.h" 12281806Srpaulo#include "defs.h" 13289549Srpaulo#include "wpa_common.h" 14289549Srpaulo#include "qca-vendor.h" 15189251Ssam#include "ieee802_11_defs.h" 16189251Ssam#include "ieee802_11_common.h" 17189251Ssam 18189251Ssam 19214734Srpaulostatic int ieee802_11_parse_vendor_specific(const u8 *pos, size_t elen, 20189251Ssam struct ieee802_11_elems *elems, 21189251Ssam int show_errors) 22189251Ssam{ 23189251Ssam unsigned int oui; 24189251Ssam 25189251Ssam /* first 3 bytes in vendor specific information element are the IEEE 26189251Ssam * OUI of the vendor. The following byte is used a vendor specific 27189251Ssam * sub-type. */ 28189251Ssam if (elen < 4) { 29189251Ssam if (show_errors) { 30189251Ssam wpa_printf(MSG_MSGDUMP, "short vendor specific " 31189251Ssam "information element ignored (len=%lu)", 32189251Ssam (unsigned long) elen); 33189251Ssam } 34189251Ssam return -1; 35189251Ssam } 36189251Ssam 37189251Ssam oui = WPA_GET_BE24(pos); 38189251Ssam switch (oui) { 39189251Ssam case OUI_MICROSOFT: 40189251Ssam /* Microsoft/Wi-Fi information elements are further typed and 41189251Ssam * subtyped */ 42189251Ssam switch (pos[3]) { 43189251Ssam case 1: 44189251Ssam /* Microsoft OUI (00:50:F2) with OUI Type 1: 45189251Ssam * real WPA information element */ 46189251Ssam elems->wpa_ie = pos; 47189251Ssam elems->wpa_ie_len = elen; 48189251Ssam break; 49209158Srpaulo case WMM_OUI_TYPE: 50209158Srpaulo /* WMM information element */ 51189251Ssam if (elen < 5) { 52209158Srpaulo wpa_printf(MSG_MSGDUMP, "short WMM " 53189251Ssam "information element ignored " 54189251Ssam "(len=%lu)", 55189251Ssam (unsigned long) elen); 56189251Ssam return -1; 57189251Ssam } 58189251Ssam switch (pos[4]) { 59209158Srpaulo case WMM_OUI_SUBTYPE_INFORMATION_ELEMENT: 60209158Srpaulo case WMM_OUI_SUBTYPE_PARAMETER_ELEMENT: 61209158Srpaulo /* 62209158Srpaulo * Share same pointer since only one of these 63209158Srpaulo * is used and they start with same data. 64209158Srpaulo * Length field can be used to distinguish the 65209158Srpaulo * IEs. 66209158Srpaulo */ 67209158Srpaulo elems->wmm = pos; 68209158Srpaulo elems->wmm_len = elen; 69189251Ssam break; 70209158Srpaulo case WMM_OUI_SUBTYPE_TSPEC_ELEMENT: 71209158Srpaulo elems->wmm_tspec = pos; 72209158Srpaulo elems->wmm_tspec_len = elen; 73189251Ssam break; 74189251Ssam default: 75252726Srpaulo wpa_printf(MSG_EXCESSIVE, "unknown WMM " 76189251Ssam "information element ignored " 77189251Ssam "(subtype=%d len=%lu)", 78189251Ssam pos[4], (unsigned long) elen); 79189251Ssam return -1; 80189251Ssam } 81189251Ssam break; 82189251Ssam case 4: 83189251Ssam /* Wi-Fi Protected Setup (WPS) IE */ 84189251Ssam elems->wps_ie = pos; 85189251Ssam elems->wps_ie_len = elen; 86189251Ssam break; 87189251Ssam default: 88252726Srpaulo wpa_printf(MSG_EXCESSIVE, "Unknown Microsoft " 89189251Ssam "information element ignored " 90252726Srpaulo "(type=%d len=%lu)", 91252726Srpaulo pos[3], (unsigned long) elen); 92252726Srpaulo return -1; 93252726Srpaulo } 94252726Srpaulo break; 95252726Srpaulo 96252726Srpaulo case OUI_WFA: 97252726Srpaulo switch (pos[3]) { 98252726Srpaulo case P2P_OUI_TYPE: 99252726Srpaulo /* Wi-Fi Alliance - P2P IE */ 100252726Srpaulo elems->p2p = pos; 101252726Srpaulo elems->p2p_len = elen; 102252726Srpaulo break; 103252726Srpaulo case WFD_OUI_TYPE: 104252726Srpaulo /* Wi-Fi Alliance - WFD IE */ 105252726Srpaulo elems->wfd = pos; 106252726Srpaulo elems->wfd_len = elen; 107252726Srpaulo break; 108252726Srpaulo case HS20_INDICATION_OUI_TYPE: 109252726Srpaulo /* Hotspot 2.0 */ 110252726Srpaulo elems->hs20 = pos; 111252726Srpaulo elems->hs20_len = elen; 112252726Srpaulo break; 113281806Srpaulo case HS20_OSEN_OUI_TYPE: 114281806Srpaulo /* Hotspot 2.0 OSEN */ 115281806Srpaulo elems->osen = pos; 116281806Srpaulo elems->osen_len = elen; 117281806Srpaulo break; 118252726Srpaulo default: 119252726Srpaulo wpa_printf(MSG_MSGDUMP, "Unknown WFA " 120252726Srpaulo "information element ignored " 121281806Srpaulo "(type=%d len=%lu)", 122189251Ssam pos[3], (unsigned long) elen); 123189251Ssam return -1; 124189251Ssam } 125189251Ssam break; 126189251Ssam 127189251Ssam case OUI_BROADCOM: 128189251Ssam switch (pos[3]) { 129189251Ssam case VENDOR_HT_CAPAB_OUI_TYPE: 130189251Ssam elems->vendor_ht_cap = pos; 131189251Ssam elems->vendor_ht_cap_len = elen; 132189251Ssam break; 133281806Srpaulo case VENDOR_VHT_TYPE: 134281806Srpaulo if (elen > 4 && 135281806Srpaulo (pos[4] == VENDOR_VHT_SUBTYPE || 136281806Srpaulo pos[4] == VENDOR_VHT_SUBTYPE2)) { 137281806Srpaulo elems->vendor_vht = pos; 138281806Srpaulo elems->vendor_vht_len = elen; 139281806Srpaulo } else 140281806Srpaulo return -1; 141281806Srpaulo break; 142189251Ssam default: 143252726Srpaulo wpa_printf(MSG_EXCESSIVE, "Unknown Broadcom " 144189251Ssam "information element ignored " 145252726Srpaulo "(type=%d len=%lu)", 146189251Ssam pos[3], (unsigned long) elen); 147189251Ssam return -1; 148189251Ssam } 149189251Ssam break; 150189251Ssam 151289549Srpaulo case OUI_QCA: 152289549Srpaulo switch (pos[3]) { 153289549Srpaulo case QCA_VENDOR_ELEM_P2P_PREF_CHAN_LIST: 154289549Srpaulo elems->pref_freq_list = pos; 155289549Srpaulo elems->pref_freq_list_len = elen; 156289549Srpaulo break; 157289549Srpaulo default: 158289549Srpaulo wpa_printf(MSG_EXCESSIVE, 159289549Srpaulo "Unknown QCA information element ignored (type=%d len=%lu)", 160289549Srpaulo pos[3], (unsigned long) elen); 161289549Srpaulo return -1; 162289549Srpaulo } 163289549Srpaulo break; 164289549Srpaulo 165189251Ssam default: 166252726Srpaulo wpa_printf(MSG_EXCESSIVE, "unknown vendor specific " 167252726Srpaulo "information element ignored (vendor OUI " 168252726Srpaulo "%02x:%02x:%02x len=%lu)", 169189251Ssam pos[0], pos[1], pos[2], (unsigned long) elen); 170189251Ssam return -1; 171189251Ssam } 172189251Ssam 173189251Ssam return 0; 174189251Ssam} 175189251Ssam 176189251Ssam 177189251Ssam/** 178189251Ssam * ieee802_11_parse_elems - Parse information elements in management frames 179189251Ssam * @start: Pointer to the start of IEs 180189251Ssam * @len: Length of IE buffer in octets 181189251Ssam * @elems: Data structure for parsed elements 182189251Ssam * @show_errors: Whether to show parsing errors in debug log 183189251Ssam * Returns: Parsing result 184189251Ssam */ 185214734SrpauloParseRes ieee802_11_parse_elems(const u8 *start, size_t len, 186189251Ssam struct ieee802_11_elems *elems, 187189251Ssam int show_errors) 188189251Ssam{ 189189251Ssam size_t left = len; 190214734Srpaulo const u8 *pos = start; 191189251Ssam int unknown = 0; 192189251Ssam 193189251Ssam os_memset(elems, 0, sizeof(*elems)); 194189251Ssam 195189251Ssam while (left >= 2) { 196189251Ssam u8 id, elen; 197189251Ssam 198189251Ssam id = *pos++; 199189251Ssam elen = *pos++; 200189251Ssam left -= 2; 201189251Ssam 202189251Ssam if (elen > left) { 203189251Ssam if (show_errors) { 204189251Ssam wpa_printf(MSG_DEBUG, "IEEE 802.11 element " 205189251Ssam "parse failed (id=%d elen=%d " 206189251Ssam "left=%lu)", 207189251Ssam id, elen, (unsigned long) left); 208189251Ssam wpa_hexdump(MSG_MSGDUMP, "IEs", start, len); 209189251Ssam } 210189251Ssam return ParseFailed; 211189251Ssam } 212189251Ssam 213189251Ssam switch (id) { 214189251Ssam case WLAN_EID_SSID: 215289549Srpaulo if (elen > SSID_MAX_LEN) { 216289549Srpaulo wpa_printf(MSG_DEBUG, 217289549Srpaulo "Ignored too long SSID element (elen=%u)", 218289549Srpaulo elen); 219289549Srpaulo break; 220289549Srpaulo } 221189251Ssam elems->ssid = pos; 222189251Ssam elems->ssid_len = elen; 223189251Ssam break; 224189251Ssam case WLAN_EID_SUPP_RATES: 225189251Ssam elems->supp_rates = pos; 226189251Ssam elems->supp_rates_len = elen; 227189251Ssam break; 228189251Ssam case WLAN_EID_DS_PARAMS: 229289549Srpaulo if (elen < 1) 230289549Srpaulo break; 231189251Ssam elems->ds_params = pos; 232189251Ssam break; 233189251Ssam case WLAN_EID_CF_PARAMS: 234189251Ssam case WLAN_EID_TIM: 235189251Ssam break; 236189251Ssam case WLAN_EID_CHALLENGE: 237189251Ssam elems->challenge = pos; 238189251Ssam elems->challenge_len = elen; 239189251Ssam break; 240189251Ssam case WLAN_EID_ERP_INFO: 241289549Srpaulo if (elen < 1) 242289549Srpaulo break; 243189251Ssam elems->erp_info = pos; 244189251Ssam break; 245189251Ssam case WLAN_EID_EXT_SUPP_RATES: 246189251Ssam elems->ext_supp_rates = pos; 247189251Ssam elems->ext_supp_rates_len = elen; 248189251Ssam break; 249189251Ssam case WLAN_EID_VENDOR_SPECIFIC: 250189251Ssam if (ieee802_11_parse_vendor_specific(pos, elen, 251189251Ssam elems, 252189251Ssam show_errors)) 253189251Ssam unknown++; 254189251Ssam break; 255189251Ssam case WLAN_EID_RSN: 256189251Ssam elems->rsn_ie = pos; 257189251Ssam elems->rsn_ie_len = elen; 258189251Ssam break; 259189251Ssam case WLAN_EID_PWR_CAPABILITY: 260189251Ssam break; 261189251Ssam case WLAN_EID_SUPPORTED_CHANNELS: 262189251Ssam elems->supp_channels = pos; 263189251Ssam elems->supp_channels_len = elen; 264189251Ssam break; 265189251Ssam case WLAN_EID_MOBILITY_DOMAIN: 266289549Srpaulo if (elen < sizeof(struct rsn_mdie)) 267289549Srpaulo break; 268189251Ssam elems->mdie = pos; 269189251Ssam elems->mdie_len = elen; 270189251Ssam break; 271189251Ssam case WLAN_EID_FAST_BSS_TRANSITION: 272289549Srpaulo if (elen < sizeof(struct rsn_ftie)) 273289549Srpaulo break; 274189251Ssam elems->ftie = pos; 275189251Ssam elems->ftie_len = elen; 276189251Ssam break; 277189251Ssam case WLAN_EID_TIMEOUT_INTERVAL: 278289549Srpaulo if (elen != 5) 279289549Srpaulo break; 280189251Ssam elems->timeout_int = pos; 281189251Ssam break; 282189251Ssam case WLAN_EID_HT_CAP: 283289549Srpaulo if (elen < sizeof(struct ieee80211_ht_capabilities)) 284289549Srpaulo break; 285189251Ssam elems->ht_capabilities = pos; 286189251Ssam break; 287189251Ssam case WLAN_EID_HT_OPERATION: 288289549Srpaulo if (elen < sizeof(struct ieee80211_ht_operation)) 289289549Srpaulo break; 290189251Ssam elems->ht_operation = pos; 291189251Ssam break; 292281806Srpaulo case WLAN_EID_MESH_CONFIG: 293281806Srpaulo elems->mesh_config = pos; 294281806Srpaulo elems->mesh_config_len = elen; 295281806Srpaulo break; 296281806Srpaulo case WLAN_EID_MESH_ID: 297281806Srpaulo elems->mesh_id = pos; 298281806Srpaulo elems->mesh_id_len = elen; 299281806Srpaulo break; 300281806Srpaulo case WLAN_EID_PEER_MGMT: 301281806Srpaulo elems->peer_mgmt = pos; 302281806Srpaulo elems->peer_mgmt_len = elen; 303281806Srpaulo break; 304252726Srpaulo case WLAN_EID_VHT_CAP: 305289549Srpaulo if (elen < sizeof(struct ieee80211_vht_capabilities)) 306289549Srpaulo break; 307252726Srpaulo elems->vht_capabilities = pos; 308252726Srpaulo break; 309252726Srpaulo case WLAN_EID_VHT_OPERATION: 310289549Srpaulo if (elen < sizeof(struct ieee80211_vht_operation)) 311289549Srpaulo break; 312252726Srpaulo elems->vht_operation = pos; 313252726Srpaulo break; 314281806Srpaulo case WLAN_EID_VHT_OPERATING_MODE_NOTIFICATION: 315281806Srpaulo if (elen != 1) 316281806Srpaulo break; 317281806Srpaulo elems->vht_opmode_notif = pos; 318281806Srpaulo break; 319252726Srpaulo case WLAN_EID_LINK_ID: 320252726Srpaulo if (elen < 18) 321252726Srpaulo break; 322252726Srpaulo elems->link_id = pos; 323252726Srpaulo break; 324252726Srpaulo case WLAN_EID_INTERWORKING: 325252726Srpaulo elems->interworking = pos; 326252726Srpaulo elems->interworking_len = elen; 327252726Srpaulo break; 328281806Srpaulo case WLAN_EID_QOS_MAP_SET: 329281806Srpaulo if (elen < 16) 330281806Srpaulo break; 331281806Srpaulo elems->qos_map_set = pos; 332281806Srpaulo elems->qos_map_set_len = elen; 333281806Srpaulo break; 334252726Srpaulo case WLAN_EID_EXT_CAPAB: 335252726Srpaulo elems->ext_capab = pos; 336252726Srpaulo elems->ext_capab_len = elen; 337252726Srpaulo break; 338252726Srpaulo case WLAN_EID_BSS_MAX_IDLE_PERIOD: 339252726Srpaulo if (elen < 3) 340252726Srpaulo break; 341252726Srpaulo elems->bss_max_idle_period = pos; 342252726Srpaulo break; 343252726Srpaulo case WLAN_EID_SSID_LIST: 344252726Srpaulo elems->ssid_list = pos; 345252726Srpaulo elems->ssid_list_len = elen; 346252726Srpaulo break; 347281806Srpaulo case WLAN_EID_AMPE: 348281806Srpaulo elems->ampe = pos; 349281806Srpaulo elems->ampe_len = elen; 350281806Srpaulo break; 351281806Srpaulo case WLAN_EID_MIC: 352281806Srpaulo elems->mic = pos; 353281806Srpaulo elems->mic_len = elen; 354281806Srpaulo /* after mic everything is encrypted, so stop. */ 355281806Srpaulo left = elen; 356281806Srpaulo break; 357289549Srpaulo case WLAN_EID_MULTI_BAND: 358289549Srpaulo if (elems->mb_ies.nof_ies >= MAX_NOF_MB_IES_SUPPORTED) { 359289549Srpaulo wpa_printf(MSG_MSGDUMP, 360289549Srpaulo "IEEE 802.11 element parse ignored MB IE (id=%d elen=%d)", 361289549Srpaulo id, elen); 362289549Srpaulo break; 363289549Srpaulo } 364289549Srpaulo 365289549Srpaulo elems->mb_ies.ies[elems->mb_ies.nof_ies].ie = pos; 366289549Srpaulo elems->mb_ies.ies[elems->mb_ies.nof_ies].ie_len = elen; 367289549Srpaulo elems->mb_ies.nof_ies++; 368289549Srpaulo break; 369189251Ssam default: 370189251Ssam unknown++; 371189251Ssam if (!show_errors) 372189251Ssam break; 373189251Ssam wpa_printf(MSG_MSGDUMP, "IEEE 802.11 element parse " 374189251Ssam "ignored unknown element (id=%d elen=%d)", 375189251Ssam id, elen); 376189251Ssam break; 377189251Ssam } 378189251Ssam 379189251Ssam left -= elen; 380189251Ssam pos += elen; 381189251Ssam } 382189251Ssam 383189251Ssam if (left) 384189251Ssam return ParseFailed; 385189251Ssam 386189251Ssam return unknown ? ParseUnknown : ParseOK; 387189251Ssam} 388214734Srpaulo 389214734Srpaulo 390214734Srpauloint ieee802_11_ie_count(const u8 *ies, size_t ies_len) 391214734Srpaulo{ 392214734Srpaulo int count = 0; 393214734Srpaulo const u8 *pos, *end; 394214734Srpaulo 395214734Srpaulo if (ies == NULL) 396214734Srpaulo return 0; 397214734Srpaulo 398214734Srpaulo pos = ies; 399214734Srpaulo end = ies + ies_len; 400214734Srpaulo 401214734Srpaulo while (pos + 2 <= end) { 402214734Srpaulo if (pos + 2 + pos[1] > end) 403214734Srpaulo break; 404214734Srpaulo count++; 405214734Srpaulo pos += 2 + pos[1]; 406214734Srpaulo } 407214734Srpaulo 408214734Srpaulo return count; 409214734Srpaulo} 410214734Srpaulo 411214734Srpaulo 412214734Srpaulostruct wpabuf * ieee802_11_vendor_ie_concat(const u8 *ies, size_t ies_len, 413214734Srpaulo u32 oui_type) 414214734Srpaulo{ 415214734Srpaulo struct wpabuf *buf; 416214734Srpaulo const u8 *end, *pos, *ie; 417214734Srpaulo 418214734Srpaulo pos = ies; 419214734Srpaulo end = ies + ies_len; 420214734Srpaulo ie = NULL; 421214734Srpaulo 422214734Srpaulo while (pos + 1 < end) { 423214734Srpaulo if (pos + 2 + pos[1] > end) 424214734Srpaulo return NULL; 425214734Srpaulo if (pos[0] == WLAN_EID_VENDOR_SPECIFIC && pos[1] >= 4 && 426214734Srpaulo WPA_GET_BE32(&pos[2]) == oui_type) { 427214734Srpaulo ie = pos; 428214734Srpaulo break; 429214734Srpaulo } 430214734Srpaulo pos += 2 + pos[1]; 431214734Srpaulo } 432214734Srpaulo 433214734Srpaulo if (ie == NULL) 434214734Srpaulo return NULL; /* No specified vendor IE found */ 435214734Srpaulo 436214734Srpaulo buf = wpabuf_alloc(ies_len); 437214734Srpaulo if (buf == NULL) 438214734Srpaulo return NULL; 439214734Srpaulo 440214734Srpaulo /* 441214734Srpaulo * There may be multiple vendor IEs in the message, so need to 442214734Srpaulo * concatenate their data fields. 443214734Srpaulo */ 444214734Srpaulo while (pos + 1 < end) { 445214734Srpaulo if (pos + 2 + pos[1] > end) 446214734Srpaulo break; 447214734Srpaulo if (pos[0] == WLAN_EID_VENDOR_SPECIFIC && pos[1] >= 4 && 448214734Srpaulo WPA_GET_BE32(&pos[2]) == oui_type) 449214734Srpaulo wpabuf_put_data(buf, pos + 6, pos[1] - 4); 450214734Srpaulo pos += 2 + pos[1]; 451214734Srpaulo } 452214734Srpaulo 453214734Srpaulo return buf; 454214734Srpaulo} 455252726Srpaulo 456252726Srpaulo 457252726Srpauloconst u8 * get_hdr_bssid(const struct ieee80211_hdr *hdr, size_t len) 458252726Srpaulo{ 459252726Srpaulo u16 fc, type, stype; 460252726Srpaulo 461252726Srpaulo /* 462252726Srpaulo * PS-Poll frames are 16 bytes. All other frames are 463252726Srpaulo * 24 bytes or longer. 464252726Srpaulo */ 465252726Srpaulo if (len < 16) 466252726Srpaulo return NULL; 467252726Srpaulo 468252726Srpaulo fc = le_to_host16(hdr->frame_control); 469252726Srpaulo type = WLAN_FC_GET_TYPE(fc); 470252726Srpaulo stype = WLAN_FC_GET_STYPE(fc); 471252726Srpaulo 472252726Srpaulo switch (type) { 473252726Srpaulo case WLAN_FC_TYPE_DATA: 474252726Srpaulo if (len < 24) 475252726Srpaulo return NULL; 476252726Srpaulo switch (fc & (WLAN_FC_FROMDS | WLAN_FC_TODS)) { 477252726Srpaulo case WLAN_FC_FROMDS | WLAN_FC_TODS: 478252726Srpaulo case WLAN_FC_TODS: 479252726Srpaulo return hdr->addr1; 480252726Srpaulo case WLAN_FC_FROMDS: 481252726Srpaulo return hdr->addr2; 482252726Srpaulo default: 483252726Srpaulo return NULL; 484252726Srpaulo } 485252726Srpaulo case WLAN_FC_TYPE_CTRL: 486252726Srpaulo if (stype != WLAN_FC_STYPE_PSPOLL) 487252726Srpaulo return NULL; 488252726Srpaulo return hdr->addr1; 489252726Srpaulo case WLAN_FC_TYPE_MGMT: 490252726Srpaulo return hdr->addr3; 491252726Srpaulo default: 492252726Srpaulo return NULL; 493252726Srpaulo } 494252726Srpaulo} 495252726Srpaulo 496252726Srpaulo 497252726Srpauloint hostapd_config_wmm_ac(struct hostapd_wmm_ac_params wmm_ac_params[], 498252726Srpaulo const char *name, const char *val) 499252726Srpaulo{ 500252726Srpaulo int num, v; 501252726Srpaulo const char *pos; 502252726Srpaulo struct hostapd_wmm_ac_params *ac; 503252726Srpaulo 504252726Srpaulo /* skip 'wme_ac_' or 'wmm_ac_' prefix */ 505252726Srpaulo pos = name + 7; 506252726Srpaulo if (os_strncmp(pos, "be_", 3) == 0) { 507252726Srpaulo num = 0; 508252726Srpaulo pos += 3; 509252726Srpaulo } else if (os_strncmp(pos, "bk_", 3) == 0) { 510252726Srpaulo num = 1; 511252726Srpaulo pos += 3; 512252726Srpaulo } else if (os_strncmp(pos, "vi_", 3) == 0) { 513252726Srpaulo num = 2; 514252726Srpaulo pos += 3; 515252726Srpaulo } else if (os_strncmp(pos, "vo_", 3) == 0) { 516252726Srpaulo num = 3; 517252726Srpaulo pos += 3; 518252726Srpaulo } else { 519252726Srpaulo wpa_printf(MSG_ERROR, "Unknown WMM name '%s'", pos); 520252726Srpaulo return -1; 521252726Srpaulo } 522252726Srpaulo 523252726Srpaulo ac = &wmm_ac_params[num]; 524252726Srpaulo 525252726Srpaulo if (os_strcmp(pos, "aifs") == 0) { 526252726Srpaulo v = atoi(val); 527252726Srpaulo if (v < 1 || v > 255) { 528252726Srpaulo wpa_printf(MSG_ERROR, "Invalid AIFS value %d", v); 529252726Srpaulo return -1; 530252726Srpaulo } 531252726Srpaulo ac->aifs = v; 532252726Srpaulo } else if (os_strcmp(pos, "cwmin") == 0) { 533252726Srpaulo v = atoi(val); 534289549Srpaulo if (v < 0 || v > 15) { 535252726Srpaulo wpa_printf(MSG_ERROR, "Invalid cwMin value %d", v); 536252726Srpaulo return -1; 537252726Srpaulo } 538252726Srpaulo ac->cwmin = v; 539252726Srpaulo } else if (os_strcmp(pos, "cwmax") == 0) { 540252726Srpaulo v = atoi(val); 541289549Srpaulo if (v < 0 || v > 15) { 542252726Srpaulo wpa_printf(MSG_ERROR, "Invalid cwMax value %d", v); 543252726Srpaulo return -1; 544252726Srpaulo } 545252726Srpaulo ac->cwmax = v; 546252726Srpaulo } else if (os_strcmp(pos, "txop_limit") == 0) { 547252726Srpaulo v = atoi(val); 548252726Srpaulo if (v < 0 || v > 0xffff) { 549252726Srpaulo wpa_printf(MSG_ERROR, "Invalid txop value %d", v); 550252726Srpaulo return -1; 551252726Srpaulo } 552252726Srpaulo ac->txop_limit = v; 553252726Srpaulo } else if (os_strcmp(pos, "acm") == 0) { 554252726Srpaulo v = atoi(val); 555252726Srpaulo if (v < 0 || v > 1) { 556252726Srpaulo wpa_printf(MSG_ERROR, "Invalid acm value %d", v); 557252726Srpaulo return -1; 558252726Srpaulo } 559252726Srpaulo ac->admission_control_mandatory = v; 560252726Srpaulo } else { 561252726Srpaulo wpa_printf(MSG_ERROR, "Unknown wmm_ac_ field '%s'", pos); 562252726Srpaulo return -1; 563252726Srpaulo } 564252726Srpaulo 565252726Srpaulo return 0; 566252726Srpaulo} 567281806Srpaulo 568281806Srpaulo 569281806Srpauloenum hostapd_hw_mode ieee80211_freq_to_chan(int freq, u8 *channel) 570281806Srpaulo{ 571289549Srpaulo u8 op_class; 572281806Srpaulo 573289549Srpaulo return ieee80211_freq_to_channel_ext(freq, 0, 0, &op_class, channel); 574289549Srpaulo} 575289549Srpaulo 576289549Srpaulo 577289549Srpaulo/** 578289549Srpaulo * ieee80211_freq_to_channel_ext - Convert frequency into channel info 579289549Srpaulo * for HT40 and VHT. DFS channels are not covered. 580289549Srpaulo * @freq: Frequency (MHz) to convert 581289549Srpaulo * @sec_channel: 0 = non-HT40, 1 = sec. channel above, -1 = sec. channel below 582289549Srpaulo * @vht: 0 - non-VHT, 1 - 80 MHz 583289549Srpaulo * @op_class: Buffer for returning operating class 584289549Srpaulo * @channel: Buffer for returning channel number 585289549Srpaulo * Returns: hw_mode on success, NUM_HOSTAPD_MODES on failure 586289549Srpaulo */ 587289549Srpauloenum hostapd_hw_mode ieee80211_freq_to_channel_ext(unsigned int freq, 588289549Srpaulo int sec_channel, int vht, 589289549Srpaulo u8 *op_class, u8 *channel) 590289549Srpaulo{ 591289549Srpaulo /* TODO: more operating classes */ 592289549Srpaulo 593289549Srpaulo if (sec_channel > 1 || sec_channel < -1) 594289549Srpaulo return NUM_HOSTAPD_MODES; 595289549Srpaulo 596281806Srpaulo if (freq >= 2412 && freq <= 2472) { 597289549Srpaulo if ((freq - 2407) % 5) 598289549Srpaulo return NUM_HOSTAPD_MODES; 599289549Srpaulo 600289549Srpaulo if (vht) 601289549Srpaulo return NUM_HOSTAPD_MODES; 602289549Srpaulo 603289549Srpaulo /* 2.407 GHz, channels 1..13 */ 604289549Srpaulo if (sec_channel == 1) 605289549Srpaulo *op_class = 83; 606289549Srpaulo else if (sec_channel == -1) 607289549Srpaulo *op_class = 84; 608289549Srpaulo else 609289549Srpaulo *op_class = 81; 610289549Srpaulo 611281806Srpaulo *channel = (freq - 2407) / 5; 612289549Srpaulo 613289549Srpaulo return HOSTAPD_MODE_IEEE80211G; 614289549Srpaulo } 615289549Srpaulo 616289549Srpaulo if (freq == 2484) { 617289549Srpaulo if (sec_channel || vht) 618289549Srpaulo return NUM_HOSTAPD_MODES; 619289549Srpaulo 620289549Srpaulo *op_class = 82; /* channel 14 */ 621281806Srpaulo *channel = 14; 622289549Srpaulo 623289549Srpaulo return HOSTAPD_MODE_IEEE80211B; 624289549Srpaulo } 625289549Srpaulo 626289549Srpaulo if (freq >= 4900 && freq < 5000) { 627289549Srpaulo if ((freq - 4000) % 5) 628289549Srpaulo return NUM_HOSTAPD_MODES; 629281806Srpaulo *channel = (freq - 4000) / 5; 630289549Srpaulo *op_class = 0; /* TODO */ 631289549Srpaulo return HOSTAPD_MODE_IEEE80211A; 632289549Srpaulo } 633289549Srpaulo 634289549Srpaulo /* 5 GHz, channels 36..48 */ 635289549Srpaulo if (freq >= 5180 && freq <= 5240) { 636289549Srpaulo if ((freq - 5000) % 5) 637289549Srpaulo return NUM_HOSTAPD_MODES; 638289549Srpaulo 639289549Srpaulo if (sec_channel == 1) 640289549Srpaulo *op_class = 116; 641289549Srpaulo else if (sec_channel == -1) 642289549Srpaulo *op_class = 117; 643289549Srpaulo else if (vht) 644289549Srpaulo *op_class = 128; 645289549Srpaulo else 646289549Srpaulo *op_class = 115; 647289549Srpaulo 648281806Srpaulo *channel = (freq - 5000) / 5; 649289549Srpaulo 650289549Srpaulo return HOSTAPD_MODE_IEEE80211A; 651289549Srpaulo } 652289549Srpaulo 653289549Srpaulo /* 5 GHz, channels 149..161 */ 654289549Srpaulo if (freq >= 5745 && freq <= 5805) { 655289549Srpaulo if ((freq - 5000) % 5) 656289549Srpaulo return NUM_HOSTAPD_MODES; 657289549Srpaulo 658289549Srpaulo if (sec_channel == 1) 659289549Srpaulo *op_class = 126; 660289549Srpaulo else if (sec_channel == -1) 661289549Srpaulo *op_class = 127; 662289549Srpaulo else if (vht) 663289549Srpaulo *op_class = 128; 664289549Srpaulo else 665289549Srpaulo *op_class = 124; 666289549Srpaulo 667289549Srpaulo *channel = (freq - 5000) / 5; 668289549Srpaulo 669289549Srpaulo return HOSTAPD_MODE_IEEE80211A; 670289549Srpaulo } 671289549Srpaulo 672289549Srpaulo /* 5 GHz, channels 149..169 */ 673289549Srpaulo if (freq >= 5745 && freq <= 5845) { 674289549Srpaulo if ((freq - 5000) % 5) 675289549Srpaulo return NUM_HOSTAPD_MODES; 676289549Srpaulo 677289549Srpaulo *op_class = 125; 678289549Srpaulo 679289549Srpaulo *channel = (freq - 5000) / 5; 680289549Srpaulo 681289549Srpaulo return HOSTAPD_MODE_IEEE80211A; 682289549Srpaulo } 683289549Srpaulo 684289549Srpaulo if (freq >= 5000 && freq < 5900) { 685289549Srpaulo if ((freq - 5000) % 5) 686289549Srpaulo return NUM_HOSTAPD_MODES; 687289549Srpaulo *channel = (freq - 5000) / 5; 688289549Srpaulo *op_class = 0; /* TODO */ 689289549Srpaulo return HOSTAPD_MODE_IEEE80211A; 690289549Srpaulo } 691289549Srpaulo 692289549Srpaulo /* 56.16 GHz, channel 1..4 */ 693289549Srpaulo if (freq >= 56160 + 2160 * 1 && freq <= 56160 + 2160 * 4) { 694289549Srpaulo if (sec_channel || vht) 695289549Srpaulo return NUM_HOSTAPD_MODES; 696289549Srpaulo 697281806Srpaulo *channel = (freq - 56160) / 2160; 698289549Srpaulo *op_class = 180; 699289549Srpaulo 700289549Srpaulo return HOSTAPD_MODE_IEEE80211AD; 701281806Srpaulo } 702281806Srpaulo 703289549Srpaulo return NUM_HOSTAPD_MODES; 704281806Srpaulo} 705281806Srpaulo 706281806Srpaulo 707289549Srpaulostatic const char *const us_op_class_cc[] = { 708281806Srpaulo "US", "CA", NULL 709281806Srpaulo}; 710281806Srpaulo 711289549Srpaulostatic const char *const eu_op_class_cc[] = { 712281806Srpaulo "AL", "AM", "AT", "AZ", "BA", "BE", "BG", "BY", "CH", "CY", "CZ", "DE", 713281806Srpaulo "DK", "EE", "EL", "ES", "FI", "FR", "GE", "HR", "HU", "IE", "IS", "IT", 714281806Srpaulo "LI", "LT", "LU", "LV", "MD", "ME", "MK", "MT", "NL", "NO", "PL", "PT", 715281806Srpaulo "RO", "RS", "RU", "SE", "SI", "SK", "TR", "UA", "UK", NULL 716281806Srpaulo}; 717281806Srpaulo 718289549Srpaulostatic const char *const jp_op_class_cc[] = { 719281806Srpaulo "JP", NULL 720281806Srpaulo}; 721281806Srpaulo 722289549Srpaulostatic const char *const cn_op_class_cc[] = { 723289549Srpaulo "CN", NULL 724281806Srpaulo}; 725281806Srpaulo 726281806Srpaulo 727289549Srpaulostatic int country_match(const char *const cc[], const char *const country) 728281806Srpaulo{ 729281806Srpaulo int i; 730281806Srpaulo 731281806Srpaulo if (country == NULL) 732281806Srpaulo return 0; 733281806Srpaulo for (i = 0; cc[i]; i++) { 734281806Srpaulo if (cc[i][0] == country[0] && cc[i][1] == country[1]) 735281806Srpaulo return 1; 736281806Srpaulo } 737281806Srpaulo 738281806Srpaulo return 0; 739281806Srpaulo} 740281806Srpaulo 741281806Srpaulo 742281806Srpaulostatic int ieee80211_chan_to_freq_us(u8 op_class, u8 chan) 743281806Srpaulo{ 744281806Srpaulo switch (op_class) { 745281806Srpaulo case 12: /* channels 1..11 */ 746281806Srpaulo case 32: /* channels 1..7; 40 MHz */ 747281806Srpaulo case 33: /* channels 5..11; 40 MHz */ 748281806Srpaulo if (chan < 1 || chan > 11) 749281806Srpaulo return -1; 750281806Srpaulo return 2407 + 5 * chan; 751281806Srpaulo case 1: /* channels 36,40,44,48 */ 752281806Srpaulo case 2: /* channels 52,56,60,64; dfs */ 753281806Srpaulo case 22: /* channels 36,44; 40 MHz */ 754281806Srpaulo case 23: /* channels 52,60; 40 MHz */ 755281806Srpaulo case 27: /* channels 40,48; 40 MHz */ 756281806Srpaulo case 28: /* channels 56,64; 40 MHz */ 757281806Srpaulo if (chan < 36 || chan > 64) 758281806Srpaulo return -1; 759281806Srpaulo return 5000 + 5 * chan; 760281806Srpaulo case 4: /* channels 100-144 */ 761281806Srpaulo case 24: /* channels 100-140; 40 MHz */ 762281806Srpaulo if (chan < 100 || chan > 144) 763281806Srpaulo return -1; 764281806Srpaulo return 5000 + 5 * chan; 765281806Srpaulo case 3: /* channels 149,153,157,161 */ 766281806Srpaulo case 25: /* channels 149,157; 40 MHz */ 767281806Srpaulo case 26: /* channels 149,157; 40 MHz */ 768281806Srpaulo case 30: /* channels 153,161; 40 MHz */ 769281806Srpaulo case 31: /* channels 153,161; 40 MHz */ 770281806Srpaulo if (chan < 149 || chan > 161) 771281806Srpaulo return -1; 772281806Srpaulo return 5000 + 5 * chan; 773289549Srpaulo case 5: /* channels 149,153,157,161,165 */ 774289549Srpaulo if (chan < 149 || chan > 165) 775289549Srpaulo return -1; 776289549Srpaulo return 5000 + 5 * chan; 777281806Srpaulo case 34: /* 60 GHz band, channels 1..3 */ 778281806Srpaulo if (chan < 1 || chan > 3) 779281806Srpaulo return -1; 780281806Srpaulo return 56160 + 2160 * chan; 781281806Srpaulo } 782281806Srpaulo return -1; 783281806Srpaulo} 784281806Srpaulo 785281806Srpaulo 786281806Srpaulostatic int ieee80211_chan_to_freq_eu(u8 op_class, u8 chan) 787281806Srpaulo{ 788281806Srpaulo switch (op_class) { 789281806Srpaulo case 4: /* channels 1..13 */ 790281806Srpaulo case 11: /* channels 1..9; 40 MHz */ 791281806Srpaulo case 12: /* channels 5..13; 40 MHz */ 792281806Srpaulo if (chan < 1 || chan > 13) 793281806Srpaulo return -1; 794281806Srpaulo return 2407 + 5 * chan; 795281806Srpaulo case 1: /* channels 36,40,44,48 */ 796281806Srpaulo case 2: /* channels 52,56,60,64; dfs */ 797281806Srpaulo case 5: /* channels 36,44; 40 MHz */ 798281806Srpaulo case 6: /* channels 52,60; 40 MHz */ 799281806Srpaulo case 8: /* channels 40,48; 40 MHz */ 800281806Srpaulo case 9: /* channels 56,64; 40 MHz */ 801281806Srpaulo if (chan < 36 || chan > 64) 802281806Srpaulo return -1; 803281806Srpaulo return 5000 + 5 * chan; 804281806Srpaulo case 3: /* channels 100-140 */ 805281806Srpaulo case 7: /* channels 100-132; 40 MHz */ 806281806Srpaulo case 10: /* channels 104-136; 40 MHz */ 807281806Srpaulo case 16: /* channels 100-140 */ 808281806Srpaulo if (chan < 100 || chan > 140) 809281806Srpaulo return -1; 810281806Srpaulo return 5000 + 5 * chan; 811281806Srpaulo case 17: /* channels 149,153,157,161,165,169 */ 812281806Srpaulo if (chan < 149 || chan > 169) 813281806Srpaulo return -1; 814281806Srpaulo return 5000 + 5 * chan; 815281806Srpaulo case 18: /* 60 GHz band, channels 1..4 */ 816281806Srpaulo if (chan < 1 || chan > 4) 817281806Srpaulo return -1; 818281806Srpaulo return 56160 + 2160 * chan; 819281806Srpaulo } 820281806Srpaulo return -1; 821281806Srpaulo} 822281806Srpaulo 823281806Srpaulo 824281806Srpaulostatic int ieee80211_chan_to_freq_jp(u8 op_class, u8 chan) 825281806Srpaulo{ 826281806Srpaulo switch (op_class) { 827281806Srpaulo case 30: /* channels 1..13 */ 828281806Srpaulo case 56: /* channels 1..9; 40 MHz */ 829281806Srpaulo case 57: /* channels 5..13; 40 MHz */ 830281806Srpaulo if (chan < 1 || chan > 13) 831281806Srpaulo return -1; 832281806Srpaulo return 2407 + 5 * chan; 833281806Srpaulo case 31: /* channel 14 */ 834281806Srpaulo if (chan != 14) 835281806Srpaulo return -1; 836281806Srpaulo return 2414 + 5 * chan; 837281806Srpaulo case 1: /* channels 34,38,42,46(old) or 36,40,44,48 */ 838281806Srpaulo case 32: /* channels 52,56,60,64 */ 839281806Srpaulo case 33: /* channels 52,56,60,64 */ 840281806Srpaulo case 36: /* channels 36,44; 40 MHz */ 841281806Srpaulo case 37: /* channels 52,60; 40 MHz */ 842281806Srpaulo case 38: /* channels 52,60; 40 MHz */ 843281806Srpaulo case 41: /* channels 40,48; 40 MHz */ 844281806Srpaulo case 42: /* channels 56,64; 40 MHz */ 845281806Srpaulo case 43: /* channels 56,64; 40 MHz */ 846281806Srpaulo if (chan < 34 || chan > 64) 847281806Srpaulo return -1; 848281806Srpaulo return 5000 + 5 * chan; 849281806Srpaulo case 34: /* channels 100-140 */ 850281806Srpaulo case 35: /* channels 100-140 */ 851281806Srpaulo case 39: /* channels 100-132; 40 MHz */ 852281806Srpaulo case 40: /* channels 100-132; 40 MHz */ 853281806Srpaulo case 44: /* channels 104-136; 40 MHz */ 854281806Srpaulo case 45: /* channels 104-136; 40 MHz */ 855281806Srpaulo case 58: /* channels 100-140 */ 856281806Srpaulo if (chan < 100 || chan > 140) 857281806Srpaulo return -1; 858281806Srpaulo return 5000 + 5 * chan; 859281806Srpaulo case 59: /* 60 GHz band, channels 1..4 */ 860281806Srpaulo if (chan < 1 || chan > 3) 861281806Srpaulo return -1; 862281806Srpaulo return 56160 + 2160 * chan; 863281806Srpaulo } 864281806Srpaulo return -1; 865281806Srpaulo} 866281806Srpaulo 867281806Srpaulo 868281806Srpaulostatic int ieee80211_chan_to_freq_cn(u8 op_class, u8 chan) 869281806Srpaulo{ 870281806Srpaulo switch (op_class) { 871281806Srpaulo case 7: /* channels 1..13 */ 872281806Srpaulo case 8: /* channels 1..9; 40 MHz */ 873281806Srpaulo case 9: /* channels 5..13; 40 MHz */ 874281806Srpaulo if (chan < 1 || chan > 13) 875281806Srpaulo return -1; 876281806Srpaulo return 2407 + 5 * chan; 877281806Srpaulo case 1: /* channels 36,40,44,48 */ 878281806Srpaulo case 2: /* channels 52,56,60,64; dfs */ 879281806Srpaulo case 4: /* channels 36,44; 40 MHz */ 880281806Srpaulo case 5: /* channels 52,60; 40 MHz */ 881281806Srpaulo if (chan < 36 || chan > 64) 882281806Srpaulo return -1; 883281806Srpaulo return 5000 + 5 * chan; 884281806Srpaulo case 3: /* channels 149,153,157,161,165 */ 885281806Srpaulo case 6: /* channels 149,157; 40 MHz */ 886281806Srpaulo if (chan < 149 || chan > 165) 887281806Srpaulo return -1; 888281806Srpaulo return 5000 + 5 * chan; 889281806Srpaulo } 890281806Srpaulo return -1; 891281806Srpaulo} 892281806Srpaulo 893281806Srpaulo 894281806Srpaulostatic int ieee80211_chan_to_freq_global(u8 op_class, u8 chan) 895281806Srpaulo{ 896281806Srpaulo /* Table E-4 in IEEE Std 802.11-2012 - Global operating classes */ 897281806Srpaulo switch (op_class) { 898281806Srpaulo case 81: 899281806Srpaulo /* channels 1..13 */ 900281806Srpaulo if (chan < 1 || chan > 13) 901281806Srpaulo return -1; 902281806Srpaulo return 2407 + 5 * chan; 903281806Srpaulo case 82: 904281806Srpaulo /* channel 14 */ 905281806Srpaulo if (chan != 14) 906281806Srpaulo return -1; 907281806Srpaulo return 2414 + 5 * chan; 908281806Srpaulo case 83: /* channels 1..9; 40 MHz */ 909281806Srpaulo case 84: /* channels 5..13; 40 MHz */ 910281806Srpaulo if (chan < 1 || chan > 13) 911281806Srpaulo return -1; 912281806Srpaulo return 2407 + 5 * chan; 913281806Srpaulo case 115: /* channels 36,40,44,48; indoor only */ 914281806Srpaulo case 116: /* channels 36,44; 40 MHz; indoor only */ 915281806Srpaulo case 117: /* channels 40,48; 40 MHz; indoor only */ 916281806Srpaulo case 118: /* channels 52,56,60,64; dfs */ 917281806Srpaulo case 119: /* channels 52,60; 40 MHz; dfs */ 918281806Srpaulo case 120: /* channels 56,64; 40 MHz; dfs */ 919281806Srpaulo if (chan < 36 || chan > 64) 920281806Srpaulo return -1; 921281806Srpaulo return 5000 + 5 * chan; 922281806Srpaulo case 121: /* channels 100-140 */ 923281806Srpaulo case 122: /* channels 100-142; 40 MHz */ 924281806Srpaulo case 123: /* channels 104-136; 40 MHz */ 925281806Srpaulo if (chan < 100 || chan > 140) 926281806Srpaulo return -1; 927281806Srpaulo return 5000 + 5 * chan; 928281806Srpaulo case 124: /* channels 149,153,157,161 */ 929281806Srpaulo case 126: /* channels 149,157; 40 MHz */ 930281806Srpaulo case 127: /* channels 153,161; 40 MHz */ 931281806Srpaulo if (chan < 149 || chan > 161) 932281806Srpaulo return -1; 933281806Srpaulo return 5000 + 5 * chan; 934289549Srpaulo case 125: /* channels 149,153,157,161,165,169 */ 935289549Srpaulo if (chan < 149 || chan > 169) 936289549Srpaulo return -1; 937289549Srpaulo return 5000 + 5 * chan; 938281806Srpaulo case 128: /* center freqs 42, 58, 106, 122, 138, 155; 80 MHz */ 939281806Srpaulo case 130: /* center freqs 42, 58, 106, 122, 138, 155; 80 MHz */ 940281806Srpaulo if (chan < 36 || chan > 161) 941281806Srpaulo return -1; 942281806Srpaulo return 5000 + 5 * chan; 943281806Srpaulo case 129: /* center freqs 50, 114; 160 MHz */ 944281806Srpaulo if (chan < 50 || chan > 114) 945281806Srpaulo return -1; 946281806Srpaulo return 5000 + 5 * chan; 947281806Srpaulo case 180: /* 60 GHz band, channels 1..4 */ 948281806Srpaulo if (chan < 1 || chan > 4) 949281806Srpaulo return -1; 950281806Srpaulo return 56160 + 2160 * chan; 951281806Srpaulo } 952281806Srpaulo return -1; 953281806Srpaulo} 954281806Srpaulo 955281806Srpaulo/** 956281806Srpaulo * ieee80211_chan_to_freq - Convert channel info to frequency 957281806Srpaulo * @country: Country code, if known; otherwise, global operating class is used 958281806Srpaulo * @op_class: Operating class 959281806Srpaulo * @chan: Channel number 960281806Srpaulo * Returns: Frequency in MHz or -1 if the specified channel is unknown 961281806Srpaulo */ 962281806Srpauloint ieee80211_chan_to_freq(const char *country, u8 op_class, u8 chan) 963281806Srpaulo{ 964281806Srpaulo int freq; 965281806Srpaulo 966281806Srpaulo if (country_match(us_op_class_cc, country)) { 967281806Srpaulo freq = ieee80211_chan_to_freq_us(op_class, chan); 968281806Srpaulo if (freq > 0) 969281806Srpaulo return freq; 970281806Srpaulo } 971281806Srpaulo 972281806Srpaulo if (country_match(eu_op_class_cc, country)) { 973281806Srpaulo freq = ieee80211_chan_to_freq_eu(op_class, chan); 974281806Srpaulo if (freq > 0) 975281806Srpaulo return freq; 976281806Srpaulo } 977281806Srpaulo 978281806Srpaulo if (country_match(jp_op_class_cc, country)) { 979281806Srpaulo freq = ieee80211_chan_to_freq_jp(op_class, chan); 980281806Srpaulo if (freq > 0) 981281806Srpaulo return freq; 982281806Srpaulo } 983281806Srpaulo 984281806Srpaulo if (country_match(cn_op_class_cc, country)) { 985281806Srpaulo freq = ieee80211_chan_to_freq_cn(op_class, chan); 986281806Srpaulo if (freq > 0) 987281806Srpaulo return freq; 988281806Srpaulo } 989281806Srpaulo 990281806Srpaulo return ieee80211_chan_to_freq_global(op_class, chan); 991281806Srpaulo} 992281806Srpaulo 993281806Srpaulo 994281806Srpauloint ieee80211_is_dfs(int freq) 995281806Srpaulo{ 996281806Srpaulo /* TODO: this could be more accurate to better cover all domains */ 997281806Srpaulo return (freq >= 5260 && freq <= 5320) || (freq >= 5500 && freq <= 5700); 998281806Srpaulo} 999281806Srpaulo 1000281806Srpaulo 1001281806Srpaulostatic int is_11b(u8 rate) 1002281806Srpaulo{ 1003281806Srpaulo return rate == 0x02 || rate == 0x04 || rate == 0x0b || rate == 0x16; 1004281806Srpaulo} 1005281806Srpaulo 1006281806Srpaulo 1007281806Srpauloint supp_rates_11b_only(struct ieee802_11_elems *elems) 1008281806Srpaulo{ 1009281806Srpaulo int num_11b = 0, num_others = 0; 1010281806Srpaulo int i; 1011281806Srpaulo 1012281806Srpaulo if (elems->supp_rates == NULL && elems->ext_supp_rates == NULL) 1013281806Srpaulo return 0; 1014281806Srpaulo 1015281806Srpaulo for (i = 0; elems->supp_rates && i < elems->supp_rates_len; i++) { 1016281806Srpaulo if (is_11b(elems->supp_rates[i])) 1017281806Srpaulo num_11b++; 1018281806Srpaulo else 1019281806Srpaulo num_others++; 1020281806Srpaulo } 1021281806Srpaulo 1022281806Srpaulo for (i = 0; elems->ext_supp_rates && i < elems->ext_supp_rates_len; 1023281806Srpaulo i++) { 1024281806Srpaulo if (is_11b(elems->ext_supp_rates[i])) 1025281806Srpaulo num_11b++; 1026281806Srpaulo else 1027281806Srpaulo num_others++; 1028281806Srpaulo } 1029281806Srpaulo 1030281806Srpaulo return num_11b > 0 && num_others == 0; 1031281806Srpaulo} 1032281806Srpaulo 1033281806Srpaulo 1034281806Srpauloconst char * fc2str(u16 fc) 1035281806Srpaulo{ 1036281806Srpaulo u16 stype = WLAN_FC_GET_STYPE(fc); 1037281806Srpaulo#define C2S(x) case x: return #x; 1038281806Srpaulo 1039281806Srpaulo switch (WLAN_FC_GET_TYPE(fc)) { 1040281806Srpaulo case WLAN_FC_TYPE_MGMT: 1041281806Srpaulo switch (stype) { 1042281806Srpaulo C2S(WLAN_FC_STYPE_ASSOC_REQ) 1043281806Srpaulo C2S(WLAN_FC_STYPE_ASSOC_RESP) 1044281806Srpaulo C2S(WLAN_FC_STYPE_REASSOC_REQ) 1045281806Srpaulo C2S(WLAN_FC_STYPE_REASSOC_RESP) 1046281806Srpaulo C2S(WLAN_FC_STYPE_PROBE_REQ) 1047281806Srpaulo C2S(WLAN_FC_STYPE_PROBE_RESP) 1048281806Srpaulo C2S(WLAN_FC_STYPE_BEACON) 1049281806Srpaulo C2S(WLAN_FC_STYPE_ATIM) 1050281806Srpaulo C2S(WLAN_FC_STYPE_DISASSOC) 1051281806Srpaulo C2S(WLAN_FC_STYPE_AUTH) 1052281806Srpaulo C2S(WLAN_FC_STYPE_DEAUTH) 1053281806Srpaulo C2S(WLAN_FC_STYPE_ACTION) 1054281806Srpaulo } 1055281806Srpaulo break; 1056281806Srpaulo case WLAN_FC_TYPE_CTRL: 1057281806Srpaulo switch (stype) { 1058281806Srpaulo C2S(WLAN_FC_STYPE_PSPOLL) 1059281806Srpaulo C2S(WLAN_FC_STYPE_RTS) 1060281806Srpaulo C2S(WLAN_FC_STYPE_CTS) 1061281806Srpaulo C2S(WLAN_FC_STYPE_ACK) 1062281806Srpaulo C2S(WLAN_FC_STYPE_CFEND) 1063281806Srpaulo C2S(WLAN_FC_STYPE_CFENDACK) 1064281806Srpaulo } 1065281806Srpaulo break; 1066281806Srpaulo case WLAN_FC_TYPE_DATA: 1067281806Srpaulo switch (stype) { 1068281806Srpaulo C2S(WLAN_FC_STYPE_DATA) 1069281806Srpaulo C2S(WLAN_FC_STYPE_DATA_CFACK) 1070281806Srpaulo C2S(WLAN_FC_STYPE_DATA_CFPOLL) 1071281806Srpaulo C2S(WLAN_FC_STYPE_DATA_CFACKPOLL) 1072281806Srpaulo C2S(WLAN_FC_STYPE_NULLFUNC) 1073281806Srpaulo C2S(WLAN_FC_STYPE_CFACK) 1074281806Srpaulo C2S(WLAN_FC_STYPE_CFPOLL) 1075281806Srpaulo C2S(WLAN_FC_STYPE_CFACKPOLL) 1076281806Srpaulo C2S(WLAN_FC_STYPE_QOS_DATA) 1077281806Srpaulo C2S(WLAN_FC_STYPE_QOS_DATA_CFACK) 1078281806Srpaulo C2S(WLAN_FC_STYPE_QOS_DATA_CFPOLL) 1079281806Srpaulo C2S(WLAN_FC_STYPE_QOS_DATA_CFACKPOLL) 1080281806Srpaulo C2S(WLAN_FC_STYPE_QOS_NULL) 1081281806Srpaulo C2S(WLAN_FC_STYPE_QOS_CFPOLL) 1082281806Srpaulo C2S(WLAN_FC_STYPE_QOS_CFACKPOLL) 1083281806Srpaulo } 1084281806Srpaulo break; 1085281806Srpaulo } 1086281806Srpaulo return "WLAN_FC_TYPE_UNKNOWN"; 1087281806Srpaulo#undef C2S 1088281806Srpaulo} 1089289549Srpaulo 1090289549Srpaulo 1091289549Srpauloint mb_ies_info_by_ies(struct mb_ies_info *info, const u8 *ies_buf, 1092289549Srpaulo size_t ies_len) 1093289549Srpaulo{ 1094289549Srpaulo os_memset(info, 0, sizeof(*info)); 1095289549Srpaulo 1096289549Srpaulo while (ies_buf && ies_len >= 2 && 1097289549Srpaulo info->nof_ies < MAX_NOF_MB_IES_SUPPORTED) { 1098289549Srpaulo size_t len = 2 + ies_buf[1]; 1099289549Srpaulo 1100289549Srpaulo if (len > ies_len) { 1101289549Srpaulo wpa_hexdump(MSG_DEBUG, "Truncated IEs", 1102289549Srpaulo ies_buf, ies_len); 1103289549Srpaulo return -1; 1104289549Srpaulo } 1105289549Srpaulo 1106289549Srpaulo if (ies_buf[0] == WLAN_EID_MULTI_BAND) { 1107289549Srpaulo wpa_printf(MSG_DEBUG, "MB IE of %zu bytes found", len); 1108289549Srpaulo info->ies[info->nof_ies].ie = ies_buf + 2; 1109289549Srpaulo info->ies[info->nof_ies].ie_len = ies_buf[1]; 1110289549Srpaulo info->nof_ies++; 1111289549Srpaulo } 1112289549Srpaulo 1113289549Srpaulo ies_len -= len; 1114289549Srpaulo ies_buf += len; 1115289549Srpaulo } 1116289549Srpaulo 1117289549Srpaulo return 0; 1118289549Srpaulo} 1119289549Srpaulo 1120289549Srpaulo 1121289549Srpaulostruct wpabuf * mb_ies_by_info(struct mb_ies_info *info) 1122289549Srpaulo{ 1123289549Srpaulo struct wpabuf *mb_ies = NULL; 1124289549Srpaulo 1125289549Srpaulo WPA_ASSERT(info != NULL); 1126289549Srpaulo 1127289549Srpaulo if (info->nof_ies) { 1128289549Srpaulo u8 i; 1129289549Srpaulo size_t mb_ies_size = 0; 1130289549Srpaulo 1131289549Srpaulo for (i = 0; i < info->nof_ies; i++) 1132289549Srpaulo mb_ies_size += 2 + info->ies[i].ie_len; 1133289549Srpaulo 1134289549Srpaulo mb_ies = wpabuf_alloc(mb_ies_size); 1135289549Srpaulo if (mb_ies) { 1136289549Srpaulo for (i = 0; i < info->nof_ies; i++) { 1137289549Srpaulo wpabuf_put_u8(mb_ies, WLAN_EID_MULTI_BAND); 1138289549Srpaulo wpabuf_put_u8(mb_ies, info->ies[i].ie_len); 1139289549Srpaulo wpabuf_put_data(mb_ies, 1140289549Srpaulo info->ies[i].ie, 1141289549Srpaulo info->ies[i].ie_len); 1142289549Srpaulo } 1143289549Srpaulo } 1144289549Srpaulo } 1145289549Srpaulo 1146289549Srpaulo return mb_ies; 1147289549Srpaulo} 1148