1189251Ssam/* 2189251Ssam * IEEE 802.11 Common routines 3252726Srpaulo * Copyright (c) 2002-2012, 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" 12189251Ssam#include "ieee802_11_defs.h" 13189251Ssam#include "ieee802_11_common.h" 14189251Ssam 15189251Ssam 16214734Srpaulostatic int ieee802_11_parse_vendor_specific(const u8 *pos, size_t elen, 17189251Ssam struct ieee802_11_elems *elems, 18189251Ssam int show_errors) 19189251Ssam{ 20189251Ssam unsigned int oui; 21189251Ssam 22189251Ssam /* first 3 bytes in vendor specific information element are the IEEE 23189251Ssam * OUI of the vendor. The following byte is used a vendor specific 24189251Ssam * sub-type. */ 25189251Ssam if (elen < 4) { 26189251Ssam if (show_errors) { 27189251Ssam wpa_printf(MSG_MSGDUMP, "short vendor specific " 28189251Ssam "information element ignored (len=%lu)", 29189251Ssam (unsigned long) elen); 30189251Ssam } 31189251Ssam return -1; 32189251Ssam } 33189251Ssam 34189251Ssam oui = WPA_GET_BE24(pos); 35189251Ssam switch (oui) { 36189251Ssam case OUI_MICROSOFT: 37189251Ssam /* Microsoft/Wi-Fi information elements are further typed and 38189251Ssam * subtyped */ 39189251Ssam switch (pos[3]) { 40189251Ssam case 1: 41189251Ssam /* Microsoft OUI (00:50:F2) with OUI Type 1: 42189251Ssam * real WPA information element */ 43189251Ssam elems->wpa_ie = pos; 44189251Ssam elems->wpa_ie_len = elen; 45189251Ssam break; 46209158Srpaulo case WMM_OUI_TYPE: 47209158Srpaulo /* WMM information element */ 48189251Ssam if (elen < 5) { 49209158Srpaulo wpa_printf(MSG_MSGDUMP, "short WMM " 50189251Ssam "information element ignored " 51189251Ssam "(len=%lu)", 52189251Ssam (unsigned long) elen); 53189251Ssam return -1; 54189251Ssam } 55189251Ssam switch (pos[4]) { 56209158Srpaulo case WMM_OUI_SUBTYPE_INFORMATION_ELEMENT: 57209158Srpaulo case WMM_OUI_SUBTYPE_PARAMETER_ELEMENT: 58209158Srpaulo /* 59209158Srpaulo * Share same pointer since only one of these 60209158Srpaulo * is used and they start with same data. 61209158Srpaulo * Length field can be used to distinguish the 62209158Srpaulo * IEs. 63209158Srpaulo */ 64209158Srpaulo elems->wmm = pos; 65209158Srpaulo elems->wmm_len = elen; 66189251Ssam break; 67209158Srpaulo case WMM_OUI_SUBTYPE_TSPEC_ELEMENT: 68209158Srpaulo elems->wmm_tspec = pos; 69209158Srpaulo elems->wmm_tspec_len = elen; 70189251Ssam break; 71189251Ssam default: 72252726Srpaulo wpa_printf(MSG_EXCESSIVE, "unknown WMM " 73189251Ssam "information element ignored " 74189251Ssam "(subtype=%d len=%lu)", 75189251Ssam pos[4], (unsigned long) elen); 76189251Ssam return -1; 77189251Ssam } 78189251Ssam break; 79189251Ssam case 4: 80189251Ssam /* Wi-Fi Protected Setup (WPS) IE */ 81189251Ssam elems->wps_ie = pos; 82189251Ssam elems->wps_ie_len = elen; 83189251Ssam break; 84189251Ssam default: 85252726Srpaulo wpa_printf(MSG_EXCESSIVE, "Unknown Microsoft " 86189251Ssam "information element ignored " 87252726Srpaulo "(type=%d len=%lu)", 88252726Srpaulo pos[3], (unsigned long) elen); 89252726Srpaulo return -1; 90252726Srpaulo } 91252726Srpaulo break; 92252726Srpaulo 93252726Srpaulo case OUI_WFA: 94252726Srpaulo switch (pos[3]) { 95252726Srpaulo case P2P_OUI_TYPE: 96252726Srpaulo /* Wi-Fi Alliance - P2P IE */ 97252726Srpaulo elems->p2p = pos; 98252726Srpaulo elems->p2p_len = elen; 99252726Srpaulo break; 100252726Srpaulo case WFD_OUI_TYPE: 101252726Srpaulo /* Wi-Fi Alliance - WFD IE */ 102252726Srpaulo elems->wfd = pos; 103252726Srpaulo elems->wfd_len = elen; 104252726Srpaulo break; 105252726Srpaulo case HS20_INDICATION_OUI_TYPE: 106252726Srpaulo /* Hotspot 2.0 */ 107252726Srpaulo elems->hs20 = pos; 108252726Srpaulo elems->hs20_len = elen; 109252726Srpaulo break; 110252726Srpaulo default: 111252726Srpaulo wpa_printf(MSG_MSGDUMP, "Unknown WFA " 112252726Srpaulo "information element ignored " 113189251Ssam "(type=%d len=%lu)\n", 114189251Ssam pos[3], (unsigned long) elen); 115189251Ssam return -1; 116189251Ssam } 117189251Ssam break; 118189251Ssam 119189251Ssam case OUI_BROADCOM: 120189251Ssam switch (pos[3]) { 121189251Ssam case VENDOR_HT_CAPAB_OUI_TYPE: 122189251Ssam elems->vendor_ht_cap = pos; 123189251Ssam elems->vendor_ht_cap_len = elen; 124189251Ssam break; 125189251Ssam default: 126252726Srpaulo wpa_printf(MSG_EXCESSIVE, "Unknown Broadcom " 127189251Ssam "information element ignored " 128252726Srpaulo "(type=%d len=%lu)", 129189251Ssam pos[3], (unsigned long) elen); 130189251Ssam return -1; 131189251Ssam } 132189251Ssam break; 133189251Ssam 134189251Ssam default: 135252726Srpaulo wpa_printf(MSG_EXCESSIVE, "unknown vendor specific " 136252726Srpaulo "information element ignored (vendor OUI " 137252726Srpaulo "%02x:%02x:%02x len=%lu)", 138189251Ssam pos[0], pos[1], pos[2], (unsigned long) elen); 139189251Ssam return -1; 140189251Ssam } 141189251Ssam 142189251Ssam return 0; 143189251Ssam} 144189251Ssam 145189251Ssam 146189251Ssam/** 147189251Ssam * ieee802_11_parse_elems - Parse information elements in management frames 148189251Ssam * @start: Pointer to the start of IEs 149189251Ssam * @len: Length of IE buffer in octets 150189251Ssam * @elems: Data structure for parsed elements 151189251Ssam * @show_errors: Whether to show parsing errors in debug log 152189251Ssam * Returns: Parsing result 153189251Ssam */ 154214734SrpauloParseRes ieee802_11_parse_elems(const u8 *start, size_t len, 155189251Ssam struct ieee802_11_elems *elems, 156189251Ssam int show_errors) 157189251Ssam{ 158189251Ssam size_t left = len; 159214734Srpaulo const u8 *pos = start; 160189251Ssam int unknown = 0; 161189251Ssam 162189251Ssam os_memset(elems, 0, sizeof(*elems)); 163189251Ssam 164189251Ssam while (left >= 2) { 165189251Ssam u8 id, elen; 166189251Ssam 167189251Ssam id = *pos++; 168189251Ssam elen = *pos++; 169189251Ssam left -= 2; 170189251Ssam 171189251Ssam if (elen > left) { 172189251Ssam if (show_errors) { 173189251Ssam wpa_printf(MSG_DEBUG, "IEEE 802.11 element " 174189251Ssam "parse failed (id=%d elen=%d " 175189251Ssam "left=%lu)", 176189251Ssam id, elen, (unsigned long) left); 177189251Ssam wpa_hexdump(MSG_MSGDUMP, "IEs", start, len); 178189251Ssam } 179189251Ssam return ParseFailed; 180189251Ssam } 181189251Ssam 182189251Ssam switch (id) { 183189251Ssam case WLAN_EID_SSID: 184189251Ssam elems->ssid = pos; 185189251Ssam elems->ssid_len = elen; 186189251Ssam break; 187189251Ssam case WLAN_EID_SUPP_RATES: 188189251Ssam elems->supp_rates = pos; 189189251Ssam elems->supp_rates_len = elen; 190189251Ssam break; 191189251Ssam case WLAN_EID_FH_PARAMS: 192189251Ssam elems->fh_params = pos; 193189251Ssam elems->fh_params_len = elen; 194189251Ssam break; 195189251Ssam case WLAN_EID_DS_PARAMS: 196189251Ssam elems->ds_params = pos; 197189251Ssam elems->ds_params_len = elen; 198189251Ssam break; 199189251Ssam case WLAN_EID_CF_PARAMS: 200189251Ssam elems->cf_params = pos; 201189251Ssam elems->cf_params_len = elen; 202189251Ssam break; 203189251Ssam case WLAN_EID_TIM: 204189251Ssam elems->tim = pos; 205189251Ssam elems->tim_len = elen; 206189251Ssam break; 207189251Ssam case WLAN_EID_IBSS_PARAMS: 208189251Ssam elems->ibss_params = pos; 209189251Ssam elems->ibss_params_len = elen; 210189251Ssam break; 211189251Ssam case WLAN_EID_CHALLENGE: 212189251Ssam elems->challenge = pos; 213189251Ssam elems->challenge_len = elen; 214189251Ssam break; 215189251Ssam case WLAN_EID_ERP_INFO: 216189251Ssam elems->erp_info = pos; 217189251Ssam elems->erp_info_len = elen; 218189251Ssam break; 219189251Ssam case WLAN_EID_EXT_SUPP_RATES: 220189251Ssam elems->ext_supp_rates = pos; 221189251Ssam elems->ext_supp_rates_len = elen; 222189251Ssam break; 223189251Ssam case WLAN_EID_VENDOR_SPECIFIC: 224189251Ssam if (ieee802_11_parse_vendor_specific(pos, elen, 225189251Ssam elems, 226189251Ssam show_errors)) 227189251Ssam unknown++; 228189251Ssam break; 229189251Ssam case WLAN_EID_RSN: 230189251Ssam elems->rsn_ie = pos; 231189251Ssam elems->rsn_ie_len = elen; 232189251Ssam break; 233189251Ssam case WLAN_EID_PWR_CAPABILITY: 234189251Ssam elems->power_cap = pos; 235189251Ssam elems->power_cap_len = elen; 236189251Ssam break; 237189251Ssam case WLAN_EID_SUPPORTED_CHANNELS: 238189251Ssam elems->supp_channels = pos; 239189251Ssam elems->supp_channels_len = elen; 240189251Ssam break; 241189251Ssam case WLAN_EID_MOBILITY_DOMAIN: 242189251Ssam elems->mdie = pos; 243189251Ssam elems->mdie_len = elen; 244189251Ssam break; 245189251Ssam case WLAN_EID_FAST_BSS_TRANSITION: 246189251Ssam elems->ftie = pos; 247189251Ssam elems->ftie_len = elen; 248189251Ssam break; 249189251Ssam case WLAN_EID_TIMEOUT_INTERVAL: 250189251Ssam elems->timeout_int = pos; 251189251Ssam elems->timeout_int_len = elen; 252189251Ssam break; 253189251Ssam case WLAN_EID_HT_CAP: 254189251Ssam elems->ht_capabilities = pos; 255189251Ssam elems->ht_capabilities_len = elen; 256189251Ssam break; 257189251Ssam case WLAN_EID_HT_OPERATION: 258189251Ssam elems->ht_operation = pos; 259189251Ssam elems->ht_operation_len = elen; 260189251Ssam break; 261252726Srpaulo case WLAN_EID_VHT_CAP: 262252726Srpaulo elems->vht_capabilities = pos; 263252726Srpaulo elems->vht_capabilities_len = elen; 264252726Srpaulo break; 265252726Srpaulo case WLAN_EID_VHT_OPERATION: 266252726Srpaulo elems->vht_operation = pos; 267252726Srpaulo elems->vht_operation_len = elen; 268252726Srpaulo break; 269252726Srpaulo case WLAN_EID_LINK_ID: 270252726Srpaulo if (elen < 18) 271252726Srpaulo break; 272252726Srpaulo elems->link_id = pos; 273252726Srpaulo break; 274252726Srpaulo case WLAN_EID_INTERWORKING: 275252726Srpaulo elems->interworking = pos; 276252726Srpaulo elems->interworking_len = elen; 277252726Srpaulo break; 278252726Srpaulo case WLAN_EID_EXT_CAPAB: 279252726Srpaulo elems->ext_capab = pos; 280252726Srpaulo elems->ext_capab_len = elen; 281252726Srpaulo break; 282252726Srpaulo case WLAN_EID_BSS_MAX_IDLE_PERIOD: 283252726Srpaulo if (elen < 3) 284252726Srpaulo break; 285252726Srpaulo elems->bss_max_idle_period = pos; 286252726Srpaulo break; 287252726Srpaulo case WLAN_EID_SSID_LIST: 288252726Srpaulo elems->ssid_list = pos; 289252726Srpaulo elems->ssid_list_len = elen; 290252726Srpaulo break; 291189251Ssam default: 292189251Ssam unknown++; 293189251Ssam if (!show_errors) 294189251Ssam break; 295189251Ssam wpa_printf(MSG_MSGDUMP, "IEEE 802.11 element parse " 296189251Ssam "ignored unknown element (id=%d elen=%d)", 297189251Ssam id, elen); 298189251Ssam break; 299189251Ssam } 300189251Ssam 301189251Ssam left -= elen; 302189251Ssam pos += elen; 303189251Ssam } 304189251Ssam 305189251Ssam if (left) 306189251Ssam return ParseFailed; 307189251Ssam 308189251Ssam return unknown ? ParseUnknown : ParseOK; 309189251Ssam} 310214734Srpaulo 311214734Srpaulo 312214734Srpauloint ieee802_11_ie_count(const u8 *ies, size_t ies_len) 313214734Srpaulo{ 314214734Srpaulo int count = 0; 315214734Srpaulo const u8 *pos, *end; 316214734Srpaulo 317214734Srpaulo if (ies == NULL) 318214734Srpaulo return 0; 319214734Srpaulo 320214734Srpaulo pos = ies; 321214734Srpaulo end = ies + ies_len; 322214734Srpaulo 323214734Srpaulo while (pos + 2 <= end) { 324214734Srpaulo if (pos + 2 + pos[1] > end) 325214734Srpaulo break; 326214734Srpaulo count++; 327214734Srpaulo pos += 2 + pos[1]; 328214734Srpaulo } 329214734Srpaulo 330214734Srpaulo return count; 331214734Srpaulo} 332214734Srpaulo 333214734Srpaulo 334214734Srpaulostruct wpabuf * ieee802_11_vendor_ie_concat(const u8 *ies, size_t ies_len, 335214734Srpaulo u32 oui_type) 336214734Srpaulo{ 337214734Srpaulo struct wpabuf *buf; 338214734Srpaulo const u8 *end, *pos, *ie; 339214734Srpaulo 340214734Srpaulo pos = ies; 341214734Srpaulo end = ies + ies_len; 342214734Srpaulo ie = NULL; 343214734Srpaulo 344214734Srpaulo while (pos + 1 < end) { 345214734Srpaulo if (pos + 2 + pos[1] > end) 346214734Srpaulo return NULL; 347214734Srpaulo if (pos[0] == WLAN_EID_VENDOR_SPECIFIC && pos[1] >= 4 && 348214734Srpaulo WPA_GET_BE32(&pos[2]) == oui_type) { 349214734Srpaulo ie = pos; 350214734Srpaulo break; 351214734Srpaulo } 352214734Srpaulo pos += 2 + pos[1]; 353214734Srpaulo } 354214734Srpaulo 355214734Srpaulo if (ie == NULL) 356214734Srpaulo return NULL; /* No specified vendor IE found */ 357214734Srpaulo 358214734Srpaulo buf = wpabuf_alloc(ies_len); 359214734Srpaulo if (buf == NULL) 360214734Srpaulo return NULL; 361214734Srpaulo 362214734Srpaulo /* 363214734Srpaulo * There may be multiple vendor IEs in the message, so need to 364214734Srpaulo * concatenate their data fields. 365214734Srpaulo */ 366214734Srpaulo while (pos + 1 < end) { 367214734Srpaulo if (pos + 2 + pos[1] > end) 368214734Srpaulo break; 369214734Srpaulo if (pos[0] == WLAN_EID_VENDOR_SPECIFIC && pos[1] >= 4 && 370214734Srpaulo WPA_GET_BE32(&pos[2]) == oui_type) 371214734Srpaulo wpabuf_put_data(buf, pos + 6, pos[1] - 4); 372214734Srpaulo pos += 2 + pos[1]; 373214734Srpaulo } 374214734Srpaulo 375214734Srpaulo return buf; 376214734Srpaulo} 377252726Srpaulo 378252726Srpaulo 379252726Srpauloconst u8 * get_hdr_bssid(const struct ieee80211_hdr *hdr, size_t len) 380252726Srpaulo{ 381252726Srpaulo u16 fc, type, stype; 382252726Srpaulo 383252726Srpaulo /* 384252726Srpaulo * PS-Poll frames are 16 bytes. All other frames are 385252726Srpaulo * 24 bytes or longer. 386252726Srpaulo */ 387252726Srpaulo if (len < 16) 388252726Srpaulo return NULL; 389252726Srpaulo 390252726Srpaulo fc = le_to_host16(hdr->frame_control); 391252726Srpaulo type = WLAN_FC_GET_TYPE(fc); 392252726Srpaulo stype = WLAN_FC_GET_STYPE(fc); 393252726Srpaulo 394252726Srpaulo switch (type) { 395252726Srpaulo case WLAN_FC_TYPE_DATA: 396252726Srpaulo if (len < 24) 397252726Srpaulo return NULL; 398252726Srpaulo switch (fc & (WLAN_FC_FROMDS | WLAN_FC_TODS)) { 399252726Srpaulo case WLAN_FC_FROMDS | WLAN_FC_TODS: 400252726Srpaulo case WLAN_FC_TODS: 401252726Srpaulo return hdr->addr1; 402252726Srpaulo case WLAN_FC_FROMDS: 403252726Srpaulo return hdr->addr2; 404252726Srpaulo default: 405252726Srpaulo return NULL; 406252726Srpaulo } 407252726Srpaulo case WLAN_FC_TYPE_CTRL: 408252726Srpaulo if (stype != WLAN_FC_STYPE_PSPOLL) 409252726Srpaulo return NULL; 410252726Srpaulo return hdr->addr1; 411252726Srpaulo case WLAN_FC_TYPE_MGMT: 412252726Srpaulo return hdr->addr3; 413252726Srpaulo default: 414252726Srpaulo return NULL; 415252726Srpaulo } 416252726Srpaulo} 417252726Srpaulo 418252726Srpaulo 419252726Srpauloint hostapd_config_wmm_ac(struct hostapd_wmm_ac_params wmm_ac_params[], 420252726Srpaulo const char *name, const char *val) 421252726Srpaulo{ 422252726Srpaulo int num, v; 423252726Srpaulo const char *pos; 424252726Srpaulo struct hostapd_wmm_ac_params *ac; 425252726Srpaulo 426252726Srpaulo /* skip 'wme_ac_' or 'wmm_ac_' prefix */ 427252726Srpaulo pos = name + 7; 428252726Srpaulo if (os_strncmp(pos, "be_", 3) == 0) { 429252726Srpaulo num = 0; 430252726Srpaulo pos += 3; 431252726Srpaulo } else if (os_strncmp(pos, "bk_", 3) == 0) { 432252726Srpaulo num = 1; 433252726Srpaulo pos += 3; 434252726Srpaulo } else if (os_strncmp(pos, "vi_", 3) == 0) { 435252726Srpaulo num = 2; 436252726Srpaulo pos += 3; 437252726Srpaulo } else if (os_strncmp(pos, "vo_", 3) == 0) { 438252726Srpaulo num = 3; 439252726Srpaulo pos += 3; 440252726Srpaulo } else { 441252726Srpaulo wpa_printf(MSG_ERROR, "Unknown WMM name '%s'", pos); 442252726Srpaulo return -1; 443252726Srpaulo } 444252726Srpaulo 445252726Srpaulo ac = &wmm_ac_params[num]; 446252726Srpaulo 447252726Srpaulo if (os_strcmp(pos, "aifs") == 0) { 448252726Srpaulo v = atoi(val); 449252726Srpaulo if (v < 1 || v > 255) { 450252726Srpaulo wpa_printf(MSG_ERROR, "Invalid AIFS value %d", v); 451252726Srpaulo return -1; 452252726Srpaulo } 453252726Srpaulo ac->aifs = v; 454252726Srpaulo } else if (os_strcmp(pos, "cwmin") == 0) { 455252726Srpaulo v = atoi(val); 456252726Srpaulo if (v < 0 || v > 12) { 457252726Srpaulo wpa_printf(MSG_ERROR, "Invalid cwMin value %d", v); 458252726Srpaulo return -1; 459252726Srpaulo } 460252726Srpaulo ac->cwmin = v; 461252726Srpaulo } else if (os_strcmp(pos, "cwmax") == 0) { 462252726Srpaulo v = atoi(val); 463252726Srpaulo if (v < 0 || v > 12) { 464252726Srpaulo wpa_printf(MSG_ERROR, "Invalid cwMax value %d", v); 465252726Srpaulo return -1; 466252726Srpaulo } 467252726Srpaulo ac->cwmax = v; 468252726Srpaulo } else if (os_strcmp(pos, "txop_limit") == 0) { 469252726Srpaulo v = atoi(val); 470252726Srpaulo if (v < 0 || v > 0xffff) { 471252726Srpaulo wpa_printf(MSG_ERROR, "Invalid txop value %d", v); 472252726Srpaulo return -1; 473252726Srpaulo } 474252726Srpaulo ac->txop_limit = v; 475252726Srpaulo } else if (os_strcmp(pos, "acm") == 0) { 476252726Srpaulo v = atoi(val); 477252726Srpaulo if (v < 0 || v > 1) { 478252726Srpaulo wpa_printf(MSG_ERROR, "Invalid acm value %d", v); 479252726Srpaulo return -1; 480252726Srpaulo } 481252726Srpaulo ac->admission_control_mandatory = v; 482252726Srpaulo } else { 483252726Srpaulo wpa_printf(MSG_ERROR, "Unknown wmm_ac_ field '%s'", pos); 484252726Srpaulo return -1; 485252726Srpaulo } 486252726Srpaulo 487252726Srpaulo return 0; 488252726Srpaulo} 489