xref: /freebsd/contrib/wpa/src/common/ieee802_11_common.c (revision e28a4053b110e06768631ac8401ed4a3c05e68a5)
139beb93cSSam Leffler /*
239beb93cSSam Leffler  * IEEE 802.11 Common routines
3*e28a4053SRui Paulo  * Copyright (c) 2002-2009, Jouni Malinen <j@w1.fi>
439beb93cSSam Leffler  *
539beb93cSSam Leffler  * This program is free software; you can redistribute it and/or modify
639beb93cSSam Leffler  * it under the terms of the GNU General Public License version 2 as
739beb93cSSam Leffler  * published by the Free Software Foundation.
839beb93cSSam Leffler  *
939beb93cSSam Leffler  * Alternatively, this software may be distributed under the terms of BSD
1039beb93cSSam Leffler  * license.
1139beb93cSSam Leffler  *
1239beb93cSSam Leffler  * See README and COPYING for more details.
1339beb93cSSam Leffler  */
1439beb93cSSam Leffler 
1539beb93cSSam Leffler #include "includes.h"
1639beb93cSSam Leffler 
1739beb93cSSam Leffler #include "common.h"
1839beb93cSSam Leffler #include "ieee802_11_defs.h"
1939beb93cSSam Leffler #include "ieee802_11_common.h"
2039beb93cSSam Leffler 
2139beb93cSSam Leffler 
22*e28a4053SRui Paulo static int ieee802_11_parse_vendor_specific(const u8 *pos, size_t elen,
2339beb93cSSam Leffler 					    struct ieee802_11_elems *elems,
2439beb93cSSam Leffler 					    int show_errors)
2539beb93cSSam Leffler {
2639beb93cSSam Leffler 	unsigned int oui;
2739beb93cSSam Leffler 
2839beb93cSSam Leffler 	/* first 3 bytes in vendor specific information element are the IEEE
2939beb93cSSam Leffler 	 * OUI of the vendor. The following byte is used a vendor specific
3039beb93cSSam Leffler 	 * sub-type. */
3139beb93cSSam Leffler 	if (elen < 4) {
3239beb93cSSam Leffler 		if (show_errors) {
3339beb93cSSam Leffler 			wpa_printf(MSG_MSGDUMP, "short vendor specific "
3439beb93cSSam Leffler 				   "information element ignored (len=%lu)",
3539beb93cSSam Leffler 				   (unsigned long) elen);
3639beb93cSSam Leffler 		}
3739beb93cSSam Leffler 		return -1;
3839beb93cSSam Leffler 	}
3939beb93cSSam Leffler 
4039beb93cSSam Leffler 	oui = WPA_GET_BE24(pos);
4139beb93cSSam Leffler 	switch (oui) {
4239beb93cSSam Leffler 	case OUI_MICROSOFT:
4339beb93cSSam Leffler 		/* Microsoft/Wi-Fi information elements are further typed and
4439beb93cSSam Leffler 		 * subtyped */
4539beb93cSSam Leffler 		switch (pos[3]) {
4639beb93cSSam Leffler 		case 1:
4739beb93cSSam Leffler 			/* Microsoft OUI (00:50:F2) with OUI Type 1:
4839beb93cSSam Leffler 			 * real WPA information element */
4939beb93cSSam Leffler 			elems->wpa_ie = pos;
5039beb93cSSam Leffler 			elems->wpa_ie_len = elen;
5139beb93cSSam Leffler 			break;
523157ba21SRui Paulo 		case WMM_OUI_TYPE:
533157ba21SRui Paulo 			/* WMM information element */
5439beb93cSSam Leffler 			if (elen < 5) {
553157ba21SRui Paulo 				wpa_printf(MSG_MSGDUMP, "short WMM "
5639beb93cSSam Leffler 					   "information element ignored "
5739beb93cSSam Leffler 					   "(len=%lu)",
5839beb93cSSam Leffler 					   (unsigned long) elen);
5939beb93cSSam Leffler 				return -1;
6039beb93cSSam Leffler 			}
6139beb93cSSam Leffler 			switch (pos[4]) {
623157ba21SRui Paulo 			case WMM_OUI_SUBTYPE_INFORMATION_ELEMENT:
633157ba21SRui Paulo 			case WMM_OUI_SUBTYPE_PARAMETER_ELEMENT:
643157ba21SRui Paulo 				/*
653157ba21SRui Paulo 				 * Share same pointer since only one of these
663157ba21SRui Paulo 				 * is used and they start with same data.
673157ba21SRui Paulo 				 * Length field can be used to distinguish the
683157ba21SRui Paulo 				 * IEs.
693157ba21SRui Paulo 				 */
703157ba21SRui Paulo 				elems->wmm = pos;
713157ba21SRui Paulo 				elems->wmm_len = elen;
7239beb93cSSam Leffler 				break;
733157ba21SRui Paulo 			case WMM_OUI_SUBTYPE_TSPEC_ELEMENT:
743157ba21SRui Paulo 				elems->wmm_tspec = pos;
753157ba21SRui Paulo 				elems->wmm_tspec_len = elen;
7639beb93cSSam Leffler 				break;
7739beb93cSSam Leffler 			default:
783157ba21SRui Paulo 				wpa_printf(MSG_MSGDUMP, "unknown WMM "
7939beb93cSSam Leffler 					   "information element ignored "
8039beb93cSSam Leffler 					   "(subtype=%d len=%lu)",
8139beb93cSSam Leffler 					   pos[4], (unsigned long) elen);
8239beb93cSSam Leffler 				return -1;
8339beb93cSSam Leffler 			}
8439beb93cSSam Leffler 			break;
8539beb93cSSam Leffler 		case 4:
8639beb93cSSam Leffler 			/* Wi-Fi Protected Setup (WPS) IE */
8739beb93cSSam Leffler 			elems->wps_ie = pos;
8839beb93cSSam Leffler 			elems->wps_ie_len = elen;
8939beb93cSSam Leffler 			break;
9039beb93cSSam Leffler 		default:
9139beb93cSSam Leffler 			wpa_printf(MSG_MSGDUMP, "Unknown Microsoft "
9239beb93cSSam Leffler 				   "information element ignored "
9339beb93cSSam Leffler 				   "(type=%d len=%lu)\n",
9439beb93cSSam Leffler 				   pos[3], (unsigned long) elen);
9539beb93cSSam Leffler 			return -1;
9639beb93cSSam Leffler 		}
9739beb93cSSam Leffler 		break;
9839beb93cSSam Leffler 
9939beb93cSSam Leffler 	case OUI_BROADCOM:
10039beb93cSSam Leffler 		switch (pos[3]) {
10139beb93cSSam Leffler 		case VENDOR_HT_CAPAB_OUI_TYPE:
10239beb93cSSam Leffler 			elems->vendor_ht_cap = pos;
10339beb93cSSam Leffler 			elems->vendor_ht_cap_len = elen;
10439beb93cSSam Leffler 			break;
10539beb93cSSam Leffler 		default:
10639beb93cSSam Leffler 			wpa_printf(MSG_MSGDUMP, "Unknown Broadcom "
10739beb93cSSam Leffler 				   "information element ignored "
10839beb93cSSam Leffler 				   "(type=%d len=%lu)\n",
10939beb93cSSam Leffler 				   pos[3], (unsigned long) elen);
11039beb93cSSam Leffler 			return -1;
11139beb93cSSam Leffler 		}
11239beb93cSSam Leffler 		break;
11339beb93cSSam Leffler 
11439beb93cSSam Leffler 	default:
11539beb93cSSam Leffler 		wpa_printf(MSG_MSGDUMP, "unknown vendor specific information "
11639beb93cSSam Leffler 			   "element ignored (vendor OUI %02x:%02x:%02x "
11739beb93cSSam Leffler 			   "len=%lu)",
11839beb93cSSam Leffler 			   pos[0], pos[1], pos[2], (unsigned long) elen);
11939beb93cSSam Leffler 		return -1;
12039beb93cSSam Leffler 	}
12139beb93cSSam Leffler 
12239beb93cSSam Leffler 	return 0;
12339beb93cSSam Leffler }
12439beb93cSSam Leffler 
12539beb93cSSam Leffler 
12639beb93cSSam Leffler /**
12739beb93cSSam Leffler  * ieee802_11_parse_elems - Parse information elements in management frames
12839beb93cSSam Leffler  * @start: Pointer to the start of IEs
12939beb93cSSam Leffler  * @len: Length of IE buffer in octets
13039beb93cSSam Leffler  * @elems: Data structure for parsed elements
13139beb93cSSam Leffler  * @show_errors: Whether to show parsing errors in debug log
13239beb93cSSam Leffler  * Returns: Parsing result
13339beb93cSSam Leffler  */
134*e28a4053SRui Paulo ParseRes ieee802_11_parse_elems(const u8 *start, size_t len,
13539beb93cSSam Leffler 				struct ieee802_11_elems *elems,
13639beb93cSSam Leffler 				int show_errors)
13739beb93cSSam Leffler {
13839beb93cSSam Leffler 	size_t left = len;
139*e28a4053SRui Paulo 	const u8 *pos = start;
14039beb93cSSam Leffler 	int unknown = 0;
14139beb93cSSam Leffler 
14239beb93cSSam Leffler 	os_memset(elems, 0, sizeof(*elems));
14339beb93cSSam Leffler 
14439beb93cSSam Leffler 	while (left >= 2) {
14539beb93cSSam Leffler 		u8 id, elen;
14639beb93cSSam Leffler 
14739beb93cSSam Leffler 		id = *pos++;
14839beb93cSSam Leffler 		elen = *pos++;
14939beb93cSSam Leffler 		left -= 2;
15039beb93cSSam Leffler 
15139beb93cSSam Leffler 		if (elen > left) {
15239beb93cSSam Leffler 			if (show_errors) {
15339beb93cSSam Leffler 				wpa_printf(MSG_DEBUG, "IEEE 802.11 element "
15439beb93cSSam Leffler 					   "parse failed (id=%d elen=%d "
15539beb93cSSam Leffler 					   "left=%lu)",
15639beb93cSSam Leffler 					   id, elen, (unsigned long) left);
15739beb93cSSam Leffler 				wpa_hexdump(MSG_MSGDUMP, "IEs", start, len);
15839beb93cSSam Leffler 			}
15939beb93cSSam Leffler 			return ParseFailed;
16039beb93cSSam Leffler 		}
16139beb93cSSam Leffler 
16239beb93cSSam Leffler 		switch (id) {
16339beb93cSSam Leffler 		case WLAN_EID_SSID:
16439beb93cSSam Leffler 			elems->ssid = pos;
16539beb93cSSam Leffler 			elems->ssid_len = elen;
16639beb93cSSam Leffler 			break;
16739beb93cSSam Leffler 		case WLAN_EID_SUPP_RATES:
16839beb93cSSam Leffler 			elems->supp_rates = pos;
16939beb93cSSam Leffler 			elems->supp_rates_len = elen;
17039beb93cSSam Leffler 			break;
17139beb93cSSam Leffler 		case WLAN_EID_FH_PARAMS:
17239beb93cSSam Leffler 			elems->fh_params = pos;
17339beb93cSSam Leffler 			elems->fh_params_len = elen;
17439beb93cSSam Leffler 			break;
17539beb93cSSam Leffler 		case WLAN_EID_DS_PARAMS:
17639beb93cSSam Leffler 			elems->ds_params = pos;
17739beb93cSSam Leffler 			elems->ds_params_len = elen;
17839beb93cSSam Leffler 			break;
17939beb93cSSam Leffler 		case WLAN_EID_CF_PARAMS:
18039beb93cSSam Leffler 			elems->cf_params = pos;
18139beb93cSSam Leffler 			elems->cf_params_len = elen;
18239beb93cSSam Leffler 			break;
18339beb93cSSam Leffler 		case WLAN_EID_TIM:
18439beb93cSSam Leffler 			elems->tim = pos;
18539beb93cSSam Leffler 			elems->tim_len = elen;
18639beb93cSSam Leffler 			break;
18739beb93cSSam Leffler 		case WLAN_EID_IBSS_PARAMS:
18839beb93cSSam Leffler 			elems->ibss_params = pos;
18939beb93cSSam Leffler 			elems->ibss_params_len = elen;
19039beb93cSSam Leffler 			break;
19139beb93cSSam Leffler 		case WLAN_EID_CHALLENGE:
19239beb93cSSam Leffler 			elems->challenge = pos;
19339beb93cSSam Leffler 			elems->challenge_len = elen;
19439beb93cSSam Leffler 			break;
19539beb93cSSam Leffler 		case WLAN_EID_ERP_INFO:
19639beb93cSSam Leffler 			elems->erp_info = pos;
19739beb93cSSam Leffler 			elems->erp_info_len = elen;
19839beb93cSSam Leffler 			break;
19939beb93cSSam Leffler 		case WLAN_EID_EXT_SUPP_RATES:
20039beb93cSSam Leffler 			elems->ext_supp_rates = pos;
20139beb93cSSam Leffler 			elems->ext_supp_rates_len = elen;
20239beb93cSSam Leffler 			break;
20339beb93cSSam Leffler 		case WLAN_EID_VENDOR_SPECIFIC:
20439beb93cSSam Leffler 			if (ieee802_11_parse_vendor_specific(pos, elen,
20539beb93cSSam Leffler 							     elems,
20639beb93cSSam Leffler 							     show_errors))
20739beb93cSSam Leffler 				unknown++;
20839beb93cSSam Leffler 			break;
20939beb93cSSam Leffler 		case WLAN_EID_RSN:
21039beb93cSSam Leffler 			elems->rsn_ie = pos;
21139beb93cSSam Leffler 			elems->rsn_ie_len = elen;
21239beb93cSSam Leffler 			break;
21339beb93cSSam Leffler 		case WLAN_EID_PWR_CAPABILITY:
21439beb93cSSam Leffler 			elems->power_cap = pos;
21539beb93cSSam Leffler 			elems->power_cap_len = elen;
21639beb93cSSam Leffler 			break;
21739beb93cSSam Leffler 		case WLAN_EID_SUPPORTED_CHANNELS:
21839beb93cSSam Leffler 			elems->supp_channels = pos;
21939beb93cSSam Leffler 			elems->supp_channels_len = elen;
22039beb93cSSam Leffler 			break;
22139beb93cSSam Leffler 		case WLAN_EID_MOBILITY_DOMAIN:
22239beb93cSSam Leffler 			elems->mdie = pos;
22339beb93cSSam Leffler 			elems->mdie_len = elen;
22439beb93cSSam Leffler 			break;
22539beb93cSSam Leffler 		case WLAN_EID_FAST_BSS_TRANSITION:
22639beb93cSSam Leffler 			elems->ftie = pos;
22739beb93cSSam Leffler 			elems->ftie_len = elen;
22839beb93cSSam Leffler 			break;
22939beb93cSSam Leffler 		case WLAN_EID_TIMEOUT_INTERVAL:
23039beb93cSSam Leffler 			elems->timeout_int = pos;
23139beb93cSSam Leffler 			elems->timeout_int_len = elen;
23239beb93cSSam Leffler 			break;
23339beb93cSSam Leffler 		case WLAN_EID_HT_CAP:
23439beb93cSSam Leffler 			elems->ht_capabilities = pos;
23539beb93cSSam Leffler 			elems->ht_capabilities_len = elen;
23639beb93cSSam Leffler 			break;
23739beb93cSSam Leffler 		case WLAN_EID_HT_OPERATION:
23839beb93cSSam Leffler 			elems->ht_operation = pos;
23939beb93cSSam Leffler 			elems->ht_operation_len = elen;
24039beb93cSSam Leffler 			break;
24139beb93cSSam Leffler 		default:
24239beb93cSSam Leffler 			unknown++;
24339beb93cSSam Leffler 			if (!show_errors)
24439beb93cSSam Leffler 				break;
24539beb93cSSam Leffler 			wpa_printf(MSG_MSGDUMP, "IEEE 802.11 element parse "
24639beb93cSSam Leffler 				   "ignored unknown element (id=%d elen=%d)",
24739beb93cSSam Leffler 				   id, elen);
24839beb93cSSam Leffler 			break;
24939beb93cSSam Leffler 		}
25039beb93cSSam Leffler 
25139beb93cSSam Leffler 		left -= elen;
25239beb93cSSam Leffler 		pos += elen;
25339beb93cSSam Leffler 	}
25439beb93cSSam Leffler 
25539beb93cSSam Leffler 	if (left)
25639beb93cSSam Leffler 		return ParseFailed;
25739beb93cSSam Leffler 
25839beb93cSSam Leffler 	return unknown ? ParseUnknown : ParseOK;
25939beb93cSSam Leffler }
260*e28a4053SRui Paulo 
261*e28a4053SRui Paulo 
262*e28a4053SRui Paulo int ieee802_11_ie_count(const u8 *ies, size_t ies_len)
263*e28a4053SRui Paulo {
264*e28a4053SRui Paulo 	int count = 0;
265*e28a4053SRui Paulo 	const u8 *pos, *end;
266*e28a4053SRui Paulo 
267*e28a4053SRui Paulo 	if (ies == NULL)
268*e28a4053SRui Paulo 		return 0;
269*e28a4053SRui Paulo 
270*e28a4053SRui Paulo 	pos = ies;
271*e28a4053SRui Paulo 	end = ies + ies_len;
272*e28a4053SRui Paulo 
273*e28a4053SRui Paulo 	while (pos + 2 <= end) {
274*e28a4053SRui Paulo 		if (pos + 2 + pos[1] > end)
275*e28a4053SRui Paulo 			break;
276*e28a4053SRui Paulo 		count++;
277*e28a4053SRui Paulo 		pos += 2 + pos[1];
278*e28a4053SRui Paulo 	}
279*e28a4053SRui Paulo 
280*e28a4053SRui Paulo 	return count;
281*e28a4053SRui Paulo }
282*e28a4053SRui Paulo 
283*e28a4053SRui Paulo 
284*e28a4053SRui Paulo struct wpabuf * ieee802_11_vendor_ie_concat(const u8 *ies, size_t ies_len,
285*e28a4053SRui Paulo 					    u32 oui_type)
286*e28a4053SRui Paulo {
287*e28a4053SRui Paulo 	struct wpabuf *buf;
288*e28a4053SRui Paulo 	const u8 *end, *pos, *ie;
289*e28a4053SRui Paulo 
290*e28a4053SRui Paulo 	pos = ies;
291*e28a4053SRui Paulo 	end = ies + ies_len;
292*e28a4053SRui Paulo 	ie = NULL;
293*e28a4053SRui Paulo 
294*e28a4053SRui Paulo 	while (pos + 1 < end) {
295*e28a4053SRui Paulo 		if (pos + 2 + pos[1] > end)
296*e28a4053SRui Paulo 			return NULL;
297*e28a4053SRui Paulo 		if (pos[0] == WLAN_EID_VENDOR_SPECIFIC && pos[1] >= 4 &&
298*e28a4053SRui Paulo 		    WPA_GET_BE32(&pos[2]) == oui_type) {
299*e28a4053SRui Paulo 			ie = pos;
300*e28a4053SRui Paulo 			break;
301*e28a4053SRui Paulo 		}
302*e28a4053SRui Paulo 		pos += 2 + pos[1];
303*e28a4053SRui Paulo 	}
304*e28a4053SRui Paulo 
305*e28a4053SRui Paulo 	if (ie == NULL)
306*e28a4053SRui Paulo 		return NULL; /* No specified vendor IE found */
307*e28a4053SRui Paulo 
308*e28a4053SRui Paulo 	buf = wpabuf_alloc(ies_len);
309*e28a4053SRui Paulo 	if (buf == NULL)
310*e28a4053SRui Paulo 		return NULL;
311*e28a4053SRui Paulo 
312*e28a4053SRui Paulo 	/*
313*e28a4053SRui Paulo 	 * There may be multiple vendor IEs in the message, so need to
314*e28a4053SRui Paulo 	 * concatenate their data fields.
315*e28a4053SRui Paulo 	 */
316*e28a4053SRui Paulo 	while (pos + 1 < end) {
317*e28a4053SRui Paulo 		if (pos + 2 + pos[1] > end)
318*e28a4053SRui Paulo 			break;
319*e28a4053SRui Paulo 		if (pos[0] == WLAN_EID_VENDOR_SPECIFIC && pos[1] >= 4 &&
320*e28a4053SRui Paulo 		    WPA_GET_BE32(&pos[2]) == oui_type)
321*e28a4053SRui Paulo 			wpabuf_put_data(buf, pos + 6, pos[1] - 4);
322*e28a4053SRui Paulo 		pos += 2 + pos[1];
323*e28a4053SRui Paulo 	}
324*e28a4053SRui Paulo 
325*e28a4053SRui Paulo 	return buf;
326*e28a4053SRui Paulo }
327