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