Home | History | Annotate | Download | only in common
      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