xref: /freebsd/contrib/wpa/src/common/ieee802_11_common.c (revision 780fb4a2fa9a9aee5ac48a60b790f567c0dc13e9)
139beb93cSSam Leffler /*
239beb93cSSam Leffler  * IEEE 802.11 Common routines
3325151a3SRui Paulo  * Copyright (c) 2002-2015, Jouni Malinen <j@w1.fi>
439beb93cSSam Leffler  *
5f05cddf9SRui Paulo  * This software may be distributed under the terms of the BSD license.
6f05cddf9SRui Paulo  * See README for more details.
739beb93cSSam Leffler  */
839beb93cSSam Leffler 
939beb93cSSam Leffler #include "includes.h"
1039beb93cSSam Leffler 
1139beb93cSSam Leffler #include "common.h"
125b9c547cSRui Paulo #include "defs.h"
13325151a3SRui Paulo #include "wpa_common.h"
14325151a3SRui Paulo #include "qca-vendor.h"
1539beb93cSSam Leffler #include "ieee802_11_defs.h"
1639beb93cSSam Leffler #include "ieee802_11_common.h"
1739beb93cSSam Leffler 
1839beb93cSSam Leffler 
19e28a4053SRui Paulo static int ieee802_11_parse_vendor_specific(const u8 *pos, size_t elen,
2039beb93cSSam Leffler 					    struct ieee802_11_elems *elems,
2139beb93cSSam Leffler 					    int show_errors)
2239beb93cSSam Leffler {
2339beb93cSSam Leffler 	unsigned int oui;
2439beb93cSSam Leffler 
2539beb93cSSam Leffler 	/* first 3 bytes in vendor specific information element are the IEEE
2639beb93cSSam Leffler 	 * OUI of the vendor. The following byte is used a vendor specific
2739beb93cSSam Leffler 	 * sub-type. */
2839beb93cSSam Leffler 	if (elen < 4) {
2939beb93cSSam Leffler 		if (show_errors) {
3039beb93cSSam Leffler 			wpa_printf(MSG_MSGDUMP, "short vendor specific "
3139beb93cSSam Leffler 				   "information element ignored (len=%lu)",
3239beb93cSSam Leffler 				   (unsigned long) elen);
3339beb93cSSam Leffler 		}
3439beb93cSSam Leffler 		return -1;
3539beb93cSSam Leffler 	}
3639beb93cSSam Leffler 
3739beb93cSSam Leffler 	oui = WPA_GET_BE24(pos);
3839beb93cSSam Leffler 	switch (oui) {
3939beb93cSSam Leffler 	case OUI_MICROSOFT:
4039beb93cSSam Leffler 		/* Microsoft/Wi-Fi information elements are further typed and
4139beb93cSSam Leffler 		 * subtyped */
4239beb93cSSam Leffler 		switch (pos[3]) {
4339beb93cSSam Leffler 		case 1:
4439beb93cSSam Leffler 			/* Microsoft OUI (00:50:F2) with OUI Type 1:
4539beb93cSSam Leffler 			 * real WPA information element */
4639beb93cSSam Leffler 			elems->wpa_ie = pos;
4739beb93cSSam Leffler 			elems->wpa_ie_len = elen;
4839beb93cSSam Leffler 			break;
493157ba21SRui Paulo 		case WMM_OUI_TYPE:
503157ba21SRui Paulo 			/* WMM information element */
5139beb93cSSam Leffler 			if (elen < 5) {
523157ba21SRui Paulo 				wpa_printf(MSG_MSGDUMP, "short WMM "
5339beb93cSSam Leffler 					   "information element ignored "
5439beb93cSSam Leffler 					   "(len=%lu)",
5539beb93cSSam Leffler 					   (unsigned long) elen);
5639beb93cSSam Leffler 				return -1;
5739beb93cSSam Leffler 			}
5839beb93cSSam Leffler 			switch (pos[4]) {
593157ba21SRui Paulo 			case WMM_OUI_SUBTYPE_INFORMATION_ELEMENT:
603157ba21SRui Paulo 			case WMM_OUI_SUBTYPE_PARAMETER_ELEMENT:
613157ba21SRui Paulo 				/*
623157ba21SRui Paulo 				 * Share same pointer since only one of these
633157ba21SRui Paulo 				 * is used and they start with same data.
643157ba21SRui Paulo 				 * Length field can be used to distinguish the
653157ba21SRui Paulo 				 * IEs.
663157ba21SRui Paulo 				 */
673157ba21SRui Paulo 				elems->wmm = pos;
683157ba21SRui Paulo 				elems->wmm_len = elen;
6939beb93cSSam Leffler 				break;
703157ba21SRui Paulo 			case WMM_OUI_SUBTYPE_TSPEC_ELEMENT:
713157ba21SRui Paulo 				elems->wmm_tspec = pos;
723157ba21SRui Paulo 				elems->wmm_tspec_len = elen;
7339beb93cSSam Leffler 				break;
7439beb93cSSam Leffler 			default:
75f05cddf9SRui Paulo 				wpa_printf(MSG_EXCESSIVE, "unknown WMM "
7639beb93cSSam Leffler 					   "information element ignored "
7739beb93cSSam Leffler 					   "(subtype=%d len=%lu)",
7839beb93cSSam Leffler 					   pos[4], (unsigned long) elen);
7939beb93cSSam Leffler 				return -1;
8039beb93cSSam Leffler 			}
8139beb93cSSam Leffler 			break;
8239beb93cSSam Leffler 		case 4:
8339beb93cSSam Leffler 			/* Wi-Fi Protected Setup (WPS) IE */
8439beb93cSSam Leffler 			elems->wps_ie = pos;
8539beb93cSSam Leffler 			elems->wps_ie_len = elen;
8639beb93cSSam Leffler 			break;
8739beb93cSSam Leffler 		default:
88f05cddf9SRui Paulo 			wpa_printf(MSG_EXCESSIVE, "Unknown Microsoft "
89f05cddf9SRui Paulo 				   "information element ignored "
90f05cddf9SRui Paulo 				   "(type=%d len=%lu)",
91f05cddf9SRui Paulo 				   pos[3], (unsigned long) elen);
92f05cddf9SRui Paulo 			return -1;
93f05cddf9SRui Paulo 		}
94f05cddf9SRui Paulo 		break;
95f05cddf9SRui Paulo 
96f05cddf9SRui Paulo 	case OUI_WFA:
97f05cddf9SRui Paulo 		switch (pos[3]) {
98f05cddf9SRui Paulo 		case P2P_OUI_TYPE:
99f05cddf9SRui Paulo 			/* Wi-Fi Alliance - P2P IE */
100f05cddf9SRui Paulo 			elems->p2p = pos;
101f05cddf9SRui Paulo 			elems->p2p_len = elen;
102f05cddf9SRui Paulo 			break;
103f05cddf9SRui Paulo 		case WFD_OUI_TYPE:
104f05cddf9SRui Paulo 			/* Wi-Fi Alliance - WFD IE */
105f05cddf9SRui Paulo 			elems->wfd = pos;
106f05cddf9SRui Paulo 			elems->wfd_len = elen;
107f05cddf9SRui Paulo 			break;
108f05cddf9SRui Paulo 		case HS20_INDICATION_OUI_TYPE:
109f05cddf9SRui Paulo 			/* Hotspot 2.0 */
110f05cddf9SRui Paulo 			elems->hs20 = pos;
111f05cddf9SRui Paulo 			elems->hs20_len = elen;
112f05cddf9SRui Paulo 			break;
1135b9c547cSRui Paulo 		case HS20_OSEN_OUI_TYPE:
1145b9c547cSRui Paulo 			/* Hotspot 2.0 OSEN */
1155b9c547cSRui Paulo 			elems->osen = pos;
1165b9c547cSRui Paulo 			elems->osen_len = elen;
1175b9c547cSRui Paulo 			break;
118*780fb4a2SCy Schubert 		case MBO_OUI_TYPE:
119*780fb4a2SCy Schubert 			/* MBO-OCE */
120*780fb4a2SCy Schubert 			elems->mbo = pos;
121*780fb4a2SCy Schubert 			elems->mbo_len = elen;
122*780fb4a2SCy Schubert 			break;
123f05cddf9SRui Paulo 		default:
124f05cddf9SRui Paulo 			wpa_printf(MSG_MSGDUMP, "Unknown WFA "
12539beb93cSSam Leffler 				   "information element ignored "
1265b9c547cSRui Paulo 				   "(type=%d len=%lu)",
12739beb93cSSam Leffler 				   pos[3], (unsigned long) elen);
12839beb93cSSam Leffler 			return -1;
12939beb93cSSam Leffler 		}
13039beb93cSSam Leffler 		break;
13139beb93cSSam Leffler 
13239beb93cSSam Leffler 	case OUI_BROADCOM:
13339beb93cSSam Leffler 		switch (pos[3]) {
13439beb93cSSam Leffler 		case VENDOR_HT_CAPAB_OUI_TYPE:
13539beb93cSSam Leffler 			elems->vendor_ht_cap = pos;
13639beb93cSSam Leffler 			elems->vendor_ht_cap_len = elen;
13739beb93cSSam Leffler 			break;
1385b9c547cSRui Paulo 		case VENDOR_VHT_TYPE:
1395b9c547cSRui Paulo 			if (elen > 4 &&
1405b9c547cSRui Paulo 			    (pos[4] == VENDOR_VHT_SUBTYPE ||
1415b9c547cSRui Paulo 			     pos[4] == VENDOR_VHT_SUBTYPE2)) {
1425b9c547cSRui Paulo 				elems->vendor_vht = pos;
1435b9c547cSRui Paulo 				elems->vendor_vht_len = elen;
1445b9c547cSRui Paulo 			} else
1455b9c547cSRui Paulo 				return -1;
1465b9c547cSRui Paulo 			break;
14739beb93cSSam Leffler 		default:
148f05cddf9SRui Paulo 			wpa_printf(MSG_EXCESSIVE, "Unknown Broadcom "
14939beb93cSSam Leffler 				   "information element ignored "
150f05cddf9SRui Paulo 				   "(type=%d len=%lu)",
15139beb93cSSam Leffler 				   pos[3], (unsigned long) elen);
15239beb93cSSam Leffler 			return -1;
15339beb93cSSam Leffler 		}
15439beb93cSSam Leffler 		break;
15539beb93cSSam Leffler 
156325151a3SRui Paulo 	case OUI_QCA:
157325151a3SRui Paulo 		switch (pos[3]) {
158325151a3SRui Paulo 		case QCA_VENDOR_ELEM_P2P_PREF_CHAN_LIST:
159325151a3SRui Paulo 			elems->pref_freq_list = pos;
160325151a3SRui Paulo 			elems->pref_freq_list_len = elen;
161325151a3SRui Paulo 			break;
162325151a3SRui Paulo 		default:
163325151a3SRui Paulo 			wpa_printf(MSG_EXCESSIVE,
164325151a3SRui Paulo 				   "Unknown QCA information element ignored (type=%d len=%lu)",
165325151a3SRui Paulo 				   pos[3], (unsigned long) elen);
166325151a3SRui Paulo 			return -1;
167325151a3SRui Paulo 		}
168325151a3SRui Paulo 		break;
169325151a3SRui Paulo 
17039beb93cSSam Leffler 	default:
171f05cddf9SRui Paulo 		wpa_printf(MSG_EXCESSIVE, "unknown vendor specific "
172f05cddf9SRui Paulo 			   "information element ignored (vendor OUI "
173f05cddf9SRui Paulo 			   "%02x:%02x:%02x len=%lu)",
17439beb93cSSam Leffler 			   pos[0], pos[1], pos[2], (unsigned long) elen);
17539beb93cSSam Leffler 		return -1;
17639beb93cSSam Leffler 	}
17739beb93cSSam Leffler 
17839beb93cSSam Leffler 	return 0;
17939beb93cSSam Leffler }
18039beb93cSSam Leffler 
18139beb93cSSam Leffler 
18239beb93cSSam Leffler /**
18339beb93cSSam Leffler  * ieee802_11_parse_elems - Parse information elements in management frames
18439beb93cSSam Leffler  * @start: Pointer to the start of IEs
18539beb93cSSam Leffler  * @len: Length of IE buffer in octets
18639beb93cSSam Leffler  * @elems: Data structure for parsed elements
18739beb93cSSam Leffler  * @show_errors: Whether to show parsing errors in debug log
18839beb93cSSam Leffler  * Returns: Parsing result
18939beb93cSSam Leffler  */
190e28a4053SRui Paulo ParseRes ieee802_11_parse_elems(const u8 *start, size_t len,
19139beb93cSSam Leffler 				struct ieee802_11_elems *elems,
19239beb93cSSam Leffler 				int show_errors)
19339beb93cSSam Leffler {
19439beb93cSSam Leffler 	size_t left = len;
195e28a4053SRui Paulo 	const u8 *pos = start;
19639beb93cSSam Leffler 	int unknown = 0;
19739beb93cSSam Leffler 
19839beb93cSSam Leffler 	os_memset(elems, 0, sizeof(*elems));
19939beb93cSSam Leffler 
20039beb93cSSam Leffler 	while (left >= 2) {
20139beb93cSSam Leffler 		u8 id, elen;
20239beb93cSSam Leffler 
20339beb93cSSam Leffler 		id = *pos++;
20439beb93cSSam Leffler 		elen = *pos++;
20539beb93cSSam Leffler 		left -= 2;
20639beb93cSSam Leffler 
20739beb93cSSam Leffler 		if (elen > left) {
20839beb93cSSam Leffler 			if (show_errors) {
20939beb93cSSam Leffler 				wpa_printf(MSG_DEBUG, "IEEE 802.11 element "
21039beb93cSSam Leffler 					   "parse failed (id=%d elen=%d "
21139beb93cSSam Leffler 					   "left=%lu)",
21239beb93cSSam Leffler 					   id, elen, (unsigned long) left);
21339beb93cSSam Leffler 				wpa_hexdump(MSG_MSGDUMP, "IEs", start, len);
21439beb93cSSam Leffler 			}
21539beb93cSSam Leffler 			return ParseFailed;
21639beb93cSSam Leffler 		}
21739beb93cSSam Leffler 
21839beb93cSSam Leffler 		switch (id) {
21939beb93cSSam Leffler 		case WLAN_EID_SSID:
220325151a3SRui Paulo 			if (elen > SSID_MAX_LEN) {
221325151a3SRui Paulo 				wpa_printf(MSG_DEBUG,
222325151a3SRui Paulo 					   "Ignored too long SSID element (elen=%u)",
223325151a3SRui Paulo 					   elen);
224325151a3SRui Paulo 				break;
225325151a3SRui Paulo 			}
22639beb93cSSam Leffler 			elems->ssid = pos;
22739beb93cSSam Leffler 			elems->ssid_len = elen;
22839beb93cSSam Leffler 			break;
22939beb93cSSam Leffler 		case WLAN_EID_SUPP_RATES:
23039beb93cSSam Leffler 			elems->supp_rates = pos;
23139beb93cSSam Leffler 			elems->supp_rates_len = elen;
23239beb93cSSam Leffler 			break;
23339beb93cSSam Leffler 		case WLAN_EID_DS_PARAMS:
234325151a3SRui Paulo 			if (elen < 1)
235325151a3SRui Paulo 				break;
23639beb93cSSam Leffler 			elems->ds_params = pos;
23739beb93cSSam Leffler 			break;
23839beb93cSSam Leffler 		case WLAN_EID_CF_PARAMS:
23939beb93cSSam Leffler 		case WLAN_EID_TIM:
24039beb93cSSam Leffler 			break;
24139beb93cSSam Leffler 		case WLAN_EID_CHALLENGE:
24239beb93cSSam Leffler 			elems->challenge = pos;
24339beb93cSSam Leffler 			elems->challenge_len = elen;
24439beb93cSSam Leffler 			break;
24539beb93cSSam Leffler 		case WLAN_EID_ERP_INFO:
246325151a3SRui Paulo 			if (elen < 1)
247325151a3SRui Paulo 				break;
24839beb93cSSam Leffler 			elems->erp_info = pos;
24939beb93cSSam Leffler 			break;
25039beb93cSSam Leffler 		case WLAN_EID_EXT_SUPP_RATES:
25139beb93cSSam Leffler 			elems->ext_supp_rates = pos;
25239beb93cSSam Leffler 			elems->ext_supp_rates_len = elen;
25339beb93cSSam Leffler 			break;
25439beb93cSSam Leffler 		case WLAN_EID_VENDOR_SPECIFIC:
25539beb93cSSam Leffler 			if (ieee802_11_parse_vendor_specific(pos, elen,
25639beb93cSSam Leffler 							     elems,
25739beb93cSSam Leffler 							     show_errors))
25839beb93cSSam Leffler 				unknown++;
25939beb93cSSam Leffler 			break;
26039beb93cSSam Leffler 		case WLAN_EID_RSN:
26139beb93cSSam Leffler 			elems->rsn_ie = pos;
26239beb93cSSam Leffler 			elems->rsn_ie_len = elen;
26339beb93cSSam Leffler 			break;
26439beb93cSSam Leffler 		case WLAN_EID_PWR_CAPABILITY:
26539beb93cSSam Leffler 			break;
26639beb93cSSam Leffler 		case WLAN_EID_SUPPORTED_CHANNELS:
26739beb93cSSam Leffler 			elems->supp_channels = pos;
26839beb93cSSam Leffler 			elems->supp_channels_len = elen;
26939beb93cSSam Leffler 			break;
27039beb93cSSam Leffler 		case WLAN_EID_MOBILITY_DOMAIN:
271325151a3SRui Paulo 			if (elen < sizeof(struct rsn_mdie))
272325151a3SRui Paulo 				break;
27339beb93cSSam Leffler 			elems->mdie = pos;
27439beb93cSSam Leffler 			elems->mdie_len = elen;
27539beb93cSSam Leffler 			break;
27639beb93cSSam Leffler 		case WLAN_EID_FAST_BSS_TRANSITION:
277325151a3SRui Paulo 			if (elen < sizeof(struct rsn_ftie))
278325151a3SRui Paulo 				break;
27939beb93cSSam Leffler 			elems->ftie = pos;
28039beb93cSSam Leffler 			elems->ftie_len = elen;
28139beb93cSSam Leffler 			break;
28239beb93cSSam Leffler 		case WLAN_EID_TIMEOUT_INTERVAL:
283325151a3SRui Paulo 			if (elen != 5)
284325151a3SRui Paulo 				break;
28539beb93cSSam Leffler 			elems->timeout_int = pos;
28639beb93cSSam Leffler 			break;
28739beb93cSSam Leffler 		case WLAN_EID_HT_CAP:
288325151a3SRui Paulo 			if (elen < sizeof(struct ieee80211_ht_capabilities))
289325151a3SRui Paulo 				break;
29039beb93cSSam Leffler 			elems->ht_capabilities = pos;
29139beb93cSSam Leffler 			break;
29239beb93cSSam Leffler 		case WLAN_EID_HT_OPERATION:
293325151a3SRui Paulo 			if (elen < sizeof(struct ieee80211_ht_operation))
294325151a3SRui Paulo 				break;
29539beb93cSSam Leffler 			elems->ht_operation = pos;
29639beb93cSSam Leffler 			break;
2975b9c547cSRui Paulo 		case WLAN_EID_MESH_CONFIG:
2985b9c547cSRui Paulo 			elems->mesh_config = pos;
2995b9c547cSRui Paulo 			elems->mesh_config_len = elen;
3005b9c547cSRui Paulo 			break;
3015b9c547cSRui Paulo 		case WLAN_EID_MESH_ID:
3025b9c547cSRui Paulo 			elems->mesh_id = pos;
3035b9c547cSRui Paulo 			elems->mesh_id_len = elen;
3045b9c547cSRui Paulo 			break;
3055b9c547cSRui Paulo 		case WLAN_EID_PEER_MGMT:
3065b9c547cSRui Paulo 			elems->peer_mgmt = pos;
3075b9c547cSRui Paulo 			elems->peer_mgmt_len = elen;
3085b9c547cSRui Paulo 			break;
309f05cddf9SRui Paulo 		case WLAN_EID_VHT_CAP:
310325151a3SRui Paulo 			if (elen < sizeof(struct ieee80211_vht_capabilities))
311325151a3SRui Paulo 				break;
312f05cddf9SRui Paulo 			elems->vht_capabilities = pos;
313f05cddf9SRui Paulo 			break;
314f05cddf9SRui Paulo 		case WLAN_EID_VHT_OPERATION:
315325151a3SRui Paulo 			if (elen < sizeof(struct ieee80211_vht_operation))
316325151a3SRui Paulo 				break;
317f05cddf9SRui Paulo 			elems->vht_operation = pos;
318f05cddf9SRui Paulo 			break;
3195b9c547cSRui Paulo 		case WLAN_EID_VHT_OPERATING_MODE_NOTIFICATION:
3205b9c547cSRui Paulo 			if (elen != 1)
3215b9c547cSRui Paulo 				break;
3225b9c547cSRui Paulo 			elems->vht_opmode_notif = pos;
3235b9c547cSRui Paulo 			break;
324f05cddf9SRui Paulo 		case WLAN_EID_LINK_ID:
325f05cddf9SRui Paulo 			if (elen < 18)
326f05cddf9SRui Paulo 				break;
327f05cddf9SRui Paulo 			elems->link_id = pos;
328f05cddf9SRui Paulo 			break;
329f05cddf9SRui Paulo 		case WLAN_EID_INTERWORKING:
330f05cddf9SRui Paulo 			elems->interworking = pos;
331f05cddf9SRui Paulo 			elems->interworking_len = elen;
332f05cddf9SRui Paulo 			break;
3335b9c547cSRui Paulo 		case WLAN_EID_QOS_MAP_SET:
3345b9c547cSRui Paulo 			if (elen < 16)
3355b9c547cSRui Paulo 				break;
3365b9c547cSRui Paulo 			elems->qos_map_set = pos;
3375b9c547cSRui Paulo 			elems->qos_map_set_len = elen;
3385b9c547cSRui Paulo 			break;
339f05cddf9SRui Paulo 		case WLAN_EID_EXT_CAPAB:
340f05cddf9SRui Paulo 			elems->ext_capab = pos;
341f05cddf9SRui Paulo 			elems->ext_capab_len = elen;
342f05cddf9SRui Paulo 			break;
343f05cddf9SRui Paulo 		case WLAN_EID_BSS_MAX_IDLE_PERIOD:
344f05cddf9SRui Paulo 			if (elen < 3)
345f05cddf9SRui Paulo 				break;
346f05cddf9SRui Paulo 			elems->bss_max_idle_period = pos;
347f05cddf9SRui Paulo 			break;
348f05cddf9SRui Paulo 		case WLAN_EID_SSID_LIST:
349f05cddf9SRui Paulo 			elems->ssid_list = pos;
350f05cddf9SRui Paulo 			elems->ssid_list_len = elen;
351f05cddf9SRui Paulo 			break;
3525b9c547cSRui Paulo 		case WLAN_EID_AMPE:
3535b9c547cSRui Paulo 			elems->ampe = pos;
3545b9c547cSRui Paulo 			elems->ampe_len = elen;
3555b9c547cSRui Paulo 			break;
3565b9c547cSRui Paulo 		case WLAN_EID_MIC:
3575b9c547cSRui Paulo 			elems->mic = pos;
3585b9c547cSRui Paulo 			elems->mic_len = elen;
3595b9c547cSRui Paulo 			/* after mic everything is encrypted, so stop. */
3605b9c547cSRui Paulo 			left = elen;
3615b9c547cSRui Paulo 			break;
362325151a3SRui Paulo 		case WLAN_EID_MULTI_BAND:
363325151a3SRui Paulo 			if (elems->mb_ies.nof_ies >= MAX_NOF_MB_IES_SUPPORTED) {
364325151a3SRui Paulo 				wpa_printf(MSG_MSGDUMP,
365325151a3SRui Paulo 					   "IEEE 802.11 element parse ignored MB IE (id=%d elen=%d)",
366325151a3SRui Paulo 					   id, elen);
367325151a3SRui Paulo 				break;
368325151a3SRui Paulo 			}
369325151a3SRui Paulo 
370325151a3SRui Paulo 			elems->mb_ies.ies[elems->mb_ies.nof_ies].ie = pos;
371325151a3SRui Paulo 			elems->mb_ies.ies[elems->mb_ies.nof_ies].ie_len = elen;
372325151a3SRui Paulo 			elems->mb_ies.nof_ies++;
373325151a3SRui Paulo 			break;
374*780fb4a2SCy Schubert 		case WLAN_EID_SUPPORTED_OPERATING_CLASSES:
375*780fb4a2SCy Schubert 			elems->supp_op_classes = pos;
376*780fb4a2SCy Schubert 			elems->supp_op_classes_len = elen;
377*780fb4a2SCy Schubert 			break;
378*780fb4a2SCy Schubert 		case WLAN_EID_RRM_ENABLED_CAPABILITIES:
379*780fb4a2SCy Schubert 			elems->rrm_enabled = pos;
380*780fb4a2SCy Schubert 			elems->rrm_enabled_len = elen;
381*780fb4a2SCy Schubert 			break;
38239beb93cSSam Leffler 		default:
38339beb93cSSam Leffler 			unknown++;
38439beb93cSSam Leffler 			if (!show_errors)
38539beb93cSSam Leffler 				break;
38639beb93cSSam Leffler 			wpa_printf(MSG_MSGDUMP, "IEEE 802.11 element parse "
38739beb93cSSam Leffler 				   "ignored unknown element (id=%d elen=%d)",
38839beb93cSSam Leffler 				   id, elen);
38939beb93cSSam Leffler 			break;
39039beb93cSSam Leffler 		}
39139beb93cSSam Leffler 
39239beb93cSSam Leffler 		left -= elen;
39339beb93cSSam Leffler 		pos += elen;
39439beb93cSSam Leffler 	}
39539beb93cSSam Leffler 
39639beb93cSSam Leffler 	if (left)
39739beb93cSSam Leffler 		return ParseFailed;
39839beb93cSSam Leffler 
39939beb93cSSam Leffler 	return unknown ? ParseUnknown : ParseOK;
40039beb93cSSam Leffler }
401e28a4053SRui Paulo 
402e28a4053SRui Paulo 
403e28a4053SRui Paulo int ieee802_11_ie_count(const u8 *ies, size_t ies_len)
404e28a4053SRui Paulo {
405e28a4053SRui Paulo 	int count = 0;
406e28a4053SRui Paulo 	const u8 *pos, *end;
407e28a4053SRui Paulo 
408e28a4053SRui Paulo 	if (ies == NULL)
409e28a4053SRui Paulo 		return 0;
410e28a4053SRui Paulo 
411e28a4053SRui Paulo 	pos = ies;
412e28a4053SRui Paulo 	end = ies + ies_len;
413e28a4053SRui Paulo 
414*780fb4a2SCy Schubert 	while (end - pos >= 2) {
415*780fb4a2SCy Schubert 		if (2 + pos[1] > end - pos)
416e28a4053SRui Paulo 			break;
417e28a4053SRui Paulo 		count++;
418e28a4053SRui Paulo 		pos += 2 + pos[1];
419e28a4053SRui Paulo 	}
420e28a4053SRui Paulo 
421e28a4053SRui Paulo 	return count;
422e28a4053SRui Paulo }
423e28a4053SRui Paulo 
424e28a4053SRui Paulo 
425e28a4053SRui Paulo struct wpabuf * ieee802_11_vendor_ie_concat(const u8 *ies, size_t ies_len,
426e28a4053SRui Paulo 					    u32 oui_type)
427e28a4053SRui Paulo {
428e28a4053SRui Paulo 	struct wpabuf *buf;
429e28a4053SRui Paulo 	const u8 *end, *pos, *ie;
430e28a4053SRui Paulo 
431e28a4053SRui Paulo 	pos = ies;
432e28a4053SRui Paulo 	end = ies + ies_len;
433e28a4053SRui Paulo 	ie = NULL;
434e28a4053SRui Paulo 
435*780fb4a2SCy Schubert 	while (end - pos > 1) {
436*780fb4a2SCy Schubert 		if (2 + pos[1] > end - pos)
437e28a4053SRui Paulo 			return NULL;
438e28a4053SRui Paulo 		if (pos[0] == WLAN_EID_VENDOR_SPECIFIC && pos[1] >= 4 &&
439e28a4053SRui Paulo 		    WPA_GET_BE32(&pos[2]) == oui_type) {
440e28a4053SRui Paulo 			ie = pos;
441e28a4053SRui Paulo 			break;
442e28a4053SRui Paulo 		}
443e28a4053SRui Paulo 		pos += 2 + pos[1];
444e28a4053SRui Paulo 	}
445e28a4053SRui Paulo 
446e28a4053SRui Paulo 	if (ie == NULL)
447e28a4053SRui Paulo 		return NULL; /* No specified vendor IE found */
448e28a4053SRui Paulo 
449e28a4053SRui Paulo 	buf = wpabuf_alloc(ies_len);
450e28a4053SRui Paulo 	if (buf == NULL)
451e28a4053SRui Paulo 		return NULL;
452e28a4053SRui Paulo 
453e28a4053SRui Paulo 	/*
454e28a4053SRui Paulo 	 * There may be multiple vendor IEs in the message, so need to
455e28a4053SRui Paulo 	 * concatenate their data fields.
456e28a4053SRui Paulo 	 */
457*780fb4a2SCy Schubert 	while (end - pos > 1) {
458*780fb4a2SCy Schubert 		if (2 + pos[1] > end - pos)
459e28a4053SRui Paulo 			break;
460e28a4053SRui Paulo 		if (pos[0] == WLAN_EID_VENDOR_SPECIFIC && pos[1] >= 4 &&
461e28a4053SRui Paulo 		    WPA_GET_BE32(&pos[2]) == oui_type)
462e28a4053SRui Paulo 			wpabuf_put_data(buf, pos + 6, pos[1] - 4);
463e28a4053SRui Paulo 		pos += 2 + pos[1];
464e28a4053SRui Paulo 	}
465e28a4053SRui Paulo 
466e28a4053SRui Paulo 	return buf;
467e28a4053SRui Paulo }
468f05cddf9SRui Paulo 
469f05cddf9SRui Paulo 
470f05cddf9SRui Paulo const u8 * get_hdr_bssid(const struct ieee80211_hdr *hdr, size_t len)
471f05cddf9SRui Paulo {
472f05cddf9SRui Paulo 	u16 fc, type, stype;
473f05cddf9SRui Paulo 
474f05cddf9SRui Paulo 	/*
475f05cddf9SRui Paulo 	 * PS-Poll frames are 16 bytes. All other frames are
476f05cddf9SRui Paulo 	 * 24 bytes or longer.
477f05cddf9SRui Paulo 	 */
478f05cddf9SRui Paulo 	if (len < 16)
479f05cddf9SRui Paulo 		return NULL;
480f05cddf9SRui Paulo 
481f05cddf9SRui Paulo 	fc = le_to_host16(hdr->frame_control);
482f05cddf9SRui Paulo 	type = WLAN_FC_GET_TYPE(fc);
483f05cddf9SRui Paulo 	stype = WLAN_FC_GET_STYPE(fc);
484f05cddf9SRui Paulo 
485f05cddf9SRui Paulo 	switch (type) {
486f05cddf9SRui Paulo 	case WLAN_FC_TYPE_DATA:
487f05cddf9SRui Paulo 		if (len < 24)
488f05cddf9SRui Paulo 			return NULL;
489f05cddf9SRui Paulo 		switch (fc & (WLAN_FC_FROMDS | WLAN_FC_TODS)) {
490f05cddf9SRui Paulo 		case WLAN_FC_FROMDS | WLAN_FC_TODS:
491f05cddf9SRui Paulo 		case WLAN_FC_TODS:
492f05cddf9SRui Paulo 			return hdr->addr1;
493f05cddf9SRui Paulo 		case WLAN_FC_FROMDS:
494f05cddf9SRui Paulo 			return hdr->addr2;
495f05cddf9SRui Paulo 		default:
496f05cddf9SRui Paulo 			return NULL;
497f05cddf9SRui Paulo 		}
498f05cddf9SRui Paulo 	case WLAN_FC_TYPE_CTRL:
499f05cddf9SRui Paulo 		if (stype != WLAN_FC_STYPE_PSPOLL)
500f05cddf9SRui Paulo 			return NULL;
501f05cddf9SRui Paulo 		return hdr->addr1;
502f05cddf9SRui Paulo 	case WLAN_FC_TYPE_MGMT:
503f05cddf9SRui Paulo 		return hdr->addr3;
504f05cddf9SRui Paulo 	default:
505f05cddf9SRui Paulo 		return NULL;
506f05cddf9SRui Paulo 	}
507f05cddf9SRui Paulo }
508f05cddf9SRui Paulo 
509f05cddf9SRui Paulo 
510f05cddf9SRui Paulo int hostapd_config_wmm_ac(struct hostapd_wmm_ac_params wmm_ac_params[],
511f05cddf9SRui Paulo 			  const char *name, const char *val)
512f05cddf9SRui Paulo {
513f05cddf9SRui Paulo 	int num, v;
514f05cddf9SRui Paulo 	const char *pos;
515f05cddf9SRui Paulo 	struct hostapd_wmm_ac_params *ac;
516f05cddf9SRui Paulo 
517f05cddf9SRui Paulo 	/* skip 'wme_ac_' or 'wmm_ac_' prefix */
518f05cddf9SRui Paulo 	pos = name + 7;
519f05cddf9SRui Paulo 	if (os_strncmp(pos, "be_", 3) == 0) {
520f05cddf9SRui Paulo 		num = 0;
521f05cddf9SRui Paulo 		pos += 3;
522f05cddf9SRui Paulo 	} else if (os_strncmp(pos, "bk_", 3) == 0) {
523f05cddf9SRui Paulo 		num = 1;
524f05cddf9SRui Paulo 		pos += 3;
525f05cddf9SRui Paulo 	} else if (os_strncmp(pos, "vi_", 3) == 0) {
526f05cddf9SRui Paulo 		num = 2;
527f05cddf9SRui Paulo 		pos += 3;
528f05cddf9SRui Paulo 	} else if (os_strncmp(pos, "vo_", 3) == 0) {
529f05cddf9SRui Paulo 		num = 3;
530f05cddf9SRui Paulo 		pos += 3;
531f05cddf9SRui Paulo 	} else {
532f05cddf9SRui Paulo 		wpa_printf(MSG_ERROR, "Unknown WMM name '%s'", pos);
533f05cddf9SRui Paulo 		return -1;
534f05cddf9SRui Paulo 	}
535f05cddf9SRui Paulo 
536f05cddf9SRui Paulo 	ac = &wmm_ac_params[num];
537f05cddf9SRui Paulo 
538f05cddf9SRui Paulo 	if (os_strcmp(pos, "aifs") == 0) {
539f05cddf9SRui Paulo 		v = atoi(val);
540f05cddf9SRui Paulo 		if (v < 1 || v > 255) {
541f05cddf9SRui Paulo 			wpa_printf(MSG_ERROR, "Invalid AIFS value %d", v);
542f05cddf9SRui Paulo 			return -1;
543f05cddf9SRui Paulo 		}
544f05cddf9SRui Paulo 		ac->aifs = v;
545f05cddf9SRui Paulo 	} else if (os_strcmp(pos, "cwmin") == 0) {
546f05cddf9SRui Paulo 		v = atoi(val);
547325151a3SRui Paulo 		if (v < 0 || v > 15) {
548f05cddf9SRui Paulo 			wpa_printf(MSG_ERROR, "Invalid cwMin value %d", v);
549f05cddf9SRui Paulo 			return -1;
550f05cddf9SRui Paulo 		}
551f05cddf9SRui Paulo 		ac->cwmin = v;
552f05cddf9SRui Paulo 	} else if (os_strcmp(pos, "cwmax") == 0) {
553f05cddf9SRui Paulo 		v = atoi(val);
554325151a3SRui Paulo 		if (v < 0 || v > 15) {
555f05cddf9SRui Paulo 			wpa_printf(MSG_ERROR, "Invalid cwMax value %d", v);
556f05cddf9SRui Paulo 			return -1;
557f05cddf9SRui Paulo 		}
558f05cddf9SRui Paulo 		ac->cwmax = v;
559f05cddf9SRui Paulo 	} else if (os_strcmp(pos, "txop_limit") == 0) {
560f05cddf9SRui Paulo 		v = atoi(val);
561f05cddf9SRui Paulo 		if (v < 0 || v > 0xffff) {
562f05cddf9SRui Paulo 			wpa_printf(MSG_ERROR, "Invalid txop value %d", v);
563f05cddf9SRui Paulo 			return -1;
564f05cddf9SRui Paulo 		}
565f05cddf9SRui Paulo 		ac->txop_limit = v;
566f05cddf9SRui Paulo 	} else if (os_strcmp(pos, "acm") == 0) {
567f05cddf9SRui Paulo 		v = atoi(val);
568f05cddf9SRui Paulo 		if (v < 0 || v > 1) {
569f05cddf9SRui Paulo 			wpa_printf(MSG_ERROR, "Invalid acm value %d", v);
570f05cddf9SRui Paulo 			return -1;
571f05cddf9SRui Paulo 		}
572f05cddf9SRui Paulo 		ac->admission_control_mandatory = v;
573f05cddf9SRui Paulo 	} else {
574f05cddf9SRui Paulo 		wpa_printf(MSG_ERROR, "Unknown wmm_ac_ field '%s'", pos);
575f05cddf9SRui Paulo 		return -1;
576f05cddf9SRui Paulo 	}
577f05cddf9SRui Paulo 
578f05cddf9SRui Paulo 	return 0;
579f05cddf9SRui Paulo }
5805b9c547cSRui Paulo 
5815b9c547cSRui Paulo 
5825b9c547cSRui Paulo enum hostapd_hw_mode ieee80211_freq_to_chan(int freq, u8 *channel)
5835b9c547cSRui Paulo {
584325151a3SRui Paulo 	u8 op_class;
585325151a3SRui Paulo 
586*780fb4a2SCy Schubert 	return ieee80211_freq_to_channel_ext(freq, 0, VHT_CHANWIDTH_USE_HT,
587*780fb4a2SCy Schubert 					     &op_class, channel);
588325151a3SRui Paulo }
589325151a3SRui Paulo 
590325151a3SRui Paulo 
591325151a3SRui Paulo /**
592325151a3SRui Paulo  * ieee80211_freq_to_channel_ext - Convert frequency into channel info
593325151a3SRui Paulo  * for HT40 and VHT. DFS channels are not covered.
594325151a3SRui Paulo  * @freq: Frequency (MHz) to convert
595325151a3SRui Paulo  * @sec_channel: 0 = non-HT40, 1 = sec. channel above, -1 = sec. channel below
596*780fb4a2SCy Schubert  * @vht: VHT channel width (VHT_CHANWIDTH_*)
597325151a3SRui Paulo  * @op_class: Buffer for returning operating class
598325151a3SRui Paulo  * @channel: Buffer for returning channel number
599325151a3SRui Paulo  * Returns: hw_mode on success, NUM_HOSTAPD_MODES on failure
600325151a3SRui Paulo  */
601325151a3SRui Paulo enum hostapd_hw_mode ieee80211_freq_to_channel_ext(unsigned int freq,
602325151a3SRui Paulo 						   int sec_channel, int vht,
603325151a3SRui Paulo 						   u8 *op_class, u8 *channel)
604325151a3SRui Paulo {
605*780fb4a2SCy Schubert 	u8 vht_opclass;
606*780fb4a2SCy Schubert 
607325151a3SRui Paulo 	/* TODO: more operating classes */
608325151a3SRui Paulo 
609325151a3SRui Paulo 	if (sec_channel > 1 || sec_channel < -1)
610325151a3SRui Paulo 		return NUM_HOSTAPD_MODES;
6115b9c547cSRui Paulo 
6125b9c547cSRui Paulo 	if (freq >= 2412 && freq <= 2472) {
613325151a3SRui Paulo 		if ((freq - 2407) % 5)
614325151a3SRui Paulo 			return NUM_HOSTAPD_MODES;
615325151a3SRui Paulo 
616325151a3SRui Paulo 		if (vht)
617325151a3SRui Paulo 			return NUM_HOSTAPD_MODES;
618325151a3SRui Paulo 
619325151a3SRui Paulo 		/* 2.407 GHz, channels 1..13 */
620325151a3SRui Paulo 		if (sec_channel == 1)
621325151a3SRui Paulo 			*op_class = 83;
622325151a3SRui Paulo 		else if (sec_channel == -1)
623325151a3SRui Paulo 			*op_class = 84;
624325151a3SRui Paulo 		else
625325151a3SRui Paulo 			*op_class = 81;
626325151a3SRui Paulo 
6275b9c547cSRui Paulo 		*channel = (freq - 2407) / 5;
628325151a3SRui Paulo 
629325151a3SRui Paulo 		return HOSTAPD_MODE_IEEE80211G;
630325151a3SRui Paulo 	}
631325151a3SRui Paulo 
632325151a3SRui Paulo 	if (freq == 2484) {
633325151a3SRui Paulo 		if (sec_channel || vht)
634325151a3SRui Paulo 			return NUM_HOSTAPD_MODES;
635325151a3SRui Paulo 
636325151a3SRui Paulo 		*op_class = 82; /* channel 14 */
6375b9c547cSRui Paulo 		*channel = 14;
638325151a3SRui Paulo 
639325151a3SRui Paulo 		return HOSTAPD_MODE_IEEE80211B;
640325151a3SRui Paulo 	}
641325151a3SRui Paulo 
642325151a3SRui Paulo 	if (freq >= 4900 && freq < 5000) {
643325151a3SRui Paulo 		if ((freq - 4000) % 5)
644325151a3SRui Paulo 			return NUM_HOSTAPD_MODES;
6455b9c547cSRui Paulo 		*channel = (freq - 4000) / 5;
646325151a3SRui Paulo 		*op_class = 0; /* TODO */
647325151a3SRui Paulo 		return HOSTAPD_MODE_IEEE80211A;
648325151a3SRui Paulo 	}
649325151a3SRui Paulo 
650*780fb4a2SCy Schubert 	switch (vht) {
651*780fb4a2SCy Schubert 	case VHT_CHANWIDTH_80MHZ:
652*780fb4a2SCy Schubert 		vht_opclass = 128;
653*780fb4a2SCy Schubert 		break;
654*780fb4a2SCy Schubert 	case VHT_CHANWIDTH_160MHZ:
655*780fb4a2SCy Schubert 		vht_opclass = 129;
656*780fb4a2SCy Schubert 		break;
657*780fb4a2SCy Schubert 	case VHT_CHANWIDTH_80P80MHZ:
658*780fb4a2SCy Schubert 		vht_opclass = 130;
659*780fb4a2SCy Schubert 		break;
660*780fb4a2SCy Schubert 	default:
661*780fb4a2SCy Schubert 		vht_opclass = 0;
662*780fb4a2SCy Schubert 		break;
663*780fb4a2SCy Schubert 	}
664*780fb4a2SCy Schubert 
665325151a3SRui Paulo 	/* 5 GHz, channels 36..48 */
666325151a3SRui Paulo 	if (freq >= 5180 && freq <= 5240) {
667325151a3SRui Paulo 		if ((freq - 5000) % 5)
668325151a3SRui Paulo 			return NUM_HOSTAPD_MODES;
669325151a3SRui Paulo 
670*780fb4a2SCy Schubert 		if (vht_opclass)
671*780fb4a2SCy Schubert 			*op_class = vht_opclass;
672*780fb4a2SCy Schubert 		else if (sec_channel == 1)
673325151a3SRui Paulo 			*op_class = 116;
674325151a3SRui Paulo 		else if (sec_channel == -1)
675325151a3SRui Paulo 			*op_class = 117;
676325151a3SRui Paulo 		else
677325151a3SRui Paulo 			*op_class = 115;
678325151a3SRui Paulo 
6795b9c547cSRui Paulo 		*channel = (freq - 5000) / 5;
680325151a3SRui Paulo 
681325151a3SRui Paulo 		return HOSTAPD_MODE_IEEE80211A;
682325151a3SRui Paulo 	}
683325151a3SRui Paulo 
684325151a3SRui Paulo 	/* 5 GHz, channels 149..169 */
685325151a3SRui Paulo 	if (freq >= 5745 && freq <= 5845) {
686325151a3SRui Paulo 		if ((freq - 5000) % 5)
687325151a3SRui Paulo 			return NUM_HOSTAPD_MODES;
688325151a3SRui Paulo 
689*780fb4a2SCy Schubert 		if (vht_opclass)
690*780fb4a2SCy Schubert 			*op_class = vht_opclass;
691*780fb4a2SCy Schubert 		else if (sec_channel == 1)
692*780fb4a2SCy Schubert 			*op_class = 126;
693*780fb4a2SCy Schubert 		else if (sec_channel == -1)
694*780fb4a2SCy Schubert 			*op_class = 127;
695*780fb4a2SCy Schubert 		else if (freq <= 5805)
696*780fb4a2SCy Schubert 			*op_class = 124;
697*780fb4a2SCy Schubert 		else
698325151a3SRui Paulo 			*op_class = 125;
699325151a3SRui Paulo 
700325151a3SRui Paulo 		*channel = (freq - 5000) / 5;
701325151a3SRui Paulo 
702325151a3SRui Paulo 		return HOSTAPD_MODE_IEEE80211A;
703325151a3SRui Paulo 	}
704325151a3SRui Paulo 
705*780fb4a2SCy Schubert 	/* 5 GHz, channels 100..140 */
706*780fb4a2SCy Schubert 	if (freq >= 5000 && freq <= 5700) {
707*780fb4a2SCy Schubert 		if ((freq - 5000) % 5)
708*780fb4a2SCy Schubert 			return NUM_HOSTAPD_MODES;
709*780fb4a2SCy Schubert 
710*780fb4a2SCy Schubert 		if (vht_opclass)
711*780fb4a2SCy Schubert 			*op_class = vht_opclass;
712*780fb4a2SCy Schubert 		else if (sec_channel == 1)
713*780fb4a2SCy Schubert 			*op_class = 122;
714*780fb4a2SCy Schubert 		else if (sec_channel == -1)
715*780fb4a2SCy Schubert 			*op_class = 123;
716*780fb4a2SCy Schubert 		else
717*780fb4a2SCy Schubert 			*op_class = 121;
718*780fb4a2SCy Schubert 
719*780fb4a2SCy Schubert 		*channel = (freq - 5000) / 5;
720*780fb4a2SCy Schubert 
721*780fb4a2SCy Schubert 		return HOSTAPD_MODE_IEEE80211A;
722*780fb4a2SCy Schubert 	}
723*780fb4a2SCy Schubert 
724325151a3SRui Paulo 	if (freq >= 5000 && freq < 5900) {
725325151a3SRui Paulo 		if ((freq - 5000) % 5)
726325151a3SRui Paulo 			return NUM_HOSTAPD_MODES;
727325151a3SRui Paulo 		*channel = (freq - 5000) / 5;
728325151a3SRui Paulo 		*op_class = 0; /* TODO */
729325151a3SRui Paulo 		return HOSTAPD_MODE_IEEE80211A;
730325151a3SRui Paulo 	}
731325151a3SRui Paulo 
732325151a3SRui Paulo 	/* 56.16 GHz, channel 1..4 */
733325151a3SRui Paulo 	if (freq >= 56160 + 2160 * 1 && freq <= 56160 + 2160 * 4) {
734325151a3SRui Paulo 		if (sec_channel || vht)
735325151a3SRui Paulo 			return NUM_HOSTAPD_MODES;
736325151a3SRui Paulo 
7375b9c547cSRui Paulo 		*channel = (freq - 56160) / 2160;
738325151a3SRui Paulo 		*op_class = 180;
739325151a3SRui Paulo 
740325151a3SRui Paulo 		return HOSTAPD_MODE_IEEE80211AD;
7415b9c547cSRui Paulo 	}
7425b9c547cSRui Paulo 
743325151a3SRui Paulo 	return NUM_HOSTAPD_MODES;
7445b9c547cSRui Paulo }
7455b9c547cSRui Paulo 
7465b9c547cSRui Paulo 
747325151a3SRui Paulo static const char *const us_op_class_cc[] = {
7485b9c547cSRui Paulo 	"US", "CA", NULL
7495b9c547cSRui Paulo };
7505b9c547cSRui Paulo 
751325151a3SRui Paulo static const char *const eu_op_class_cc[] = {
7525b9c547cSRui Paulo 	"AL", "AM", "AT", "AZ", "BA", "BE", "BG", "BY", "CH", "CY", "CZ", "DE",
7535b9c547cSRui Paulo 	"DK", "EE", "EL", "ES", "FI", "FR", "GE", "HR", "HU", "IE", "IS", "IT",
7545b9c547cSRui Paulo 	"LI", "LT", "LU", "LV", "MD", "ME", "MK", "MT", "NL", "NO", "PL", "PT",
7555b9c547cSRui Paulo 	"RO", "RS", "RU", "SE", "SI", "SK", "TR", "UA", "UK", NULL
7565b9c547cSRui Paulo };
7575b9c547cSRui Paulo 
758325151a3SRui Paulo static const char *const jp_op_class_cc[] = {
7595b9c547cSRui Paulo 	"JP", NULL
7605b9c547cSRui Paulo };
7615b9c547cSRui Paulo 
762325151a3SRui Paulo static const char *const cn_op_class_cc[] = {
763325151a3SRui Paulo 	"CN", NULL
7645b9c547cSRui Paulo };
7655b9c547cSRui Paulo 
7665b9c547cSRui Paulo 
767325151a3SRui Paulo static int country_match(const char *const cc[], const char *const country)
7685b9c547cSRui Paulo {
7695b9c547cSRui Paulo 	int i;
7705b9c547cSRui Paulo 
7715b9c547cSRui Paulo 	if (country == NULL)
7725b9c547cSRui Paulo 		return 0;
7735b9c547cSRui Paulo 	for (i = 0; cc[i]; i++) {
7745b9c547cSRui Paulo 		if (cc[i][0] == country[0] && cc[i][1] == country[1])
7755b9c547cSRui Paulo 			return 1;
7765b9c547cSRui Paulo 	}
7775b9c547cSRui Paulo 
7785b9c547cSRui Paulo 	return 0;
7795b9c547cSRui Paulo }
7805b9c547cSRui Paulo 
7815b9c547cSRui Paulo 
7825b9c547cSRui Paulo static int ieee80211_chan_to_freq_us(u8 op_class, u8 chan)
7835b9c547cSRui Paulo {
7845b9c547cSRui Paulo 	switch (op_class) {
7855b9c547cSRui Paulo 	case 12: /* channels 1..11 */
7865b9c547cSRui Paulo 	case 32: /* channels 1..7; 40 MHz */
7875b9c547cSRui Paulo 	case 33: /* channels 5..11; 40 MHz */
7885b9c547cSRui Paulo 		if (chan < 1 || chan > 11)
7895b9c547cSRui Paulo 			return -1;
7905b9c547cSRui Paulo 		return 2407 + 5 * chan;
7915b9c547cSRui Paulo 	case 1: /* channels 36,40,44,48 */
7925b9c547cSRui Paulo 	case 2: /* channels 52,56,60,64; dfs */
7935b9c547cSRui Paulo 	case 22: /* channels 36,44; 40 MHz */
7945b9c547cSRui Paulo 	case 23: /* channels 52,60; 40 MHz */
7955b9c547cSRui Paulo 	case 27: /* channels 40,48; 40 MHz */
7965b9c547cSRui Paulo 	case 28: /* channels 56,64; 40 MHz */
7975b9c547cSRui Paulo 		if (chan < 36 || chan > 64)
7985b9c547cSRui Paulo 			return -1;
7995b9c547cSRui Paulo 		return 5000 + 5 * chan;
8005b9c547cSRui Paulo 	case 4: /* channels 100-144 */
8015b9c547cSRui Paulo 	case 24: /* channels 100-140; 40 MHz */
8025b9c547cSRui Paulo 		if (chan < 100 || chan > 144)
8035b9c547cSRui Paulo 			return -1;
8045b9c547cSRui Paulo 		return 5000 + 5 * chan;
8055b9c547cSRui Paulo 	case 3: /* channels 149,153,157,161 */
8065b9c547cSRui Paulo 	case 25: /* channels 149,157; 40 MHz */
8075b9c547cSRui Paulo 	case 26: /* channels 149,157; 40 MHz */
8085b9c547cSRui Paulo 	case 30: /* channels 153,161; 40 MHz */
8095b9c547cSRui Paulo 	case 31: /* channels 153,161; 40 MHz */
8105b9c547cSRui Paulo 		if (chan < 149 || chan > 161)
8115b9c547cSRui Paulo 			return -1;
8125b9c547cSRui Paulo 		return 5000 + 5 * chan;
813325151a3SRui Paulo 	case 5: /* channels 149,153,157,161,165 */
814325151a3SRui Paulo 		if (chan < 149 || chan > 165)
815325151a3SRui Paulo 			return -1;
816325151a3SRui Paulo 		return 5000 + 5 * chan;
8175b9c547cSRui Paulo 	case 34: /* 60 GHz band, channels 1..3 */
8185b9c547cSRui Paulo 		if (chan < 1 || chan > 3)
8195b9c547cSRui Paulo 			return -1;
8205b9c547cSRui Paulo 		return 56160 + 2160 * chan;
8215b9c547cSRui Paulo 	}
8225b9c547cSRui Paulo 	return -1;
8235b9c547cSRui Paulo }
8245b9c547cSRui Paulo 
8255b9c547cSRui Paulo 
8265b9c547cSRui Paulo static int ieee80211_chan_to_freq_eu(u8 op_class, u8 chan)
8275b9c547cSRui Paulo {
8285b9c547cSRui Paulo 	switch (op_class) {
8295b9c547cSRui Paulo 	case 4: /* channels 1..13 */
8305b9c547cSRui Paulo 	case 11: /* channels 1..9; 40 MHz */
8315b9c547cSRui Paulo 	case 12: /* channels 5..13; 40 MHz */
8325b9c547cSRui Paulo 		if (chan < 1 || chan > 13)
8335b9c547cSRui Paulo 			return -1;
8345b9c547cSRui Paulo 		return 2407 + 5 * chan;
8355b9c547cSRui Paulo 	case 1: /* channels 36,40,44,48 */
8365b9c547cSRui Paulo 	case 2: /* channels 52,56,60,64; dfs */
8375b9c547cSRui Paulo 	case 5: /* channels 36,44; 40 MHz */
8385b9c547cSRui Paulo 	case 6: /* channels 52,60; 40 MHz */
8395b9c547cSRui Paulo 	case 8: /* channels 40,48; 40 MHz */
8405b9c547cSRui Paulo 	case 9: /* channels 56,64; 40 MHz */
8415b9c547cSRui Paulo 		if (chan < 36 || chan > 64)
8425b9c547cSRui Paulo 			return -1;
8435b9c547cSRui Paulo 		return 5000 + 5 * chan;
8445b9c547cSRui Paulo 	case 3: /* channels 100-140 */
8455b9c547cSRui Paulo 	case 7: /* channels 100-132; 40 MHz */
8465b9c547cSRui Paulo 	case 10: /* channels 104-136; 40 MHz */
8475b9c547cSRui Paulo 	case 16: /* channels 100-140 */
8485b9c547cSRui Paulo 		if (chan < 100 || chan > 140)
8495b9c547cSRui Paulo 			return -1;
8505b9c547cSRui Paulo 		return 5000 + 5 * chan;
8515b9c547cSRui Paulo 	case 17: /* channels 149,153,157,161,165,169 */
8525b9c547cSRui Paulo 		if (chan < 149 || chan > 169)
8535b9c547cSRui Paulo 			return -1;
8545b9c547cSRui Paulo 		return 5000 + 5 * chan;
8555b9c547cSRui Paulo 	case 18: /* 60 GHz band, channels 1..4 */
8565b9c547cSRui Paulo 		if (chan < 1 || chan > 4)
8575b9c547cSRui Paulo 			return -1;
8585b9c547cSRui Paulo 		return 56160 + 2160 * chan;
8595b9c547cSRui Paulo 	}
8605b9c547cSRui Paulo 	return -1;
8615b9c547cSRui Paulo }
8625b9c547cSRui Paulo 
8635b9c547cSRui Paulo 
8645b9c547cSRui Paulo static int ieee80211_chan_to_freq_jp(u8 op_class, u8 chan)
8655b9c547cSRui Paulo {
8665b9c547cSRui Paulo 	switch (op_class) {
8675b9c547cSRui Paulo 	case 30: /* channels 1..13 */
8685b9c547cSRui Paulo 	case 56: /* channels 1..9; 40 MHz */
8695b9c547cSRui Paulo 	case 57: /* channels 5..13; 40 MHz */
8705b9c547cSRui Paulo 		if (chan < 1 || chan > 13)
8715b9c547cSRui Paulo 			return -1;
8725b9c547cSRui Paulo 		return 2407 + 5 * chan;
8735b9c547cSRui Paulo 	case 31: /* channel 14 */
8745b9c547cSRui Paulo 		if (chan != 14)
8755b9c547cSRui Paulo 			return -1;
8765b9c547cSRui Paulo 		return 2414 + 5 * chan;
8775b9c547cSRui Paulo 	case 1: /* channels 34,38,42,46(old) or 36,40,44,48 */
8785b9c547cSRui Paulo 	case 32: /* channels 52,56,60,64 */
8795b9c547cSRui Paulo 	case 33: /* channels 52,56,60,64 */
8805b9c547cSRui Paulo 	case 36: /* channels 36,44; 40 MHz */
8815b9c547cSRui Paulo 	case 37: /* channels 52,60; 40 MHz */
8825b9c547cSRui Paulo 	case 38: /* channels 52,60; 40 MHz */
8835b9c547cSRui Paulo 	case 41: /* channels 40,48; 40 MHz */
8845b9c547cSRui Paulo 	case 42: /* channels 56,64; 40 MHz */
8855b9c547cSRui Paulo 	case 43: /* channels 56,64; 40 MHz */
8865b9c547cSRui Paulo 		if (chan < 34 || chan > 64)
8875b9c547cSRui Paulo 			return -1;
8885b9c547cSRui Paulo 		return 5000 + 5 * chan;
8895b9c547cSRui Paulo 	case 34: /* channels 100-140 */
8905b9c547cSRui Paulo 	case 35: /* channels 100-140 */
8915b9c547cSRui Paulo 	case 39: /* channels 100-132; 40 MHz */
8925b9c547cSRui Paulo 	case 40: /* channels 100-132; 40 MHz */
8935b9c547cSRui Paulo 	case 44: /* channels 104-136; 40 MHz */
8945b9c547cSRui Paulo 	case 45: /* channels 104-136; 40 MHz */
8955b9c547cSRui Paulo 	case 58: /* channels 100-140 */
8965b9c547cSRui Paulo 		if (chan < 100 || chan > 140)
8975b9c547cSRui Paulo 			return -1;
8985b9c547cSRui Paulo 		return 5000 + 5 * chan;
8995b9c547cSRui Paulo 	case 59: /* 60 GHz band, channels 1..4 */
9005b9c547cSRui Paulo 		if (chan < 1 || chan > 3)
9015b9c547cSRui Paulo 			return -1;
9025b9c547cSRui Paulo 		return 56160 + 2160 * chan;
9035b9c547cSRui Paulo 	}
9045b9c547cSRui Paulo 	return -1;
9055b9c547cSRui Paulo }
9065b9c547cSRui Paulo 
9075b9c547cSRui Paulo 
9085b9c547cSRui Paulo static int ieee80211_chan_to_freq_cn(u8 op_class, u8 chan)
9095b9c547cSRui Paulo {
9105b9c547cSRui Paulo 	switch (op_class) {
9115b9c547cSRui Paulo 	case 7: /* channels 1..13 */
9125b9c547cSRui Paulo 	case 8: /* channels 1..9; 40 MHz */
9135b9c547cSRui Paulo 	case 9: /* channels 5..13; 40 MHz */
9145b9c547cSRui Paulo 		if (chan < 1 || chan > 13)
9155b9c547cSRui Paulo 			return -1;
9165b9c547cSRui Paulo 		return 2407 + 5 * chan;
9175b9c547cSRui Paulo 	case 1: /* channels 36,40,44,48 */
9185b9c547cSRui Paulo 	case 2: /* channels 52,56,60,64; dfs */
9195b9c547cSRui Paulo 	case 4: /* channels 36,44; 40 MHz */
9205b9c547cSRui Paulo 	case 5: /* channels 52,60; 40 MHz */
9215b9c547cSRui Paulo 		if (chan < 36 || chan > 64)
9225b9c547cSRui Paulo 			return -1;
9235b9c547cSRui Paulo 		return 5000 + 5 * chan;
9245b9c547cSRui Paulo 	case 3: /* channels 149,153,157,161,165 */
9255b9c547cSRui Paulo 	case 6: /* channels 149,157; 40 MHz */
9265b9c547cSRui Paulo 		if (chan < 149 || chan > 165)
9275b9c547cSRui Paulo 			return -1;
9285b9c547cSRui Paulo 		return 5000 + 5 * chan;
9295b9c547cSRui Paulo 	}
9305b9c547cSRui Paulo 	return -1;
9315b9c547cSRui Paulo }
9325b9c547cSRui Paulo 
9335b9c547cSRui Paulo 
9345b9c547cSRui Paulo static int ieee80211_chan_to_freq_global(u8 op_class, u8 chan)
9355b9c547cSRui Paulo {
9365b9c547cSRui Paulo 	/* Table E-4 in IEEE Std 802.11-2012 - Global operating classes */
9375b9c547cSRui Paulo 	switch (op_class) {
9385b9c547cSRui Paulo 	case 81:
9395b9c547cSRui Paulo 		/* channels 1..13 */
9405b9c547cSRui Paulo 		if (chan < 1 || chan > 13)
9415b9c547cSRui Paulo 			return -1;
9425b9c547cSRui Paulo 		return 2407 + 5 * chan;
9435b9c547cSRui Paulo 	case 82:
9445b9c547cSRui Paulo 		/* channel 14 */
9455b9c547cSRui Paulo 		if (chan != 14)
9465b9c547cSRui Paulo 			return -1;
9475b9c547cSRui Paulo 		return 2414 + 5 * chan;
9485b9c547cSRui Paulo 	case 83: /* channels 1..9; 40 MHz */
9495b9c547cSRui Paulo 	case 84: /* channels 5..13; 40 MHz */
9505b9c547cSRui Paulo 		if (chan < 1 || chan > 13)
9515b9c547cSRui Paulo 			return -1;
9525b9c547cSRui Paulo 		return 2407 + 5 * chan;
9535b9c547cSRui Paulo 	case 115: /* channels 36,40,44,48; indoor only */
9545b9c547cSRui Paulo 	case 116: /* channels 36,44; 40 MHz; indoor only */
9555b9c547cSRui Paulo 	case 117: /* channels 40,48; 40 MHz; indoor only */
9565b9c547cSRui Paulo 	case 118: /* channels 52,56,60,64; dfs */
9575b9c547cSRui Paulo 	case 119: /* channels 52,60; 40 MHz; dfs */
9585b9c547cSRui Paulo 	case 120: /* channels 56,64; 40 MHz; dfs */
9595b9c547cSRui Paulo 		if (chan < 36 || chan > 64)
9605b9c547cSRui Paulo 			return -1;
9615b9c547cSRui Paulo 		return 5000 + 5 * chan;
9625b9c547cSRui Paulo 	case 121: /* channels 100-140 */
9635b9c547cSRui Paulo 	case 122: /* channels 100-142; 40 MHz */
9645b9c547cSRui Paulo 	case 123: /* channels 104-136; 40 MHz */
9655b9c547cSRui Paulo 		if (chan < 100 || chan > 140)
9665b9c547cSRui Paulo 			return -1;
9675b9c547cSRui Paulo 		return 5000 + 5 * chan;
9685b9c547cSRui Paulo 	case 124: /* channels 149,153,157,161 */
9695b9c547cSRui Paulo 	case 126: /* channels 149,157; 40 MHz */
9705b9c547cSRui Paulo 	case 127: /* channels 153,161; 40 MHz */
9715b9c547cSRui Paulo 		if (chan < 149 || chan > 161)
9725b9c547cSRui Paulo 			return -1;
9735b9c547cSRui Paulo 		return 5000 + 5 * chan;
974325151a3SRui Paulo 	case 125: /* channels 149,153,157,161,165,169 */
975325151a3SRui Paulo 		if (chan < 149 || chan > 169)
976325151a3SRui Paulo 			return -1;
977325151a3SRui Paulo 		return 5000 + 5 * chan;
9785b9c547cSRui Paulo 	case 128: /* center freqs 42, 58, 106, 122, 138, 155; 80 MHz */
9795b9c547cSRui Paulo 	case 130: /* center freqs 42, 58, 106, 122, 138, 155; 80 MHz */
9805b9c547cSRui Paulo 		if (chan < 36 || chan > 161)
9815b9c547cSRui Paulo 			return -1;
9825b9c547cSRui Paulo 		return 5000 + 5 * chan;
9835b9c547cSRui Paulo 	case 129: /* center freqs 50, 114; 160 MHz */
9845b9c547cSRui Paulo 		if (chan < 50 || chan > 114)
9855b9c547cSRui Paulo 			return -1;
9865b9c547cSRui Paulo 		return 5000 + 5 * chan;
9875b9c547cSRui Paulo 	case 180: /* 60 GHz band, channels 1..4 */
9885b9c547cSRui Paulo 		if (chan < 1 || chan > 4)
9895b9c547cSRui Paulo 			return -1;
9905b9c547cSRui Paulo 		return 56160 + 2160 * chan;
9915b9c547cSRui Paulo 	}
9925b9c547cSRui Paulo 	return -1;
9935b9c547cSRui Paulo }
9945b9c547cSRui Paulo 
9955b9c547cSRui Paulo /**
9965b9c547cSRui Paulo  * ieee80211_chan_to_freq - Convert channel info to frequency
9975b9c547cSRui Paulo  * @country: Country code, if known; otherwise, global operating class is used
9985b9c547cSRui Paulo  * @op_class: Operating class
9995b9c547cSRui Paulo  * @chan: Channel number
10005b9c547cSRui Paulo  * Returns: Frequency in MHz or -1 if the specified channel is unknown
10015b9c547cSRui Paulo  */
10025b9c547cSRui Paulo int ieee80211_chan_to_freq(const char *country, u8 op_class, u8 chan)
10035b9c547cSRui Paulo {
10045b9c547cSRui Paulo 	int freq;
10055b9c547cSRui Paulo 
10065b9c547cSRui Paulo 	if (country_match(us_op_class_cc, country)) {
10075b9c547cSRui Paulo 		freq = ieee80211_chan_to_freq_us(op_class, chan);
10085b9c547cSRui Paulo 		if (freq > 0)
10095b9c547cSRui Paulo 			return freq;
10105b9c547cSRui Paulo 	}
10115b9c547cSRui Paulo 
10125b9c547cSRui Paulo 	if (country_match(eu_op_class_cc, country)) {
10135b9c547cSRui Paulo 		freq = ieee80211_chan_to_freq_eu(op_class, chan);
10145b9c547cSRui Paulo 		if (freq > 0)
10155b9c547cSRui Paulo 			return freq;
10165b9c547cSRui Paulo 	}
10175b9c547cSRui Paulo 
10185b9c547cSRui Paulo 	if (country_match(jp_op_class_cc, country)) {
10195b9c547cSRui Paulo 		freq = ieee80211_chan_to_freq_jp(op_class, chan);
10205b9c547cSRui Paulo 		if (freq > 0)
10215b9c547cSRui Paulo 			return freq;
10225b9c547cSRui Paulo 	}
10235b9c547cSRui Paulo 
10245b9c547cSRui Paulo 	if (country_match(cn_op_class_cc, country)) {
10255b9c547cSRui Paulo 		freq = ieee80211_chan_to_freq_cn(op_class, chan);
10265b9c547cSRui Paulo 		if (freq > 0)
10275b9c547cSRui Paulo 			return freq;
10285b9c547cSRui Paulo 	}
10295b9c547cSRui Paulo 
10305b9c547cSRui Paulo 	return ieee80211_chan_to_freq_global(op_class, chan);
10315b9c547cSRui Paulo }
10325b9c547cSRui Paulo 
10335b9c547cSRui Paulo 
10345b9c547cSRui Paulo int ieee80211_is_dfs(int freq)
10355b9c547cSRui Paulo {
10365b9c547cSRui Paulo 	/* TODO: this could be more accurate to better cover all domains */
10375b9c547cSRui Paulo 	return (freq >= 5260 && freq <= 5320) || (freq >= 5500 && freq <= 5700);
10385b9c547cSRui Paulo }
10395b9c547cSRui Paulo 
10405b9c547cSRui Paulo 
10415b9c547cSRui Paulo static int is_11b(u8 rate)
10425b9c547cSRui Paulo {
10435b9c547cSRui Paulo 	return rate == 0x02 || rate == 0x04 || rate == 0x0b || rate == 0x16;
10445b9c547cSRui Paulo }
10455b9c547cSRui Paulo 
10465b9c547cSRui Paulo 
10475b9c547cSRui Paulo int supp_rates_11b_only(struct ieee802_11_elems *elems)
10485b9c547cSRui Paulo {
10495b9c547cSRui Paulo 	int num_11b = 0, num_others = 0;
10505b9c547cSRui Paulo 	int i;
10515b9c547cSRui Paulo 
10525b9c547cSRui Paulo 	if (elems->supp_rates == NULL && elems->ext_supp_rates == NULL)
10535b9c547cSRui Paulo 		return 0;
10545b9c547cSRui Paulo 
10555b9c547cSRui Paulo 	for (i = 0; elems->supp_rates && i < elems->supp_rates_len; i++) {
10565b9c547cSRui Paulo 		if (is_11b(elems->supp_rates[i]))
10575b9c547cSRui Paulo 			num_11b++;
10585b9c547cSRui Paulo 		else
10595b9c547cSRui Paulo 			num_others++;
10605b9c547cSRui Paulo 	}
10615b9c547cSRui Paulo 
10625b9c547cSRui Paulo 	for (i = 0; elems->ext_supp_rates && i < elems->ext_supp_rates_len;
10635b9c547cSRui Paulo 	     i++) {
10645b9c547cSRui Paulo 		if (is_11b(elems->ext_supp_rates[i]))
10655b9c547cSRui Paulo 			num_11b++;
10665b9c547cSRui Paulo 		else
10675b9c547cSRui Paulo 			num_others++;
10685b9c547cSRui Paulo 	}
10695b9c547cSRui Paulo 
10705b9c547cSRui Paulo 	return num_11b > 0 && num_others == 0;
10715b9c547cSRui Paulo }
10725b9c547cSRui Paulo 
10735b9c547cSRui Paulo 
10745b9c547cSRui Paulo const char * fc2str(u16 fc)
10755b9c547cSRui Paulo {
10765b9c547cSRui Paulo 	u16 stype = WLAN_FC_GET_STYPE(fc);
10775b9c547cSRui Paulo #define C2S(x) case x: return #x;
10785b9c547cSRui Paulo 
10795b9c547cSRui Paulo 	switch (WLAN_FC_GET_TYPE(fc)) {
10805b9c547cSRui Paulo 	case WLAN_FC_TYPE_MGMT:
10815b9c547cSRui Paulo 		switch (stype) {
10825b9c547cSRui Paulo 		C2S(WLAN_FC_STYPE_ASSOC_REQ)
10835b9c547cSRui Paulo 		C2S(WLAN_FC_STYPE_ASSOC_RESP)
10845b9c547cSRui Paulo 		C2S(WLAN_FC_STYPE_REASSOC_REQ)
10855b9c547cSRui Paulo 		C2S(WLAN_FC_STYPE_REASSOC_RESP)
10865b9c547cSRui Paulo 		C2S(WLAN_FC_STYPE_PROBE_REQ)
10875b9c547cSRui Paulo 		C2S(WLAN_FC_STYPE_PROBE_RESP)
10885b9c547cSRui Paulo 		C2S(WLAN_FC_STYPE_BEACON)
10895b9c547cSRui Paulo 		C2S(WLAN_FC_STYPE_ATIM)
10905b9c547cSRui Paulo 		C2S(WLAN_FC_STYPE_DISASSOC)
10915b9c547cSRui Paulo 		C2S(WLAN_FC_STYPE_AUTH)
10925b9c547cSRui Paulo 		C2S(WLAN_FC_STYPE_DEAUTH)
10935b9c547cSRui Paulo 		C2S(WLAN_FC_STYPE_ACTION)
10945b9c547cSRui Paulo 		}
10955b9c547cSRui Paulo 		break;
10965b9c547cSRui Paulo 	case WLAN_FC_TYPE_CTRL:
10975b9c547cSRui Paulo 		switch (stype) {
10985b9c547cSRui Paulo 		C2S(WLAN_FC_STYPE_PSPOLL)
10995b9c547cSRui Paulo 		C2S(WLAN_FC_STYPE_RTS)
11005b9c547cSRui Paulo 		C2S(WLAN_FC_STYPE_CTS)
11015b9c547cSRui Paulo 		C2S(WLAN_FC_STYPE_ACK)
11025b9c547cSRui Paulo 		C2S(WLAN_FC_STYPE_CFEND)
11035b9c547cSRui Paulo 		C2S(WLAN_FC_STYPE_CFENDACK)
11045b9c547cSRui Paulo 		}
11055b9c547cSRui Paulo 		break;
11065b9c547cSRui Paulo 	case WLAN_FC_TYPE_DATA:
11075b9c547cSRui Paulo 		switch (stype) {
11085b9c547cSRui Paulo 		C2S(WLAN_FC_STYPE_DATA)
11095b9c547cSRui Paulo 		C2S(WLAN_FC_STYPE_DATA_CFACK)
11105b9c547cSRui Paulo 		C2S(WLAN_FC_STYPE_DATA_CFPOLL)
11115b9c547cSRui Paulo 		C2S(WLAN_FC_STYPE_DATA_CFACKPOLL)
11125b9c547cSRui Paulo 		C2S(WLAN_FC_STYPE_NULLFUNC)
11135b9c547cSRui Paulo 		C2S(WLAN_FC_STYPE_CFACK)
11145b9c547cSRui Paulo 		C2S(WLAN_FC_STYPE_CFPOLL)
11155b9c547cSRui Paulo 		C2S(WLAN_FC_STYPE_CFACKPOLL)
11165b9c547cSRui Paulo 		C2S(WLAN_FC_STYPE_QOS_DATA)
11175b9c547cSRui Paulo 		C2S(WLAN_FC_STYPE_QOS_DATA_CFACK)
11185b9c547cSRui Paulo 		C2S(WLAN_FC_STYPE_QOS_DATA_CFPOLL)
11195b9c547cSRui Paulo 		C2S(WLAN_FC_STYPE_QOS_DATA_CFACKPOLL)
11205b9c547cSRui Paulo 		C2S(WLAN_FC_STYPE_QOS_NULL)
11215b9c547cSRui Paulo 		C2S(WLAN_FC_STYPE_QOS_CFPOLL)
11225b9c547cSRui Paulo 		C2S(WLAN_FC_STYPE_QOS_CFACKPOLL)
11235b9c547cSRui Paulo 		}
11245b9c547cSRui Paulo 		break;
11255b9c547cSRui Paulo 	}
11265b9c547cSRui Paulo 	return "WLAN_FC_TYPE_UNKNOWN";
11275b9c547cSRui Paulo #undef C2S
11285b9c547cSRui Paulo }
1129325151a3SRui Paulo 
1130325151a3SRui Paulo 
1131325151a3SRui Paulo int mb_ies_info_by_ies(struct mb_ies_info *info, const u8 *ies_buf,
1132325151a3SRui Paulo 		       size_t ies_len)
1133325151a3SRui Paulo {
1134325151a3SRui Paulo 	os_memset(info, 0, sizeof(*info));
1135325151a3SRui Paulo 
1136325151a3SRui Paulo 	while (ies_buf && ies_len >= 2 &&
1137325151a3SRui Paulo 	       info->nof_ies < MAX_NOF_MB_IES_SUPPORTED) {
1138325151a3SRui Paulo 		size_t len = 2 + ies_buf[1];
1139325151a3SRui Paulo 
1140325151a3SRui Paulo 		if (len > ies_len) {
1141325151a3SRui Paulo 			wpa_hexdump(MSG_DEBUG, "Truncated IEs",
1142325151a3SRui Paulo 				    ies_buf, ies_len);
1143325151a3SRui Paulo 			return -1;
1144325151a3SRui Paulo 		}
1145325151a3SRui Paulo 
1146325151a3SRui Paulo 		if (ies_buf[0] == WLAN_EID_MULTI_BAND) {
1147325151a3SRui Paulo 			wpa_printf(MSG_DEBUG, "MB IE of %zu bytes found", len);
1148325151a3SRui Paulo 			info->ies[info->nof_ies].ie = ies_buf + 2;
1149325151a3SRui Paulo 			info->ies[info->nof_ies].ie_len = ies_buf[1];
1150325151a3SRui Paulo 			info->nof_ies++;
1151325151a3SRui Paulo 		}
1152325151a3SRui Paulo 
1153325151a3SRui Paulo 		ies_len -= len;
1154325151a3SRui Paulo 		ies_buf += len;
1155325151a3SRui Paulo 	}
1156325151a3SRui Paulo 
1157325151a3SRui Paulo 	return 0;
1158325151a3SRui Paulo }
1159325151a3SRui Paulo 
1160325151a3SRui Paulo 
1161325151a3SRui Paulo struct wpabuf * mb_ies_by_info(struct mb_ies_info *info)
1162325151a3SRui Paulo {
1163325151a3SRui Paulo 	struct wpabuf *mb_ies = NULL;
1164325151a3SRui Paulo 
1165325151a3SRui Paulo 	WPA_ASSERT(info != NULL);
1166325151a3SRui Paulo 
1167325151a3SRui Paulo 	if (info->nof_ies) {
1168325151a3SRui Paulo 		u8 i;
1169325151a3SRui Paulo 		size_t mb_ies_size = 0;
1170325151a3SRui Paulo 
1171325151a3SRui Paulo 		for (i = 0; i < info->nof_ies; i++)
1172325151a3SRui Paulo 			mb_ies_size += 2 + info->ies[i].ie_len;
1173325151a3SRui Paulo 
1174325151a3SRui Paulo 		mb_ies = wpabuf_alloc(mb_ies_size);
1175325151a3SRui Paulo 		if (mb_ies) {
1176325151a3SRui Paulo 			for (i = 0; i < info->nof_ies; i++) {
1177325151a3SRui Paulo 				wpabuf_put_u8(mb_ies, WLAN_EID_MULTI_BAND);
1178325151a3SRui Paulo 				wpabuf_put_u8(mb_ies, info->ies[i].ie_len);
1179325151a3SRui Paulo 				wpabuf_put_data(mb_ies,
1180325151a3SRui Paulo 						info->ies[i].ie,
1181325151a3SRui Paulo 						info->ies[i].ie_len);
1182325151a3SRui Paulo 			}
1183325151a3SRui Paulo 		}
1184325151a3SRui Paulo 	}
1185325151a3SRui Paulo 
1186325151a3SRui Paulo 	return mb_ies;
1187325151a3SRui Paulo }
1188*780fb4a2SCy Schubert 
1189*780fb4a2SCy Schubert 
1190*780fb4a2SCy Schubert const struct oper_class_map global_op_class[] = {
1191*780fb4a2SCy Schubert 	{ HOSTAPD_MODE_IEEE80211G, 81, 1, 13, 1, BW20, P2P_SUPP },
1192*780fb4a2SCy Schubert 	{ HOSTAPD_MODE_IEEE80211G, 82, 14, 14, 1, BW20, NO_P2P_SUPP },
1193*780fb4a2SCy Schubert 
1194*780fb4a2SCy Schubert 	/* Do not enable HT40 on 2.4 GHz for P2P use for now */
1195*780fb4a2SCy Schubert 	{ HOSTAPD_MODE_IEEE80211G, 83, 1, 9, 1, BW40PLUS, NO_P2P_SUPP },
1196*780fb4a2SCy Schubert 	{ HOSTAPD_MODE_IEEE80211G, 84, 5, 13, 1, BW40MINUS, NO_P2P_SUPP },
1197*780fb4a2SCy Schubert 
1198*780fb4a2SCy Schubert 	{ HOSTAPD_MODE_IEEE80211A, 115, 36, 48, 4, BW20, P2P_SUPP },
1199*780fb4a2SCy Schubert 	{ HOSTAPD_MODE_IEEE80211A, 116, 36, 44, 8, BW40PLUS, P2P_SUPP },
1200*780fb4a2SCy Schubert 	{ HOSTAPD_MODE_IEEE80211A, 117, 40, 48, 8, BW40MINUS, P2P_SUPP },
1201*780fb4a2SCy Schubert 	{ HOSTAPD_MODE_IEEE80211A, 118, 52, 64, 4, BW20, NO_P2P_SUPP },
1202*780fb4a2SCy Schubert 	{ HOSTAPD_MODE_IEEE80211A, 119, 52, 60, 8, BW40PLUS, NO_P2P_SUPP },
1203*780fb4a2SCy Schubert 	{ HOSTAPD_MODE_IEEE80211A, 120, 56, 64, 8, BW40MINUS, NO_P2P_SUPP },
1204*780fb4a2SCy Schubert 	{ HOSTAPD_MODE_IEEE80211A, 121, 100, 140, 4, BW20, NO_P2P_SUPP },
1205*780fb4a2SCy Schubert 	{ HOSTAPD_MODE_IEEE80211A, 122, 100, 132, 8, BW40PLUS, NO_P2P_SUPP },
1206*780fb4a2SCy Schubert 	{ HOSTAPD_MODE_IEEE80211A, 123, 104, 136, 8, BW40MINUS, NO_P2P_SUPP },
1207*780fb4a2SCy Schubert 	{ HOSTAPD_MODE_IEEE80211A, 124, 149, 161, 4, BW20, P2P_SUPP },
1208*780fb4a2SCy Schubert 	{ HOSTAPD_MODE_IEEE80211A, 125, 149, 169, 4, BW20, P2P_SUPP },
1209*780fb4a2SCy Schubert 	{ HOSTAPD_MODE_IEEE80211A, 126, 149, 157, 8, BW40PLUS, P2P_SUPP },
1210*780fb4a2SCy Schubert 	{ HOSTAPD_MODE_IEEE80211A, 127, 153, 161, 8, BW40MINUS, P2P_SUPP },
1211*780fb4a2SCy Schubert 
1212*780fb4a2SCy Schubert 	/*
1213*780fb4a2SCy Schubert 	 * IEEE P802.11ac/D7.0 Table E-4 actually talks about channel center
1214*780fb4a2SCy Schubert 	 * frequency index 42, 58, 106, 122, 138, 155 with channel spacing of
1215*780fb4a2SCy Schubert 	 * 80 MHz, but currently use the following definition for simplicity
1216*780fb4a2SCy Schubert 	 * (these center frequencies are not actual channels, which makes
1217*780fb4a2SCy Schubert 	 * wpas_p2p_allow_channel() fail). wpas_p2p_verify_80mhz() should take
1218*780fb4a2SCy Schubert 	 * care of removing invalid channels.
1219*780fb4a2SCy Schubert 	 */
1220*780fb4a2SCy Schubert 	{ HOSTAPD_MODE_IEEE80211A, 128, 36, 161, 4, BW80, P2P_SUPP },
1221*780fb4a2SCy Schubert 	{ HOSTAPD_MODE_IEEE80211A, 129, 50, 114, 16, BW160, P2P_SUPP },
1222*780fb4a2SCy Schubert 	{ HOSTAPD_MODE_IEEE80211A, 130, 36, 161, 4, BW80P80, P2P_SUPP },
1223*780fb4a2SCy Schubert 	{ HOSTAPD_MODE_IEEE80211AD, 180, 1, 4, 1, BW2160, P2P_SUPP },
1224*780fb4a2SCy Schubert 	{ -1, 0, 0, 0, 0, BW20, NO_P2P_SUPP }
1225*780fb4a2SCy Schubert };
1226*780fb4a2SCy Schubert 
1227*780fb4a2SCy Schubert 
1228*780fb4a2SCy Schubert static enum phy_type ieee80211_phy_type_by_freq(int freq)
1229*780fb4a2SCy Schubert {
1230*780fb4a2SCy Schubert 	enum hostapd_hw_mode hw_mode;
1231*780fb4a2SCy Schubert 	u8 channel;
1232*780fb4a2SCy Schubert 
1233*780fb4a2SCy Schubert 	hw_mode = ieee80211_freq_to_chan(freq, &channel);
1234*780fb4a2SCy Schubert 
1235*780fb4a2SCy Schubert 	switch (hw_mode) {
1236*780fb4a2SCy Schubert 	case HOSTAPD_MODE_IEEE80211A:
1237*780fb4a2SCy Schubert 		return PHY_TYPE_OFDM;
1238*780fb4a2SCy Schubert 	case HOSTAPD_MODE_IEEE80211B:
1239*780fb4a2SCy Schubert 		return PHY_TYPE_HRDSSS;
1240*780fb4a2SCy Schubert 	case HOSTAPD_MODE_IEEE80211G:
1241*780fb4a2SCy Schubert 		return PHY_TYPE_ERP;
1242*780fb4a2SCy Schubert 	case HOSTAPD_MODE_IEEE80211AD:
1243*780fb4a2SCy Schubert 		return PHY_TYPE_DMG;
1244*780fb4a2SCy Schubert 	default:
1245*780fb4a2SCy Schubert 		return PHY_TYPE_UNSPECIFIED;
1246*780fb4a2SCy Schubert 	};
1247*780fb4a2SCy Schubert }
1248*780fb4a2SCy Schubert 
1249*780fb4a2SCy Schubert 
1250*780fb4a2SCy Schubert /* ieee80211_get_phy_type - Derive the phy type by freq and bandwidth */
1251*780fb4a2SCy Schubert enum phy_type ieee80211_get_phy_type(int freq, int ht, int vht)
1252*780fb4a2SCy Schubert {
1253*780fb4a2SCy Schubert 	if (vht)
1254*780fb4a2SCy Schubert 		return PHY_TYPE_VHT;
1255*780fb4a2SCy Schubert 	if (ht)
1256*780fb4a2SCy Schubert 		return PHY_TYPE_HT;
1257*780fb4a2SCy Schubert 
1258*780fb4a2SCy Schubert 	return ieee80211_phy_type_by_freq(freq);
1259*780fb4a2SCy Schubert }
1260*780fb4a2SCy Schubert 
1261*780fb4a2SCy Schubert 
1262*780fb4a2SCy Schubert size_t global_op_class_size = ARRAY_SIZE(global_op_class);
1263*780fb4a2SCy Schubert 
1264*780fb4a2SCy Schubert 
1265*780fb4a2SCy Schubert /**
1266*780fb4a2SCy Schubert  * get_ie - Fetch a specified information element from IEs buffer
1267*780fb4a2SCy Schubert  * @ies: Information elements buffer
1268*780fb4a2SCy Schubert  * @len: Information elements buffer length
1269*780fb4a2SCy Schubert  * @eid: Information element identifier (WLAN_EID_*)
1270*780fb4a2SCy Schubert  * Returns: Pointer to the information element (id field) or %NULL if not found
1271*780fb4a2SCy Schubert  *
1272*780fb4a2SCy Schubert  * This function returns the first matching information element in the IEs
1273*780fb4a2SCy Schubert  * buffer or %NULL in case the element is not found.
1274*780fb4a2SCy Schubert  */
1275*780fb4a2SCy Schubert const u8 * get_ie(const u8 *ies, size_t len, u8 eid)
1276*780fb4a2SCy Schubert {
1277*780fb4a2SCy Schubert 	const u8 *end;
1278*780fb4a2SCy Schubert 
1279*780fb4a2SCy Schubert 	if (!ies)
1280*780fb4a2SCy Schubert 		return NULL;
1281*780fb4a2SCy Schubert 
1282*780fb4a2SCy Schubert 	end = ies + len;
1283*780fb4a2SCy Schubert 
1284*780fb4a2SCy Schubert 	while (end - ies > 1) {
1285*780fb4a2SCy Schubert 		if (2 + ies[1] > end - ies)
1286*780fb4a2SCy Schubert 			break;
1287*780fb4a2SCy Schubert 
1288*780fb4a2SCy Schubert 		if (ies[0] == eid)
1289*780fb4a2SCy Schubert 			return ies;
1290*780fb4a2SCy Schubert 
1291*780fb4a2SCy Schubert 		ies += 2 + ies[1];
1292*780fb4a2SCy Schubert 	}
1293*780fb4a2SCy Schubert 
1294*780fb4a2SCy Schubert 	return NULL;
1295*780fb4a2SCy Schubert }
1296*780fb4a2SCy Schubert 
1297*780fb4a2SCy Schubert 
1298*780fb4a2SCy Schubert size_t mbo_add_ie(u8 *buf, size_t len, const u8 *attr, size_t attr_len)
1299*780fb4a2SCy Schubert {
1300*780fb4a2SCy Schubert 	/*
1301*780fb4a2SCy Schubert 	 * MBO IE requires 6 bytes without the attributes: EID (1), length (1),
1302*780fb4a2SCy Schubert 	 * OUI (3), OUI type (1).
1303*780fb4a2SCy Schubert 	 */
1304*780fb4a2SCy Schubert 	if (len < 6 + attr_len) {
1305*780fb4a2SCy Schubert 		wpa_printf(MSG_DEBUG,
1306*780fb4a2SCy Schubert 			   "MBO: Not enough room in buffer for MBO IE: buf len = %zu, attr_len = %zu",
1307*780fb4a2SCy Schubert 			   len, attr_len);
1308*780fb4a2SCy Schubert 		return 0;
1309*780fb4a2SCy Schubert 	}
1310*780fb4a2SCy Schubert 
1311*780fb4a2SCy Schubert 	*buf++ = WLAN_EID_VENDOR_SPECIFIC;
1312*780fb4a2SCy Schubert 	*buf++ = attr_len + 4;
1313*780fb4a2SCy Schubert 	WPA_PUT_BE24(buf, OUI_WFA);
1314*780fb4a2SCy Schubert 	buf += 3;
1315*780fb4a2SCy Schubert 	*buf++ = MBO_OUI_TYPE;
1316*780fb4a2SCy Schubert 	os_memcpy(buf, attr, attr_len);
1317*780fb4a2SCy Schubert 
1318*780fb4a2SCy Schubert 	return 6 + attr_len;
1319*780fb4a2SCy Schubert }
1320