1 /* 2 * IEEE 802.11 Common routines 3 * Copyright (c) 2002-2012, Jouni Malinen <j (at) w1.fi> 4 * 5 * This software may be distributed under the terms of the BSD license. 6 * See README for more details. 7 */ 8 9 #include "includes.h" 10 11 #include "common.h" 12 #include "ieee802_11_defs.h" 13 #include "ieee802_11_common.h" 14 15 16 static int ieee802_11_parse_vendor_specific(const u8 *pos, size_t elen, 17 struct ieee802_11_elems *elems, 18 int show_errors) 19 { 20 unsigned int oui; 21 22 /* first 3 bytes in vendor specific information element are the IEEE 23 * OUI of the vendor. The following byte is used a vendor specific 24 * sub-type. */ 25 if (elen < 4) { 26 if (show_errors) { 27 wpa_printf(MSG_MSGDUMP, "short vendor specific " 28 "information element ignored (len=%lu)", 29 (unsigned long) elen); 30 } 31 return -1; 32 } 33 34 oui = WPA_GET_BE24(pos); 35 switch (oui) { 36 case OUI_MICROSOFT: 37 /* Microsoft/Wi-Fi information elements are further typed and 38 * subtyped */ 39 switch (pos[3]) { 40 case 1: 41 /* Microsoft OUI (00:50:F2) with OUI Type 1: 42 * real WPA information element */ 43 elems->wpa_ie = pos; 44 elems->wpa_ie_len = elen; 45 break; 46 case WMM_OUI_TYPE: 47 /* WMM information element */ 48 if (elen < 5) { 49 wpa_printf(MSG_MSGDUMP, "short WMM " 50 "information element ignored " 51 "(len=%lu)", 52 (unsigned long) elen); 53 return -1; 54 } 55 switch (pos[4]) { 56 case WMM_OUI_SUBTYPE_INFORMATION_ELEMENT: 57 case WMM_OUI_SUBTYPE_PARAMETER_ELEMENT: 58 /* 59 * Share same pointer since only one of these 60 * is used and they start with same data. 61 * Length field can be used to distinguish the 62 * IEs. 63 */ 64 elems->wmm = pos; 65 elems->wmm_len = elen; 66 break; 67 case WMM_OUI_SUBTYPE_TSPEC_ELEMENT: 68 elems->wmm_tspec = pos; 69 elems->wmm_tspec_len = elen; 70 break; 71 default: 72 wpa_printf(MSG_EXCESSIVE, "unknown WMM " 73 "information element ignored " 74 "(subtype=%d len=%lu)", 75 pos[4], (unsigned long) elen); 76 return -1; 77 } 78 break; 79 case 4: 80 /* Wi-Fi Protected Setup (WPS) IE */ 81 elems->wps_ie = pos; 82 elems->wps_ie_len = elen; 83 break; 84 default: 85 wpa_printf(MSG_EXCESSIVE, "Unknown Microsoft " 86 "information element ignored " 87 "(type=%d len=%lu)", 88 pos[3], (unsigned long) elen); 89 return -1; 90 } 91 break; 92 93 case OUI_WFA: 94 switch (pos[3]) { 95 case P2P_OUI_TYPE: 96 /* Wi-Fi Alliance - P2P IE */ 97 elems->p2p = pos; 98 elems->p2p_len = elen; 99 break; 100 case WFD_OUI_TYPE: 101 /* Wi-Fi Alliance - WFD IE */ 102 elems->wfd = pos; 103 elems->wfd_len = elen; 104 break; 105 case HS20_INDICATION_OUI_TYPE: 106 /* Hotspot 2.0 */ 107 elems->hs20 = pos; 108 elems->hs20_len = elen; 109 break; 110 default: 111 wpa_printf(MSG_MSGDUMP, "Unknown WFA " 112 "information element ignored " 113 "(type=%d len=%lu)\n", 114 pos[3], (unsigned long) elen); 115 return -1; 116 } 117 break; 118 119 case OUI_BROADCOM: 120 switch (pos[3]) { 121 case VENDOR_HT_CAPAB_OUI_TYPE: 122 elems->vendor_ht_cap = pos; 123 elems->vendor_ht_cap_len = elen; 124 break; 125 default: 126 wpa_printf(MSG_EXCESSIVE, "Unknown Broadcom " 127 "information element ignored " 128 "(type=%d len=%lu)", 129 pos[3], (unsigned long) elen); 130 return -1; 131 } 132 break; 133 134 default: 135 wpa_printf(MSG_EXCESSIVE, "unknown vendor specific " 136 "information element ignored (vendor OUI " 137 "%02x:%02x:%02x len=%lu)", 138 pos[0], pos[1], pos[2], (unsigned long) elen); 139 return -1; 140 } 141 142 return 0; 143 } 144 145 146 /** 147 * ieee802_11_parse_elems - Parse information elements in management frames 148 * @start: Pointer to the start of IEs 149 * @len: Length of IE buffer in octets 150 * @elems: Data structure for parsed elements 151 * @show_errors: Whether to show parsing errors in debug log 152 * Returns: Parsing result 153 */ 154 ParseRes ieee802_11_parse_elems(const u8 *start, size_t len, 155 struct ieee802_11_elems *elems, 156 int show_errors) 157 { 158 size_t left = len; 159 const u8 *pos = start; 160 int unknown = 0; 161 162 os_memset(elems, 0, sizeof(*elems)); 163 164 while (left >= 2) { 165 u8 id, elen; 166 167 id = *pos++; 168 elen = *pos++; 169 left -= 2; 170 171 if (elen > left) { 172 if (show_errors) { 173 wpa_printf(MSG_DEBUG, "IEEE 802.11 element " 174 "parse failed (id=%d elen=%d " 175 "left=%lu)", 176 id, elen, (unsigned long) left); 177 wpa_hexdump(MSG_MSGDUMP, "IEs", start, len); 178 } 179 return ParseFailed; 180 } 181 182 switch (id) { 183 case WLAN_EID_SSID: 184 elems->ssid = pos; 185 elems->ssid_len = elen; 186 break; 187 case WLAN_EID_SUPP_RATES: 188 elems->supp_rates = pos; 189 elems->supp_rates_len = elen; 190 break; 191 case WLAN_EID_FH_PARAMS: 192 elems->fh_params = pos; 193 elems->fh_params_len = elen; 194 break; 195 case WLAN_EID_DS_PARAMS: 196 elems->ds_params = pos; 197 elems->ds_params_len = elen; 198 break; 199 case WLAN_EID_CF_PARAMS: 200 elems->cf_params = pos; 201 elems->cf_params_len = elen; 202 break; 203 case WLAN_EID_TIM: 204 elems->tim = pos; 205 elems->tim_len = elen; 206 break; 207 case WLAN_EID_IBSS_PARAMS: 208 elems->ibss_params = pos; 209 elems->ibss_params_len = elen; 210 break; 211 case WLAN_EID_CHALLENGE: 212 elems->challenge = pos; 213 elems->challenge_len = elen; 214 break; 215 case WLAN_EID_ERP_INFO: 216 elems->erp_info = pos; 217 elems->erp_info_len = elen; 218 break; 219 case WLAN_EID_EXT_SUPP_RATES: 220 elems->ext_supp_rates = pos; 221 elems->ext_supp_rates_len = elen; 222 break; 223 case WLAN_EID_VENDOR_SPECIFIC: 224 if (ieee802_11_parse_vendor_specific(pos, elen, 225 elems, 226 show_errors)) 227 unknown++; 228 break; 229 case WLAN_EID_RSN: 230 elems->rsn_ie = pos; 231 elems->rsn_ie_len = elen; 232 break; 233 case WLAN_EID_PWR_CAPABILITY: 234 elems->power_cap = pos; 235 elems->power_cap_len = elen; 236 break; 237 case WLAN_EID_SUPPORTED_CHANNELS: 238 elems->supp_channels = pos; 239 elems->supp_channels_len = elen; 240 break; 241 case WLAN_EID_MOBILITY_DOMAIN: 242 elems->mdie = pos; 243 elems->mdie_len = elen; 244 break; 245 case WLAN_EID_FAST_BSS_TRANSITION: 246 elems->ftie = pos; 247 elems->ftie_len = elen; 248 break; 249 case WLAN_EID_TIMEOUT_INTERVAL: 250 elems->timeout_int = pos; 251 elems->timeout_int_len = elen; 252 break; 253 case WLAN_EID_HT_CAP: 254 elems->ht_capabilities = pos; 255 elems->ht_capabilities_len = elen; 256 break; 257 case WLAN_EID_HT_OPERATION: 258 elems->ht_operation = pos; 259 elems->ht_operation_len = elen; 260 break; 261 case WLAN_EID_VHT_CAP: 262 elems->vht_capabilities = pos; 263 elems->vht_capabilities_len = elen; 264 break; 265 case WLAN_EID_VHT_OPERATION: 266 elems->vht_operation = pos; 267 elems->vht_operation_len = elen; 268 break; 269 case WLAN_EID_LINK_ID: 270 if (elen < 18) 271 break; 272 elems->link_id = pos; 273 break; 274 case WLAN_EID_INTERWORKING: 275 elems->interworking = pos; 276 elems->interworking_len = elen; 277 break; 278 case WLAN_EID_EXT_CAPAB: 279 elems->ext_capab = pos; 280 elems->ext_capab_len = elen; 281 break; 282 case WLAN_EID_BSS_MAX_IDLE_PERIOD: 283 if (elen < 3) 284 break; 285 elems->bss_max_idle_period = pos; 286 break; 287 case WLAN_EID_SSID_LIST: 288 elems->ssid_list = pos; 289 elems->ssid_list_len = elen; 290 break; 291 default: 292 unknown++; 293 if (!show_errors) 294 break; 295 wpa_printf(MSG_MSGDUMP, "IEEE 802.11 element parse " 296 "ignored unknown element (id=%d elen=%d)", 297 id, elen); 298 break; 299 } 300 301 left -= elen; 302 pos += elen; 303 } 304 305 if (left) 306 return ParseFailed; 307 308 return unknown ? ParseUnknown : ParseOK; 309 } 310 311 312 int ieee802_11_ie_count(const u8 *ies, size_t ies_len) 313 { 314 int count = 0; 315 const u8 *pos, *end; 316 317 if (ies == NULL) 318 return 0; 319 320 pos = ies; 321 end = ies + ies_len; 322 323 while (pos + 2 <= end) { 324 if (pos + 2 + pos[1] > end) 325 break; 326 count++; 327 pos += 2 + pos[1]; 328 } 329 330 return count; 331 } 332 333 334 struct wpabuf * ieee802_11_vendor_ie_concat(const u8 *ies, size_t ies_len, 335 u32 oui_type) 336 { 337 struct wpabuf *buf; 338 const u8 *end, *pos, *ie; 339 340 pos = ies; 341 end = ies + ies_len; 342 ie = NULL; 343 344 while (pos + 1 < end) { 345 if (pos + 2 + pos[1] > end) 346 return NULL; 347 if (pos[0] == WLAN_EID_VENDOR_SPECIFIC && pos[1] >= 4 && 348 WPA_GET_BE32(&pos[2]) == oui_type) { 349 ie = pos; 350 break; 351 } 352 pos += 2 + pos[1]; 353 } 354 355 if (ie == NULL) 356 return NULL; /* No specified vendor IE found */ 357 358 buf = wpabuf_alloc(ies_len); 359 if (buf == NULL) 360 return NULL; 361 362 /* 363 * There may be multiple vendor IEs in the message, so need to 364 * concatenate their data fields. 365 */ 366 while (pos + 1 < end) { 367 if (pos + 2 + pos[1] > end) 368 break; 369 if (pos[0] == WLAN_EID_VENDOR_SPECIFIC && pos[1] >= 4 && 370 WPA_GET_BE32(&pos[2]) == oui_type) 371 wpabuf_put_data(buf, pos + 6, pos[1] - 4); 372 pos += 2 + pos[1]; 373 } 374 375 return buf; 376 } 377 378 379 const u8 * get_hdr_bssid(const struct ieee80211_hdr *hdr, size_t len) 380 { 381 u16 fc, type, stype; 382 383 /* 384 * PS-Poll frames are 16 bytes. All other frames are 385 * 24 bytes or longer. 386 */ 387 if (len < 16) 388 return NULL; 389 390 fc = le_to_host16(hdr->frame_control); 391 type = WLAN_FC_GET_TYPE(fc); 392 stype = WLAN_FC_GET_STYPE(fc); 393 394 switch (type) { 395 case WLAN_FC_TYPE_DATA: 396 if (len < 24) 397 return NULL; 398 switch (fc & (WLAN_FC_FROMDS | WLAN_FC_TODS)) { 399 case WLAN_FC_FROMDS | WLAN_FC_TODS: 400 case WLAN_FC_TODS: 401 return hdr->addr1; 402 case WLAN_FC_FROMDS: 403 return hdr->addr2; 404 default: 405 return NULL; 406 } 407 case WLAN_FC_TYPE_CTRL: 408 if (stype != WLAN_FC_STYPE_PSPOLL) 409 return NULL; 410 return hdr->addr1; 411 case WLAN_FC_TYPE_MGMT: 412 return hdr->addr3; 413 default: 414 return NULL; 415 } 416 } 417 418 419 int hostapd_config_wmm_ac(struct hostapd_wmm_ac_params wmm_ac_params[], 420 const char *name, const char *val) 421 { 422 int num, v; 423 const char *pos; 424 struct hostapd_wmm_ac_params *ac; 425 426 /* skip 'wme_ac_' or 'wmm_ac_' prefix */ 427 pos = name + 7; 428 if (os_strncmp(pos, "be_", 3) == 0) { 429 num = 0; 430 pos += 3; 431 } else if (os_strncmp(pos, "bk_", 3) == 0) { 432 num = 1; 433 pos += 3; 434 } else if (os_strncmp(pos, "vi_", 3) == 0) { 435 num = 2; 436 pos += 3; 437 } else if (os_strncmp(pos, "vo_", 3) == 0) { 438 num = 3; 439 pos += 3; 440 } else { 441 wpa_printf(MSG_ERROR, "Unknown WMM name '%s'", pos); 442 return -1; 443 } 444 445 ac = &wmm_ac_params[num]; 446 447 if (os_strcmp(pos, "aifs") == 0) { 448 v = atoi(val); 449 if (v < 1 || v > 255) { 450 wpa_printf(MSG_ERROR, "Invalid AIFS value %d", v); 451 return -1; 452 } 453 ac->aifs = v; 454 } else if (os_strcmp(pos, "cwmin") == 0) { 455 v = atoi(val); 456 if (v < 0 || v > 12) { 457 wpa_printf(MSG_ERROR, "Invalid cwMin value %d", v); 458 return -1; 459 } 460 ac->cwmin = v; 461 } else if (os_strcmp(pos, "cwmax") == 0) { 462 v = atoi(val); 463 if (v < 0 || v > 12) { 464 wpa_printf(MSG_ERROR, "Invalid cwMax value %d", v); 465 return -1; 466 } 467 ac->cwmax = v; 468 } else if (os_strcmp(pos, "txop_limit") == 0) { 469 v = atoi(val); 470 if (v < 0 || v > 0xffff) { 471 wpa_printf(MSG_ERROR, "Invalid txop value %d", v); 472 return -1; 473 } 474 ac->txop_limit = v; 475 } else if (os_strcmp(pos, "acm") == 0) { 476 v = atoi(val); 477 if (v < 0 || v > 1) { 478 wpa_printf(MSG_ERROR, "Invalid acm value %d", v); 479 return -1; 480 } 481 ac->admission_control_mandatory = v; 482 } else { 483 wpa_printf(MSG_ERROR, "Unknown wmm_ac_ field '%s'", pos); 484 return -1; 485 } 486 487 return 0; 488 } 489