xref: /freebsd/contrib/wpa/src/common/ieee802_11_common.c (revision 4bc523382c7e72183c32be2c3aedecc1f5e844dd)
139beb93cSSam Leffler /*
239beb93cSSam Leffler  * IEEE 802.11 Common routines
3*4bc52338SCy Schubert  * Copyright (c) 2002-2019, 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"
1485732ac8SCy Schubert #include "drivers/driver.h"
15325151a3SRui Paulo #include "qca-vendor.h"
1639beb93cSSam Leffler #include "ieee802_11_defs.h"
1739beb93cSSam Leffler #include "ieee802_11_common.h"
1839beb93cSSam Leffler 
1939beb93cSSam Leffler 
20e28a4053SRui Paulo static int ieee802_11_parse_vendor_specific(const u8 *pos, size_t elen,
2139beb93cSSam Leffler 					    struct ieee802_11_elems *elems,
2239beb93cSSam Leffler 					    int show_errors)
2339beb93cSSam Leffler {
2439beb93cSSam Leffler 	unsigned int oui;
2539beb93cSSam Leffler 
2639beb93cSSam Leffler 	/* first 3 bytes in vendor specific information element are the IEEE
2739beb93cSSam Leffler 	 * OUI of the vendor. The following byte is used a vendor specific
2839beb93cSSam Leffler 	 * sub-type. */
2939beb93cSSam Leffler 	if (elen < 4) {
3039beb93cSSam Leffler 		if (show_errors) {
3139beb93cSSam Leffler 			wpa_printf(MSG_MSGDUMP, "short vendor specific "
3239beb93cSSam Leffler 				   "information element ignored (len=%lu)",
3339beb93cSSam Leffler 				   (unsigned long) elen);
3439beb93cSSam Leffler 		}
3539beb93cSSam Leffler 		return -1;
3639beb93cSSam Leffler 	}
3739beb93cSSam Leffler 
3839beb93cSSam Leffler 	oui = WPA_GET_BE24(pos);
3939beb93cSSam Leffler 	switch (oui) {
4039beb93cSSam Leffler 	case OUI_MICROSOFT:
4139beb93cSSam Leffler 		/* Microsoft/Wi-Fi information elements are further typed and
4239beb93cSSam Leffler 		 * subtyped */
4339beb93cSSam Leffler 		switch (pos[3]) {
4439beb93cSSam Leffler 		case 1:
4539beb93cSSam Leffler 			/* Microsoft OUI (00:50:F2) with OUI Type 1:
4639beb93cSSam Leffler 			 * real WPA information element */
4739beb93cSSam Leffler 			elems->wpa_ie = pos;
4839beb93cSSam Leffler 			elems->wpa_ie_len = elen;
4939beb93cSSam Leffler 			break;
503157ba21SRui Paulo 		case WMM_OUI_TYPE:
513157ba21SRui Paulo 			/* WMM information element */
5239beb93cSSam Leffler 			if (elen < 5) {
533157ba21SRui Paulo 				wpa_printf(MSG_MSGDUMP, "short WMM "
5439beb93cSSam Leffler 					   "information element ignored "
5539beb93cSSam Leffler 					   "(len=%lu)",
5639beb93cSSam Leffler 					   (unsigned long) elen);
5739beb93cSSam Leffler 				return -1;
5839beb93cSSam Leffler 			}
5939beb93cSSam Leffler 			switch (pos[4]) {
603157ba21SRui Paulo 			case WMM_OUI_SUBTYPE_INFORMATION_ELEMENT:
613157ba21SRui Paulo 			case WMM_OUI_SUBTYPE_PARAMETER_ELEMENT:
623157ba21SRui Paulo 				/*
633157ba21SRui Paulo 				 * Share same pointer since only one of these
643157ba21SRui Paulo 				 * is used and they start with same data.
653157ba21SRui Paulo 				 * Length field can be used to distinguish the
663157ba21SRui Paulo 				 * IEs.
673157ba21SRui Paulo 				 */
683157ba21SRui Paulo 				elems->wmm = pos;
693157ba21SRui Paulo 				elems->wmm_len = elen;
7039beb93cSSam Leffler 				break;
713157ba21SRui Paulo 			case WMM_OUI_SUBTYPE_TSPEC_ELEMENT:
723157ba21SRui Paulo 				elems->wmm_tspec = pos;
733157ba21SRui Paulo 				elems->wmm_tspec_len = elen;
7439beb93cSSam Leffler 				break;
7539beb93cSSam Leffler 			default:
76f05cddf9SRui Paulo 				wpa_printf(MSG_EXCESSIVE, "unknown WMM "
7739beb93cSSam Leffler 					   "information element ignored "
7839beb93cSSam Leffler 					   "(subtype=%d len=%lu)",
7939beb93cSSam Leffler 					   pos[4], (unsigned long) elen);
8039beb93cSSam Leffler 				return -1;
8139beb93cSSam Leffler 			}
8239beb93cSSam Leffler 			break;
8339beb93cSSam Leffler 		case 4:
8439beb93cSSam Leffler 			/* Wi-Fi Protected Setup (WPS) IE */
8539beb93cSSam Leffler 			elems->wps_ie = pos;
8639beb93cSSam Leffler 			elems->wps_ie_len = elen;
8739beb93cSSam Leffler 			break;
8839beb93cSSam Leffler 		default:
89f05cddf9SRui Paulo 			wpa_printf(MSG_EXCESSIVE, "Unknown Microsoft "
90f05cddf9SRui Paulo 				   "information element ignored "
91f05cddf9SRui Paulo 				   "(type=%d len=%lu)",
92f05cddf9SRui Paulo 				   pos[3], (unsigned long) elen);
93f05cddf9SRui Paulo 			return -1;
94f05cddf9SRui Paulo 		}
95f05cddf9SRui Paulo 		break;
96f05cddf9SRui Paulo 
97f05cddf9SRui Paulo 	case OUI_WFA:
98f05cddf9SRui Paulo 		switch (pos[3]) {
99f05cddf9SRui Paulo 		case P2P_OUI_TYPE:
100f05cddf9SRui Paulo 			/* Wi-Fi Alliance - P2P IE */
101f05cddf9SRui Paulo 			elems->p2p = pos;
102f05cddf9SRui Paulo 			elems->p2p_len = elen;
103f05cddf9SRui Paulo 			break;
104f05cddf9SRui Paulo 		case WFD_OUI_TYPE:
105f05cddf9SRui Paulo 			/* Wi-Fi Alliance - WFD IE */
106f05cddf9SRui Paulo 			elems->wfd = pos;
107f05cddf9SRui Paulo 			elems->wfd_len = elen;
108f05cddf9SRui Paulo 			break;
109f05cddf9SRui Paulo 		case HS20_INDICATION_OUI_TYPE:
110f05cddf9SRui Paulo 			/* Hotspot 2.0 */
111f05cddf9SRui Paulo 			elems->hs20 = pos;
112f05cddf9SRui Paulo 			elems->hs20_len = elen;
113f05cddf9SRui Paulo 			break;
1145b9c547cSRui Paulo 		case HS20_OSEN_OUI_TYPE:
1155b9c547cSRui Paulo 			/* Hotspot 2.0 OSEN */
1165b9c547cSRui Paulo 			elems->osen = pos;
1175b9c547cSRui Paulo 			elems->osen_len = elen;
1185b9c547cSRui Paulo 			break;
119780fb4a2SCy Schubert 		case MBO_OUI_TYPE:
120780fb4a2SCy Schubert 			/* MBO-OCE */
121780fb4a2SCy Schubert 			elems->mbo = pos;
122780fb4a2SCy Schubert 			elems->mbo_len = elen;
123780fb4a2SCy Schubert 			break;
12485732ac8SCy Schubert 		case HS20_ROAMING_CONS_SEL_OUI_TYPE:
12585732ac8SCy Schubert 			/* Hotspot 2.0 Roaming Consortium Selection */
12685732ac8SCy Schubert 			elems->roaming_cons_sel = pos;
12785732ac8SCy Schubert 			elems->roaming_cons_sel_len = elen;
12885732ac8SCy Schubert 			break;
129*4bc52338SCy Schubert 		case MULTI_AP_OUI_TYPE:
130*4bc52338SCy Schubert 			elems->multi_ap = pos;
131*4bc52338SCy Schubert 			elems->multi_ap_len = elen;
132*4bc52338SCy Schubert 			break;
133f05cddf9SRui Paulo 		default:
134f05cddf9SRui Paulo 			wpa_printf(MSG_MSGDUMP, "Unknown WFA "
13539beb93cSSam Leffler 				   "information element ignored "
1365b9c547cSRui Paulo 				   "(type=%d len=%lu)",
13739beb93cSSam Leffler 				   pos[3], (unsigned long) elen);
13839beb93cSSam Leffler 			return -1;
13939beb93cSSam Leffler 		}
14039beb93cSSam Leffler 		break;
14139beb93cSSam Leffler 
14239beb93cSSam Leffler 	case OUI_BROADCOM:
14339beb93cSSam Leffler 		switch (pos[3]) {
14439beb93cSSam Leffler 		case VENDOR_HT_CAPAB_OUI_TYPE:
14539beb93cSSam Leffler 			elems->vendor_ht_cap = pos;
14639beb93cSSam Leffler 			elems->vendor_ht_cap_len = elen;
14739beb93cSSam Leffler 			break;
1485b9c547cSRui Paulo 		case VENDOR_VHT_TYPE:
1495b9c547cSRui Paulo 			if (elen > 4 &&
1505b9c547cSRui Paulo 			    (pos[4] == VENDOR_VHT_SUBTYPE ||
1515b9c547cSRui Paulo 			     pos[4] == VENDOR_VHT_SUBTYPE2)) {
1525b9c547cSRui Paulo 				elems->vendor_vht = pos;
1535b9c547cSRui Paulo 				elems->vendor_vht_len = elen;
1545b9c547cSRui Paulo 			} else
1555b9c547cSRui Paulo 				return -1;
1565b9c547cSRui Paulo 			break;
15739beb93cSSam Leffler 		default:
158f05cddf9SRui Paulo 			wpa_printf(MSG_EXCESSIVE, "Unknown Broadcom "
15939beb93cSSam Leffler 				   "information element ignored "
160f05cddf9SRui Paulo 				   "(type=%d len=%lu)",
16139beb93cSSam Leffler 				   pos[3], (unsigned long) elen);
16239beb93cSSam Leffler 			return -1;
16339beb93cSSam Leffler 		}
16439beb93cSSam Leffler 		break;
16539beb93cSSam Leffler 
166325151a3SRui Paulo 	case OUI_QCA:
167325151a3SRui Paulo 		switch (pos[3]) {
168325151a3SRui Paulo 		case QCA_VENDOR_ELEM_P2P_PREF_CHAN_LIST:
169325151a3SRui Paulo 			elems->pref_freq_list = pos;
170325151a3SRui Paulo 			elems->pref_freq_list_len = elen;
171325151a3SRui Paulo 			break;
172325151a3SRui Paulo 		default:
173325151a3SRui Paulo 			wpa_printf(MSG_EXCESSIVE,
174325151a3SRui Paulo 				   "Unknown QCA information element ignored (type=%d len=%lu)",
175325151a3SRui Paulo 				   pos[3], (unsigned long) elen);
176325151a3SRui Paulo 			return -1;
177325151a3SRui Paulo 		}
178325151a3SRui Paulo 		break;
179325151a3SRui Paulo 
18039beb93cSSam Leffler 	default:
181f05cddf9SRui Paulo 		wpa_printf(MSG_EXCESSIVE, "unknown vendor specific "
182f05cddf9SRui Paulo 			   "information element ignored (vendor OUI "
183f05cddf9SRui Paulo 			   "%02x:%02x:%02x len=%lu)",
18439beb93cSSam Leffler 			   pos[0], pos[1], pos[2], (unsigned long) elen);
18539beb93cSSam Leffler 		return -1;
18639beb93cSSam Leffler 	}
18739beb93cSSam Leffler 
18839beb93cSSam Leffler 	return 0;
18939beb93cSSam Leffler }
19039beb93cSSam Leffler 
19139beb93cSSam Leffler 
19285732ac8SCy Schubert static int ieee802_11_parse_extension(const u8 *pos, size_t elen,
19385732ac8SCy Schubert 				      struct ieee802_11_elems *elems,
19485732ac8SCy Schubert 				      int show_errors)
19585732ac8SCy Schubert {
19685732ac8SCy Schubert 	u8 ext_id;
19785732ac8SCy Schubert 
19885732ac8SCy Schubert 	if (elen < 1) {
19985732ac8SCy Schubert 		if (show_errors) {
20085732ac8SCy Schubert 			wpa_printf(MSG_MSGDUMP,
20185732ac8SCy Schubert 				   "short information element (Ext)");
20285732ac8SCy Schubert 		}
20385732ac8SCy Schubert 		return -1;
20485732ac8SCy Schubert 	}
20585732ac8SCy Schubert 
20685732ac8SCy Schubert 	ext_id = *pos++;
20785732ac8SCy Schubert 	elen--;
20885732ac8SCy Schubert 
20985732ac8SCy Schubert 	switch (ext_id) {
21085732ac8SCy Schubert 	case WLAN_EID_EXT_ASSOC_DELAY_INFO:
21185732ac8SCy Schubert 		if (elen != 1)
21285732ac8SCy Schubert 			break;
21385732ac8SCy Schubert 		elems->assoc_delay_info = pos;
21485732ac8SCy Schubert 		break;
21585732ac8SCy Schubert 	case WLAN_EID_EXT_FILS_REQ_PARAMS:
21685732ac8SCy Schubert 		if (elen < 3)
21785732ac8SCy Schubert 			break;
21885732ac8SCy Schubert 		elems->fils_req_params = pos;
21985732ac8SCy Schubert 		elems->fils_req_params_len = elen;
22085732ac8SCy Schubert 		break;
22185732ac8SCy Schubert 	case WLAN_EID_EXT_FILS_KEY_CONFIRM:
22285732ac8SCy Schubert 		elems->fils_key_confirm = pos;
22385732ac8SCy Schubert 		elems->fils_key_confirm_len = elen;
22485732ac8SCy Schubert 		break;
22585732ac8SCy Schubert 	case WLAN_EID_EXT_FILS_SESSION:
22685732ac8SCy Schubert 		if (elen != FILS_SESSION_LEN)
22785732ac8SCy Schubert 			break;
22885732ac8SCy Schubert 		elems->fils_session = pos;
22985732ac8SCy Schubert 		break;
23085732ac8SCy Schubert 	case WLAN_EID_EXT_FILS_HLP_CONTAINER:
23185732ac8SCy Schubert 		if (elen < 2 * ETH_ALEN)
23285732ac8SCy Schubert 			break;
23385732ac8SCy Schubert 		elems->fils_hlp = pos;
23485732ac8SCy Schubert 		elems->fils_hlp_len = elen;
23585732ac8SCy Schubert 		break;
23685732ac8SCy Schubert 	case WLAN_EID_EXT_FILS_IP_ADDR_ASSIGN:
23785732ac8SCy Schubert 		if (elen < 1)
23885732ac8SCy Schubert 			break;
23985732ac8SCy Schubert 		elems->fils_ip_addr_assign = pos;
24085732ac8SCy Schubert 		elems->fils_ip_addr_assign_len = elen;
24185732ac8SCy Schubert 		break;
24285732ac8SCy Schubert 	case WLAN_EID_EXT_KEY_DELIVERY:
24385732ac8SCy Schubert 		if (elen < WPA_KEY_RSC_LEN)
24485732ac8SCy Schubert 			break;
24585732ac8SCy Schubert 		elems->key_delivery = pos;
24685732ac8SCy Schubert 		elems->key_delivery_len = elen;
24785732ac8SCy Schubert 		break;
24885732ac8SCy Schubert 	case WLAN_EID_EXT_FILS_WRAPPED_DATA:
24985732ac8SCy Schubert 		elems->fils_wrapped_data = pos;
25085732ac8SCy Schubert 		elems->fils_wrapped_data_len = elen;
25185732ac8SCy Schubert 		break;
25285732ac8SCy Schubert 	case WLAN_EID_EXT_FILS_PUBLIC_KEY:
25385732ac8SCy Schubert 		if (elen < 1)
25485732ac8SCy Schubert 			break;
25585732ac8SCy Schubert 		elems->fils_pk = pos;
25685732ac8SCy Schubert 		elems->fils_pk_len = elen;
25785732ac8SCy Schubert 		break;
25885732ac8SCy Schubert 	case WLAN_EID_EXT_FILS_NONCE:
25985732ac8SCy Schubert 		if (elen != FILS_NONCE_LEN)
26085732ac8SCy Schubert 			break;
26185732ac8SCy Schubert 		elems->fils_nonce = pos;
26285732ac8SCy Schubert 		break;
26385732ac8SCy Schubert 	case WLAN_EID_EXT_OWE_DH_PARAM:
26485732ac8SCy Schubert 		if (elen < 2)
26585732ac8SCy Schubert 			break;
26685732ac8SCy Schubert 		elems->owe_dh = pos;
26785732ac8SCy Schubert 		elems->owe_dh_len = elen;
26885732ac8SCy Schubert 		break;
26985732ac8SCy Schubert 	case WLAN_EID_EXT_PASSWORD_IDENTIFIER:
27085732ac8SCy Schubert 		elems->password_id = pos;
27185732ac8SCy Schubert 		elems->password_id_len = elen;
27285732ac8SCy Schubert 		break;
273*4bc52338SCy Schubert 	case WLAN_EID_EXT_HE_CAPABILITIES:
274*4bc52338SCy Schubert 		elems->he_capabilities = pos;
275*4bc52338SCy Schubert 		elems->he_capabilities_len = elen;
276*4bc52338SCy Schubert 		break;
277*4bc52338SCy Schubert 	case WLAN_EID_EXT_OCV_OCI:
278*4bc52338SCy Schubert 		elems->oci = pos;
279*4bc52338SCy Schubert 		elems->oci_len = elen;
280*4bc52338SCy Schubert 		break;
28185732ac8SCy Schubert 	default:
28285732ac8SCy Schubert 		if (show_errors) {
28385732ac8SCy Schubert 			wpa_printf(MSG_MSGDUMP,
28485732ac8SCy Schubert 				   "IEEE 802.11 element parsing ignored unknown element extension (ext_id=%u elen=%u)",
28585732ac8SCy Schubert 				   ext_id, (unsigned int) elen);
28685732ac8SCy Schubert 		}
28785732ac8SCy Schubert 		return -1;
28885732ac8SCy Schubert 	}
28985732ac8SCy Schubert 
29085732ac8SCy Schubert 	return 0;
29185732ac8SCy Schubert }
29285732ac8SCy Schubert 
29385732ac8SCy Schubert 
29439beb93cSSam Leffler /**
29539beb93cSSam Leffler  * ieee802_11_parse_elems - Parse information elements in management frames
29639beb93cSSam Leffler  * @start: Pointer to the start of IEs
29739beb93cSSam Leffler  * @len: Length of IE buffer in octets
29839beb93cSSam Leffler  * @elems: Data structure for parsed elements
29939beb93cSSam Leffler  * @show_errors: Whether to show parsing errors in debug log
30039beb93cSSam Leffler  * Returns: Parsing result
30139beb93cSSam Leffler  */
302e28a4053SRui Paulo ParseRes ieee802_11_parse_elems(const u8 *start, size_t len,
30339beb93cSSam Leffler 				struct ieee802_11_elems *elems,
30439beb93cSSam Leffler 				int show_errors)
30539beb93cSSam Leffler {
306*4bc52338SCy Schubert 	const struct element *elem;
30739beb93cSSam Leffler 	int unknown = 0;
30839beb93cSSam Leffler 
30939beb93cSSam Leffler 	os_memset(elems, 0, sizeof(*elems));
31039beb93cSSam Leffler 
311*4bc52338SCy Schubert 	if (!start)
312*4bc52338SCy Schubert 		return ParseOK;
31339beb93cSSam Leffler 
314*4bc52338SCy Schubert 	for_each_element(elem, start, len) {
315*4bc52338SCy Schubert 		u8 id = elem->id, elen = elem->datalen;
316*4bc52338SCy Schubert 		const u8 *pos = elem->data;
31739beb93cSSam Leffler 
31839beb93cSSam Leffler 		switch (id) {
31939beb93cSSam Leffler 		case WLAN_EID_SSID:
320325151a3SRui Paulo 			if (elen > SSID_MAX_LEN) {
321325151a3SRui Paulo 				wpa_printf(MSG_DEBUG,
322325151a3SRui Paulo 					   "Ignored too long SSID element (elen=%u)",
323325151a3SRui Paulo 					   elen);
324325151a3SRui Paulo 				break;
325325151a3SRui Paulo 			}
32639beb93cSSam Leffler 			elems->ssid = pos;
32739beb93cSSam Leffler 			elems->ssid_len = elen;
32839beb93cSSam Leffler 			break;
32939beb93cSSam Leffler 		case WLAN_EID_SUPP_RATES:
33039beb93cSSam Leffler 			elems->supp_rates = pos;
33139beb93cSSam Leffler 			elems->supp_rates_len = elen;
33239beb93cSSam Leffler 			break;
33339beb93cSSam Leffler 		case WLAN_EID_DS_PARAMS:
334325151a3SRui Paulo 			if (elen < 1)
335325151a3SRui Paulo 				break;
33639beb93cSSam Leffler 			elems->ds_params = pos;
33739beb93cSSam Leffler 			break;
33839beb93cSSam Leffler 		case WLAN_EID_CF_PARAMS:
33939beb93cSSam Leffler 		case WLAN_EID_TIM:
34039beb93cSSam Leffler 			break;
34139beb93cSSam Leffler 		case WLAN_EID_CHALLENGE:
34239beb93cSSam Leffler 			elems->challenge = pos;
34339beb93cSSam Leffler 			elems->challenge_len = elen;
34439beb93cSSam Leffler 			break;
34539beb93cSSam Leffler 		case WLAN_EID_ERP_INFO:
346325151a3SRui Paulo 			if (elen < 1)
347325151a3SRui Paulo 				break;
34839beb93cSSam Leffler 			elems->erp_info = pos;
34939beb93cSSam Leffler 			break;
35039beb93cSSam Leffler 		case WLAN_EID_EXT_SUPP_RATES:
35139beb93cSSam Leffler 			elems->ext_supp_rates = pos;
35239beb93cSSam Leffler 			elems->ext_supp_rates_len = elen;
35339beb93cSSam Leffler 			break;
35439beb93cSSam Leffler 		case WLAN_EID_VENDOR_SPECIFIC:
35539beb93cSSam Leffler 			if (ieee802_11_parse_vendor_specific(pos, elen,
35639beb93cSSam Leffler 							     elems,
35739beb93cSSam Leffler 							     show_errors))
35839beb93cSSam Leffler 				unknown++;
35939beb93cSSam Leffler 			break;
36039beb93cSSam Leffler 		case WLAN_EID_RSN:
36139beb93cSSam Leffler 			elems->rsn_ie = pos;
36239beb93cSSam Leffler 			elems->rsn_ie_len = elen;
36339beb93cSSam Leffler 			break;
36439beb93cSSam Leffler 		case WLAN_EID_PWR_CAPABILITY:
36585732ac8SCy Schubert 			if (elen < 2)
36685732ac8SCy Schubert 				break;
36785732ac8SCy Schubert 			elems->power_capab = pos;
36885732ac8SCy Schubert 			elems->power_capab_len = elen;
36939beb93cSSam Leffler 			break;
37039beb93cSSam Leffler 		case WLAN_EID_SUPPORTED_CHANNELS:
37139beb93cSSam Leffler 			elems->supp_channels = pos;
37239beb93cSSam Leffler 			elems->supp_channels_len = elen;
37339beb93cSSam Leffler 			break;
37439beb93cSSam Leffler 		case WLAN_EID_MOBILITY_DOMAIN:
375325151a3SRui Paulo 			if (elen < sizeof(struct rsn_mdie))
376325151a3SRui Paulo 				break;
37739beb93cSSam Leffler 			elems->mdie = pos;
37839beb93cSSam Leffler 			elems->mdie_len = elen;
37939beb93cSSam Leffler 			break;
38039beb93cSSam Leffler 		case WLAN_EID_FAST_BSS_TRANSITION:
381325151a3SRui Paulo 			if (elen < sizeof(struct rsn_ftie))
382325151a3SRui Paulo 				break;
38339beb93cSSam Leffler 			elems->ftie = pos;
38439beb93cSSam Leffler 			elems->ftie_len = elen;
38539beb93cSSam Leffler 			break;
38639beb93cSSam Leffler 		case WLAN_EID_TIMEOUT_INTERVAL:
387325151a3SRui Paulo 			if (elen != 5)
388325151a3SRui Paulo 				break;
38939beb93cSSam Leffler 			elems->timeout_int = pos;
39039beb93cSSam Leffler 			break;
39139beb93cSSam Leffler 		case WLAN_EID_HT_CAP:
392325151a3SRui Paulo 			if (elen < sizeof(struct ieee80211_ht_capabilities))
393325151a3SRui Paulo 				break;
39439beb93cSSam Leffler 			elems->ht_capabilities = pos;
39539beb93cSSam Leffler 			break;
39639beb93cSSam Leffler 		case WLAN_EID_HT_OPERATION:
397325151a3SRui Paulo 			if (elen < sizeof(struct ieee80211_ht_operation))
398325151a3SRui Paulo 				break;
39939beb93cSSam Leffler 			elems->ht_operation = pos;
40039beb93cSSam Leffler 			break;
4015b9c547cSRui Paulo 		case WLAN_EID_MESH_CONFIG:
4025b9c547cSRui Paulo 			elems->mesh_config = pos;
4035b9c547cSRui Paulo 			elems->mesh_config_len = elen;
4045b9c547cSRui Paulo 			break;
4055b9c547cSRui Paulo 		case WLAN_EID_MESH_ID:
4065b9c547cSRui Paulo 			elems->mesh_id = pos;
4075b9c547cSRui Paulo 			elems->mesh_id_len = elen;
4085b9c547cSRui Paulo 			break;
4095b9c547cSRui Paulo 		case WLAN_EID_PEER_MGMT:
4105b9c547cSRui Paulo 			elems->peer_mgmt = pos;
4115b9c547cSRui Paulo 			elems->peer_mgmt_len = elen;
4125b9c547cSRui Paulo 			break;
413f05cddf9SRui Paulo 		case WLAN_EID_VHT_CAP:
414325151a3SRui Paulo 			if (elen < sizeof(struct ieee80211_vht_capabilities))
415325151a3SRui Paulo 				break;
416f05cddf9SRui Paulo 			elems->vht_capabilities = pos;
417f05cddf9SRui Paulo 			break;
418f05cddf9SRui Paulo 		case WLAN_EID_VHT_OPERATION:
419325151a3SRui Paulo 			if (elen < sizeof(struct ieee80211_vht_operation))
420325151a3SRui Paulo 				break;
421f05cddf9SRui Paulo 			elems->vht_operation = pos;
422f05cddf9SRui Paulo 			break;
4235b9c547cSRui Paulo 		case WLAN_EID_VHT_OPERATING_MODE_NOTIFICATION:
4245b9c547cSRui Paulo 			if (elen != 1)
4255b9c547cSRui Paulo 				break;
4265b9c547cSRui Paulo 			elems->vht_opmode_notif = pos;
4275b9c547cSRui Paulo 			break;
428f05cddf9SRui Paulo 		case WLAN_EID_LINK_ID:
429f05cddf9SRui Paulo 			if (elen < 18)
430f05cddf9SRui Paulo 				break;
431f05cddf9SRui Paulo 			elems->link_id = pos;
432f05cddf9SRui Paulo 			break;
433f05cddf9SRui Paulo 		case WLAN_EID_INTERWORKING:
434f05cddf9SRui Paulo 			elems->interworking = pos;
435f05cddf9SRui Paulo 			elems->interworking_len = elen;
436f05cddf9SRui Paulo 			break;
4375b9c547cSRui Paulo 		case WLAN_EID_QOS_MAP_SET:
4385b9c547cSRui Paulo 			if (elen < 16)
4395b9c547cSRui Paulo 				break;
4405b9c547cSRui Paulo 			elems->qos_map_set = pos;
4415b9c547cSRui Paulo 			elems->qos_map_set_len = elen;
4425b9c547cSRui Paulo 			break;
443f05cddf9SRui Paulo 		case WLAN_EID_EXT_CAPAB:
444f05cddf9SRui Paulo 			elems->ext_capab = pos;
445f05cddf9SRui Paulo 			elems->ext_capab_len = elen;
446f05cddf9SRui Paulo 			break;
447f05cddf9SRui Paulo 		case WLAN_EID_BSS_MAX_IDLE_PERIOD:
448f05cddf9SRui Paulo 			if (elen < 3)
449f05cddf9SRui Paulo 				break;
450f05cddf9SRui Paulo 			elems->bss_max_idle_period = pos;
451f05cddf9SRui Paulo 			break;
452f05cddf9SRui Paulo 		case WLAN_EID_SSID_LIST:
453f05cddf9SRui Paulo 			elems->ssid_list = pos;
454f05cddf9SRui Paulo 			elems->ssid_list_len = elen;
455f05cddf9SRui Paulo 			break;
4565b9c547cSRui Paulo 		case WLAN_EID_AMPE:
4575b9c547cSRui Paulo 			elems->ampe = pos;
4585b9c547cSRui Paulo 			elems->ampe_len = elen;
4595b9c547cSRui Paulo 			break;
4605b9c547cSRui Paulo 		case WLAN_EID_MIC:
4615b9c547cSRui Paulo 			elems->mic = pos;
4625b9c547cSRui Paulo 			elems->mic_len = elen;
4635b9c547cSRui Paulo 			/* after mic everything is encrypted, so stop. */
464*4bc52338SCy Schubert 			goto done;
465325151a3SRui Paulo 		case WLAN_EID_MULTI_BAND:
466325151a3SRui Paulo 			if (elems->mb_ies.nof_ies >= MAX_NOF_MB_IES_SUPPORTED) {
467325151a3SRui Paulo 				wpa_printf(MSG_MSGDUMP,
468325151a3SRui Paulo 					   "IEEE 802.11 element parse ignored MB IE (id=%d elen=%d)",
469325151a3SRui Paulo 					   id, elen);
470325151a3SRui Paulo 				break;
471325151a3SRui Paulo 			}
472325151a3SRui Paulo 
473325151a3SRui Paulo 			elems->mb_ies.ies[elems->mb_ies.nof_ies].ie = pos;
474325151a3SRui Paulo 			elems->mb_ies.ies[elems->mb_ies.nof_ies].ie_len = elen;
475325151a3SRui Paulo 			elems->mb_ies.nof_ies++;
476325151a3SRui Paulo 			break;
477780fb4a2SCy Schubert 		case WLAN_EID_SUPPORTED_OPERATING_CLASSES:
478780fb4a2SCy Schubert 			elems->supp_op_classes = pos;
479780fb4a2SCy Schubert 			elems->supp_op_classes_len = elen;
480780fb4a2SCy Schubert 			break;
481780fb4a2SCy Schubert 		case WLAN_EID_RRM_ENABLED_CAPABILITIES:
482780fb4a2SCy Schubert 			elems->rrm_enabled = pos;
483780fb4a2SCy Schubert 			elems->rrm_enabled_len = elen;
484780fb4a2SCy Schubert 			break;
48585732ac8SCy Schubert 		case WLAN_EID_CAG_NUMBER:
48685732ac8SCy Schubert 			elems->cag_number = pos;
48785732ac8SCy Schubert 			elems->cag_number_len = elen;
48885732ac8SCy Schubert 			break;
48985732ac8SCy Schubert 		case WLAN_EID_AP_CSN:
49085732ac8SCy Schubert 			if (elen < 1)
49185732ac8SCy Schubert 				break;
49285732ac8SCy Schubert 			elems->ap_csn = pos;
49385732ac8SCy Schubert 			break;
49485732ac8SCy Schubert 		case WLAN_EID_FILS_INDICATION:
49585732ac8SCy Schubert 			if (elen < 2)
49685732ac8SCy Schubert 				break;
49785732ac8SCy Schubert 			elems->fils_indic = pos;
49885732ac8SCy Schubert 			elems->fils_indic_len = elen;
49985732ac8SCy Schubert 			break;
50085732ac8SCy Schubert 		case WLAN_EID_DILS:
50185732ac8SCy Schubert 			if (elen < 2)
50285732ac8SCy Schubert 				break;
50385732ac8SCy Schubert 			elems->dils = pos;
50485732ac8SCy Schubert 			elems->dils_len = elen;
50585732ac8SCy Schubert 			break;
50685732ac8SCy Schubert 		case WLAN_EID_FRAGMENT:
50785732ac8SCy Schubert 			/* TODO */
50885732ac8SCy Schubert 			break;
50985732ac8SCy Schubert 		case WLAN_EID_EXTENSION:
51085732ac8SCy Schubert 			if (ieee802_11_parse_extension(pos, elen, elems,
51185732ac8SCy Schubert 						       show_errors))
51285732ac8SCy Schubert 				unknown++;
51385732ac8SCy Schubert 			break;
51439beb93cSSam Leffler 		default:
51539beb93cSSam Leffler 			unknown++;
51639beb93cSSam Leffler 			if (!show_errors)
51739beb93cSSam Leffler 				break;
51839beb93cSSam Leffler 			wpa_printf(MSG_MSGDUMP, "IEEE 802.11 element parse "
51939beb93cSSam Leffler 				   "ignored unknown element (id=%d elen=%d)",
52039beb93cSSam Leffler 				   id, elen);
52139beb93cSSam Leffler 			break;
52239beb93cSSam Leffler 		}
52339beb93cSSam Leffler 	}
52439beb93cSSam Leffler 
525*4bc52338SCy Schubert 	if (!for_each_element_completed(elem, start, len)) {
526*4bc52338SCy Schubert 		if (show_errors) {
527*4bc52338SCy Schubert 			wpa_printf(MSG_DEBUG,
528*4bc52338SCy Schubert 				   "IEEE 802.11 element parse failed @%d",
529*4bc52338SCy Schubert 				   (int) (start + len - (const u8 *) elem));
530*4bc52338SCy Schubert 			wpa_hexdump(MSG_MSGDUMP, "IEs", start, len);
531*4bc52338SCy Schubert 		}
53239beb93cSSam Leffler 		return ParseFailed;
533*4bc52338SCy Schubert 	}
53439beb93cSSam Leffler 
535*4bc52338SCy Schubert done:
53639beb93cSSam Leffler 	return unknown ? ParseUnknown : ParseOK;
53739beb93cSSam Leffler }
538e28a4053SRui Paulo 
539e28a4053SRui Paulo 
540e28a4053SRui Paulo int ieee802_11_ie_count(const u8 *ies, size_t ies_len)
541e28a4053SRui Paulo {
542*4bc52338SCy Schubert 	const struct element *elem;
543e28a4053SRui Paulo 	int count = 0;
544e28a4053SRui Paulo 
545e28a4053SRui Paulo 	if (ies == NULL)
546e28a4053SRui Paulo 		return 0;
547e28a4053SRui Paulo 
548*4bc52338SCy Schubert 	for_each_element(elem, ies, ies_len)
549e28a4053SRui Paulo 		count++;
550e28a4053SRui Paulo 
551e28a4053SRui Paulo 	return count;
552e28a4053SRui Paulo }
553e28a4053SRui Paulo 
554e28a4053SRui Paulo 
555e28a4053SRui Paulo struct wpabuf * ieee802_11_vendor_ie_concat(const u8 *ies, size_t ies_len,
556e28a4053SRui Paulo 					    u32 oui_type)
557e28a4053SRui Paulo {
558e28a4053SRui Paulo 	struct wpabuf *buf;
559*4bc52338SCy Schubert 	const struct element *elem, *found = NULL;
560e28a4053SRui Paulo 
561*4bc52338SCy Schubert 	for_each_element_id(elem, WLAN_EID_VENDOR_SPECIFIC, ies, ies_len) {
562*4bc52338SCy Schubert 		if (elem->datalen >= 4 &&
563*4bc52338SCy Schubert 		    WPA_GET_BE32(elem->data) == oui_type) {
564*4bc52338SCy Schubert 			found = elem;
565e28a4053SRui Paulo 			break;
566e28a4053SRui Paulo 		}
567e28a4053SRui Paulo 	}
568e28a4053SRui Paulo 
569*4bc52338SCy Schubert 	if (!found)
570e28a4053SRui Paulo 		return NULL; /* No specified vendor IE found */
571e28a4053SRui Paulo 
572e28a4053SRui Paulo 	buf = wpabuf_alloc(ies_len);
573e28a4053SRui Paulo 	if (buf == NULL)
574e28a4053SRui Paulo 		return NULL;
575e28a4053SRui Paulo 
576e28a4053SRui Paulo 	/*
577e28a4053SRui Paulo 	 * There may be multiple vendor IEs in the message, so need to
578e28a4053SRui Paulo 	 * concatenate their data fields.
579e28a4053SRui Paulo 	 */
580*4bc52338SCy Schubert 	for_each_element_id(elem, WLAN_EID_VENDOR_SPECIFIC, ies, ies_len) {
581*4bc52338SCy Schubert 		if (elem->datalen >= 4 && WPA_GET_BE32(elem->data) == oui_type)
582*4bc52338SCy Schubert 			wpabuf_put_data(buf, elem->data + 4, elem->datalen - 4);
583e28a4053SRui Paulo 	}
584e28a4053SRui Paulo 
585e28a4053SRui Paulo 	return buf;
586e28a4053SRui Paulo }
587f05cddf9SRui Paulo 
588f05cddf9SRui Paulo 
589f05cddf9SRui Paulo const u8 * get_hdr_bssid(const struct ieee80211_hdr *hdr, size_t len)
590f05cddf9SRui Paulo {
591f05cddf9SRui Paulo 	u16 fc, type, stype;
592f05cddf9SRui Paulo 
593f05cddf9SRui Paulo 	/*
594f05cddf9SRui Paulo 	 * PS-Poll frames are 16 bytes. All other frames are
595f05cddf9SRui Paulo 	 * 24 bytes or longer.
596f05cddf9SRui Paulo 	 */
597f05cddf9SRui Paulo 	if (len < 16)
598f05cddf9SRui Paulo 		return NULL;
599f05cddf9SRui Paulo 
600f05cddf9SRui Paulo 	fc = le_to_host16(hdr->frame_control);
601f05cddf9SRui Paulo 	type = WLAN_FC_GET_TYPE(fc);
602f05cddf9SRui Paulo 	stype = WLAN_FC_GET_STYPE(fc);
603f05cddf9SRui Paulo 
604f05cddf9SRui Paulo 	switch (type) {
605f05cddf9SRui Paulo 	case WLAN_FC_TYPE_DATA:
606f05cddf9SRui Paulo 		if (len < 24)
607f05cddf9SRui Paulo 			return NULL;
608f05cddf9SRui Paulo 		switch (fc & (WLAN_FC_FROMDS | WLAN_FC_TODS)) {
609f05cddf9SRui Paulo 		case WLAN_FC_FROMDS | WLAN_FC_TODS:
610f05cddf9SRui Paulo 		case WLAN_FC_TODS:
611f05cddf9SRui Paulo 			return hdr->addr1;
612f05cddf9SRui Paulo 		case WLAN_FC_FROMDS:
613f05cddf9SRui Paulo 			return hdr->addr2;
614f05cddf9SRui Paulo 		default:
615f05cddf9SRui Paulo 			return NULL;
616f05cddf9SRui Paulo 		}
617f05cddf9SRui Paulo 	case WLAN_FC_TYPE_CTRL:
618f05cddf9SRui Paulo 		if (stype != WLAN_FC_STYPE_PSPOLL)
619f05cddf9SRui Paulo 			return NULL;
620f05cddf9SRui Paulo 		return hdr->addr1;
621f05cddf9SRui Paulo 	case WLAN_FC_TYPE_MGMT:
622f05cddf9SRui Paulo 		return hdr->addr3;
623f05cddf9SRui Paulo 	default:
624f05cddf9SRui Paulo 		return NULL;
625f05cddf9SRui Paulo 	}
626f05cddf9SRui Paulo }
627f05cddf9SRui Paulo 
628f05cddf9SRui Paulo 
629f05cddf9SRui Paulo int hostapd_config_wmm_ac(struct hostapd_wmm_ac_params wmm_ac_params[],
630f05cddf9SRui Paulo 			  const char *name, const char *val)
631f05cddf9SRui Paulo {
632f05cddf9SRui Paulo 	int num, v;
633f05cddf9SRui Paulo 	const char *pos;
634f05cddf9SRui Paulo 	struct hostapd_wmm_ac_params *ac;
635f05cddf9SRui Paulo 
636f05cddf9SRui Paulo 	/* skip 'wme_ac_' or 'wmm_ac_' prefix */
637f05cddf9SRui Paulo 	pos = name + 7;
638f05cddf9SRui Paulo 	if (os_strncmp(pos, "be_", 3) == 0) {
639f05cddf9SRui Paulo 		num = 0;
640f05cddf9SRui Paulo 		pos += 3;
641f05cddf9SRui Paulo 	} else if (os_strncmp(pos, "bk_", 3) == 0) {
642f05cddf9SRui Paulo 		num = 1;
643f05cddf9SRui Paulo 		pos += 3;
644f05cddf9SRui Paulo 	} else if (os_strncmp(pos, "vi_", 3) == 0) {
645f05cddf9SRui Paulo 		num = 2;
646f05cddf9SRui Paulo 		pos += 3;
647f05cddf9SRui Paulo 	} else if (os_strncmp(pos, "vo_", 3) == 0) {
648f05cddf9SRui Paulo 		num = 3;
649f05cddf9SRui Paulo 		pos += 3;
650f05cddf9SRui Paulo 	} else {
651f05cddf9SRui Paulo 		wpa_printf(MSG_ERROR, "Unknown WMM name '%s'", pos);
652f05cddf9SRui Paulo 		return -1;
653f05cddf9SRui Paulo 	}
654f05cddf9SRui Paulo 
655f05cddf9SRui Paulo 	ac = &wmm_ac_params[num];
656f05cddf9SRui Paulo 
657f05cddf9SRui Paulo 	if (os_strcmp(pos, "aifs") == 0) {
658f05cddf9SRui Paulo 		v = atoi(val);
659f05cddf9SRui Paulo 		if (v < 1 || v > 255) {
660f05cddf9SRui Paulo 			wpa_printf(MSG_ERROR, "Invalid AIFS value %d", v);
661f05cddf9SRui Paulo 			return -1;
662f05cddf9SRui Paulo 		}
663f05cddf9SRui Paulo 		ac->aifs = v;
664f05cddf9SRui Paulo 	} else if (os_strcmp(pos, "cwmin") == 0) {
665f05cddf9SRui Paulo 		v = atoi(val);
666325151a3SRui Paulo 		if (v < 0 || v > 15) {
667f05cddf9SRui Paulo 			wpa_printf(MSG_ERROR, "Invalid cwMin value %d", v);
668f05cddf9SRui Paulo 			return -1;
669f05cddf9SRui Paulo 		}
670f05cddf9SRui Paulo 		ac->cwmin = v;
671f05cddf9SRui Paulo 	} else if (os_strcmp(pos, "cwmax") == 0) {
672f05cddf9SRui Paulo 		v = atoi(val);
673325151a3SRui Paulo 		if (v < 0 || v > 15) {
674f05cddf9SRui Paulo 			wpa_printf(MSG_ERROR, "Invalid cwMax value %d", v);
675f05cddf9SRui Paulo 			return -1;
676f05cddf9SRui Paulo 		}
677f05cddf9SRui Paulo 		ac->cwmax = v;
678f05cddf9SRui Paulo 	} else if (os_strcmp(pos, "txop_limit") == 0) {
679f05cddf9SRui Paulo 		v = atoi(val);
680f05cddf9SRui Paulo 		if (v < 0 || v > 0xffff) {
681f05cddf9SRui Paulo 			wpa_printf(MSG_ERROR, "Invalid txop value %d", v);
682f05cddf9SRui Paulo 			return -1;
683f05cddf9SRui Paulo 		}
684f05cddf9SRui Paulo 		ac->txop_limit = v;
685f05cddf9SRui Paulo 	} else if (os_strcmp(pos, "acm") == 0) {
686f05cddf9SRui Paulo 		v = atoi(val);
687f05cddf9SRui Paulo 		if (v < 0 || v > 1) {
688f05cddf9SRui Paulo 			wpa_printf(MSG_ERROR, "Invalid acm value %d", v);
689f05cddf9SRui Paulo 			return -1;
690f05cddf9SRui Paulo 		}
691f05cddf9SRui Paulo 		ac->admission_control_mandatory = v;
692f05cddf9SRui Paulo 	} else {
693f05cddf9SRui Paulo 		wpa_printf(MSG_ERROR, "Unknown wmm_ac_ field '%s'", pos);
694f05cddf9SRui Paulo 		return -1;
695f05cddf9SRui Paulo 	}
696f05cddf9SRui Paulo 
697f05cddf9SRui Paulo 	return 0;
698f05cddf9SRui Paulo }
6995b9c547cSRui Paulo 
7005b9c547cSRui Paulo 
7015b9c547cSRui Paulo enum hostapd_hw_mode ieee80211_freq_to_chan(int freq, u8 *channel)
7025b9c547cSRui Paulo {
703325151a3SRui Paulo 	u8 op_class;
704325151a3SRui Paulo 
705780fb4a2SCy Schubert 	return ieee80211_freq_to_channel_ext(freq, 0, VHT_CHANWIDTH_USE_HT,
706780fb4a2SCy Schubert 					     &op_class, channel);
707325151a3SRui Paulo }
708325151a3SRui Paulo 
709325151a3SRui Paulo 
710325151a3SRui Paulo /**
711325151a3SRui Paulo  * ieee80211_freq_to_channel_ext - Convert frequency into channel info
712325151a3SRui Paulo  * for HT40 and VHT. DFS channels are not covered.
713325151a3SRui Paulo  * @freq: Frequency (MHz) to convert
714325151a3SRui Paulo  * @sec_channel: 0 = non-HT40, 1 = sec. channel above, -1 = sec. channel below
715780fb4a2SCy Schubert  * @vht: VHT channel width (VHT_CHANWIDTH_*)
716325151a3SRui Paulo  * @op_class: Buffer for returning operating class
717325151a3SRui Paulo  * @channel: Buffer for returning channel number
718325151a3SRui Paulo  * Returns: hw_mode on success, NUM_HOSTAPD_MODES on failure
719325151a3SRui Paulo  */
720325151a3SRui Paulo enum hostapd_hw_mode ieee80211_freq_to_channel_ext(unsigned int freq,
721325151a3SRui Paulo 						   int sec_channel, int vht,
722325151a3SRui Paulo 						   u8 *op_class, u8 *channel)
723325151a3SRui Paulo {
724780fb4a2SCy Schubert 	u8 vht_opclass;
725780fb4a2SCy Schubert 
726325151a3SRui Paulo 	/* TODO: more operating classes */
727325151a3SRui Paulo 
728325151a3SRui Paulo 	if (sec_channel > 1 || sec_channel < -1)
729325151a3SRui Paulo 		return NUM_HOSTAPD_MODES;
7305b9c547cSRui Paulo 
7315b9c547cSRui Paulo 	if (freq >= 2412 && freq <= 2472) {
732325151a3SRui Paulo 		if ((freq - 2407) % 5)
733325151a3SRui Paulo 			return NUM_HOSTAPD_MODES;
734325151a3SRui Paulo 
735325151a3SRui Paulo 		if (vht)
736325151a3SRui Paulo 			return NUM_HOSTAPD_MODES;
737325151a3SRui Paulo 
738325151a3SRui Paulo 		/* 2.407 GHz, channels 1..13 */
739325151a3SRui Paulo 		if (sec_channel == 1)
740325151a3SRui Paulo 			*op_class = 83;
741325151a3SRui Paulo 		else if (sec_channel == -1)
742325151a3SRui Paulo 			*op_class = 84;
743325151a3SRui Paulo 		else
744325151a3SRui Paulo 			*op_class = 81;
745325151a3SRui Paulo 
7465b9c547cSRui Paulo 		*channel = (freq - 2407) / 5;
747325151a3SRui Paulo 
748325151a3SRui Paulo 		return HOSTAPD_MODE_IEEE80211G;
749325151a3SRui Paulo 	}
750325151a3SRui Paulo 
751325151a3SRui Paulo 	if (freq == 2484) {
752325151a3SRui Paulo 		if (sec_channel || vht)
753325151a3SRui Paulo 			return NUM_HOSTAPD_MODES;
754325151a3SRui Paulo 
755325151a3SRui Paulo 		*op_class = 82; /* channel 14 */
7565b9c547cSRui Paulo 		*channel = 14;
757325151a3SRui Paulo 
758325151a3SRui Paulo 		return HOSTAPD_MODE_IEEE80211B;
759325151a3SRui Paulo 	}
760325151a3SRui Paulo 
761325151a3SRui Paulo 	if (freq >= 4900 && freq < 5000) {
762325151a3SRui Paulo 		if ((freq - 4000) % 5)
763325151a3SRui Paulo 			return NUM_HOSTAPD_MODES;
7645b9c547cSRui Paulo 		*channel = (freq - 4000) / 5;
765325151a3SRui Paulo 		*op_class = 0; /* TODO */
766325151a3SRui Paulo 		return HOSTAPD_MODE_IEEE80211A;
767325151a3SRui Paulo 	}
768325151a3SRui Paulo 
769780fb4a2SCy Schubert 	switch (vht) {
770780fb4a2SCy Schubert 	case VHT_CHANWIDTH_80MHZ:
771780fb4a2SCy Schubert 		vht_opclass = 128;
772780fb4a2SCy Schubert 		break;
773780fb4a2SCy Schubert 	case VHT_CHANWIDTH_160MHZ:
774780fb4a2SCy Schubert 		vht_opclass = 129;
775780fb4a2SCy Schubert 		break;
776780fb4a2SCy Schubert 	case VHT_CHANWIDTH_80P80MHZ:
777780fb4a2SCy Schubert 		vht_opclass = 130;
778780fb4a2SCy Schubert 		break;
779780fb4a2SCy Schubert 	default:
780780fb4a2SCy Schubert 		vht_opclass = 0;
781780fb4a2SCy Schubert 		break;
782780fb4a2SCy Schubert 	}
783780fb4a2SCy Schubert 
784325151a3SRui Paulo 	/* 5 GHz, channels 36..48 */
785325151a3SRui Paulo 	if (freq >= 5180 && freq <= 5240) {
786325151a3SRui Paulo 		if ((freq - 5000) % 5)
787325151a3SRui Paulo 			return NUM_HOSTAPD_MODES;
788325151a3SRui Paulo 
789780fb4a2SCy Schubert 		if (vht_opclass)
790780fb4a2SCy Schubert 			*op_class = vht_opclass;
791780fb4a2SCy Schubert 		else if (sec_channel == 1)
792325151a3SRui Paulo 			*op_class = 116;
793325151a3SRui Paulo 		else if (sec_channel == -1)
794325151a3SRui Paulo 			*op_class = 117;
795325151a3SRui Paulo 		else
796325151a3SRui Paulo 			*op_class = 115;
797325151a3SRui Paulo 
7985b9c547cSRui Paulo 		*channel = (freq - 5000) / 5;
799325151a3SRui Paulo 
800325151a3SRui Paulo 		return HOSTAPD_MODE_IEEE80211A;
801325151a3SRui Paulo 	}
802325151a3SRui Paulo 
80385732ac8SCy Schubert 	/* 5 GHz, channels 52..64 */
80485732ac8SCy Schubert 	if (freq >= 5260 && freq <= 5320) {
80585732ac8SCy Schubert 		if ((freq - 5000) % 5)
80685732ac8SCy Schubert 			return NUM_HOSTAPD_MODES;
80785732ac8SCy Schubert 
80885732ac8SCy Schubert 		if (vht_opclass)
80985732ac8SCy Schubert 			*op_class = vht_opclass;
81085732ac8SCy Schubert 		else if (sec_channel == 1)
81185732ac8SCy Schubert 			*op_class = 119;
81285732ac8SCy Schubert 		else if (sec_channel == -1)
81385732ac8SCy Schubert 			*op_class = 120;
81485732ac8SCy Schubert 		else
81585732ac8SCy Schubert 			*op_class = 118;
81685732ac8SCy Schubert 
81785732ac8SCy Schubert 		*channel = (freq - 5000) / 5;
81885732ac8SCy Schubert 
81985732ac8SCy Schubert 		return HOSTAPD_MODE_IEEE80211A;
82085732ac8SCy Schubert 	}
82185732ac8SCy Schubert 
822325151a3SRui Paulo 	/* 5 GHz, channels 149..169 */
823325151a3SRui Paulo 	if (freq >= 5745 && freq <= 5845) {
824325151a3SRui Paulo 		if ((freq - 5000) % 5)
825325151a3SRui Paulo 			return NUM_HOSTAPD_MODES;
826325151a3SRui Paulo 
827780fb4a2SCy Schubert 		if (vht_opclass)
828780fb4a2SCy Schubert 			*op_class = vht_opclass;
829780fb4a2SCy Schubert 		else if (sec_channel == 1)
830780fb4a2SCy Schubert 			*op_class = 126;
831780fb4a2SCy Schubert 		else if (sec_channel == -1)
832780fb4a2SCy Schubert 			*op_class = 127;
833780fb4a2SCy Schubert 		else if (freq <= 5805)
834780fb4a2SCy Schubert 			*op_class = 124;
835780fb4a2SCy Schubert 		else
836325151a3SRui Paulo 			*op_class = 125;
837325151a3SRui Paulo 
838325151a3SRui Paulo 		*channel = (freq - 5000) / 5;
839325151a3SRui Paulo 
840325151a3SRui Paulo 		return HOSTAPD_MODE_IEEE80211A;
841325151a3SRui Paulo 	}
842325151a3SRui Paulo 
843780fb4a2SCy Schubert 	/* 5 GHz, channels 100..140 */
844780fb4a2SCy Schubert 	if (freq >= 5000 && freq <= 5700) {
845780fb4a2SCy Schubert 		if ((freq - 5000) % 5)
846780fb4a2SCy Schubert 			return NUM_HOSTAPD_MODES;
847780fb4a2SCy Schubert 
848780fb4a2SCy Schubert 		if (vht_opclass)
849780fb4a2SCy Schubert 			*op_class = vht_opclass;
850780fb4a2SCy Schubert 		else if (sec_channel == 1)
851780fb4a2SCy Schubert 			*op_class = 122;
852780fb4a2SCy Schubert 		else if (sec_channel == -1)
853780fb4a2SCy Schubert 			*op_class = 123;
854780fb4a2SCy Schubert 		else
855780fb4a2SCy Schubert 			*op_class = 121;
856780fb4a2SCy Schubert 
857780fb4a2SCy Schubert 		*channel = (freq - 5000) / 5;
858780fb4a2SCy Schubert 
859780fb4a2SCy Schubert 		return HOSTAPD_MODE_IEEE80211A;
860780fb4a2SCy Schubert 	}
861780fb4a2SCy Schubert 
862325151a3SRui Paulo 	if (freq >= 5000 && freq < 5900) {
863325151a3SRui Paulo 		if ((freq - 5000) % 5)
864325151a3SRui Paulo 			return NUM_HOSTAPD_MODES;
865325151a3SRui Paulo 		*channel = (freq - 5000) / 5;
866325151a3SRui Paulo 		*op_class = 0; /* TODO */
867325151a3SRui Paulo 		return HOSTAPD_MODE_IEEE80211A;
868325151a3SRui Paulo 	}
869325151a3SRui Paulo 
870325151a3SRui Paulo 	/* 56.16 GHz, channel 1..4 */
871325151a3SRui Paulo 	if (freq >= 56160 + 2160 * 1 && freq <= 56160 + 2160 * 4) {
872325151a3SRui Paulo 		if (sec_channel || vht)
873325151a3SRui Paulo 			return NUM_HOSTAPD_MODES;
874325151a3SRui Paulo 
8755b9c547cSRui Paulo 		*channel = (freq - 56160) / 2160;
876325151a3SRui Paulo 		*op_class = 180;
877325151a3SRui Paulo 
878325151a3SRui Paulo 		return HOSTAPD_MODE_IEEE80211AD;
8795b9c547cSRui Paulo 	}
8805b9c547cSRui Paulo 
881325151a3SRui Paulo 	return NUM_HOSTAPD_MODES;
8825b9c547cSRui Paulo }
8835b9c547cSRui Paulo 
8845b9c547cSRui Paulo 
885*4bc52338SCy Schubert int ieee80211_chaninfo_to_channel(unsigned int freq, enum chan_width chanwidth,
886*4bc52338SCy Schubert 				  int sec_channel, u8 *op_class, u8 *channel)
887*4bc52338SCy Schubert {
888*4bc52338SCy Schubert 	int vht = CHAN_WIDTH_UNKNOWN;
889*4bc52338SCy Schubert 
890*4bc52338SCy Schubert 	switch (chanwidth) {
891*4bc52338SCy Schubert 	case CHAN_WIDTH_UNKNOWN:
892*4bc52338SCy Schubert 	case CHAN_WIDTH_20_NOHT:
893*4bc52338SCy Schubert 	case CHAN_WIDTH_20:
894*4bc52338SCy Schubert 	case CHAN_WIDTH_40:
895*4bc52338SCy Schubert 		vht = VHT_CHANWIDTH_USE_HT;
896*4bc52338SCy Schubert 		break;
897*4bc52338SCy Schubert 	case CHAN_WIDTH_80:
898*4bc52338SCy Schubert 		vht = VHT_CHANWIDTH_80MHZ;
899*4bc52338SCy Schubert 		break;
900*4bc52338SCy Schubert 	case CHAN_WIDTH_80P80:
901*4bc52338SCy Schubert 		vht = VHT_CHANWIDTH_80P80MHZ;
902*4bc52338SCy Schubert 		break;
903*4bc52338SCy Schubert 	case CHAN_WIDTH_160:
904*4bc52338SCy Schubert 		vht = VHT_CHANWIDTH_160MHZ;
905*4bc52338SCy Schubert 		break;
906*4bc52338SCy Schubert 	}
907*4bc52338SCy Schubert 
908*4bc52338SCy Schubert 	if (ieee80211_freq_to_channel_ext(freq, sec_channel, vht, op_class,
909*4bc52338SCy Schubert 					  channel) == NUM_HOSTAPD_MODES) {
910*4bc52338SCy Schubert 		wpa_printf(MSG_WARNING,
911*4bc52338SCy Schubert 			   "Cannot determine operating class and channel (freq=%u chanwidth=%d sec_channel=%d)",
912*4bc52338SCy Schubert 			   freq, chanwidth, sec_channel);
913*4bc52338SCy Schubert 		return -1;
914*4bc52338SCy Schubert 	}
915*4bc52338SCy Schubert 
916*4bc52338SCy Schubert 	return 0;
917*4bc52338SCy Schubert }
918*4bc52338SCy Schubert 
919*4bc52338SCy Schubert 
920325151a3SRui Paulo static const char *const us_op_class_cc[] = {
9215b9c547cSRui Paulo 	"US", "CA", NULL
9225b9c547cSRui Paulo };
9235b9c547cSRui Paulo 
924325151a3SRui Paulo static const char *const eu_op_class_cc[] = {
9255b9c547cSRui Paulo 	"AL", "AM", "AT", "AZ", "BA", "BE", "BG", "BY", "CH", "CY", "CZ", "DE",
9265b9c547cSRui Paulo 	"DK", "EE", "EL", "ES", "FI", "FR", "GE", "HR", "HU", "IE", "IS", "IT",
9275b9c547cSRui Paulo 	"LI", "LT", "LU", "LV", "MD", "ME", "MK", "MT", "NL", "NO", "PL", "PT",
9285b9c547cSRui Paulo 	"RO", "RS", "RU", "SE", "SI", "SK", "TR", "UA", "UK", NULL
9295b9c547cSRui Paulo };
9305b9c547cSRui Paulo 
931325151a3SRui Paulo static const char *const jp_op_class_cc[] = {
9325b9c547cSRui Paulo 	"JP", NULL
9335b9c547cSRui Paulo };
9345b9c547cSRui Paulo 
935325151a3SRui Paulo static const char *const cn_op_class_cc[] = {
936325151a3SRui Paulo 	"CN", NULL
9375b9c547cSRui Paulo };
9385b9c547cSRui Paulo 
9395b9c547cSRui Paulo 
940325151a3SRui Paulo static int country_match(const char *const cc[], const char *const country)
9415b9c547cSRui Paulo {
9425b9c547cSRui Paulo 	int i;
9435b9c547cSRui Paulo 
9445b9c547cSRui Paulo 	if (country == NULL)
9455b9c547cSRui Paulo 		return 0;
9465b9c547cSRui Paulo 	for (i = 0; cc[i]; i++) {
9475b9c547cSRui Paulo 		if (cc[i][0] == country[0] && cc[i][1] == country[1])
9485b9c547cSRui Paulo 			return 1;
9495b9c547cSRui Paulo 	}
9505b9c547cSRui Paulo 
9515b9c547cSRui Paulo 	return 0;
9525b9c547cSRui Paulo }
9535b9c547cSRui Paulo 
9545b9c547cSRui Paulo 
9555b9c547cSRui Paulo static int ieee80211_chan_to_freq_us(u8 op_class, u8 chan)
9565b9c547cSRui Paulo {
9575b9c547cSRui Paulo 	switch (op_class) {
9585b9c547cSRui Paulo 	case 12: /* channels 1..11 */
9595b9c547cSRui Paulo 	case 32: /* channels 1..7; 40 MHz */
9605b9c547cSRui Paulo 	case 33: /* channels 5..11; 40 MHz */
9615b9c547cSRui Paulo 		if (chan < 1 || chan > 11)
9625b9c547cSRui Paulo 			return -1;
9635b9c547cSRui Paulo 		return 2407 + 5 * chan;
9645b9c547cSRui Paulo 	case 1: /* channels 36,40,44,48 */
9655b9c547cSRui Paulo 	case 2: /* channels 52,56,60,64; dfs */
9665b9c547cSRui Paulo 	case 22: /* channels 36,44; 40 MHz */
9675b9c547cSRui Paulo 	case 23: /* channels 52,60; 40 MHz */
9685b9c547cSRui Paulo 	case 27: /* channels 40,48; 40 MHz */
9695b9c547cSRui Paulo 	case 28: /* channels 56,64; 40 MHz */
9705b9c547cSRui Paulo 		if (chan < 36 || chan > 64)
9715b9c547cSRui Paulo 			return -1;
9725b9c547cSRui Paulo 		return 5000 + 5 * chan;
9735b9c547cSRui Paulo 	case 4: /* channels 100-144 */
9745b9c547cSRui Paulo 	case 24: /* channels 100-140; 40 MHz */
9755b9c547cSRui Paulo 		if (chan < 100 || chan > 144)
9765b9c547cSRui Paulo 			return -1;
9775b9c547cSRui Paulo 		return 5000 + 5 * chan;
9785b9c547cSRui Paulo 	case 3: /* channels 149,153,157,161 */
9795b9c547cSRui Paulo 	case 25: /* channels 149,157; 40 MHz */
9805b9c547cSRui Paulo 	case 26: /* channels 149,157; 40 MHz */
9815b9c547cSRui Paulo 	case 30: /* channels 153,161; 40 MHz */
9825b9c547cSRui Paulo 	case 31: /* channels 153,161; 40 MHz */
9835b9c547cSRui Paulo 		if (chan < 149 || chan > 161)
9845b9c547cSRui Paulo 			return -1;
9855b9c547cSRui Paulo 		return 5000 + 5 * chan;
986325151a3SRui Paulo 	case 5: /* channels 149,153,157,161,165 */
987325151a3SRui Paulo 		if (chan < 149 || chan > 165)
988325151a3SRui Paulo 			return -1;
989325151a3SRui Paulo 		return 5000 + 5 * chan;
9905b9c547cSRui Paulo 	case 34: /* 60 GHz band, channels 1..3 */
9915b9c547cSRui Paulo 		if (chan < 1 || chan > 3)
9925b9c547cSRui Paulo 			return -1;
9935b9c547cSRui Paulo 		return 56160 + 2160 * chan;
9945b9c547cSRui Paulo 	}
9955b9c547cSRui Paulo 	return -1;
9965b9c547cSRui Paulo }
9975b9c547cSRui Paulo 
9985b9c547cSRui Paulo 
9995b9c547cSRui Paulo static int ieee80211_chan_to_freq_eu(u8 op_class, u8 chan)
10005b9c547cSRui Paulo {
10015b9c547cSRui Paulo 	switch (op_class) {
10025b9c547cSRui Paulo 	case 4: /* channels 1..13 */
10035b9c547cSRui Paulo 	case 11: /* channels 1..9; 40 MHz */
10045b9c547cSRui Paulo 	case 12: /* channels 5..13; 40 MHz */
10055b9c547cSRui Paulo 		if (chan < 1 || chan > 13)
10065b9c547cSRui Paulo 			return -1;
10075b9c547cSRui Paulo 		return 2407 + 5 * chan;
10085b9c547cSRui Paulo 	case 1: /* channels 36,40,44,48 */
10095b9c547cSRui Paulo 	case 2: /* channels 52,56,60,64; dfs */
10105b9c547cSRui Paulo 	case 5: /* channels 36,44; 40 MHz */
10115b9c547cSRui Paulo 	case 6: /* channels 52,60; 40 MHz */
10125b9c547cSRui Paulo 	case 8: /* channels 40,48; 40 MHz */
10135b9c547cSRui Paulo 	case 9: /* channels 56,64; 40 MHz */
10145b9c547cSRui Paulo 		if (chan < 36 || chan > 64)
10155b9c547cSRui Paulo 			return -1;
10165b9c547cSRui Paulo 		return 5000 + 5 * chan;
10175b9c547cSRui Paulo 	case 3: /* channels 100-140 */
10185b9c547cSRui Paulo 	case 7: /* channels 100-132; 40 MHz */
10195b9c547cSRui Paulo 	case 10: /* channels 104-136; 40 MHz */
10205b9c547cSRui Paulo 	case 16: /* channels 100-140 */
10215b9c547cSRui Paulo 		if (chan < 100 || chan > 140)
10225b9c547cSRui Paulo 			return -1;
10235b9c547cSRui Paulo 		return 5000 + 5 * chan;
10245b9c547cSRui Paulo 	case 17: /* channels 149,153,157,161,165,169 */
10255b9c547cSRui Paulo 		if (chan < 149 || chan > 169)
10265b9c547cSRui Paulo 			return -1;
10275b9c547cSRui Paulo 		return 5000 + 5 * chan;
10285b9c547cSRui Paulo 	case 18: /* 60 GHz band, channels 1..4 */
10295b9c547cSRui Paulo 		if (chan < 1 || chan > 4)
10305b9c547cSRui Paulo 			return -1;
10315b9c547cSRui Paulo 		return 56160 + 2160 * chan;
10325b9c547cSRui Paulo 	}
10335b9c547cSRui Paulo 	return -1;
10345b9c547cSRui Paulo }
10355b9c547cSRui Paulo 
10365b9c547cSRui Paulo 
10375b9c547cSRui Paulo static int ieee80211_chan_to_freq_jp(u8 op_class, u8 chan)
10385b9c547cSRui Paulo {
10395b9c547cSRui Paulo 	switch (op_class) {
10405b9c547cSRui Paulo 	case 30: /* channels 1..13 */
10415b9c547cSRui Paulo 	case 56: /* channels 1..9; 40 MHz */
10425b9c547cSRui Paulo 	case 57: /* channels 5..13; 40 MHz */
10435b9c547cSRui Paulo 		if (chan < 1 || chan > 13)
10445b9c547cSRui Paulo 			return -1;
10455b9c547cSRui Paulo 		return 2407 + 5 * chan;
10465b9c547cSRui Paulo 	case 31: /* channel 14 */
10475b9c547cSRui Paulo 		if (chan != 14)
10485b9c547cSRui Paulo 			return -1;
10495b9c547cSRui Paulo 		return 2414 + 5 * chan;
10505b9c547cSRui Paulo 	case 1: /* channels 34,38,42,46(old) or 36,40,44,48 */
10515b9c547cSRui Paulo 	case 32: /* channels 52,56,60,64 */
10525b9c547cSRui Paulo 	case 33: /* channels 52,56,60,64 */
10535b9c547cSRui Paulo 	case 36: /* channels 36,44; 40 MHz */
10545b9c547cSRui Paulo 	case 37: /* channels 52,60; 40 MHz */
10555b9c547cSRui Paulo 	case 38: /* channels 52,60; 40 MHz */
10565b9c547cSRui Paulo 	case 41: /* channels 40,48; 40 MHz */
10575b9c547cSRui Paulo 	case 42: /* channels 56,64; 40 MHz */
10585b9c547cSRui Paulo 	case 43: /* channels 56,64; 40 MHz */
10595b9c547cSRui Paulo 		if (chan < 34 || chan > 64)
10605b9c547cSRui Paulo 			return -1;
10615b9c547cSRui Paulo 		return 5000 + 5 * chan;
10625b9c547cSRui Paulo 	case 34: /* channels 100-140 */
10635b9c547cSRui Paulo 	case 35: /* channels 100-140 */
10645b9c547cSRui Paulo 	case 39: /* channels 100-132; 40 MHz */
10655b9c547cSRui Paulo 	case 40: /* channels 100-132; 40 MHz */
10665b9c547cSRui Paulo 	case 44: /* channels 104-136; 40 MHz */
10675b9c547cSRui Paulo 	case 45: /* channels 104-136; 40 MHz */
10685b9c547cSRui Paulo 	case 58: /* channels 100-140 */
10695b9c547cSRui Paulo 		if (chan < 100 || chan > 140)
10705b9c547cSRui Paulo 			return -1;
10715b9c547cSRui Paulo 		return 5000 + 5 * chan;
10725b9c547cSRui Paulo 	case 59: /* 60 GHz band, channels 1..4 */
10735b9c547cSRui Paulo 		if (chan < 1 || chan > 3)
10745b9c547cSRui Paulo 			return -1;
10755b9c547cSRui Paulo 		return 56160 + 2160 * chan;
10765b9c547cSRui Paulo 	}
10775b9c547cSRui Paulo 	return -1;
10785b9c547cSRui Paulo }
10795b9c547cSRui Paulo 
10805b9c547cSRui Paulo 
10815b9c547cSRui Paulo static int ieee80211_chan_to_freq_cn(u8 op_class, u8 chan)
10825b9c547cSRui Paulo {
10835b9c547cSRui Paulo 	switch (op_class) {
10845b9c547cSRui Paulo 	case 7: /* channels 1..13 */
10855b9c547cSRui Paulo 	case 8: /* channels 1..9; 40 MHz */
10865b9c547cSRui Paulo 	case 9: /* channels 5..13; 40 MHz */
10875b9c547cSRui Paulo 		if (chan < 1 || chan > 13)
10885b9c547cSRui Paulo 			return -1;
10895b9c547cSRui Paulo 		return 2407 + 5 * chan;
10905b9c547cSRui Paulo 	case 1: /* channels 36,40,44,48 */
10915b9c547cSRui Paulo 	case 2: /* channels 52,56,60,64; dfs */
10925b9c547cSRui Paulo 	case 4: /* channels 36,44; 40 MHz */
10935b9c547cSRui Paulo 	case 5: /* channels 52,60; 40 MHz */
10945b9c547cSRui Paulo 		if (chan < 36 || chan > 64)
10955b9c547cSRui Paulo 			return -1;
10965b9c547cSRui Paulo 		return 5000 + 5 * chan;
10975b9c547cSRui Paulo 	case 3: /* channels 149,153,157,161,165 */
10985b9c547cSRui Paulo 	case 6: /* channels 149,157; 40 MHz */
10995b9c547cSRui Paulo 		if (chan < 149 || chan > 165)
11005b9c547cSRui Paulo 			return -1;
11015b9c547cSRui Paulo 		return 5000 + 5 * chan;
11025b9c547cSRui Paulo 	}
11035b9c547cSRui Paulo 	return -1;
11045b9c547cSRui Paulo }
11055b9c547cSRui Paulo 
11065b9c547cSRui Paulo 
11075b9c547cSRui Paulo static int ieee80211_chan_to_freq_global(u8 op_class, u8 chan)
11085b9c547cSRui Paulo {
11095b9c547cSRui Paulo 	/* Table E-4 in IEEE Std 802.11-2012 - Global operating classes */
11105b9c547cSRui Paulo 	switch (op_class) {
11115b9c547cSRui Paulo 	case 81:
11125b9c547cSRui Paulo 		/* channels 1..13 */
11135b9c547cSRui Paulo 		if (chan < 1 || chan > 13)
11145b9c547cSRui Paulo 			return -1;
11155b9c547cSRui Paulo 		return 2407 + 5 * chan;
11165b9c547cSRui Paulo 	case 82:
11175b9c547cSRui Paulo 		/* channel 14 */
11185b9c547cSRui Paulo 		if (chan != 14)
11195b9c547cSRui Paulo 			return -1;
11205b9c547cSRui Paulo 		return 2414 + 5 * chan;
11215b9c547cSRui Paulo 	case 83: /* channels 1..9; 40 MHz */
11225b9c547cSRui Paulo 	case 84: /* channels 5..13; 40 MHz */
11235b9c547cSRui Paulo 		if (chan < 1 || chan > 13)
11245b9c547cSRui Paulo 			return -1;
11255b9c547cSRui Paulo 		return 2407 + 5 * chan;
11265b9c547cSRui Paulo 	case 115: /* channels 36,40,44,48; indoor only */
11275b9c547cSRui Paulo 	case 116: /* channels 36,44; 40 MHz; indoor only */
11285b9c547cSRui Paulo 	case 117: /* channels 40,48; 40 MHz; indoor only */
11295b9c547cSRui Paulo 	case 118: /* channels 52,56,60,64; dfs */
11305b9c547cSRui Paulo 	case 119: /* channels 52,60; 40 MHz; dfs */
11315b9c547cSRui Paulo 	case 120: /* channels 56,64; 40 MHz; dfs */
11325b9c547cSRui Paulo 		if (chan < 36 || chan > 64)
11335b9c547cSRui Paulo 			return -1;
11345b9c547cSRui Paulo 		return 5000 + 5 * chan;
11355b9c547cSRui Paulo 	case 121: /* channels 100-140 */
11365b9c547cSRui Paulo 	case 122: /* channels 100-142; 40 MHz */
11375b9c547cSRui Paulo 	case 123: /* channels 104-136; 40 MHz */
11385b9c547cSRui Paulo 		if (chan < 100 || chan > 140)
11395b9c547cSRui Paulo 			return -1;
11405b9c547cSRui Paulo 		return 5000 + 5 * chan;
11415b9c547cSRui Paulo 	case 124: /* channels 149,153,157,161 */
11425b9c547cSRui Paulo 	case 126: /* channels 149,157; 40 MHz */
11435b9c547cSRui Paulo 	case 127: /* channels 153,161; 40 MHz */
11445b9c547cSRui Paulo 		if (chan < 149 || chan > 161)
11455b9c547cSRui Paulo 			return -1;
11465b9c547cSRui Paulo 		return 5000 + 5 * chan;
1147325151a3SRui Paulo 	case 125: /* channels 149,153,157,161,165,169 */
1148325151a3SRui Paulo 		if (chan < 149 || chan > 169)
1149325151a3SRui Paulo 			return -1;
1150325151a3SRui Paulo 		return 5000 + 5 * chan;
11515b9c547cSRui Paulo 	case 128: /* center freqs 42, 58, 106, 122, 138, 155; 80 MHz */
11525b9c547cSRui Paulo 	case 130: /* center freqs 42, 58, 106, 122, 138, 155; 80 MHz */
11535b9c547cSRui Paulo 		if (chan < 36 || chan > 161)
11545b9c547cSRui Paulo 			return -1;
11555b9c547cSRui Paulo 		return 5000 + 5 * chan;
11565b9c547cSRui Paulo 	case 129: /* center freqs 50, 114; 160 MHz */
115785732ac8SCy Schubert 		if (chan < 36 || chan > 128)
11585b9c547cSRui Paulo 			return -1;
11595b9c547cSRui Paulo 		return 5000 + 5 * chan;
11605b9c547cSRui Paulo 	case 180: /* 60 GHz band, channels 1..4 */
11615b9c547cSRui Paulo 		if (chan < 1 || chan > 4)
11625b9c547cSRui Paulo 			return -1;
11635b9c547cSRui Paulo 		return 56160 + 2160 * chan;
11645b9c547cSRui Paulo 	}
11655b9c547cSRui Paulo 	return -1;
11665b9c547cSRui Paulo }
11675b9c547cSRui Paulo 
11685b9c547cSRui Paulo /**
11695b9c547cSRui Paulo  * ieee80211_chan_to_freq - Convert channel info to frequency
11705b9c547cSRui Paulo  * @country: Country code, if known; otherwise, global operating class is used
11715b9c547cSRui Paulo  * @op_class: Operating class
11725b9c547cSRui Paulo  * @chan: Channel number
11735b9c547cSRui Paulo  * Returns: Frequency in MHz or -1 if the specified channel is unknown
11745b9c547cSRui Paulo  */
11755b9c547cSRui Paulo int ieee80211_chan_to_freq(const char *country, u8 op_class, u8 chan)
11765b9c547cSRui Paulo {
11775b9c547cSRui Paulo 	int freq;
11785b9c547cSRui Paulo 
11795b9c547cSRui Paulo 	if (country_match(us_op_class_cc, country)) {
11805b9c547cSRui Paulo 		freq = ieee80211_chan_to_freq_us(op_class, chan);
11815b9c547cSRui Paulo 		if (freq > 0)
11825b9c547cSRui Paulo 			return freq;
11835b9c547cSRui Paulo 	}
11845b9c547cSRui Paulo 
11855b9c547cSRui Paulo 	if (country_match(eu_op_class_cc, country)) {
11865b9c547cSRui Paulo 		freq = ieee80211_chan_to_freq_eu(op_class, chan);
11875b9c547cSRui Paulo 		if (freq > 0)
11885b9c547cSRui Paulo 			return freq;
11895b9c547cSRui Paulo 	}
11905b9c547cSRui Paulo 
11915b9c547cSRui Paulo 	if (country_match(jp_op_class_cc, country)) {
11925b9c547cSRui Paulo 		freq = ieee80211_chan_to_freq_jp(op_class, chan);
11935b9c547cSRui Paulo 		if (freq > 0)
11945b9c547cSRui Paulo 			return freq;
11955b9c547cSRui Paulo 	}
11965b9c547cSRui Paulo 
11975b9c547cSRui Paulo 	if (country_match(cn_op_class_cc, country)) {
11985b9c547cSRui Paulo 		freq = ieee80211_chan_to_freq_cn(op_class, chan);
11995b9c547cSRui Paulo 		if (freq > 0)
12005b9c547cSRui Paulo 			return freq;
12015b9c547cSRui Paulo 	}
12025b9c547cSRui Paulo 
12035b9c547cSRui Paulo 	return ieee80211_chan_to_freq_global(op_class, chan);
12045b9c547cSRui Paulo }
12055b9c547cSRui Paulo 
12065b9c547cSRui Paulo 
120785732ac8SCy Schubert int ieee80211_is_dfs(int freq, const struct hostapd_hw_modes *modes,
120885732ac8SCy Schubert 		     u16 num_modes)
12095b9c547cSRui Paulo {
121085732ac8SCy Schubert 	int i, j;
121185732ac8SCy Schubert 
121285732ac8SCy Schubert 	if (!modes || !num_modes)
121385732ac8SCy Schubert 		return (freq >= 5260 && freq <= 5320) ||
121485732ac8SCy Schubert 			(freq >= 5500 && freq <= 5700);
121585732ac8SCy Schubert 
121685732ac8SCy Schubert 	for (i = 0; i < num_modes; i++) {
121785732ac8SCy Schubert 		for (j = 0; j < modes[i].num_channels; j++) {
121885732ac8SCy Schubert 			if (modes[i].channels[j].freq == freq &&
121985732ac8SCy Schubert 			    (modes[i].channels[j].flag & HOSTAPD_CHAN_RADAR))
122085732ac8SCy Schubert 				return 1;
122185732ac8SCy Schubert 		}
122285732ac8SCy Schubert 	}
122385732ac8SCy Schubert 
122485732ac8SCy Schubert 	return 0;
12255b9c547cSRui Paulo }
12265b9c547cSRui Paulo 
12275b9c547cSRui Paulo 
12285b9c547cSRui Paulo static int is_11b(u8 rate)
12295b9c547cSRui Paulo {
12305b9c547cSRui Paulo 	return rate == 0x02 || rate == 0x04 || rate == 0x0b || rate == 0x16;
12315b9c547cSRui Paulo }
12325b9c547cSRui Paulo 
12335b9c547cSRui Paulo 
12345b9c547cSRui Paulo int supp_rates_11b_only(struct ieee802_11_elems *elems)
12355b9c547cSRui Paulo {
12365b9c547cSRui Paulo 	int num_11b = 0, num_others = 0;
12375b9c547cSRui Paulo 	int i;
12385b9c547cSRui Paulo 
12395b9c547cSRui Paulo 	if (elems->supp_rates == NULL && elems->ext_supp_rates == NULL)
12405b9c547cSRui Paulo 		return 0;
12415b9c547cSRui Paulo 
12425b9c547cSRui Paulo 	for (i = 0; elems->supp_rates && i < elems->supp_rates_len; i++) {
12435b9c547cSRui Paulo 		if (is_11b(elems->supp_rates[i]))
12445b9c547cSRui Paulo 			num_11b++;
12455b9c547cSRui Paulo 		else
12465b9c547cSRui Paulo 			num_others++;
12475b9c547cSRui Paulo 	}
12485b9c547cSRui Paulo 
12495b9c547cSRui Paulo 	for (i = 0; elems->ext_supp_rates && i < elems->ext_supp_rates_len;
12505b9c547cSRui Paulo 	     i++) {
12515b9c547cSRui Paulo 		if (is_11b(elems->ext_supp_rates[i]))
12525b9c547cSRui Paulo 			num_11b++;
12535b9c547cSRui Paulo 		else
12545b9c547cSRui Paulo 			num_others++;
12555b9c547cSRui Paulo 	}
12565b9c547cSRui Paulo 
12575b9c547cSRui Paulo 	return num_11b > 0 && num_others == 0;
12585b9c547cSRui Paulo }
12595b9c547cSRui Paulo 
12605b9c547cSRui Paulo 
12615b9c547cSRui Paulo const char * fc2str(u16 fc)
12625b9c547cSRui Paulo {
12635b9c547cSRui Paulo 	u16 stype = WLAN_FC_GET_STYPE(fc);
12645b9c547cSRui Paulo #define C2S(x) case x: return #x;
12655b9c547cSRui Paulo 
12665b9c547cSRui Paulo 	switch (WLAN_FC_GET_TYPE(fc)) {
12675b9c547cSRui Paulo 	case WLAN_FC_TYPE_MGMT:
12685b9c547cSRui Paulo 		switch (stype) {
12695b9c547cSRui Paulo 		C2S(WLAN_FC_STYPE_ASSOC_REQ)
12705b9c547cSRui Paulo 		C2S(WLAN_FC_STYPE_ASSOC_RESP)
12715b9c547cSRui Paulo 		C2S(WLAN_FC_STYPE_REASSOC_REQ)
12725b9c547cSRui Paulo 		C2S(WLAN_FC_STYPE_REASSOC_RESP)
12735b9c547cSRui Paulo 		C2S(WLAN_FC_STYPE_PROBE_REQ)
12745b9c547cSRui Paulo 		C2S(WLAN_FC_STYPE_PROBE_RESP)
12755b9c547cSRui Paulo 		C2S(WLAN_FC_STYPE_BEACON)
12765b9c547cSRui Paulo 		C2S(WLAN_FC_STYPE_ATIM)
12775b9c547cSRui Paulo 		C2S(WLAN_FC_STYPE_DISASSOC)
12785b9c547cSRui Paulo 		C2S(WLAN_FC_STYPE_AUTH)
12795b9c547cSRui Paulo 		C2S(WLAN_FC_STYPE_DEAUTH)
12805b9c547cSRui Paulo 		C2S(WLAN_FC_STYPE_ACTION)
12815b9c547cSRui Paulo 		}
12825b9c547cSRui Paulo 		break;
12835b9c547cSRui Paulo 	case WLAN_FC_TYPE_CTRL:
12845b9c547cSRui Paulo 		switch (stype) {
12855b9c547cSRui Paulo 		C2S(WLAN_FC_STYPE_PSPOLL)
12865b9c547cSRui Paulo 		C2S(WLAN_FC_STYPE_RTS)
12875b9c547cSRui Paulo 		C2S(WLAN_FC_STYPE_CTS)
12885b9c547cSRui Paulo 		C2S(WLAN_FC_STYPE_ACK)
12895b9c547cSRui Paulo 		C2S(WLAN_FC_STYPE_CFEND)
12905b9c547cSRui Paulo 		C2S(WLAN_FC_STYPE_CFENDACK)
12915b9c547cSRui Paulo 		}
12925b9c547cSRui Paulo 		break;
12935b9c547cSRui Paulo 	case WLAN_FC_TYPE_DATA:
12945b9c547cSRui Paulo 		switch (stype) {
12955b9c547cSRui Paulo 		C2S(WLAN_FC_STYPE_DATA)
12965b9c547cSRui Paulo 		C2S(WLAN_FC_STYPE_DATA_CFACK)
12975b9c547cSRui Paulo 		C2S(WLAN_FC_STYPE_DATA_CFPOLL)
12985b9c547cSRui Paulo 		C2S(WLAN_FC_STYPE_DATA_CFACKPOLL)
12995b9c547cSRui Paulo 		C2S(WLAN_FC_STYPE_NULLFUNC)
13005b9c547cSRui Paulo 		C2S(WLAN_FC_STYPE_CFACK)
13015b9c547cSRui Paulo 		C2S(WLAN_FC_STYPE_CFPOLL)
13025b9c547cSRui Paulo 		C2S(WLAN_FC_STYPE_CFACKPOLL)
13035b9c547cSRui Paulo 		C2S(WLAN_FC_STYPE_QOS_DATA)
13045b9c547cSRui Paulo 		C2S(WLAN_FC_STYPE_QOS_DATA_CFACK)
13055b9c547cSRui Paulo 		C2S(WLAN_FC_STYPE_QOS_DATA_CFPOLL)
13065b9c547cSRui Paulo 		C2S(WLAN_FC_STYPE_QOS_DATA_CFACKPOLL)
13075b9c547cSRui Paulo 		C2S(WLAN_FC_STYPE_QOS_NULL)
13085b9c547cSRui Paulo 		C2S(WLAN_FC_STYPE_QOS_CFPOLL)
13095b9c547cSRui Paulo 		C2S(WLAN_FC_STYPE_QOS_CFACKPOLL)
13105b9c547cSRui Paulo 		}
13115b9c547cSRui Paulo 		break;
13125b9c547cSRui Paulo 	}
13135b9c547cSRui Paulo 	return "WLAN_FC_TYPE_UNKNOWN";
13145b9c547cSRui Paulo #undef C2S
13155b9c547cSRui Paulo }
1316325151a3SRui Paulo 
1317325151a3SRui Paulo 
1318325151a3SRui Paulo int mb_ies_info_by_ies(struct mb_ies_info *info, const u8 *ies_buf,
1319325151a3SRui Paulo 		       size_t ies_len)
1320325151a3SRui Paulo {
1321*4bc52338SCy Schubert 	const struct element *elem;
1322*4bc52338SCy Schubert 
1323325151a3SRui Paulo 	os_memset(info, 0, sizeof(*info));
1324325151a3SRui Paulo 
1325*4bc52338SCy Schubert 	if (!ies_buf)
1326*4bc52338SCy Schubert 		return 0;
1327325151a3SRui Paulo 
1328*4bc52338SCy Schubert 	for_each_element_id(elem, WLAN_EID_MULTI_BAND, ies_buf, ies_len) {
1329*4bc52338SCy Schubert 		if (info->nof_ies >= MAX_NOF_MB_IES_SUPPORTED)
1330*4bc52338SCy Schubert 			return 0;
1331325151a3SRui Paulo 
1332*4bc52338SCy Schubert 		wpa_printf(MSG_DEBUG, "MB IE of %u bytes found",
1333*4bc52338SCy Schubert 			   elem->datalen + 2);
1334*4bc52338SCy Schubert 		info->ies[info->nof_ies].ie = elem->data;
1335*4bc52338SCy Schubert 		info->ies[info->nof_ies].ie_len = elem->datalen;
1336325151a3SRui Paulo 		info->nof_ies++;
1337325151a3SRui Paulo 	}
1338325151a3SRui Paulo 
1339*4bc52338SCy Schubert 	if (!for_each_element_completed(elem, ies_buf, ies_len)) {
1340*4bc52338SCy Schubert 		wpa_hexdump(MSG_DEBUG, "Truncated IEs", ies_buf, ies_len);
1341*4bc52338SCy Schubert 		return -1;
1342325151a3SRui Paulo 	}
1343325151a3SRui Paulo 
1344325151a3SRui Paulo 	return 0;
1345325151a3SRui Paulo }
1346325151a3SRui Paulo 
1347325151a3SRui Paulo 
1348325151a3SRui Paulo struct wpabuf * mb_ies_by_info(struct mb_ies_info *info)
1349325151a3SRui Paulo {
1350325151a3SRui Paulo 	struct wpabuf *mb_ies = NULL;
1351325151a3SRui Paulo 
1352325151a3SRui Paulo 	WPA_ASSERT(info != NULL);
1353325151a3SRui Paulo 
1354325151a3SRui Paulo 	if (info->nof_ies) {
1355325151a3SRui Paulo 		u8 i;
1356325151a3SRui Paulo 		size_t mb_ies_size = 0;
1357325151a3SRui Paulo 
1358325151a3SRui Paulo 		for (i = 0; i < info->nof_ies; i++)
1359325151a3SRui Paulo 			mb_ies_size += 2 + info->ies[i].ie_len;
1360325151a3SRui Paulo 
1361325151a3SRui Paulo 		mb_ies = wpabuf_alloc(mb_ies_size);
1362325151a3SRui Paulo 		if (mb_ies) {
1363325151a3SRui Paulo 			for (i = 0; i < info->nof_ies; i++) {
1364325151a3SRui Paulo 				wpabuf_put_u8(mb_ies, WLAN_EID_MULTI_BAND);
1365325151a3SRui Paulo 				wpabuf_put_u8(mb_ies, info->ies[i].ie_len);
1366325151a3SRui Paulo 				wpabuf_put_data(mb_ies,
1367325151a3SRui Paulo 						info->ies[i].ie,
1368325151a3SRui Paulo 						info->ies[i].ie_len);
1369325151a3SRui Paulo 			}
1370325151a3SRui Paulo 		}
1371325151a3SRui Paulo 	}
1372325151a3SRui Paulo 
1373325151a3SRui Paulo 	return mb_ies;
1374325151a3SRui Paulo }
1375780fb4a2SCy Schubert 
1376780fb4a2SCy Schubert 
1377780fb4a2SCy Schubert const struct oper_class_map global_op_class[] = {
1378780fb4a2SCy Schubert 	{ HOSTAPD_MODE_IEEE80211G, 81, 1, 13, 1, BW20, P2P_SUPP },
1379780fb4a2SCy Schubert 	{ HOSTAPD_MODE_IEEE80211G, 82, 14, 14, 1, BW20, NO_P2P_SUPP },
1380780fb4a2SCy Schubert 
1381780fb4a2SCy Schubert 	/* Do not enable HT40 on 2.4 GHz for P2P use for now */
1382780fb4a2SCy Schubert 	{ HOSTAPD_MODE_IEEE80211G, 83, 1, 9, 1, BW40PLUS, NO_P2P_SUPP },
1383780fb4a2SCy Schubert 	{ HOSTAPD_MODE_IEEE80211G, 84, 5, 13, 1, BW40MINUS, NO_P2P_SUPP },
1384780fb4a2SCy Schubert 
1385780fb4a2SCy Schubert 	{ HOSTAPD_MODE_IEEE80211A, 115, 36, 48, 4, BW20, P2P_SUPP },
1386780fb4a2SCy Schubert 	{ HOSTAPD_MODE_IEEE80211A, 116, 36, 44, 8, BW40PLUS, P2P_SUPP },
1387780fb4a2SCy Schubert 	{ HOSTAPD_MODE_IEEE80211A, 117, 40, 48, 8, BW40MINUS, P2P_SUPP },
1388780fb4a2SCy Schubert 	{ HOSTAPD_MODE_IEEE80211A, 118, 52, 64, 4, BW20, NO_P2P_SUPP },
1389780fb4a2SCy Schubert 	{ HOSTAPD_MODE_IEEE80211A, 119, 52, 60, 8, BW40PLUS, NO_P2P_SUPP },
1390780fb4a2SCy Schubert 	{ HOSTAPD_MODE_IEEE80211A, 120, 56, 64, 8, BW40MINUS, NO_P2P_SUPP },
1391780fb4a2SCy Schubert 	{ HOSTAPD_MODE_IEEE80211A, 121, 100, 140, 4, BW20, NO_P2P_SUPP },
1392780fb4a2SCy Schubert 	{ HOSTAPD_MODE_IEEE80211A, 122, 100, 132, 8, BW40PLUS, NO_P2P_SUPP },
1393780fb4a2SCy Schubert 	{ HOSTAPD_MODE_IEEE80211A, 123, 104, 136, 8, BW40MINUS, NO_P2P_SUPP },
1394780fb4a2SCy Schubert 	{ HOSTAPD_MODE_IEEE80211A, 124, 149, 161, 4, BW20, P2P_SUPP },
1395780fb4a2SCy Schubert 	{ HOSTAPD_MODE_IEEE80211A, 125, 149, 169, 4, BW20, P2P_SUPP },
1396780fb4a2SCy Schubert 	{ HOSTAPD_MODE_IEEE80211A, 126, 149, 157, 8, BW40PLUS, P2P_SUPP },
1397780fb4a2SCy Schubert 	{ HOSTAPD_MODE_IEEE80211A, 127, 153, 161, 8, BW40MINUS, P2P_SUPP },
1398780fb4a2SCy Schubert 
1399780fb4a2SCy Schubert 	/*
1400780fb4a2SCy Schubert 	 * IEEE P802.11ac/D7.0 Table E-4 actually talks about channel center
1401780fb4a2SCy Schubert 	 * frequency index 42, 58, 106, 122, 138, 155 with channel spacing of
1402780fb4a2SCy Schubert 	 * 80 MHz, but currently use the following definition for simplicity
1403780fb4a2SCy Schubert 	 * (these center frequencies are not actual channels, which makes
1404780fb4a2SCy Schubert 	 * wpas_p2p_allow_channel() fail). wpas_p2p_verify_80mhz() should take
1405780fb4a2SCy Schubert 	 * care of removing invalid channels.
1406780fb4a2SCy Schubert 	 */
1407780fb4a2SCy Schubert 	{ HOSTAPD_MODE_IEEE80211A, 128, 36, 161, 4, BW80, P2P_SUPP },
1408780fb4a2SCy Schubert 	{ HOSTAPD_MODE_IEEE80211A, 129, 50, 114, 16, BW160, P2P_SUPP },
1409780fb4a2SCy Schubert 	{ HOSTAPD_MODE_IEEE80211A, 130, 36, 161, 4, BW80P80, P2P_SUPP },
1410780fb4a2SCy Schubert 	{ HOSTAPD_MODE_IEEE80211AD, 180, 1, 4, 1, BW2160, P2P_SUPP },
1411780fb4a2SCy Schubert 	{ -1, 0, 0, 0, 0, BW20, NO_P2P_SUPP }
1412780fb4a2SCy Schubert };
1413780fb4a2SCy Schubert 
1414780fb4a2SCy Schubert 
1415780fb4a2SCy Schubert static enum phy_type ieee80211_phy_type_by_freq(int freq)
1416780fb4a2SCy Schubert {
1417780fb4a2SCy Schubert 	enum hostapd_hw_mode hw_mode;
1418780fb4a2SCy Schubert 	u8 channel;
1419780fb4a2SCy Schubert 
1420780fb4a2SCy Schubert 	hw_mode = ieee80211_freq_to_chan(freq, &channel);
1421780fb4a2SCy Schubert 
1422780fb4a2SCy Schubert 	switch (hw_mode) {
1423780fb4a2SCy Schubert 	case HOSTAPD_MODE_IEEE80211A:
1424780fb4a2SCy Schubert 		return PHY_TYPE_OFDM;
1425780fb4a2SCy Schubert 	case HOSTAPD_MODE_IEEE80211B:
1426780fb4a2SCy Schubert 		return PHY_TYPE_HRDSSS;
1427780fb4a2SCy Schubert 	case HOSTAPD_MODE_IEEE80211G:
1428780fb4a2SCy Schubert 		return PHY_TYPE_ERP;
1429780fb4a2SCy Schubert 	case HOSTAPD_MODE_IEEE80211AD:
1430780fb4a2SCy Schubert 		return PHY_TYPE_DMG;
1431780fb4a2SCy Schubert 	default:
1432780fb4a2SCy Schubert 		return PHY_TYPE_UNSPECIFIED;
1433780fb4a2SCy Schubert 	};
1434780fb4a2SCy Schubert }
1435780fb4a2SCy Schubert 
1436780fb4a2SCy Schubert 
1437780fb4a2SCy Schubert /* ieee80211_get_phy_type - Derive the phy type by freq and bandwidth */
1438780fb4a2SCy Schubert enum phy_type ieee80211_get_phy_type(int freq, int ht, int vht)
1439780fb4a2SCy Schubert {
1440780fb4a2SCy Schubert 	if (vht)
1441780fb4a2SCy Schubert 		return PHY_TYPE_VHT;
1442780fb4a2SCy Schubert 	if (ht)
1443780fb4a2SCy Schubert 		return PHY_TYPE_HT;
1444780fb4a2SCy Schubert 
1445780fb4a2SCy Schubert 	return ieee80211_phy_type_by_freq(freq);
1446780fb4a2SCy Schubert }
1447780fb4a2SCy Schubert 
1448780fb4a2SCy Schubert 
1449780fb4a2SCy Schubert size_t global_op_class_size = ARRAY_SIZE(global_op_class);
1450780fb4a2SCy Schubert 
1451780fb4a2SCy Schubert 
1452780fb4a2SCy Schubert /**
1453780fb4a2SCy Schubert  * get_ie - Fetch a specified information element from IEs buffer
1454780fb4a2SCy Schubert  * @ies: Information elements buffer
1455780fb4a2SCy Schubert  * @len: Information elements buffer length
1456780fb4a2SCy Schubert  * @eid: Information element identifier (WLAN_EID_*)
1457780fb4a2SCy Schubert  * Returns: Pointer to the information element (id field) or %NULL if not found
1458780fb4a2SCy Schubert  *
1459780fb4a2SCy Schubert  * This function returns the first matching information element in the IEs
1460780fb4a2SCy Schubert  * buffer or %NULL in case the element is not found.
1461780fb4a2SCy Schubert  */
1462780fb4a2SCy Schubert const u8 * get_ie(const u8 *ies, size_t len, u8 eid)
1463780fb4a2SCy Schubert {
1464*4bc52338SCy Schubert 	const struct element *elem;
1465780fb4a2SCy Schubert 
1466780fb4a2SCy Schubert 	if (!ies)
1467780fb4a2SCy Schubert 		return NULL;
1468780fb4a2SCy Schubert 
1469*4bc52338SCy Schubert 	for_each_element_id(elem, eid, ies, len)
1470*4bc52338SCy Schubert 		return &elem->id;
1471780fb4a2SCy Schubert 
1472780fb4a2SCy Schubert 	return NULL;
1473780fb4a2SCy Schubert }
1474780fb4a2SCy Schubert 
1475780fb4a2SCy Schubert 
147685732ac8SCy Schubert /**
147785732ac8SCy Schubert  * get_ie_ext - Fetch a specified extended information element from IEs buffer
147885732ac8SCy Schubert  * @ies: Information elements buffer
147985732ac8SCy Schubert  * @len: Information elements buffer length
148085732ac8SCy Schubert  * @ext: Information element extension identifier (WLAN_EID_EXT_*)
148185732ac8SCy Schubert  * Returns: Pointer to the information element (id field) or %NULL if not found
148285732ac8SCy Schubert  *
148385732ac8SCy Schubert  * This function returns the first matching information element in the IEs
148485732ac8SCy Schubert  * buffer or %NULL in case the element is not found.
148585732ac8SCy Schubert  */
148685732ac8SCy Schubert const u8 * get_ie_ext(const u8 *ies, size_t len, u8 ext)
148785732ac8SCy Schubert {
1488*4bc52338SCy Schubert 	const struct element *elem;
148985732ac8SCy Schubert 
149085732ac8SCy Schubert 	if (!ies)
149185732ac8SCy Schubert 		return NULL;
149285732ac8SCy Schubert 
1493*4bc52338SCy Schubert 	for_each_element_extid(elem, ext, ies, len)
1494*4bc52338SCy Schubert 		return &elem->id;
149585732ac8SCy Schubert 
1496*4bc52338SCy Schubert 	return NULL;
1497*4bc52338SCy Schubert }
149885732ac8SCy Schubert 
149985732ac8SCy Schubert 
1500*4bc52338SCy Schubert const u8 * get_vendor_ie(const u8 *ies, size_t len, u32 vendor_type)
1501*4bc52338SCy Schubert {
1502*4bc52338SCy Schubert 	const struct element *elem;
1503*4bc52338SCy Schubert 
1504*4bc52338SCy Schubert 	for_each_element_id(elem, WLAN_EID_VENDOR_SPECIFIC, ies, len) {
1505*4bc52338SCy Schubert 		if (elem->datalen >= 4 &&
1506*4bc52338SCy Schubert 		    vendor_type == WPA_GET_BE32(elem->data))
1507*4bc52338SCy Schubert 			return &elem->id;
150885732ac8SCy Schubert 	}
150985732ac8SCy Schubert 
151085732ac8SCy Schubert 	return NULL;
151185732ac8SCy Schubert }
151285732ac8SCy Schubert 
151385732ac8SCy Schubert 
1514780fb4a2SCy Schubert size_t mbo_add_ie(u8 *buf, size_t len, const u8 *attr, size_t attr_len)
1515780fb4a2SCy Schubert {
1516780fb4a2SCy Schubert 	/*
1517780fb4a2SCy Schubert 	 * MBO IE requires 6 bytes without the attributes: EID (1), length (1),
1518780fb4a2SCy Schubert 	 * OUI (3), OUI type (1).
1519780fb4a2SCy Schubert 	 */
1520780fb4a2SCy Schubert 	if (len < 6 + attr_len) {
1521780fb4a2SCy Schubert 		wpa_printf(MSG_DEBUG,
1522780fb4a2SCy Schubert 			   "MBO: Not enough room in buffer for MBO IE: buf len = %zu, attr_len = %zu",
1523780fb4a2SCy Schubert 			   len, attr_len);
1524780fb4a2SCy Schubert 		return 0;
1525780fb4a2SCy Schubert 	}
1526780fb4a2SCy Schubert 
1527780fb4a2SCy Schubert 	*buf++ = WLAN_EID_VENDOR_SPECIFIC;
1528780fb4a2SCy Schubert 	*buf++ = attr_len + 4;
1529780fb4a2SCy Schubert 	WPA_PUT_BE24(buf, OUI_WFA);
1530780fb4a2SCy Schubert 	buf += 3;
1531780fb4a2SCy Schubert 	*buf++ = MBO_OUI_TYPE;
1532780fb4a2SCy Schubert 	os_memcpy(buf, attr, attr_len);
1533780fb4a2SCy Schubert 
1534780fb4a2SCy Schubert 	return 6 + attr_len;
1535780fb4a2SCy Schubert }
153685732ac8SCy Schubert 
153785732ac8SCy Schubert 
1538*4bc52338SCy Schubert size_t add_multi_ap_ie(u8 *buf, size_t len, u8 value)
1539*4bc52338SCy Schubert {
1540*4bc52338SCy Schubert 	u8 *pos = buf;
1541*4bc52338SCy Schubert 
1542*4bc52338SCy Schubert 	if (len < 9)
1543*4bc52338SCy Schubert 		return 0;
1544*4bc52338SCy Schubert 
1545*4bc52338SCy Schubert 	*pos++ = WLAN_EID_VENDOR_SPECIFIC;
1546*4bc52338SCy Schubert 	*pos++ = 7; /* len */
1547*4bc52338SCy Schubert 	WPA_PUT_BE24(pos, OUI_WFA);
1548*4bc52338SCy Schubert 	pos += 3;
1549*4bc52338SCy Schubert 	*pos++ = MULTI_AP_OUI_TYPE;
1550*4bc52338SCy Schubert 	*pos++ = MULTI_AP_SUB_ELEM_TYPE;
1551*4bc52338SCy Schubert 	*pos++ = 1; /* len */
1552*4bc52338SCy Schubert 	*pos++ = value;
1553*4bc52338SCy Schubert 
1554*4bc52338SCy Schubert 	return pos - buf;
1555*4bc52338SCy Schubert }
1556*4bc52338SCy Schubert 
1557*4bc52338SCy Schubert 
155885732ac8SCy Schubert static const struct country_op_class us_op_class[] = {
155985732ac8SCy Schubert 	{ 1, 115 },
156085732ac8SCy Schubert 	{ 2, 118 },
156185732ac8SCy Schubert 	{ 3, 124 },
156285732ac8SCy Schubert 	{ 4, 121 },
156385732ac8SCy Schubert 	{ 5, 125 },
156485732ac8SCy Schubert 	{ 12, 81 },
156585732ac8SCy Schubert 	{ 22, 116 },
156685732ac8SCy Schubert 	{ 23, 119 },
156785732ac8SCy Schubert 	{ 24, 122 },
156885732ac8SCy Schubert 	{ 25, 126 },
156985732ac8SCy Schubert 	{ 26, 126 },
157085732ac8SCy Schubert 	{ 27, 117 },
157185732ac8SCy Schubert 	{ 28, 120 },
157285732ac8SCy Schubert 	{ 29, 123 },
157385732ac8SCy Schubert 	{ 30, 127 },
157485732ac8SCy Schubert 	{ 31, 127 },
157585732ac8SCy Schubert 	{ 32, 83 },
157685732ac8SCy Schubert 	{ 33, 84 },
157785732ac8SCy Schubert 	{ 34, 180 },
157885732ac8SCy Schubert };
157985732ac8SCy Schubert 
158085732ac8SCy Schubert static const struct country_op_class eu_op_class[] = {
158185732ac8SCy Schubert 	{ 1, 115 },
158285732ac8SCy Schubert 	{ 2, 118 },
158385732ac8SCy Schubert 	{ 3, 121 },
158485732ac8SCy Schubert 	{ 4, 81 },
158585732ac8SCy Schubert 	{ 5, 116 },
158685732ac8SCy Schubert 	{ 6, 119 },
158785732ac8SCy Schubert 	{ 7, 122 },
158885732ac8SCy Schubert 	{ 8, 117 },
158985732ac8SCy Schubert 	{ 9, 120 },
159085732ac8SCy Schubert 	{ 10, 123 },
159185732ac8SCy Schubert 	{ 11, 83 },
159285732ac8SCy Schubert 	{ 12, 84 },
159385732ac8SCy Schubert 	{ 17, 125 },
159485732ac8SCy Schubert 	{ 18, 180 },
159585732ac8SCy Schubert };
159685732ac8SCy Schubert 
159785732ac8SCy Schubert static const struct country_op_class jp_op_class[] = {
159885732ac8SCy Schubert 	{ 1, 115 },
159985732ac8SCy Schubert 	{ 30, 81 },
160085732ac8SCy Schubert 	{ 31, 82 },
160185732ac8SCy Schubert 	{ 32, 118 },
160285732ac8SCy Schubert 	{ 33, 118 },
160385732ac8SCy Schubert 	{ 34, 121 },
160485732ac8SCy Schubert 	{ 35, 121 },
160585732ac8SCy Schubert 	{ 36, 116 },
160685732ac8SCy Schubert 	{ 37, 119 },
160785732ac8SCy Schubert 	{ 38, 119 },
160885732ac8SCy Schubert 	{ 39, 122 },
160985732ac8SCy Schubert 	{ 40, 122 },
161085732ac8SCy Schubert 	{ 41, 117 },
161185732ac8SCy Schubert 	{ 42, 120 },
161285732ac8SCy Schubert 	{ 43, 120 },
161385732ac8SCy Schubert 	{ 44, 123 },
161485732ac8SCy Schubert 	{ 45, 123 },
161585732ac8SCy Schubert 	{ 56, 83 },
161685732ac8SCy Schubert 	{ 57, 84 },
161785732ac8SCy Schubert 	{ 58, 121 },
161885732ac8SCy Schubert 	{ 59, 180 },
161985732ac8SCy Schubert };
162085732ac8SCy Schubert 
162185732ac8SCy Schubert static const struct country_op_class cn_op_class[] = {
162285732ac8SCy Schubert 	{ 1, 115 },
162385732ac8SCy Schubert 	{ 2, 118 },
162485732ac8SCy Schubert 	{ 3, 125 },
162585732ac8SCy Schubert 	{ 4, 116 },
162685732ac8SCy Schubert 	{ 5, 119 },
162785732ac8SCy Schubert 	{ 6, 126 },
162885732ac8SCy Schubert 	{ 7, 81 },
162985732ac8SCy Schubert 	{ 8, 83 },
163085732ac8SCy Schubert 	{ 9, 84 },
163185732ac8SCy Schubert };
163285732ac8SCy Schubert 
163385732ac8SCy Schubert static u8
163485732ac8SCy Schubert global_op_class_from_country_array(u8 op_class, size_t array_size,
163585732ac8SCy Schubert 				   const struct country_op_class *country_array)
163685732ac8SCy Schubert {
163785732ac8SCy Schubert 	size_t i;
163885732ac8SCy Schubert 
163985732ac8SCy Schubert 	for (i = 0; i < array_size; i++) {
164085732ac8SCy Schubert 		if (country_array[i].country_op_class == op_class)
164185732ac8SCy Schubert 			return country_array[i].global_op_class;
164285732ac8SCy Schubert 	}
164385732ac8SCy Schubert 
164485732ac8SCy Schubert 	return 0;
164585732ac8SCy Schubert }
164685732ac8SCy Schubert 
164785732ac8SCy Schubert 
164885732ac8SCy Schubert u8 country_to_global_op_class(const char *country, u8 op_class)
164985732ac8SCy Schubert {
165085732ac8SCy Schubert 	const struct country_op_class *country_array;
165185732ac8SCy Schubert 	size_t size;
165285732ac8SCy Schubert 	u8 g_op_class;
165385732ac8SCy Schubert 
165485732ac8SCy Schubert 	if (country_match(us_op_class_cc, country)) {
165585732ac8SCy Schubert 		country_array = us_op_class;
165685732ac8SCy Schubert 		size = ARRAY_SIZE(us_op_class);
165785732ac8SCy Schubert 	} else if (country_match(eu_op_class_cc, country)) {
165885732ac8SCy Schubert 		country_array = eu_op_class;
165985732ac8SCy Schubert 		size = ARRAY_SIZE(eu_op_class);
166085732ac8SCy Schubert 	} else if (country_match(jp_op_class_cc, country)) {
166185732ac8SCy Schubert 		country_array = jp_op_class;
166285732ac8SCy Schubert 		size = ARRAY_SIZE(jp_op_class);
166385732ac8SCy Schubert 	} else if (country_match(cn_op_class_cc, country)) {
166485732ac8SCy Schubert 		country_array = cn_op_class;
166585732ac8SCy Schubert 		size = ARRAY_SIZE(cn_op_class);
166685732ac8SCy Schubert 	} else {
166785732ac8SCy Schubert 		/*
166885732ac8SCy Schubert 		 * Countries that do not match any of the above countries use
166985732ac8SCy Schubert 		 * global operating classes
167085732ac8SCy Schubert 		 */
167185732ac8SCy Schubert 		return op_class;
167285732ac8SCy Schubert 	}
167385732ac8SCy Schubert 
167485732ac8SCy Schubert 	g_op_class = global_op_class_from_country_array(op_class, size,
167585732ac8SCy Schubert 							country_array);
167685732ac8SCy Schubert 
167785732ac8SCy Schubert 	/*
167885732ac8SCy Schubert 	 * If the given operating class did not match any of the country's
167985732ac8SCy Schubert 	 * operating classes, assume that global operating class is used.
168085732ac8SCy Schubert 	 */
168185732ac8SCy Schubert 	return g_op_class ? g_op_class : op_class;
168285732ac8SCy Schubert }
168385732ac8SCy Schubert 
168485732ac8SCy Schubert 
168585732ac8SCy Schubert const struct oper_class_map * get_oper_class(const char *country, u8 op_class)
168685732ac8SCy Schubert {
168785732ac8SCy Schubert 	const struct oper_class_map *op;
168885732ac8SCy Schubert 
168985732ac8SCy Schubert 	if (country)
169085732ac8SCy Schubert 		op_class = country_to_global_op_class(country, op_class);
169185732ac8SCy Schubert 
169285732ac8SCy Schubert 	op = &global_op_class[0];
169385732ac8SCy Schubert 	while (op->op_class && op->op_class != op_class)
169485732ac8SCy Schubert 		op++;
169585732ac8SCy Schubert 
169685732ac8SCy Schubert 	if (!op->op_class)
169785732ac8SCy Schubert 		return NULL;
169885732ac8SCy Schubert 
169985732ac8SCy Schubert 	return op;
170085732ac8SCy Schubert }
170185732ac8SCy Schubert 
170285732ac8SCy Schubert 
1703*4bc52338SCy Schubert int oper_class_bw_to_int(const struct oper_class_map *map)
1704*4bc52338SCy Schubert {
1705*4bc52338SCy Schubert 	switch (map->bw) {
1706*4bc52338SCy Schubert 	case BW20:
1707*4bc52338SCy Schubert 		return 20;
1708*4bc52338SCy Schubert 	case BW40PLUS:
1709*4bc52338SCy Schubert 	case BW40MINUS:
1710*4bc52338SCy Schubert 		return 40;
1711*4bc52338SCy Schubert 	case BW80:
1712*4bc52338SCy Schubert 		return 80;
1713*4bc52338SCy Schubert 	case BW80P80:
1714*4bc52338SCy Schubert 	case BW160:
1715*4bc52338SCy Schubert 		return 160;
1716*4bc52338SCy Schubert 	case BW2160:
1717*4bc52338SCy Schubert 		return 2160;
1718*4bc52338SCy Schubert 	default:
1719*4bc52338SCy Schubert 		return 0;
1720*4bc52338SCy Schubert 	}
1721*4bc52338SCy Schubert }
1722*4bc52338SCy Schubert 
1723*4bc52338SCy Schubert 
172485732ac8SCy Schubert int ieee802_11_parse_candidate_list(const char *pos, u8 *nei_rep,
172585732ac8SCy Schubert 				    size_t nei_rep_len)
172685732ac8SCy Schubert {
172785732ac8SCy Schubert 	u8 *nei_pos = nei_rep;
172885732ac8SCy Schubert 	const char *end;
172985732ac8SCy Schubert 
173085732ac8SCy Schubert 	/*
173185732ac8SCy Schubert 	 * BSS Transition Candidate List Entries - Neighbor Report elements
173285732ac8SCy Schubert 	 * neighbor=<BSSID>,<BSSID Information>,<Operating Class>,
173385732ac8SCy Schubert 	 * <Channel Number>,<PHY Type>[,<hexdump of Optional Subelements>]
173485732ac8SCy Schubert 	 */
173585732ac8SCy Schubert 	while (pos) {
173685732ac8SCy Schubert 		u8 *nei_start;
173785732ac8SCy Schubert 		long int val;
173885732ac8SCy Schubert 		char *endptr, *tmp;
173985732ac8SCy Schubert 
174085732ac8SCy Schubert 		pos = os_strstr(pos, " neighbor=");
174185732ac8SCy Schubert 		if (!pos)
174285732ac8SCy Schubert 			break;
174385732ac8SCy Schubert 		if (nei_pos + 15 > nei_rep + nei_rep_len) {
174485732ac8SCy Schubert 			wpa_printf(MSG_DEBUG,
174585732ac8SCy Schubert 				   "Not enough room for additional neighbor");
174685732ac8SCy Schubert 			return -1;
174785732ac8SCy Schubert 		}
174885732ac8SCy Schubert 		pos += 10;
174985732ac8SCy Schubert 
175085732ac8SCy Schubert 		nei_start = nei_pos;
175185732ac8SCy Schubert 		*nei_pos++ = WLAN_EID_NEIGHBOR_REPORT;
175285732ac8SCy Schubert 		nei_pos++; /* length to be filled in */
175385732ac8SCy Schubert 
175485732ac8SCy Schubert 		if (hwaddr_aton(pos, nei_pos)) {
175585732ac8SCy Schubert 			wpa_printf(MSG_DEBUG, "Invalid BSSID");
175685732ac8SCy Schubert 			return -1;
175785732ac8SCy Schubert 		}
175885732ac8SCy Schubert 		nei_pos += ETH_ALEN;
175985732ac8SCy Schubert 		pos += 17;
176085732ac8SCy Schubert 		if (*pos != ',') {
176185732ac8SCy Schubert 			wpa_printf(MSG_DEBUG, "Missing BSSID Information");
176285732ac8SCy Schubert 			return -1;
176385732ac8SCy Schubert 		}
176485732ac8SCy Schubert 		pos++;
176585732ac8SCy Schubert 
176685732ac8SCy Schubert 		val = strtol(pos, &endptr, 0);
176785732ac8SCy Schubert 		WPA_PUT_LE32(nei_pos, val);
176885732ac8SCy Schubert 		nei_pos += 4;
176985732ac8SCy Schubert 		if (*endptr != ',') {
177085732ac8SCy Schubert 			wpa_printf(MSG_DEBUG, "Missing Operating Class");
177185732ac8SCy Schubert 			return -1;
177285732ac8SCy Schubert 		}
177385732ac8SCy Schubert 		pos = endptr + 1;
177485732ac8SCy Schubert 
177585732ac8SCy Schubert 		*nei_pos++ = atoi(pos); /* Operating Class */
177685732ac8SCy Schubert 		pos = os_strchr(pos, ',');
177785732ac8SCy Schubert 		if (pos == NULL) {
177885732ac8SCy Schubert 			wpa_printf(MSG_DEBUG, "Missing Channel Number");
177985732ac8SCy Schubert 			return -1;
178085732ac8SCy Schubert 		}
178185732ac8SCy Schubert 		pos++;
178285732ac8SCy Schubert 
178385732ac8SCy Schubert 		*nei_pos++ = atoi(pos); /* Channel Number */
178485732ac8SCy Schubert 		pos = os_strchr(pos, ',');
178585732ac8SCy Schubert 		if (pos == NULL) {
178685732ac8SCy Schubert 			wpa_printf(MSG_DEBUG, "Missing PHY Type");
178785732ac8SCy Schubert 			return -1;
178885732ac8SCy Schubert 		}
178985732ac8SCy Schubert 		pos++;
179085732ac8SCy Schubert 
179185732ac8SCy Schubert 		*nei_pos++ = atoi(pos); /* PHY Type */
179285732ac8SCy Schubert 		end = os_strchr(pos, ' ');
179385732ac8SCy Schubert 		tmp = os_strchr(pos, ',');
179485732ac8SCy Schubert 		if (tmp && (!end || tmp < end)) {
179585732ac8SCy Schubert 			/* Optional Subelements (hexdump) */
179685732ac8SCy Schubert 			size_t len;
179785732ac8SCy Schubert 
179885732ac8SCy Schubert 			pos = tmp + 1;
179985732ac8SCy Schubert 			end = os_strchr(pos, ' ');
180085732ac8SCy Schubert 			if (end)
180185732ac8SCy Schubert 				len = end - pos;
180285732ac8SCy Schubert 			else
180385732ac8SCy Schubert 				len = os_strlen(pos);
180485732ac8SCy Schubert 			if (nei_pos + len / 2 > nei_rep + nei_rep_len) {
180585732ac8SCy Schubert 				wpa_printf(MSG_DEBUG,
180685732ac8SCy Schubert 					   "Not enough room for neighbor subelements");
180785732ac8SCy Schubert 				return -1;
180885732ac8SCy Schubert 			}
180985732ac8SCy Schubert 			if (len & 0x01 ||
181085732ac8SCy Schubert 			    hexstr2bin(pos, nei_pos, len / 2) < 0) {
181185732ac8SCy Schubert 				wpa_printf(MSG_DEBUG,
181285732ac8SCy Schubert 					   "Invalid neighbor subelement info");
181385732ac8SCy Schubert 				return -1;
181485732ac8SCy Schubert 			}
181585732ac8SCy Schubert 			nei_pos += len / 2;
181685732ac8SCy Schubert 			pos = end;
181785732ac8SCy Schubert 		}
181885732ac8SCy Schubert 
181985732ac8SCy Schubert 		nei_start[1] = nei_pos - nei_start - 2;
182085732ac8SCy Schubert 	}
182185732ac8SCy Schubert 
182285732ac8SCy Schubert 	return nei_pos - nei_rep;
182385732ac8SCy Schubert }
1824*4bc52338SCy Schubert 
1825*4bc52338SCy Schubert 
1826*4bc52338SCy Schubert int ieee802_11_ext_capab(const u8 *ie, unsigned int capab)
1827*4bc52338SCy Schubert {
1828*4bc52338SCy Schubert 	if (!ie || ie[1] <= capab / 8)
1829*4bc52338SCy Schubert 		return 0;
1830*4bc52338SCy Schubert 	return !!(ie[2 + capab / 8] & BIT(capab % 8));
1831*4bc52338SCy Schubert }
1832