xref: /freebsd/contrib/wpa/src/common/ieee802_11_common.c (revision f05cddf940dbfc5b657f5e9beb9de2c31e509e5b)
139beb93cSSam Leffler /*
239beb93cSSam Leffler  * IEEE 802.11 Common routines
3*f05cddf9SRui Paulo  * Copyright (c) 2002-2012, Jouni Malinen <j@w1.fi>
439beb93cSSam Leffler  *
5*f05cddf9SRui Paulo  * This software may be distributed under the terms of the BSD license.
6*f05cddf9SRui Paulo  * See README for more details.
739beb93cSSam Leffler  */
839beb93cSSam Leffler 
939beb93cSSam Leffler #include "includes.h"
1039beb93cSSam Leffler 
1139beb93cSSam Leffler #include "common.h"
1239beb93cSSam Leffler #include "ieee802_11_defs.h"
1339beb93cSSam Leffler #include "ieee802_11_common.h"
1439beb93cSSam Leffler 
1539beb93cSSam Leffler 
16e28a4053SRui Paulo static int ieee802_11_parse_vendor_specific(const u8 *pos, size_t elen,
1739beb93cSSam Leffler 					    struct ieee802_11_elems *elems,
1839beb93cSSam Leffler 					    int show_errors)
1939beb93cSSam Leffler {
2039beb93cSSam Leffler 	unsigned int oui;
2139beb93cSSam Leffler 
2239beb93cSSam Leffler 	/* first 3 bytes in vendor specific information element are the IEEE
2339beb93cSSam Leffler 	 * OUI of the vendor. The following byte is used a vendor specific
2439beb93cSSam Leffler 	 * sub-type. */
2539beb93cSSam Leffler 	if (elen < 4) {
2639beb93cSSam Leffler 		if (show_errors) {
2739beb93cSSam Leffler 			wpa_printf(MSG_MSGDUMP, "short vendor specific "
2839beb93cSSam Leffler 				   "information element ignored (len=%lu)",
2939beb93cSSam Leffler 				   (unsigned long) elen);
3039beb93cSSam Leffler 		}
3139beb93cSSam Leffler 		return -1;
3239beb93cSSam Leffler 	}
3339beb93cSSam Leffler 
3439beb93cSSam Leffler 	oui = WPA_GET_BE24(pos);
3539beb93cSSam Leffler 	switch (oui) {
3639beb93cSSam Leffler 	case OUI_MICROSOFT:
3739beb93cSSam Leffler 		/* Microsoft/Wi-Fi information elements are further typed and
3839beb93cSSam Leffler 		 * subtyped */
3939beb93cSSam Leffler 		switch (pos[3]) {
4039beb93cSSam Leffler 		case 1:
4139beb93cSSam Leffler 			/* Microsoft OUI (00:50:F2) with OUI Type 1:
4239beb93cSSam Leffler 			 * real WPA information element */
4339beb93cSSam Leffler 			elems->wpa_ie = pos;
4439beb93cSSam Leffler 			elems->wpa_ie_len = elen;
4539beb93cSSam Leffler 			break;
463157ba21SRui Paulo 		case WMM_OUI_TYPE:
473157ba21SRui Paulo 			/* WMM information element */
4839beb93cSSam Leffler 			if (elen < 5) {
493157ba21SRui Paulo 				wpa_printf(MSG_MSGDUMP, "short WMM "
5039beb93cSSam Leffler 					   "information element ignored "
5139beb93cSSam Leffler 					   "(len=%lu)",
5239beb93cSSam Leffler 					   (unsigned long) elen);
5339beb93cSSam Leffler 				return -1;
5439beb93cSSam Leffler 			}
5539beb93cSSam Leffler 			switch (pos[4]) {
563157ba21SRui Paulo 			case WMM_OUI_SUBTYPE_INFORMATION_ELEMENT:
573157ba21SRui Paulo 			case WMM_OUI_SUBTYPE_PARAMETER_ELEMENT:
583157ba21SRui Paulo 				/*
593157ba21SRui Paulo 				 * Share same pointer since only one of these
603157ba21SRui Paulo 				 * is used and they start with same data.
613157ba21SRui Paulo 				 * Length field can be used to distinguish the
623157ba21SRui Paulo 				 * IEs.
633157ba21SRui Paulo 				 */
643157ba21SRui Paulo 				elems->wmm = pos;
653157ba21SRui Paulo 				elems->wmm_len = elen;
6639beb93cSSam Leffler 				break;
673157ba21SRui Paulo 			case WMM_OUI_SUBTYPE_TSPEC_ELEMENT:
683157ba21SRui Paulo 				elems->wmm_tspec = pos;
693157ba21SRui Paulo 				elems->wmm_tspec_len = elen;
7039beb93cSSam Leffler 				break;
7139beb93cSSam Leffler 			default:
72*f05cddf9SRui Paulo 				wpa_printf(MSG_EXCESSIVE, "unknown WMM "
7339beb93cSSam Leffler 					   "information element ignored "
7439beb93cSSam Leffler 					   "(subtype=%d len=%lu)",
7539beb93cSSam Leffler 					   pos[4], (unsigned long) elen);
7639beb93cSSam Leffler 				return -1;
7739beb93cSSam Leffler 			}
7839beb93cSSam Leffler 			break;
7939beb93cSSam Leffler 		case 4:
8039beb93cSSam Leffler 			/* Wi-Fi Protected Setup (WPS) IE */
8139beb93cSSam Leffler 			elems->wps_ie = pos;
8239beb93cSSam Leffler 			elems->wps_ie_len = elen;
8339beb93cSSam Leffler 			break;
8439beb93cSSam Leffler 		default:
85*f05cddf9SRui Paulo 			wpa_printf(MSG_EXCESSIVE, "Unknown Microsoft "
86*f05cddf9SRui Paulo 				   "information element ignored "
87*f05cddf9SRui Paulo 				   "(type=%d len=%lu)",
88*f05cddf9SRui Paulo 				   pos[3], (unsigned long) elen);
89*f05cddf9SRui Paulo 			return -1;
90*f05cddf9SRui Paulo 		}
91*f05cddf9SRui Paulo 		break;
92*f05cddf9SRui Paulo 
93*f05cddf9SRui Paulo 	case OUI_WFA:
94*f05cddf9SRui Paulo 		switch (pos[3]) {
95*f05cddf9SRui Paulo 		case P2P_OUI_TYPE:
96*f05cddf9SRui Paulo 			/* Wi-Fi Alliance - P2P IE */
97*f05cddf9SRui Paulo 			elems->p2p = pos;
98*f05cddf9SRui Paulo 			elems->p2p_len = elen;
99*f05cddf9SRui Paulo 			break;
100*f05cddf9SRui Paulo 		case WFD_OUI_TYPE:
101*f05cddf9SRui Paulo 			/* Wi-Fi Alliance - WFD IE */
102*f05cddf9SRui Paulo 			elems->wfd = pos;
103*f05cddf9SRui Paulo 			elems->wfd_len = elen;
104*f05cddf9SRui Paulo 			break;
105*f05cddf9SRui Paulo 		case HS20_INDICATION_OUI_TYPE:
106*f05cddf9SRui Paulo 			/* Hotspot 2.0 */
107*f05cddf9SRui Paulo 			elems->hs20 = pos;
108*f05cddf9SRui Paulo 			elems->hs20_len = elen;
109*f05cddf9SRui Paulo 			break;
110*f05cddf9SRui Paulo 		default:
111*f05cddf9SRui Paulo 			wpa_printf(MSG_MSGDUMP, "Unknown WFA "
11239beb93cSSam Leffler 				   "information element ignored "
11339beb93cSSam Leffler 				   "(type=%d len=%lu)\n",
11439beb93cSSam Leffler 				   pos[3], (unsigned long) elen);
11539beb93cSSam Leffler 			return -1;
11639beb93cSSam Leffler 		}
11739beb93cSSam Leffler 		break;
11839beb93cSSam Leffler 
11939beb93cSSam Leffler 	case OUI_BROADCOM:
12039beb93cSSam Leffler 		switch (pos[3]) {
12139beb93cSSam Leffler 		case VENDOR_HT_CAPAB_OUI_TYPE:
12239beb93cSSam Leffler 			elems->vendor_ht_cap = pos;
12339beb93cSSam Leffler 			elems->vendor_ht_cap_len = elen;
12439beb93cSSam Leffler 			break;
12539beb93cSSam Leffler 		default:
126*f05cddf9SRui Paulo 			wpa_printf(MSG_EXCESSIVE, "Unknown Broadcom "
12739beb93cSSam Leffler 				   "information element ignored "
128*f05cddf9SRui Paulo 				   "(type=%d len=%lu)",
12939beb93cSSam Leffler 				   pos[3], (unsigned long) elen);
13039beb93cSSam Leffler 			return -1;
13139beb93cSSam Leffler 		}
13239beb93cSSam Leffler 		break;
13339beb93cSSam Leffler 
13439beb93cSSam Leffler 	default:
135*f05cddf9SRui Paulo 		wpa_printf(MSG_EXCESSIVE, "unknown vendor specific "
136*f05cddf9SRui Paulo 			   "information element ignored (vendor OUI "
137*f05cddf9SRui Paulo 			   "%02x:%02x:%02x len=%lu)",
13839beb93cSSam Leffler 			   pos[0], pos[1], pos[2], (unsigned long) elen);
13939beb93cSSam Leffler 		return -1;
14039beb93cSSam Leffler 	}
14139beb93cSSam Leffler 
14239beb93cSSam Leffler 	return 0;
14339beb93cSSam Leffler }
14439beb93cSSam Leffler 
14539beb93cSSam Leffler 
14639beb93cSSam Leffler /**
14739beb93cSSam Leffler  * ieee802_11_parse_elems - Parse information elements in management frames
14839beb93cSSam Leffler  * @start: Pointer to the start of IEs
14939beb93cSSam Leffler  * @len: Length of IE buffer in octets
15039beb93cSSam Leffler  * @elems: Data structure for parsed elements
15139beb93cSSam Leffler  * @show_errors: Whether to show parsing errors in debug log
15239beb93cSSam Leffler  * Returns: Parsing result
15339beb93cSSam Leffler  */
154e28a4053SRui Paulo ParseRes ieee802_11_parse_elems(const u8 *start, size_t len,
15539beb93cSSam Leffler 				struct ieee802_11_elems *elems,
15639beb93cSSam Leffler 				int show_errors)
15739beb93cSSam Leffler {
15839beb93cSSam Leffler 	size_t left = len;
159e28a4053SRui Paulo 	const u8 *pos = start;
16039beb93cSSam Leffler 	int unknown = 0;
16139beb93cSSam Leffler 
16239beb93cSSam Leffler 	os_memset(elems, 0, sizeof(*elems));
16339beb93cSSam Leffler 
16439beb93cSSam Leffler 	while (left >= 2) {
16539beb93cSSam Leffler 		u8 id, elen;
16639beb93cSSam Leffler 
16739beb93cSSam Leffler 		id = *pos++;
16839beb93cSSam Leffler 		elen = *pos++;
16939beb93cSSam Leffler 		left -= 2;
17039beb93cSSam Leffler 
17139beb93cSSam Leffler 		if (elen > left) {
17239beb93cSSam Leffler 			if (show_errors) {
17339beb93cSSam Leffler 				wpa_printf(MSG_DEBUG, "IEEE 802.11 element "
17439beb93cSSam Leffler 					   "parse failed (id=%d elen=%d "
17539beb93cSSam Leffler 					   "left=%lu)",
17639beb93cSSam Leffler 					   id, elen, (unsigned long) left);
17739beb93cSSam Leffler 				wpa_hexdump(MSG_MSGDUMP, "IEs", start, len);
17839beb93cSSam Leffler 			}
17939beb93cSSam Leffler 			return ParseFailed;
18039beb93cSSam Leffler 		}
18139beb93cSSam Leffler 
18239beb93cSSam Leffler 		switch (id) {
18339beb93cSSam Leffler 		case WLAN_EID_SSID:
18439beb93cSSam Leffler 			elems->ssid = pos;
18539beb93cSSam Leffler 			elems->ssid_len = elen;
18639beb93cSSam Leffler 			break;
18739beb93cSSam Leffler 		case WLAN_EID_SUPP_RATES:
18839beb93cSSam Leffler 			elems->supp_rates = pos;
18939beb93cSSam Leffler 			elems->supp_rates_len = elen;
19039beb93cSSam Leffler 			break;
19139beb93cSSam Leffler 		case WLAN_EID_FH_PARAMS:
19239beb93cSSam Leffler 			elems->fh_params = pos;
19339beb93cSSam Leffler 			elems->fh_params_len = elen;
19439beb93cSSam Leffler 			break;
19539beb93cSSam Leffler 		case WLAN_EID_DS_PARAMS:
19639beb93cSSam Leffler 			elems->ds_params = pos;
19739beb93cSSam Leffler 			elems->ds_params_len = elen;
19839beb93cSSam Leffler 			break;
19939beb93cSSam Leffler 		case WLAN_EID_CF_PARAMS:
20039beb93cSSam Leffler 			elems->cf_params = pos;
20139beb93cSSam Leffler 			elems->cf_params_len = elen;
20239beb93cSSam Leffler 			break;
20339beb93cSSam Leffler 		case WLAN_EID_TIM:
20439beb93cSSam Leffler 			elems->tim = pos;
20539beb93cSSam Leffler 			elems->tim_len = elen;
20639beb93cSSam Leffler 			break;
20739beb93cSSam Leffler 		case WLAN_EID_IBSS_PARAMS:
20839beb93cSSam Leffler 			elems->ibss_params = pos;
20939beb93cSSam Leffler 			elems->ibss_params_len = elen;
21039beb93cSSam Leffler 			break;
21139beb93cSSam Leffler 		case WLAN_EID_CHALLENGE:
21239beb93cSSam Leffler 			elems->challenge = pos;
21339beb93cSSam Leffler 			elems->challenge_len = elen;
21439beb93cSSam Leffler 			break;
21539beb93cSSam Leffler 		case WLAN_EID_ERP_INFO:
21639beb93cSSam Leffler 			elems->erp_info = pos;
21739beb93cSSam Leffler 			elems->erp_info_len = elen;
21839beb93cSSam Leffler 			break;
21939beb93cSSam Leffler 		case WLAN_EID_EXT_SUPP_RATES:
22039beb93cSSam Leffler 			elems->ext_supp_rates = pos;
22139beb93cSSam Leffler 			elems->ext_supp_rates_len = elen;
22239beb93cSSam Leffler 			break;
22339beb93cSSam Leffler 		case WLAN_EID_VENDOR_SPECIFIC:
22439beb93cSSam Leffler 			if (ieee802_11_parse_vendor_specific(pos, elen,
22539beb93cSSam Leffler 							     elems,
22639beb93cSSam Leffler 							     show_errors))
22739beb93cSSam Leffler 				unknown++;
22839beb93cSSam Leffler 			break;
22939beb93cSSam Leffler 		case WLAN_EID_RSN:
23039beb93cSSam Leffler 			elems->rsn_ie = pos;
23139beb93cSSam Leffler 			elems->rsn_ie_len = elen;
23239beb93cSSam Leffler 			break;
23339beb93cSSam Leffler 		case WLAN_EID_PWR_CAPABILITY:
23439beb93cSSam Leffler 			elems->power_cap = pos;
23539beb93cSSam Leffler 			elems->power_cap_len = elen;
23639beb93cSSam Leffler 			break;
23739beb93cSSam Leffler 		case WLAN_EID_SUPPORTED_CHANNELS:
23839beb93cSSam Leffler 			elems->supp_channels = pos;
23939beb93cSSam Leffler 			elems->supp_channels_len = elen;
24039beb93cSSam Leffler 			break;
24139beb93cSSam Leffler 		case WLAN_EID_MOBILITY_DOMAIN:
24239beb93cSSam Leffler 			elems->mdie = pos;
24339beb93cSSam Leffler 			elems->mdie_len = elen;
24439beb93cSSam Leffler 			break;
24539beb93cSSam Leffler 		case WLAN_EID_FAST_BSS_TRANSITION:
24639beb93cSSam Leffler 			elems->ftie = pos;
24739beb93cSSam Leffler 			elems->ftie_len = elen;
24839beb93cSSam Leffler 			break;
24939beb93cSSam Leffler 		case WLAN_EID_TIMEOUT_INTERVAL:
25039beb93cSSam Leffler 			elems->timeout_int = pos;
25139beb93cSSam Leffler 			elems->timeout_int_len = elen;
25239beb93cSSam Leffler 			break;
25339beb93cSSam Leffler 		case WLAN_EID_HT_CAP:
25439beb93cSSam Leffler 			elems->ht_capabilities = pos;
25539beb93cSSam Leffler 			elems->ht_capabilities_len = elen;
25639beb93cSSam Leffler 			break;
25739beb93cSSam Leffler 		case WLAN_EID_HT_OPERATION:
25839beb93cSSam Leffler 			elems->ht_operation = pos;
25939beb93cSSam Leffler 			elems->ht_operation_len = elen;
26039beb93cSSam Leffler 			break;
261*f05cddf9SRui Paulo 		case WLAN_EID_VHT_CAP:
262*f05cddf9SRui Paulo 			elems->vht_capabilities = pos;
263*f05cddf9SRui Paulo 			elems->vht_capabilities_len = elen;
264*f05cddf9SRui Paulo 			break;
265*f05cddf9SRui Paulo 		case WLAN_EID_VHT_OPERATION:
266*f05cddf9SRui Paulo 			elems->vht_operation = pos;
267*f05cddf9SRui Paulo 			elems->vht_operation_len = elen;
268*f05cddf9SRui Paulo 			break;
269*f05cddf9SRui Paulo 		case WLAN_EID_LINK_ID:
270*f05cddf9SRui Paulo 			if (elen < 18)
271*f05cddf9SRui Paulo 				break;
272*f05cddf9SRui Paulo 			elems->link_id = pos;
273*f05cddf9SRui Paulo 			break;
274*f05cddf9SRui Paulo 		case WLAN_EID_INTERWORKING:
275*f05cddf9SRui Paulo 			elems->interworking = pos;
276*f05cddf9SRui Paulo 			elems->interworking_len = elen;
277*f05cddf9SRui Paulo 			break;
278*f05cddf9SRui Paulo 		case WLAN_EID_EXT_CAPAB:
279*f05cddf9SRui Paulo 			elems->ext_capab = pos;
280*f05cddf9SRui Paulo 			elems->ext_capab_len = elen;
281*f05cddf9SRui Paulo 			break;
282*f05cddf9SRui Paulo 		case WLAN_EID_BSS_MAX_IDLE_PERIOD:
283*f05cddf9SRui Paulo 			if (elen < 3)
284*f05cddf9SRui Paulo 				break;
285*f05cddf9SRui Paulo 			elems->bss_max_idle_period = pos;
286*f05cddf9SRui Paulo 			break;
287*f05cddf9SRui Paulo 		case WLAN_EID_SSID_LIST:
288*f05cddf9SRui Paulo 			elems->ssid_list = pos;
289*f05cddf9SRui Paulo 			elems->ssid_list_len = elen;
290*f05cddf9SRui Paulo 			break;
29139beb93cSSam Leffler 		default:
29239beb93cSSam Leffler 			unknown++;
29339beb93cSSam Leffler 			if (!show_errors)
29439beb93cSSam Leffler 				break;
29539beb93cSSam Leffler 			wpa_printf(MSG_MSGDUMP, "IEEE 802.11 element parse "
29639beb93cSSam Leffler 				   "ignored unknown element (id=%d elen=%d)",
29739beb93cSSam Leffler 				   id, elen);
29839beb93cSSam Leffler 			break;
29939beb93cSSam Leffler 		}
30039beb93cSSam Leffler 
30139beb93cSSam Leffler 		left -= elen;
30239beb93cSSam Leffler 		pos += elen;
30339beb93cSSam Leffler 	}
30439beb93cSSam Leffler 
30539beb93cSSam Leffler 	if (left)
30639beb93cSSam Leffler 		return ParseFailed;
30739beb93cSSam Leffler 
30839beb93cSSam Leffler 	return unknown ? ParseUnknown : ParseOK;
30939beb93cSSam Leffler }
310e28a4053SRui Paulo 
311e28a4053SRui Paulo 
312e28a4053SRui Paulo int ieee802_11_ie_count(const u8 *ies, size_t ies_len)
313e28a4053SRui Paulo {
314e28a4053SRui Paulo 	int count = 0;
315e28a4053SRui Paulo 	const u8 *pos, *end;
316e28a4053SRui Paulo 
317e28a4053SRui Paulo 	if (ies == NULL)
318e28a4053SRui Paulo 		return 0;
319e28a4053SRui Paulo 
320e28a4053SRui Paulo 	pos = ies;
321e28a4053SRui Paulo 	end = ies + ies_len;
322e28a4053SRui Paulo 
323e28a4053SRui Paulo 	while (pos + 2 <= end) {
324e28a4053SRui Paulo 		if (pos + 2 + pos[1] > end)
325e28a4053SRui Paulo 			break;
326e28a4053SRui Paulo 		count++;
327e28a4053SRui Paulo 		pos += 2 + pos[1];
328e28a4053SRui Paulo 	}
329e28a4053SRui Paulo 
330e28a4053SRui Paulo 	return count;
331e28a4053SRui Paulo }
332e28a4053SRui Paulo 
333e28a4053SRui Paulo 
334e28a4053SRui Paulo struct wpabuf * ieee802_11_vendor_ie_concat(const u8 *ies, size_t ies_len,
335e28a4053SRui Paulo 					    u32 oui_type)
336e28a4053SRui Paulo {
337e28a4053SRui Paulo 	struct wpabuf *buf;
338e28a4053SRui Paulo 	const u8 *end, *pos, *ie;
339e28a4053SRui Paulo 
340e28a4053SRui Paulo 	pos = ies;
341e28a4053SRui Paulo 	end = ies + ies_len;
342e28a4053SRui Paulo 	ie = NULL;
343e28a4053SRui Paulo 
344e28a4053SRui Paulo 	while (pos + 1 < end) {
345e28a4053SRui Paulo 		if (pos + 2 + pos[1] > end)
346e28a4053SRui Paulo 			return NULL;
347e28a4053SRui Paulo 		if (pos[0] == WLAN_EID_VENDOR_SPECIFIC && pos[1] >= 4 &&
348e28a4053SRui Paulo 		    WPA_GET_BE32(&pos[2]) == oui_type) {
349e28a4053SRui Paulo 			ie = pos;
350e28a4053SRui Paulo 			break;
351e28a4053SRui Paulo 		}
352e28a4053SRui Paulo 		pos += 2 + pos[1];
353e28a4053SRui Paulo 	}
354e28a4053SRui Paulo 
355e28a4053SRui Paulo 	if (ie == NULL)
356e28a4053SRui Paulo 		return NULL; /* No specified vendor IE found */
357e28a4053SRui Paulo 
358e28a4053SRui Paulo 	buf = wpabuf_alloc(ies_len);
359e28a4053SRui Paulo 	if (buf == NULL)
360e28a4053SRui Paulo 		return NULL;
361e28a4053SRui Paulo 
362e28a4053SRui Paulo 	/*
363e28a4053SRui Paulo 	 * There may be multiple vendor IEs in the message, so need to
364e28a4053SRui Paulo 	 * concatenate their data fields.
365e28a4053SRui Paulo 	 */
366e28a4053SRui Paulo 	while (pos + 1 < end) {
367e28a4053SRui Paulo 		if (pos + 2 + pos[1] > end)
368e28a4053SRui Paulo 			break;
369e28a4053SRui Paulo 		if (pos[0] == WLAN_EID_VENDOR_SPECIFIC && pos[1] >= 4 &&
370e28a4053SRui Paulo 		    WPA_GET_BE32(&pos[2]) == oui_type)
371e28a4053SRui Paulo 			wpabuf_put_data(buf, pos + 6, pos[1] - 4);
372e28a4053SRui Paulo 		pos += 2 + pos[1];
373e28a4053SRui Paulo 	}
374e28a4053SRui Paulo 
375e28a4053SRui Paulo 	return buf;
376e28a4053SRui Paulo }
377*f05cddf9SRui Paulo 
378*f05cddf9SRui Paulo 
379*f05cddf9SRui Paulo const u8 * get_hdr_bssid(const struct ieee80211_hdr *hdr, size_t len)
380*f05cddf9SRui Paulo {
381*f05cddf9SRui Paulo 	u16 fc, type, stype;
382*f05cddf9SRui Paulo 
383*f05cddf9SRui Paulo 	/*
384*f05cddf9SRui Paulo 	 * PS-Poll frames are 16 bytes. All other frames are
385*f05cddf9SRui Paulo 	 * 24 bytes or longer.
386*f05cddf9SRui Paulo 	 */
387*f05cddf9SRui Paulo 	if (len < 16)
388*f05cddf9SRui Paulo 		return NULL;
389*f05cddf9SRui Paulo 
390*f05cddf9SRui Paulo 	fc = le_to_host16(hdr->frame_control);
391*f05cddf9SRui Paulo 	type = WLAN_FC_GET_TYPE(fc);
392*f05cddf9SRui Paulo 	stype = WLAN_FC_GET_STYPE(fc);
393*f05cddf9SRui Paulo 
394*f05cddf9SRui Paulo 	switch (type) {
395*f05cddf9SRui Paulo 	case WLAN_FC_TYPE_DATA:
396*f05cddf9SRui Paulo 		if (len < 24)
397*f05cddf9SRui Paulo 			return NULL;
398*f05cddf9SRui Paulo 		switch (fc & (WLAN_FC_FROMDS | WLAN_FC_TODS)) {
399*f05cddf9SRui Paulo 		case WLAN_FC_FROMDS | WLAN_FC_TODS:
400*f05cddf9SRui Paulo 		case WLAN_FC_TODS:
401*f05cddf9SRui Paulo 			return hdr->addr1;
402*f05cddf9SRui Paulo 		case WLAN_FC_FROMDS:
403*f05cddf9SRui Paulo 			return hdr->addr2;
404*f05cddf9SRui Paulo 		default:
405*f05cddf9SRui Paulo 			return NULL;
406*f05cddf9SRui Paulo 		}
407*f05cddf9SRui Paulo 	case WLAN_FC_TYPE_CTRL:
408*f05cddf9SRui Paulo 		if (stype != WLAN_FC_STYPE_PSPOLL)
409*f05cddf9SRui Paulo 			return NULL;
410*f05cddf9SRui Paulo 		return hdr->addr1;
411*f05cddf9SRui Paulo 	case WLAN_FC_TYPE_MGMT:
412*f05cddf9SRui Paulo 		return hdr->addr3;
413*f05cddf9SRui Paulo 	default:
414*f05cddf9SRui Paulo 		return NULL;
415*f05cddf9SRui Paulo 	}
416*f05cddf9SRui Paulo }
417*f05cddf9SRui Paulo 
418*f05cddf9SRui Paulo 
419*f05cddf9SRui Paulo int hostapd_config_wmm_ac(struct hostapd_wmm_ac_params wmm_ac_params[],
420*f05cddf9SRui Paulo 			  const char *name, const char *val)
421*f05cddf9SRui Paulo {
422*f05cddf9SRui Paulo 	int num, v;
423*f05cddf9SRui Paulo 	const char *pos;
424*f05cddf9SRui Paulo 	struct hostapd_wmm_ac_params *ac;
425*f05cddf9SRui Paulo 
426*f05cddf9SRui Paulo 	/* skip 'wme_ac_' or 'wmm_ac_' prefix */
427*f05cddf9SRui Paulo 	pos = name + 7;
428*f05cddf9SRui Paulo 	if (os_strncmp(pos, "be_", 3) == 0) {
429*f05cddf9SRui Paulo 		num = 0;
430*f05cddf9SRui Paulo 		pos += 3;
431*f05cddf9SRui Paulo 	} else if (os_strncmp(pos, "bk_", 3) == 0) {
432*f05cddf9SRui Paulo 		num = 1;
433*f05cddf9SRui Paulo 		pos += 3;
434*f05cddf9SRui Paulo 	} else if (os_strncmp(pos, "vi_", 3) == 0) {
435*f05cddf9SRui Paulo 		num = 2;
436*f05cddf9SRui Paulo 		pos += 3;
437*f05cddf9SRui Paulo 	} else if (os_strncmp(pos, "vo_", 3) == 0) {
438*f05cddf9SRui Paulo 		num = 3;
439*f05cddf9SRui Paulo 		pos += 3;
440*f05cddf9SRui Paulo 	} else {
441*f05cddf9SRui Paulo 		wpa_printf(MSG_ERROR, "Unknown WMM name '%s'", pos);
442*f05cddf9SRui Paulo 		return -1;
443*f05cddf9SRui Paulo 	}
444*f05cddf9SRui Paulo 
445*f05cddf9SRui Paulo 	ac = &wmm_ac_params[num];
446*f05cddf9SRui Paulo 
447*f05cddf9SRui Paulo 	if (os_strcmp(pos, "aifs") == 0) {
448*f05cddf9SRui Paulo 		v = atoi(val);
449*f05cddf9SRui Paulo 		if (v < 1 || v > 255) {
450*f05cddf9SRui Paulo 			wpa_printf(MSG_ERROR, "Invalid AIFS value %d", v);
451*f05cddf9SRui Paulo 			return -1;
452*f05cddf9SRui Paulo 		}
453*f05cddf9SRui Paulo 		ac->aifs = v;
454*f05cddf9SRui Paulo 	} else if (os_strcmp(pos, "cwmin") == 0) {
455*f05cddf9SRui Paulo 		v = atoi(val);
456*f05cddf9SRui Paulo 		if (v < 0 || v > 12) {
457*f05cddf9SRui Paulo 			wpa_printf(MSG_ERROR, "Invalid cwMin value %d", v);
458*f05cddf9SRui Paulo 			return -1;
459*f05cddf9SRui Paulo 		}
460*f05cddf9SRui Paulo 		ac->cwmin = v;
461*f05cddf9SRui Paulo 	} else if (os_strcmp(pos, "cwmax") == 0) {
462*f05cddf9SRui Paulo 		v = atoi(val);
463*f05cddf9SRui Paulo 		if (v < 0 || v > 12) {
464*f05cddf9SRui Paulo 			wpa_printf(MSG_ERROR, "Invalid cwMax value %d", v);
465*f05cddf9SRui Paulo 			return -1;
466*f05cddf9SRui Paulo 		}
467*f05cddf9SRui Paulo 		ac->cwmax = v;
468*f05cddf9SRui Paulo 	} else if (os_strcmp(pos, "txop_limit") == 0) {
469*f05cddf9SRui Paulo 		v = atoi(val);
470*f05cddf9SRui Paulo 		if (v < 0 || v > 0xffff) {
471*f05cddf9SRui Paulo 			wpa_printf(MSG_ERROR, "Invalid txop value %d", v);
472*f05cddf9SRui Paulo 			return -1;
473*f05cddf9SRui Paulo 		}
474*f05cddf9SRui Paulo 		ac->txop_limit = v;
475*f05cddf9SRui Paulo 	} else if (os_strcmp(pos, "acm") == 0) {
476*f05cddf9SRui Paulo 		v = atoi(val);
477*f05cddf9SRui Paulo 		if (v < 0 || v > 1) {
478*f05cddf9SRui Paulo 			wpa_printf(MSG_ERROR, "Invalid acm value %d", v);
479*f05cddf9SRui Paulo 			return -1;
480*f05cddf9SRui Paulo 		}
481*f05cddf9SRui Paulo 		ac->admission_control_mandatory = v;
482*f05cddf9SRui Paulo 	} else {
483*f05cddf9SRui Paulo 		wpa_printf(MSG_ERROR, "Unknown wmm_ac_ field '%s'", pos);
484*f05cddf9SRui Paulo 		return -1;
485*f05cddf9SRui Paulo 	}
486*f05cddf9SRui Paulo 
487*f05cddf9SRui Paulo 	return 0;
488*f05cddf9SRui Paulo }
489