xref: /freebsd/contrib/wpa/src/common/ieee802_11_common.c (revision 206b73d0429edb7c49b612537544e677fa568e83)
139beb93cSSam Leffler /*
239beb93cSSam Leffler  * IEEE 802.11 Common routines
34bc52338SCy 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;
1294bc52338SCy Schubert 		case MULTI_AP_OUI_TYPE:
1304bc52338SCy Schubert 			elems->multi_ap = pos;
1314bc52338SCy Schubert 			elems->multi_ap_len = elen;
1324bc52338SCy 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;
2734bc52338SCy Schubert 	case WLAN_EID_EXT_HE_CAPABILITIES:
2744bc52338SCy Schubert 		elems->he_capabilities = pos;
2754bc52338SCy Schubert 		elems->he_capabilities_len = elen;
2764bc52338SCy Schubert 		break;
277*206b73d0SCy Schubert 	case WLAN_EID_EXT_HE_OPERATION:
278*206b73d0SCy Schubert 		elems->he_operation = pos;
279*206b73d0SCy Schubert 		elems->he_operation_len = elen;
280*206b73d0SCy Schubert 		break;
2814bc52338SCy Schubert 	case WLAN_EID_EXT_OCV_OCI:
2824bc52338SCy Schubert 		elems->oci = pos;
2834bc52338SCy Schubert 		elems->oci_len = elen;
2844bc52338SCy Schubert 		break;
28585732ac8SCy Schubert 	default:
28685732ac8SCy Schubert 		if (show_errors) {
28785732ac8SCy Schubert 			wpa_printf(MSG_MSGDUMP,
28885732ac8SCy Schubert 				   "IEEE 802.11 element parsing ignored unknown element extension (ext_id=%u elen=%u)",
28985732ac8SCy Schubert 				   ext_id, (unsigned int) elen);
29085732ac8SCy Schubert 		}
29185732ac8SCy Schubert 		return -1;
29285732ac8SCy Schubert 	}
29385732ac8SCy Schubert 
29485732ac8SCy Schubert 	return 0;
29585732ac8SCy Schubert }
29685732ac8SCy Schubert 
29785732ac8SCy Schubert 
29839beb93cSSam Leffler /**
29939beb93cSSam Leffler  * ieee802_11_parse_elems - Parse information elements in management frames
30039beb93cSSam Leffler  * @start: Pointer to the start of IEs
30139beb93cSSam Leffler  * @len: Length of IE buffer in octets
30239beb93cSSam Leffler  * @elems: Data structure for parsed elements
30339beb93cSSam Leffler  * @show_errors: Whether to show parsing errors in debug log
30439beb93cSSam Leffler  * Returns: Parsing result
30539beb93cSSam Leffler  */
306e28a4053SRui Paulo ParseRes ieee802_11_parse_elems(const u8 *start, size_t len,
30739beb93cSSam Leffler 				struct ieee802_11_elems *elems,
30839beb93cSSam Leffler 				int show_errors)
30939beb93cSSam Leffler {
3104bc52338SCy Schubert 	const struct element *elem;
31139beb93cSSam Leffler 	int unknown = 0;
31239beb93cSSam Leffler 
31339beb93cSSam Leffler 	os_memset(elems, 0, sizeof(*elems));
31439beb93cSSam Leffler 
3154bc52338SCy Schubert 	if (!start)
3164bc52338SCy Schubert 		return ParseOK;
31739beb93cSSam Leffler 
3184bc52338SCy Schubert 	for_each_element(elem, start, len) {
3194bc52338SCy Schubert 		u8 id = elem->id, elen = elem->datalen;
3204bc52338SCy Schubert 		const u8 *pos = elem->data;
32139beb93cSSam Leffler 
32239beb93cSSam Leffler 		switch (id) {
32339beb93cSSam Leffler 		case WLAN_EID_SSID:
324325151a3SRui Paulo 			if (elen > SSID_MAX_LEN) {
325325151a3SRui Paulo 				wpa_printf(MSG_DEBUG,
326325151a3SRui Paulo 					   "Ignored too long SSID element (elen=%u)",
327325151a3SRui Paulo 					   elen);
328325151a3SRui Paulo 				break;
329325151a3SRui Paulo 			}
33039beb93cSSam Leffler 			elems->ssid = pos;
33139beb93cSSam Leffler 			elems->ssid_len = elen;
33239beb93cSSam Leffler 			break;
33339beb93cSSam Leffler 		case WLAN_EID_SUPP_RATES:
33439beb93cSSam Leffler 			elems->supp_rates = pos;
33539beb93cSSam Leffler 			elems->supp_rates_len = elen;
33639beb93cSSam Leffler 			break;
33739beb93cSSam Leffler 		case WLAN_EID_DS_PARAMS:
338325151a3SRui Paulo 			if (elen < 1)
339325151a3SRui Paulo 				break;
34039beb93cSSam Leffler 			elems->ds_params = pos;
34139beb93cSSam Leffler 			break;
34239beb93cSSam Leffler 		case WLAN_EID_CF_PARAMS:
34339beb93cSSam Leffler 		case WLAN_EID_TIM:
34439beb93cSSam Leffler 			break;
34539beb93cSSam Leffler 		case WLAN_EID_CHALLENGE:
34639beb93cSSam Leffler 			elems->challenge = pos;
34739beb93cSSam Leffler 			elems->challenge_len = elen;
34839beb93cSSam Leffler 			break;
34939beb93cSSam Leffler 		case WLAN_EID_ERP_INFO:
350325151a3SRui Paulo 			if (elen < 1)
351325151a3SRui Paulo 				break;
35239beb93cSSam Leffler 			elems->erp_info = pos;
35339beb93cSSam Leffler 			break;
35439beb93cSSam Leffler 		case WLAN_EID_EXT_SUPP_RATES:
35539beb93cSSam Leffler 			elems->ext_supp_rates = pos;
35639beb93cSSam Leffler 			elems->ext_supp_rates_len = elen;
35739beb93cSSam Leffler 			break;
35839beb93cSSam Leffler 		case WLAN_EID_VENDOR_SPECIFIC:
35939beb93cSSam Leffler 			if (ieee802_11_parse_vendor_specific(pos, elen,
36039beb93cSSam Leffler 							     elems,
36139beb93cSSam Leffler 							     show_errors))
36239beb93cSSam Leffler 				unknown++;
36339beb93cSSam Leffler 			break;
36439beb93cSSam Leffler 		case WLAN_EID_RSN:
36539beb93cSSam Leffler 			elems->rsn_ie = pos;
36639beb93cSSam Leffler 			elems->rsn_ie_len = elen;
36739beb93cSSam Leffler 			break;
36839beb93cSSam Leffler 		case WLAN_EID_PWR_CAPABILITY:
36985732ac8SCy Schubert 			if (elen < 2)
37085732ac8SCy Schubert 				break;
37185732ac8SCy Schubert 			elems->power_capab = pos;
37285732ac8SCy Schubert 			elems->power_capab_len = elen;
37339beb93cSSam Leffler 			break;
37439beb93cSSam Leffler 		case WLAN_EID_SUPPORTED_CHANNELS:
37539beb93cSSam Leffler 			elems->supp_channels = pos;
37639beb93cSSam Leffler 			elems->supp_channels_len = elen;
37739beb93cSSam Leffler 			break;
37839beb93cSSam Leffler 		case WLAN_EID_MOBILITY_DOMAIN:
379325151a3SRui Paulo 			if (elen < sizeof(struct rsn_mdie))
380325151a3SRui Paulo 				break;
38139beb93cSSam Leffler 			elems->mdie = pos;
38239beb93cSSam Leffler 			elems->mdie_len = elen;
38339beb93cSSam Leffler 			break;
38439beb93cSSam Leffler 		case WLAN_EID_FAST_BSS_TRANSITION:
385325151a3SRui Paulo 			if (elen < sizeof(struct rsn_ftie))
386325151a3SRui Paulo 				break;
38739beb93cSSam Leffler 			elems->ftie = pos;
38839beb93cSSam Leffler 			elems->ftie_len = elen;
38939beb93cSSam Leffler 			break;
39039beb93cSSam Leffler 		case WLAN_EID_TIMEOUT_INTERVAL:
391325151a3SRui Paulo 			if (elen != 5)
392325151a3SRui Paulo 				break;
39339beb93cSSam Leffler 			elems->timeout_int = pos;
39439beb93cSSam Leffler 			break;
39539beb93cSSam Leffler 		case WLAN_EID_HT_CAP:
396325151a3SRui Paulo 			if (elen < sizeof(struct ieee80211_ht_capabilities))
397325151a3SRui Paulo 				break;
39839beb93cSSam Leffler 			elems->ht_capabilities = pos;
39939beb93cSSam Leffler 			break;
40039beb93cSSam Leffler 		case WLAN_EID_HT_OPERATION:
401325151a3SRui Paulo 			if (elen < sizeof(struct ieee80211_ht_operation))
402325151a3SRui Paulo 				break;
40339beb93cSSam Leffler 			elems->ht_operation = pos;
40439beb93cSSam Leffler 			break;
4055b9c547cSRui Paulo 		case WLAN_EID_MESH_CONFIG:
4065b9c547cSRui Paulo 			elems->mesh_config = pos;
4075b9c547cSRui Paulo 			elems->mesh_config_len = elen;
4085b9c547cSRui Paulo 			break;
4095b9c547cSRui Paulo 		case WLAN_EID_MESH_ID:
4105b9c547cSRui Paulo 			elems->mesh_id = pos;
4115b9c547cSRui Paulo 			elems->mesh_id_len = elen;
4125b9c547cSRui Paulo 			break;
4135b9c547cSRui Paulo 		case WLAN_EID_PEER_MGMT:
4145b9c547cSRui Paulo 			elems->peer_mgmt = pos;
4155b9c547cSRui Paulo 			elems->peer_mgmt_len = elen;
4165b9c547cSRui Paulo 			break;
417f05cddf9SRui Paulo 		case WLAN_EID_VHT_CAP:
418325151a3SRui Paulo 			if (elen < sizeof(struct ieee80211_vht_capabilities))
419325151a3SRui Paulo 				break;
420f05cddf9SRui Paulo 			elems->vht_capabilities = pos;
421f05cddf9SRui Paulo 			break;
422f05cddf9SRui Paulo 		case WLAN_EID_VHT_OPERATION:
423325151a3SRui Paulo 			if (elen < sizeof(struct ieee80211_vht_operation))
424325151a3SRui Paulo 				break;
425f05cddf9SRui Paulo 			elems->vht_operation = pos;
426f05cddf9SRui Paulo 			break;
4275b9c547cSRui Paulo 		case WLAN_EID_VHT_OPERATING_MODE_NOTIFICATION:
4285b9c547cSRui Paulo 			if (elen != 1)
4295b9c547cSRui Paulo 				break;
4305b9c547cSRui Paulo 			elems->vht_opmode_notif = pos;
4315b9c547cSRui Paulo 			break;
432f05cddf9SRui Paulo 		case WLAN_EID_LINK_ID:
433f05cddf9SRui Paulo 			if (elen < 18)
434f05cddf9SRui Paulo 				break;
435f05cddf9SRui Paulo 			elems->link_id = pos;
436f05cddf9SRui Paulo 			break;
437f05cddf9SRui Paulo 		case WLAN_EID_INTERWORKING:
438f05cddf9SRui Paulo 			elems->interworking = pos;
439f05cddf9SRui Paulo 			elems->interworking_len = elen;
440f05cddf9SRui Paulo 			break;
4415b9c547cSRui Paulo 		case WLAN_EID_QOS_MAP_SET:
4425b9c547cSRui Paulo 			if (elen < 16)
4435b9c547cSRui Paulo 				break;
4445b9c547cSRui Paulo 			elems->qos_map_set = pos;
4455b9c547cSRui Paulo 			elems->qos_map_set_len = elen;
4465b9c547cSRui Paulo 			break;
447f05cddf9SRui Paulo 		case WLAN_EID_EXT_CAPAB:
448f05cddf9SRui Paulo 			elems->ext_capab = pos;
449f05cddf9SRui Paulo 			elems->ext_capab_len = elen;
450f05cddf9SRui Paulo 			break;
451f05cddf9SRui Paulo 		case WLAN_EID_BSS_MAX_IDLE_PERIOD:
452f05cddf9SRui Paulo 			if (elen < 3)
453f05cddf9SRui Paulo 				break;
454f05cddf9SRui Paulo 			elems->bss_max_idle_period = pos;
455f05cddf9SRui Paulo 			break;
456f05cddf9SRui Paulo 		case WLAN_EID_SSID_LIST:
457f05cddf9SRui Paulo 			elems->ssid_list = pos;
458f05cddf9SRui Paulo 			elems->ssid_list_len = elen;
459f05cddf9SRui Paulo 			break;
4605b9c547cSRui Paulo 		case WLAN_EID_AMPE:
4615b9c547cSRui Paulo 			elems->ampe = pos;
4625b9c547cSRui Paulo 			elems->ampe_len = elen;
4635b9c547cSRui Paulo 			break;
4645b9c547cSRui Paulo 		case WLAN_EID_MIC:
4655b9c547cSRui Paulo 			elems->mic = pos;
4665b9c547cSRui Paulo 			elems->mic_len = elen;
4675b9c547cSRui Paulo 			/* after mic everything is encrypted, so stop. */
4684bc52338SCy Schubert 			goto done;
469325151a3SRui Paulo 		case WLAN_EID_MULTI_BAND:
470325151a3SRui Paulo 			if (elems->mb_ies.nof_ies >= MAX_NOF_MB_IES_SUPPORTED) {
471325151a3SRui Paulo 				wpa_printf(MSG_MSGDUMP,
472325151a3SRui Paulo 					   "IEEE 802.11 element parse ignored MB IE (id=%d elen=%d)",
473325151a3SRui Paulo 					   id, elen);
474325151a3SRui Paulo 				break;
475325151a3SRui Paulo 			}
476325151a3SRui Paulo 
477325151a3SRui Paulo 			elems->mb_ies.ies[elems->mb_ies.nof_ies].ie = pos;
478325151a3SRui Paulo 			elems->mb_ies.ies[elems->mb_ies.nof_ies].ie_len = elen;
479325151a3SRui Paulo 			elems->mb_ies.nof_ies++;
480325151a3SRui Paulo 			break;
481780fb4a2SCy Schubert 		case WLAN_EID_SUPPORTED_OPERATING_CLASSES:
482780fb4a2SCy Schubert 			elems->supp_op_classes = pos;
483780fb4a2SCy Schubert 			elems->supp_op_classes_len = elen;
484780fb4a2SCy Schubert 			break;
485780fb4a2SCy Schubert 		case WLAN_EID_RRM_ENABLED_CAPABILITIES:
486780fb4a2SCy Schubert 			elems->rrm_enabled = pos;
487780fb4a2SCy Schubert 			elems->rrm_enabled_len = elen;
488780fb4a2SCy Schubert 			break;
48985732ac8SCy Schubert 		case WLAN_EID_CAG_NUMBER:
49085732ac8SCy Schubert 			elems->cag_number = pos;
49185732ac8SCy Schubert 			elems->cag_number_len = elen;
49285732ac8SCy Schubert 			break;
49385732ac8SCy Schubert 		case WLAN_EID_AP_CSN:
49485732ac8SCy Schubert 			if (elen < 1)
49585732ac8SCy Schubert 				break;
49685732ac8SCy Schubert 			elems->ap_csn = pos;
49785732ac8SCy Schubert 			break;
49885732ac8SCy Schubert 		case WLAN_EID_FILS_INDICATION:
49985732ac8SCy Schubert 			if (elen < 2)
50085732ac8SCy Schubert 				break;
50185732ac8SCy Schubert 			elems->fils_indic = pos;
50285732ac8SCy Schubert 			elems->fils_indic_len = elen;
50385732ac8SCy Schubert 			break;
50485732ac8SCy Schubert 		case WLAN_EID_DILS:
50585732ac8SCy Schubert 			if (elen < 2)
50685732ac8SCy Schubert 				break;
50785732ac8SCy Schubert 			elems->dils = pos;
50885732ac8SCy Schubert 			elems->dils_len = elen;
50985732ac8SCy Schubert 			break;
51085732ac8SCy Schubert 		case WLAN_EID_FRAGMENT:
51185732ac8SCy Schubert 			/* TODO */
51285732ac8SCy Schubert 			break;
51385732ac8SCy Schubert 		case WLAN_EID_EXTENSION:
51485732ac8SCy Schubert 			if (ieee802_11_parse_extension(pos, elen, elems,
51585732ac8SCy Schubert 						       show_errors))
51685732ac8SCy Schubert 				unknown++;
51785732ac8SCy Schubert 			break;
51839beb93cSSam Leffler 		default:
51939beb93cSSam Leffler 			unknown++;
52039beb93cSSam Leffler 			if (!show_errors)
52139beb93cSSam Leffler 				break;
52239beb93cSSam Leffler 			wpa_printf(MSG_MSGDUMP, "IEEE 802.11 element parse "
52339beb93cSSam Leffler 				   "ignored unknown element (id=%d elen=%d)",
52439beb93cSSam Leffler 				   id, elen);
52539beb93cSSam Leffler 			break;
52639beb93cSSam Leffler 		}
52739beb93cSSam Leffler 	}
52839beb93cSSam Leffler 
5294bc52338SCy Schubert 	if (!for_each_element_completed(elem, start, len)) {
5304bc52338SCy Schubert 		if (show_errors) {
5314bc52338SCy Schubert 			wpa_printf(MSG_DEBUG,
5324bc52338SCy Schubert 				   "IEEE 802.11 element parse failed @%d",
5334bc52338SCy Schubert 				   (int) (start + len - (const u8 *) elem));
5344bc52338SCy Schubert 			wpa_hexdump(MSG_MSGDUMP, "IEs", start, len);
5354bc52338SCy Schubert 		}
53639beb93cSSam Leffler 		return ParseFailed;
5374bc52338SCy Schubert 	}
53839beb93cSSam Leffler 
5394bc52338SCy Schubert done:
54039beb93cSSam Leffler 	return unknown ? ParseUnknown : ParseOK;
54139beb93cSSam Leffler }
542e28a4053SRui Paulo 
543e28a4053SRui Paulo 
544e28a4053SRui Paulo int ieee802_11_ie_count(const u8 *ies, size_t ies_len)
545e28a4053SRui Paulo {
5464bc52338SCy Schubert 	const struct element *elem;
547e28a4053SRui Paulo 	int count = 0;
548e28a4053SRui Paulo 
549e28a4053SRui Paulo 	if (ies == NULL)
550e28a4053SRui Paulo 		return 0;
551e28a4053SRui Paulo 
5524bc52338SCy Schubert 	for_each_element(elem, ies, ies_len)
553e28a4053SRui Paulo 		count++;
554e28a4053SRui Paulo 
555e28a4053SRui Paulo 	return count;
556e28a4053SRui Paulo }
557e28a4053SRui Paulo 
558e28a4053SRui Paulo 
559e28a4053SRui Paulo struct wpabuf * ieee802_11_vendor_ie_concat(const u8 *ies, size_t ies_len,
560e28a4053SRui Paulo 					    u32 oui_type)
561e28a4053SRui Paulo {
562e28a4053SRui Paulo 	struct wpabuf *buf;
5634bc52338SCy Schubert 	const struct element *elem, *found = NULL;
564e28a4053SRui Paulo 
5654bc52338SCy Schubert 	for_each_element_id(elem, WLAN_EID_VENDOR_SPECIFIC, ies, ies_len) {
5664bc52338SCy Schubert 		if (elem->datalen >= 4 &&
5674bc52338SCy Schubert 		    WPA_GET_BE32(elem->data) == oui_type) {
5684bc52338SCy Schubert 			found = elem;
569e28a4053SRui Paulo 			break;
570e28a4053SRui Paulo 		}
571e28a4053SRui Paulo 	}
572e28a4053SRui Paulo 
5734bc52338SCy Schubert 	if (!found)
574e28a4053SRui Paulo 		return NULL; /* No specified vendor IE found */
575e28a4053SRui Paulo 
576e28a4053SRui Paulo 	buf = wpabuf_alloc(ies_len);
577e28a4053SRui Paulo 	if (buf == NULL)
578e28a4053SRui Paulo 		return NULL;
579e28a4053SRui Paulo 
580e28a4053SRui Paulo 	/*
581e28a4053SRui Paulo 	 * There may be multiple vendor IEs in the message, so need to
582e28a4053SRui Paulo 	 * concatenate their data fields.
583e28a4053SRui Paulo 	 */
5844bc52338SCy Schubert 	for_each_element_id(elem, WLAN_EID_VENDOR_SPECIFIC, ies, ies_len) {
5854bc52338SCy Schubert 		if (elem->datalen >= 4 && WPA_GET_BE32(elem->data) == oui_type)
5864bc52338SCy Schubert 			wpabuf_put_data(buf, elem->data + 4, elem->datalen - 4);
587e28a4053SRui Paulo 	}
588e28a4053SRui Paulo 
589e28a4053SRui Paulo 	return buf;
590e28a4053SRui Paulo }
591f05cddf9SRui Paulo 
592f05cddf9SRui Paulo 
593f05cddf9SRui Paulo const u8 * get_hdr_bssid(const struct ieee80211_hdr *hdr, size_t len)
594f05cddf9SRui Paulo {
595f05cddf9SRui Paulo 	u16 fc, type, stype;
596f05cddf9SRui Paulo 
597f05cddf9SRui Paulo 	/*
598f05cddf9SRui Paulo 	 * PS-Poll frames are 16 bytes. All other frames are
599f05cddf9SRui Paulo 	 * 24 bytes or longer.
600f05cddf9SRui Paulo 	 */
601f05cddf9SRui Paulo 	if (len < 16)
602f05cddf9SRui Paulo 		return NULL;
603f05cddf9SRui Paulo 
604f05cddf9SRui Paulo 	fc = le_to_host16(hdr->frame_control);
605f05cddf9SRui Paulo 	type = WLAN_FC_GET_TYPE(fc);
606f05cddf9SRui Paulo 	stype = WLAN_FC_GET_STYPE(fc);
607f05cddf9SRui Paulo 
608f05cddf9SRui Paulo 	switch (type) {
609f05cddf9SRui Paulo 	case WLAN_FC_TYPE_DATA:
610f05cddf9SRui Paulo 		if (len < 24)
611f05cddf9SRui Paulo 			return NULL;
612f05cddf9SRui Paulo 		switch (fc & (WLAN_FC_FROMDS | WLAN_FC_TODS)) {
613f05cddf9SRui Paulo 		case WLAN_FC_FROMDS | WLAN_FC_TODS:
614f05cddf9SRui Paulo 		case WLAN_FC_TODS:
615f05cddf9SRui Paulo 			return hdr->addr1;
616f05cddf9SRui Paulo 		case WLAN_FC_FROMDS:
617f05cddf9SRui Paulo 			return hdr->addr2;
618f05cddf9SRui Paulo 		default:
619f05cddf9SRui Paulo 			return NULL;
620f05cddf9SRui Paulo 		}
621f05cddf9SRui Paulo 	case WLAN_FC_TYPE_CTRL:
622f05cddf9SRui Paulo 		if (stype != WLAN_FC_STYPE_PSPOLL)
623f05cddf9SRui Paulo 			return NULL;
624f05cddf9SRui Paulo 		return hdr->addr1;
625f05cddf9SRui Paulo 	case WLAN_FC_TYPE_MGMT:
626f05cddf9SRui Paulo 		return hdr->addr3;
627f05cddf9SRui Paulo 	default:
628f05cddf9SRui Paulo 		return NULL;
629f05cddf9SRui Paulo 	}
630f05cddf9SRui Paulo }
631f05cddf9SRui Paulo 
632f05cddf9SRui Paulo 
633f05cddf9SRui Paulo int hostapd_config_wmm_ac(struct hostapd_wmm_ac_params wmm_ac_params[],
634f05cddf9SRui Paulo 			  const char *name, const char *val)
635f05cddf9SRui Paulo {
636f05cddf9SRui Paulo 	int num, v;
637f05cddf9SRui Paulo 	const char *pos;
638f05cddf9SRui Paulo 	struct hostapd_wmm_ac_params *ac;
639f05cddf9SRui Paulo 
640f05cddf9SRui Paulo 	/* skip 'wme_ac_' or 'wmm_ac_' prefix */
641f05cddf9SRui Paulo 	pos = name + 7;
642f05cddf9SRui Paulo 	if (os_strncmp(pos, "be_", 3) == 0) {
643f05cddf9SRui Paulo 		num = 0;
644f05cddf9SRui Paulo 		pos += 3;
645f05cddf9SRui Paulo 	} else if (os_strncmp(pos, "bk_", 3) == 0) {
646f05cddf9SRui Paulo 		num = 1;
647f05cddf9SRui Paulo 		pos += 3;
648f05cddf9SRui Paulo 	} else if (os_strncmp(pos, "vi_", 3) == 0) {
649f05cddf9SRui Paulo 		num = 2;
650f05cddf9SRui Paulo 		pos += 3;
651f05cddf9SRui Paulo 	} else if (os_strncmp(pos, "vo_", 3) == 0) {
652f05cddf9SRui Paulo 		num = 3;
653f05cddf9SRui Paulo 		pos += 3;
654f05cddf9SRui Paulo 	} else {
655f05cddf9SRui Paulo 		wpa_printf(MSG_ERROR, "Unknown WMM name '%s'", pos);
656f05cddf9SRui Paulo 		return -1;
657f05cddf9SRui Paulo 	}
658f05cddf9SRui Paulo 
659f05cddf9SRui Paulo 	ac = &wmm_ac_params[num];
660f05cddf9SRui Paulo 
661f05cddf9SRui Paulo 	if (os_strcmp(pos, "aifs") == 0) {
662f05cddf9SRui Paulo 		v = atoi(val);
663f05cddf9SRui Paulo 		if (v < 1 || v > 255) {
664f05cddf9SRui Paulo 			wpa_printf(MSG_ERROR, "Invalid AIFS value %d", v);
665f05cddf9SRui Paulo 			return -1;
666f05cddf9SRui Paulo 		}
667f05cddf9SRui Paulo 		ac->aifs = v;
668f05cddf9SRui Paulo 	} else if (os_strcmp(pos, "cwmin") == 0) {
669f05cddf9SRui Paulo 		v = atoi(val);
670325151a3SRui Paulo 		if (v < 0 || v > 15) {
671f05cddf9SRui Paulo 			wpa_printf(MSG_ERROR, "Invalid cwMin value %d", v);
672f05cddf9SRui Paulo 			return -1;
673f05cddf9SRui Paulo 		}
674f05cddf9SRui Paulo 		ac->cwmin = v;
675f05cddf9SRui Paulo 	} else if (os_strcmp(pos, "cwmax") == 0) {
676f05cddf9SRui Paulo 		v = atoi(val);
677325151a3SRui Paulo 		if (v < 0 || v > 15) {
678f05cddf9SRui Paulo 			wpa_printf(MSG_ERROR, "Invalid cwMax value %d", v);
679f05cddf9SRui Paulo 			return -1;
680f05cddf9SRui Paulo 		}
681f05cddf9SRui Paulo 		ac->cwmax = v;
682f05cddf9SRui Paulo 	} else if (os_strcmp(pos, "txop_limit") == 0) {
683f05cddf9SRui Paulo 		v = atoi(val);
684f05cddf9SRui Paulo 		if (v < 0 || v > 0xffff) {
685f05cddf9SRui Paulo 			wpa_printf(MSG_ERROR, "Invalid txop value %d", v);
686f05cddf9SRui Paulo 			return -1;
687f05cddf9SRui Paulo 		}
688f05cddf9SRui Paulo 		ac->txop_limit = v;
689f05cddf9SRui Paulo 	} else if (os_strcmp(pos, "acm") == 0) {
690f05cddf9SRui Paulo 		v = atoi(val);
691f05cddf9SRui Paulo 		if (v < 0 || v > 1) {
692f05cddf9SRui Paulo 			wpa_printf(MSG_ERROR, "Invalid acm value %d", v);
693f05cddf9SRui Paulo 			return -1;
694f05cddf9SRui Paulo 		}
695f05cddf9SRui Paulo 		ac->admission_control_mandatory = v;
696f05cddf9SRui Paulo 	} else {
697f05cddf9SRui Paulo 		wpa_printf(MSG_ERROR, "Unknown wmm_ac_ field '%s'", pos);
698f05cddf9SRui Paulo 		return -1;
699f05cddf9SRui Paulo 	}
700f05cddf9SRui Paulo 
701f05cddf9SRui Paulo 	return 0;
702f05cddf9SRui Paulo }
7035b9c547cSRui Paulo 
7045b9c547cSRui Paulo 
7055b9c547cSRui Paulo enum hostapd_hw_mode ieee80211_freq_to_chan(int freq, u8 *channel)
7065b9c547cSRui Paulo {
707325151a3SRui Paulo 	u8 op_class;
708325151a3SRui Paulo 
709*206b73d0SCy Schubert 	return ieee80211_freq_to_channel_ext(freq, 0, CHANWIDTH_USE_HT,
710780fb4a2SCy Schubert 					     &op_class, channel);
711325151a3SRui Paulo }
712325151a3SRui Paulo 
713325151a3SRui Paulo 
714325151a3SRui Paulo /**
715325151a3SRui Paulo  * ieee80211_freq_to_channel_ext - Convert frequency into channel info
716325151a3SRui Paulo  * for HT40 and VHT. DFS channels are not covered.
717325151a3SRui Paulo  * @freq: Frequency (MHz) to convert
718325151a3SRui Paulo  * @sec_channel: 0 = non-HT40, 1 = sec. channel above, -1 = sec. channel below
719*206b73d0SCy Schubert  * @vht: VHT channel width (CHANWIDTH_*)
720325151a3SRui Paulo  * @op_class: Buffer for returning operating class
721325151a3SRui Paulo  * @channel: Buffer for returning channel number
722325151a3SRui Paulo  * Returns: hw_mode on success, NUM_HOSTAPD_MODES on failure
723325151a3SRui Paulo  */
724325151a3SRui Paulo enum hostapd_hw_mode ieee80211_freq_to_channel_ext(unsigned int freq,
725325151a3SRui Paulo 						   int sec_channel, int vht,
726325151a3SRui Paulo 						   u8 *op_class, u8 *channel)
727325151a3SRui Paulo {
728780fb4a2SCy Schubert 	u8 vht_opclass;
729780fb4a2SCy Schubert 
730325151a3SRui Paulo 	/* TODO: more operating classes */
731325151a3SRui Paulo 
732325151a3SRui Paulo 	if (sec_channel > 1 || sec_channel < -1)
733325151a3SRui Paulo 		return NUM_HOSTAPD_MODES;
7345b9c547cSRui Paulo 
7355b9c547cSRui Paulo 	if (freq >= 2412 && freq <= 2472) {
736325151a3SRui Paulo 		if ((freq - 2407) % 5)
737325151a3SRui Paulo 			return NUM_HOSTAPD_MODES;
738325151a3SRui Paulo 
739325151a3SRui Paulo 		if (vht)
740325151a3SRui Paulo 			return NUM_HOSTAPD_MODES;
741325151a3SRui Paulo 
742325151a3SRui Paulo 		/* 2.407 GHz, channels 1..13 */
743325151a3SRui Paulo 		if (sec_channel == 1)
744325151a3SRui Paulo 			*op_class = 83;
745325151a3SRui Paulo 		else if (sec_channel == -1)
746325151a3SRui Paulo 			*op_class = 84;
747325151a3SRui Paulo 		else
748325151a3SRui Paulo 			*op_class = 81;
749325151a3SRui Paulo 
7505b9c547cSRui Paulo 		*channel = (freq - 2407) / 5;
751325151a3SRui Paulo 
752325151a3SRui Paulo 		return HOSTAPD_MODE_IEEE80211G;
753325151a3SRui Paulo 	}
754325151a3SRui Paulo 
755325151a3SRui Paulo 	if (freq == 2484) {
756325151a3SRui Paulo 		if (sec_channel || vht)
757325151a3SRui Paulo 			return NUM_HOSTAPD_MODES;
758325151a3SRui Paulo 
759325151a3SRui Paulo 		*op_class = 82; /* channel 14 */
7605b9c547cSRui Paulo 		*channel = 14;
761325151a3SRui Paulo 
762325151a3SRui Paulo 		return HOSTAPD_MODE_IEEE80211B;
763325151a3SRui Paulo 	}
764325151a3SRui Paulo 
765325151a3SRui Paulo 	if (freq >= 4900 && freq < 5000) {
766325151a3SRui Paulo 		if ((freq - 4000) % 5)
767325151a3SRui Paulo 			return NUM_HOSTAPD_MODES;
7685b9c547cSRui Paulo 		*channel = (freq - 4000) / 5;
769325151a3SRui Paulo 		*op_class = 0; /* TODO */
770325151a3SRui Paulo 		return HOSTAPD_MODE_IEEE80211A;
771325151a3SRui Paulo 	}
772325151a3SRui Paulo 
773780fb4a2SCy Schubert 	switch (vht) {
774*206b73d0SCy Schubert 	case CHANWIDTH_80MHZ:
775780fb4a2SCy Schubert 		vht_opclass = 128;
776780fb4a2SCy Schubert 		break;
777*206b73d0SCy Schubert 	case CHANWIDTH_160MHZ:
778780fb4a2SCy Schubert 		vht_opclass = 129;
779780fb4a2SCy Schubert 		break;
780*206b73d0SCy Schubert 	case CHANWIDTH_80P80MHZ:
781780fb4a2SCy Schubert 		vht_opclass = 130;
782780fb4a2SCy Schubert 		break;
783780fb4a2SCy Schubert 	default:
784780fb4a2SCy Schubert 		vht_opclass = 0;
785780fb4a2SCy Schubert 		break;
786780fb4a2SCy Schubert 	}
787780fb4a2SCy Schubert 
788325151a3SRui Paulo 	/* 5 GHz, channels 36..48 */
789325151a3SRui Paulo 	if (freq >= 5180 && freq <= 5240) {
790325151a3SRui Paulo 		if ((freq - 5000) % 5)
791325151a3SRui Paulo 			return NUM_HOSTAPD_MODES;
792325151a3SRui Paulo 
793780fb4a2SCy Schubert 		if (vht_opclass)
794780fb4a2SCy Schubert 			*op_class = vht_opclass;
795780fb4a2SCy Schubert 		else if (sec_channel == 1)
796325151a3SRui Paulo 			*op_class = 116;
797325151a3SRui Paulo 		else if (sec_channel == -1)
798325151a3SRui Paulo 			*op_class = 117;
799325151a3SRui Paulo 		else
800325151a3SRui Paulo 			*op_class = 115;
801325151a3SRui Paulo 
8025b9c547cSRui Paulo 		*channel = (freq - 5000) / 5;
803325151a3SRui Paulo 
804325151a3SRui Paulo 		return HOSTAPD_MODE_IEEE80211A;
805325151a3SRui Paulo 	}
806325151a3SRui Paulo 
80785732ac8SCy Schubert 	/* 5 GHz, channels 52..64 */
80885732ac8SCy Schubert 	if (freq >= 5260 && freq <= 5320) {
80985732ac8SCy Schubert 		if ((freq - 5000) % 5)
81085732ac8SCy Schubert 			return NUM_HOSTAPD_MODES;
81185732ac8SCy Schubert 
81285732ac8SCy Schubert 		if (vht_opclass)
81385732ac8SCy Schubert 			*op_class = vht_opclass;
81485732ac8SCy Schubert 		else if (sec_channel == 1)
81585732ac8SCy Schubert 			*op_class = 119;
81685732ac8SCy Schubert 		else if (sec_channel == -1)
81785732ac8SCy Schubert 			*op_class = 120;
81885732ac8SCy Schubert 		else
81985732ac8SCy Schubert 			*op_class = 118;
82085732ac8SCy Schubert 
82185732ac8SCy Schubert 		*channel = (freq - 5000) / 5;
82285732ac8SCy Schubert 
82385732ac8SCy Schubert 		return HOSTAPD_MODE_IEEE80211A;
82485732ac8SCy Schubert 	}
82585732ac8SCy Schubert 
826325151a3SRui Paulo 	/* 5 GHz, channels 149..169 */
827325151a3SRui Paulo 	if (freq >= 5745 && freq <= 5845) {
828325151a3SRui Paulo 		if ((freq - 5000) % 5)
829325151a3SRui Paulo 			return NUM_HOSTAPD_MODES;
830325151a3SRui Paulo 
831780fb4a2SCy Schubert 		if (vht_opclass)
832780fb4a2SCy Schubert 			*op_class = vht_opclass;
833780fb4a2SCy Schubert 		else if (sec_channel == 1)
834780fb4a2SCy Schubert 			*op_class = 126;
835780fb4a2SCy Schubert 		else if (sec_channel == -1)
836780fb4a2SCy Schubert 			*op_class = 127;
837780fb4a2SCy Schubert 		else if (freq <= 5805)
838780fb4a2SCy Schubert 			*op_class = 124;
839780fb4a2SCy Schubert 		else
840325151a3SRui Paulo 			*op_class = 125;
841325151a3SRui Paulo 
842325151a3SRui Paulo 		*channel = (freq - 5000) / 5;
843325151a3SRui Paulo 
844325151a3SRui Paulo 		return HOSTAPD_MODE_IEEE80211A;
845325151a3SRui Paulo 	}
846325151a3SRui Paulo 
847780fb4a2SCy Schubert 	/* 5 GHz, channels 100..140 */
848780fb4a2SCy Schubert 	if (freq >= 5000 && freq <= 5700) {
849780fb4a2SCy Schubert 		if ((freq - 5000) % 5)
850780fb4a2SCy Schubert 			return NUM_HOSTAPD_MODES;
851780fb4a2SCy Schubert 
852780fb4a2SCy Schubert 		if (vht_opclass)
853780fb4a2SCy Schubert 			*op_class = vht_opclass;
854780fb4a2SCy Schubert 		else if (sec_channel == 1)
855780fb4a2SCy Schubert 			*op_class = 122;
856780fb4a2SCy Schubert 		else if (sec_channel == -1)
857780fb4a2SCy Schubert 			*op_class = 123;
858780fb4a2SCy Schubert 		else
859780fb4a2SCy Schubert 			*op_class = 121;
860780fb4a2SCy Schubert 
861780fb4a2SCy Schubert 		*channel = (freq - 5000) / 5;
862780fb4a2SCy Schubert 
863780fb4a2SCy Schubert 		return HOSTAPD_MODE_IEEE80211A;
864780fb4a2SCy Schubert 	}
865780fb4a2SCy Schubert 
866325151a3SRui Paulo 	if (freq >= 5000 && freq < 5900) {
867325151a3SRui Paulo 		if ((freq - 5000) % 5)
868325151a3SRui Paulo 			return NUM_HOSTAPD_MODES;
869325151a3SRui Paulo 		*channel = (freq - 5000) / 5;
870325151a3SRui Paulo 		*op_class = 0; /* TODO */
871325151a3SRui Paulo 		return HOSTAPD_MODE_IEEE80211A;
872325151a3SRui Paulo 	}
873325151a3SRui Paulo 
874325151a3SRui Paulo 	/* 56.16 GHz, channel 1..4 */
875325151a3SRui Paulo 	if (freq >= 56160 + 2160 * 1 && freq <= 56160 + 2160 * 4) {
876325151a3SRui Paulo 		if (sec_channel || vht)
877325151a3SRui Paulo 			return NUM_HOSTAPD_MODES;
878325151a3SRui Paulo 
8795b9c547cSRui Paulo 		*channel = (freq - 56160) / 2160;
880325151a3SRui Paulo 		*op_class = 180;
881325151a3SRui Paulo 
882325151a3SRui Paulo 		return HOSTAPD_MODE_IEEE80211AD;
8835b9c547cSRui Paulo 	}
8845b9c547cSRui Paulo 
885325151a3SRui Paulo 	return NUM_HOSTAPD_MODES;
8865b9c547cSRui Paulo }
8875b9c547cSRui Paulo 
8885b9c547cSRui Paulo 
8894bc52338SCy Schubert int ieee80211_chaninfo_to_channel(unsigned int freq, enum chan_width chanwidth,
8904bc52338SCy Schubert 				  int sec_channel, u8 *op_class, u8 *channel)
8914bc52338SCy Schubert {
8924bc52338SCy Schubert 	int vht = CHAN_WIDTH_UNKNOWN;
8934bc52338SCy Schubert 
8944bc52338SCy Schubert 	switch (chanwidth) {
8954bc52338SCy Schubert 	case CHAN_WIDTH_UNKNOWN:
8964bc52338SCy Schubert 	case CHAN_WIDTH_20_NOHT:
8974bc52338SCy Schubert 	case CHAN_WIDTH_20:
8984bc52338SCy Schubert 	case CHAN_WIDTH_40:
899*206b73d0SCy Schubert 		vht = CHANWIDTH_USE_HT;
9004bc52338SCy Schubert 		break;
9014bc52338SCy Schubert 	case CHAN_WIDTH_80:
902*206b73d0SCy Schubert 		vht = CHANWIDTH_80MHZ;
9034bc52338SCy Schubert 		break;
9044bc52338SCy Schubert 	case CHAN_WIDTH_80P80:
905*206b73d0SCy Schubert 		vht = CHANWIDTH_80P80MHZ;
9064bc52338SCy Schubert 		break;
9074bc52338SCy Schubert 	case CHAN_WIDTH_160:
908*206b73d0SCy Schubert 		vht = CHANWIDTH_160MHZ;
9094bc52338SCy Schubert 		break;
9104bc52338SCy Schubert 	}
9114bc52338SCy Schubert 
9124bc52338SCy Schubert 	if (ieee80211_freq_to_channel_ext(freq, sec_channel, vht, op_class,
9134bc52338SCy Schubert 					  channel) == NUM_HOSTAPD_MODES) {
9144bc52338SCy Schubert 		wpa_printf(MSG_WARNING,
9154bc52338SCy Schubert 			   "Cannot determine operating class and channel (freq=%u chanwidth=%d sec_channel=%d)",
9164bc52338SCy Schubert 			   freq, chanwidth, sec_channel);
9174bc52338SCy Schubert 		return -1;
9184bc52338SCy Schubert 	}
9194bc52338SCy Schubert 
9204bc52338SCy Schubert 	return 0;
9214bc52338SCy Schubert }
9224bc52338SCy Schubert 
9234bc52338SCy Schubert 
924325151a3SRui Paulo static const char *const us_op_class_cc[] = {
9255b9c547cSRui Paulo 	"US", "CA", NULL
9265b9c547cSRui Paulo };
9275b9c547cSRui Paulo 
928325151a3SRui Paulo static const char *const eu_op_class_cc[] = {
9295b9c547cSRui Paulo 	"AL", "AM", "AT", "AZ", "BA", "BE", "BG", "BY", "CH", "CY", "CZ", "DE",
9305b9c547cSRui Paulo 	"DK", "EE", "EL", "ES", "FI", "FR", "GE", "HR", "HU", "IE", "IS", "IT",
9315b9c547cSRui Paulo 	"LI", "LT", "LU", "LV", "MD", "ME", "MK", "MT", "NL", "NO", "PL", "PT",
9325b9c547cSRui Paulo 	"RO", "RS", "RU", "SE", "SI", "SK", "TR", "UA", "UK", NULL
9335b9c547cSRui Paulo };
9345b9c547cSRui Paulo 
935325151a3SRui Paulo static const char *const jp_op_class_cc[] = {
9365b9c547cSRui Paulo 	"JP", NULL
9375b9c547cSRui Paulo };
9385b9c547cSRui Paulo 
939325151a3SRui Paulo static const char *const cn_op_class_cc[] = {
940325151a3SRui Paulo 	"CN", NULL
9415b9c547cSRui Paulo };
9425b9c547cSRui Paulo 
9435b9c547cSRui Paulo 
944325151a3SRui Paulo static int country_match(const char *const cc[], const char *const country)
9455b9c547cSRui Paulo {
9465b9c547cSRui Paulo 	int i;
9475b9c547cSRui Paulo 
9485b9c547cSRui Paulo 	if (country == NULL)
9495b9c547cSRui Paulo 		return 0;
9505b9c547cSRui Paulo 	for (i = 0; cc[i]; i++) {
9515b9c547cSRui Paulo 		if (cc[i][0] == country[0] && cc[i][1] == country[1])
9525b9c547cSRui Paulo 			return 1;
9535b9c547cSRui Paulo 	}
9545b9c547cSRui Paulo 
9555b9c547cSRui Paulo 	return 0;
9565b9c547cSRui Paulo }
9575b9c547cSRui Paulo 
9585b9c547cSRui Paulo 
9595b9c547cSRui Paulo static int ieee80211_chan_to_freq_us(u8 op_class, u8 chan)
9605b9c547cSRui Paulo {
9615b9c547cSRui Paulo 	switch (op_class) {
9625b9c547cSRui Paulo 	case 12: /* channels 1..11 */
9635b9c547cSRui Paulo 	case 32: /* channels 1..7; 40 MHz */
9645b9c547cSRui Paulo 	case 33: /* channels 5..11; 40 MHz */
9655b9c547cSRui Paulo 		if (chan < 1 || chan > 11)
9665b9c547cSRui Paulo 			return -1;
9675b9c547cSRui Paulo 		return 2407 + 5 * chan;
9685b9c547cSRui Paulo 	case 1: /* channels 36,40,44,48 */
9695b9c547cSRui Paulo 	case 2: /* channels 52,56,60,64; dfs */
9705b9c547cSRui Paulo 	case 22: /* channels 36,44; 40 MHz */
9715b9c547cSRui Paulo 	case 23: /* channels 52,60; 40 MHz */
9725b9c547cSRui Paulo 	case 27: /* channels 40,48; 40 MHz */
9735b9c547cSRui Paulo 	case 28: /* channels 56,64; 40 MHz */
9745b9c547cSRui Paulo 		if (chan < 36 || chan > 64)
9755b9c547cSRui Paulo 			return -1;
9765b9c547cSRui Paulo 		return 5000 + 5 * chan;
9775b9c547cSRui Paulo 	case 4: /* channels 100-144 */
9785b9c547cSRui Paulo 	case 24: /* channels 100-140; 40 MHz */
9795b9c547cSRui Paulo 		if (chan < 100 || chan > 144)
9805b9c547cSRui Paulo 			return -1;
9815b9c547cSRui Paulo 		return 5000 + 5 * chan;
9825b9c547cSRui Paulo 	case 3: /* channels 149,153,157,161 */
9835b9c547cSRui Paulo 	case 25: /* channels 149,157; 40 MHz */
9845b9c547cSRui Paulo 	case 26: /* channels 149,157; 40 MHz */
9855b9c547cSRui Paulo 	case 30: /* channels 153,161; 40 MHz */
9865b9c547cSRui Paulo 	case 31: /* channels 153,161; 40 MHz */
9875b9c547cSRui Paulo 		if (chan < 149 || chan > 161)
9885b9c547cSRui Paulo 			return -1;
9895b9c547cSRui Paulo 		return 5000 + 5 * chan;
990325151a3SRui Paulo 	case 5: /* channels 149,153,157,161,165 */
991325151a3SRui Paulo 		if (chan < 149 || chan > 165)
992325151a3SRui Paulo 			return -1;
993325151a3SRui Paulo 		return 5000 + 5 * chan;
9945b9c547cSRui Paulo 	case 34: /* 60 GHz band, channels 1..3 */
9955b9c547cSRui Paulo 		if (chan < 1 || chan > 3)
9965b9c547cSRui Paulo 			return -1;
9975b9c547cSRui Paulo 		return 56160 + 2160 * chan;
9985b9c547cSRui Paulo 	}
9995b9c547cSRui Paulo 	return -1;
10005b9c547cSRui Paulo }
10015b9c547cSRui Paulo 
10025b9c547cSRui Paulo 
10035b9c547cSRui Paulo static int ieee80211_chan_to_freq_eu(u8 op_class, u8 chan)
10045b9c547cSRui Paulo {
10055b9c547cSRui Paulo 	switch (op_class) {
10065b9c547cSRui Paulo 	case 4: /* channels 1..13 */
10075b9c547cSRui Paulo 	case 11: /* channels 1..9; 40 MHz */
10085b9c547cSRui Paulo 	case 12: /* channels 5..13; 40 MHz */
10095b9c547cSRui Paulo 		if (chan < 1 || chan > 13)
10105b9c547cSRui Paulo 			return -1;
10115b9c547cSRui Paulo 		return 2407 + 5 * chan;
10125b9c547cSRui Paulo 	case 1: /* channels 36,40,44,48 */
10135b9c547cSRui Paulo 	case 2: /* channels 52,56,60,64; dfs */
10145b9c547cSRui Paulo 	case 5: /* channels 36,44; 40 MHz */
10155b9c547cSRui Paulo 	case 6: /* channels 52,60; 40 MHz */
10165b9c547cSRui Paulo 	case 8: /* channels 40,48; 40 MHz */
10175b9c547cSRui Paulo 	case 9: /* channels 56,64; 40 MHz */
10185b9c547cSRui Paulo 		if (chan < 36 || chan > 64)
10195b9c547cSRui Paulo 			return -1;
10205b9c547cSRui Paulo 		return 5000 + 5 * chan;
10215b9c547cSRui Paulo 	case 3: /* channels 100-140 */
10225b9c547cSRui Paulo 	case 7: /* channels 100-132; 40 MHz */
10235b9c547cSRui Paulo 	case 10: /* channels 104-136; 40 MHz */
10245b9c547cSRui Paulo 	case 16: /* channels 100-140 */
10255b9c547cSRui Paulo 		if (chan < 100 || chan > 140)
10265b9c547cSRui Paulo 			return -1;
10275b9c547cSRui Paulo 		return 5000 + 5 * chan;
10285b9c547cSRui Paulo 	case 17: /* channels 149,153,157,161,165,169 */
10295b9c547cSRui Paulo 		if (chan < 149 || chan > 169)
10305b9c547cSRui Paulo 			return -1;
10315b9c547cSRui Paulo 		return 5000 + 5 * chan;
10325b9c547cSRui Paulo 	case 18: /* 60 GHz band, channels 1..4 */
10335b9c547cSRui Paulo 		if (chan < 1 || chan > 4)
10345b9c547cSRui Paulo 			return -1;
10355b9c547cSRui Paulo 		return 56160 + 2160 * chan;
10365b9c547cSRui Paulo 	}
10375b9c547cSRui Paulo 	return -1;
10385b9c547cSRui Paulo }
10395b9c547cSRui Paulo 
10405b9c547cSRui Paulo 
10415b9c547cSRui Paulo static int ieee80211_chan_to_freq_jp(u8 op_class, u8 chan)
10425b9c547cSRui Paulo {
10435b9c547cSRui Paulo 	switch (op_class) {
10445b9c547cSRui Paulo 	case 30: /* channels 1..13 */
10455b9c547cSRui Paulo 	case 56: /* channels 1..9; 40 MHz */
10465b9c547cSRui Paulo 	case 57: /* channels 5..13; 40 MHz */
10475b9c547cSRui Paulo 		if (chan < 1 || chan > 13)
10485b9c547cSRui Paulo 			return -1;
10495b9c547cSRui Paulo 		return 2407 + 5 * chan;
10505b9c547cSRui Paulo 	case 31: /* channel 14 */
10515b9c547cSRui Paulo 		if (chan != 14)
10525b9c547cSRui Paulo 			return -1;
10535b9c547cSRui Paulo 		return 2414 + 5 * chan;
10545b9c547cSRui Paulo 	case 1: /* channels 34,38,42,46(old) or 36,40,44,48 */
10555b9c547cSRui Paulo 	case 32: /* channels 52,56,60,64 */
10565b9c547cSRui Paulo 	case 33: /* channels 52,56,60,64 */
10575b9c547cSRui Paulo 	case 36: /* channels 36,44; 40 MHz */
10585b9c547cSRui Paulo 	case 37: /* channels 52,60; 40 MHz */
10595b9c547cSRui Paulo 	case 38: /* channels 52,60; 40 MHz */
10605b9c547cSRui Paulo 	case 41: /* channels 40,48; 40 MHz */
10615b9c547cSRui Paulo 	case 42: /* channels 56,64; 40 MHz */
10625b9c547cSRui Paulo 	case 43: /* channels 56,64; 40 MHz */
10635b9c547cSRui Paulo 		if (chan < 34 || chan > 64)
10645b9c547cSRui Paulo 			return -1;
10655b9c547cSRui Paulo 		return 5000 + 5 * chan;
10665b9c547cSRui Paulo 	case 34: /* channels 100-140 */
10675b9c547cSRui Paulo 	case 35: /* channels 100-140 */
10685b9c547cSRui Paulo 	case 39: /* channels 100-132; 40 MHz */
10695b9c547cSRui Paulo 	case 40: /* channels 100-132; 40 MHz */
10705b9c547cSRui Paulo 	case 44: /* channels 104-136; 40 MHz */
10715b9c547cSRui Paulo 	case 45: /* channels 104-136; 40 MHz */
10725b9c547cSRui Paulo 	case 58: /* channels 100-140 */
10735b9c547cSRui Paulo 		if (chan < 100 || chan > 140)
10745b9c547cSRui Paulo 			return -1;
10755b9c547cSRui Paulo 		return 5000 + 5 * chan;
10765b9c547cSRui Paulo 	case 59: /* 60 GHz band, channels 1..4 */
10775b9c547cSRui Paulo 		if (chan < 1 || chan > 3)
10785b9c547cSRui Paulo 			return -1;
10795b9c547cSRui Paulo 		return 56160 + 2160 * chan;
10805b9c547cSRui Paulo 	}
10815b9c547cSRui Paulo 	return -1;
10825b9c547cSRui Paulo }
10835b9c547cSRui Paulo 
10845b9c547cSRui Paulo 
10855b9c547cSRui Paulo static int ieee80211_chan_to_freq_cn(u8 op_class, u8 chan)
10865b9c547cSRui Paulo {
10875b9c547cSRui Paulo 	switch (op_class) {
10885b9c547cSRui Paulo 	case 7: /* channels 1..13 */
10895b9c547cSRui Paulo 	case 8: /* channels 1..9; 40 MHz */
10905b9c547cSRui Paulo 	case 9: /* channels 5..13; 40 MHz */
10915b9c547cSRui Paulo 		if (chan < 1 || chan > 13)
10925b9c547cSRui Paulo 			return -1;
10935b9c547cSRui Paulo 		return 2407 + 5 * chan;
10945b9c547cSRui Paulo 	case 1: /* channels 36,40,44,48 */
10955b9c547cSRui Paulo 	case 2: /* channels 52,56,60,64; dfs */
10965b9c547cSRui Paulo 	case 4: /* channels 36,44; 40 MHz */
10975b9c547cSRui Paulo 	case 5: /* channels 52,60; 40 MHz */
10985b9c547cSRui Paulo 		if (chan < 36 || chan > 64)
10995b9c547cSRui Paulo 			return -1;
11005b9c547cSRui Paulo 		return 5000 + 5 * chan;
11015b9c547cSRui Paulo 	case 3: /* channels 149,153,157,161,165 */
11025b9c547cSRui Paulo 	case 6: /* channels 149,157; 40 MHz */
11035b9c547cSRui Paulo 		if (chan < 149 || chan > 165)
11045b9c547cSRui Paulo 			return -1;
11055b9c547cSRui Paulo 		return 5000 + 5 * chan;
11065b9c547cSRui Paulo 	}
11075b9c547cSRui Paulo 	return -1;
11085b9c547cSRui Paulo }
11095b9c547cSRui Paulo 
11105b9c547cSRui Paulo 
11115b9c547cSRui Paulo static int ieee80211_chan_to_freq_global(u8 op_class, u8 chan)
11125b9c547cSRui Paulo {
11135b9c547cSRui Paulo 	/* Table E-4 in IEEE Std 802.11-2012 - Global operating classes */
11145b9c547cSRui Paulo 	switch (op_class) {
11155b9c547cSRui Paulo 	case 81:
11165b9c547cSRui Paulo 		/* channels 1..13 */
11175b9c547cSRui Paulo 		if (chan < 1 || chan > 13)
11185b9c547cSRui Paulo 			return -1;
11195b9c547cSRui Paulo 		return 2407 + 5 * chan;
11205b9c547cSRui Paulo 	case 82:
11215b9c547cSRui Paulo 		/* channel 14 */
11225b9c547cSRui Paulo 		if (chan != 14)
11235b9c547cSRui Paulo 			return -1;
11245b9c547cSRui Paulo 		return 2414 + 5 * chan;
11255b9c547cSRui Paulo 	case 83: /* channels 1..9; 40 MHz */
11265b9c547cSRui Paulo 	case 84: /* channels 5..13; 40 MHz */
11275b9c547cSRui Paulo 		if (chan < 1 || chan > 13)
11285b9c547cSRui Paulo 			return -1;
11295b9c547cSRui Paulo 		return 2407 + 5 * chan;
11305b9c547cSRui Paulo 	case 115: /* channels 36,40,44,48; indoor only */
11315b9c547cSRui Paulo 	case 116: /* channels 36,44; 40 MHz; indoor only */
11325b9c547cSRui Paulo 	case 117: /* channels 40,48; 40 MHz; indoor only */
11335b9c547cSRui Paulo 	case 118: /* channels 52,56,60,64; dfs */
11345b9c547cSRui Paulo 	case 119: /* channels 52,60; 40 MHz; dfs */
11355b9c547cSRui Paulo 	case 120: /* channels 56,64; 40 MHz; dfs */
11365b9c547cSRui Paulo 		if (chan < 36 || chan > 64)
11375b9c547cSRui Paulo 			return -1;
11385b9c547cSRui Paulo 		return 5000 + 5 * chan;
11395b9c547cSRui Paulo 	case 121: /* channels 100-140 */
11405b9c547cSRui Paulo 	case 122: /* channels 100-142; 40 MHz */
11415b9c547cSRui Paulo 	case 123: /* channels 104-136; 40 MHz */
11425b9c547cSRui Paulo 		if (chan < 100 || chan > 140)
11435b9c547cSRui Paulo 			return -1;
11445b9c547cSRui Paulo 		return 5000 + 5 * chan;
11455b9c547cSRui Paulo 	case 124: /* channels 149,153,157,161 */
11465b9c547cSRui Paulo 	case 126: /* channels 149,157; 40 MHz */
11475b9c547cSRui Paulo 	case 127: /* channels 153,161; 40 MHz */
11485b9c547cSRui Paulo 		if (chan < 149 || chan > 161)
11495b9c547cSRui Paulo 			return -1;
11505b9c547cSRui Paulo 		return 5000 + 5 * chan;
1151325151a3SRui Paulo 	case 125: /* channels 149,153,157,161,165,169 */
1152325151a3SRui Paulo 		if (chan < 149 || chan > 169)
1153325151a3SRui Paulo 			return -1;
1154325151a3SRui Paulo 		return 5000 + 5 * chan;
11555b9c547cSRui Paulo 	case 128: /* center freqs 42, 58, 106, 122, 138, 155; 80 MHz */
11565b9c547cSRui Paulo 	case 130: /* center freqs 42, 58, 106, 122, 138, 155; 80 MHz */
11575b9c547cSRui Paulo 		if (chan < 36 || chan > 161)
11585b9c547cSRui Paulo 			return -1;
11595b9c547cSRui Paulo 		return 5000 + 5 * chan;
11605b9c547cSRui Paulo 	case 129: /* center freqs 50, 114; 160 MHz */
116185732ac8SCy Schubert 		if (chan < 36 || chan > 128)
11625b9c547cSRui Paulo 			return -1;
11635b9c547cSRui Paulo 		return 5000 + 5 * chan;
11645b9c547cSRui Paulo 	case 180: /* 60 GHz band, channels 1..4 */
11655b9c547cSRui Paulo 		if (chan < 1 || chan > 4)
11665b9c547cSRui Paulo 			return -1;
11675b9c547cSRui Paulo 		return 56160 + 2160 * chan;
11685b9c547cSRui Paulo 	}
11695b9c547cSRui Paulo 	return -1;
11705b9c547cSRui Paulo }
11715b9c547cSRui Paulo 
11725b9c547cSRui Paulo /**
11735b9c547cSRui Paulo  * ieee80211_chan_to_freq - Convert channel info to frequency
11745b9c547cSRui Paulo  * @country: Country code, if known; otherwise, global operating class is used
11755b9c547cSRui Paulo  * @op_class: Operating class
11765b9c547cSRui Paulo  * @chan: Channel number
11775b9c547cSRui Paulo  * Returns: Frequency in MHz or -1 if the specified channel is unknown
11785b9c547cSRui Paulo  */
11795b9c547cSRui Paulo int ieee80211_chan_to_freq(const char *country, u8 op_class, u8 chan)
11805b9c547cSRui Paulo {
11815b9c547cSRui Paulo 	int freq;
11825b9c547cSRui Paulo 
11835b9c547cSRui Paulo 	if (country_match(us_op_class_cc, country)) {
11845b9c547cSRui Paulo 		freq = ieee80211_chan_to_freq_us(op_class, chan);
11855b9c547cSRui Paulo 		if (freq > 0)
11865b9c547cSRui Paulo 			return freq;
11875b9c547cSRui Paulo 	}
11885b9c547cSRui Paulo 
11895b9c547cSRui Paulo 	if (country_match(eu_op_class_cc, country)) {
11905b9c547cSRui Paulo 		freq = ieee80211_chan_to_freq_eu(op_class, chan);
11915b9c547cSRui Paulo 		if (freq > 0)
11925b9c547cSRui Paulo 			return freq;
11935b9c547cSRui Paulo 	}
11945b9c547cSRui Paulo 
11955b9c547cSRui Paulo 	if (country_match(jp_op_class_cc, country)) {
11965b9c547cSRui Paulo 		freq = ieee80211_chan_to_freq_jp(op_class, chan);
11975b9c547cSRui Paulo 		if (freq > 0)
11985b9c547cSRui Paulo 			return freq;
11995b9c547cSRui Paulo 	}
12005b9c547cSRui Paulo 
12015b9c547cSRui Paulo 	if (country_match(cn_op_class_cc, country)) {
12025b9c547cSRui Paulo 		freq = ieee80211_chan_to_freq_cn(op_class, chan);
12035b9c547cSRui Paulo 		if (freq > 0)
12045b9c547cSRui Paulo 			return freq;
12055b9c547cSRui Paulo 	}
12065b9c547cSRui Paulo 
12075b9c547cSRui Paulo 	return ieee80211_chan_to_freq_global(op_class, chan);
12085b9c547cSRui Paulo }
12095b9c547cSRui Paulo 
12105b9c547cSRui Paulo 
121185732ac8SCy Schubert int ieee80211_is_dfs(int freq, const struct hostapd_hw_modes *modes,
121285732ac8SCy Schubert 		     u16 num_modes)
12135b9c547cSRui Paulo {
121485732ac8SCy Schubert 	int i, j;
121585732ac8SCy Schubert 
121685732ac8SCy Schubert 	if (!modes || !num_modes)
121785732ac8SCy Schubert 		return (freq >= 5260 && freq <= 5320) ||
121885732ac8SCy Schubert 			(freq >= 5500 && freq <= 5700);
121985732ac8SCy Schubert 
122085732ac8SCy Schubert 	for (i = 0; i < num_modes; i++) {
122185732ac8SCy Schubert 		for (j = 0; j < modes[i].num_channels; j++) {
122285732ac8SCy Schubert 			if (modes[i].channels[j].freq == freq &&
122385732ac8SCy Schubert 			    (modes[i].channels[j].flag & HOSTAPD_CHAN_RADAR))
122485732ac8SCy Schubert 				return 1;
122585732ac8SCy Schubert 		}
122685732ac8SCy Schubert 	}
122785732ac8SCy Schubert 
122885732ac8SCy Schubert 	return 0;
12295b9c547cSRui Paulo }
12305b9c547cSRui Paulo 
12315b9c547cSRui Paulo 
12325b9c547cSRui Paulo static int is_11b(u8 rate)
12335b9c547cSRui Paulo {
12345b9c547cSRui Paulo 	return rate == 0x02 || rate == 0x04 || rate == 0x0b || rate == 0x16;
12355b9c547cSRui Paulo }
12365b9c547cSRui Paulo 
12375b9c547cSRui Paulo 
12385b9c547cSRui Paulo int supp_rates_11b_only(struct ieee802_11_elems *elems)
12395b9c547cSRui Paulo {
12405b9c547cSRui Paulo 	int num_11b = 0, num_others = 0;
12415b9c547cSRui Paulo 	int i;
12425b9c547cSRui Paulo 
12435b9c547cSRui Paulo 	if (elems->supp_rates == NULL && elems->ext_supp_rates == NULL)
12445b9c547cSRui Paulo 		return 0;
12455b9c547cSRui Paulo 
12465b9c547cSRui Paulo 	for (i = 0; elems->supp_rates && i < elems->supp_rates_len; i++) {
12475b9c547cSRui Paulo 		if (is_11b(elems->supp_rates[i]))
12485b9c547cSRui Paulo 			num_11b++;
12495b9c547cSRui Paulo 		else
12505b9c547cSRui Paulo 			num_others++;
12515b9c547cSRui Paulo 	}
12525b9c547cSRui Paulo 
12535b9c547cSRui Paulo 	for (i = 0; elems->ext_supp_rates && i < elems->ext_supp_rates_len;
12545b9c547cSRui Paulo 	     i++) {
12555b9c547cSRui Paulo 		if (is_11b(elems->ext_supp_rates[i]))
12565b9c547cSRui Paulo 			num_11b++;
12575b9c547cSRui Paulo 		else
12585b9c547cSRui Paulo 			num_others++;
12595b9c547cSRui Paulo 	}
12605b9c547cSRui Paulo 
12615b9c547cSRui Paulo 	return num_11b > 0 && num_others == 0;
12625b9c547cSRui Paulo }
12635b9c547cSRui Paulo 
12645b9c547cSRui Paulo 
12655b9c547cSRui Paulo const char * fc2str(u16 fc)
12665b9c547cSRui Paulo {
12675b9c547cSRui Paulo 	u16 stype = WLAN_FC_GET_STYPE(fc);
12685b9c547cSRui Paulo #define C2S(x) case x: return #x;
12695b9c547cSRui Paulo 
12705b9c547cSRui Paulo 	switch (WLAN_FC_GET_TYPE(fc)) {
12715b9c547cSRui Paulo 	case WLAN_FC_TYPE_MGMT:
12725b9c547cSRui Paulo 		switch (stype) {
12735b9c547cSRui Paulo 		C2S(WLAN_FC_STYPE_ASSOC_REQ)
12745b9c547cSRui Paulo 		C2S(WLAN_FC_STYPE_ASSOC_RESP)
12755b9c547cSRui Paulo 		C2S(WLAN_FC_STYPE_REASSOC_REQ)
12765b9c547cSRui Paulo 		C2S(WLAN_FC_STYPE_REASSOC_RESP)
12775b9c547cSRui Paulo 		C2S(WLAN_FC_STYPE_PROBE_REQ)
12785b9c547cSRui Paulo 		C2S(WLAN_FC_STYPE_PROBE_RESP)
12795b9c547cSRui Paulo 		C2S(WLAN_FC_STYPE_BEACON)
12805b9c547cSRui Paulo 		C2S(WLAN_FC_STYPE_ATIM)
12815b9c547cSRui Paulo 		C2S(WLAN_FC_STYPE_DISASSOC)
12825b9c547cSRui Paulo 		C2S(WLAN_FC_STYPE_AUTH)
12835b9c547cSRui Paulo 		C2S(WLAN_FC_STYPE_DEAUTH)
12845b9c547cSRui Paulo 		C2S(WLAN_FC_STYPE_ACTION)
12855b9c547cSRui Paulo 		}
12865b9c547cSRui Paulo 		break;
12875b9c547cSRui Paulo 	case WLAN_FC_TYPE_CTRL:
12885b9c547cSRui Paulo 		switch (stype) {
12895b9c547cSRui Paulo 		C2S(WLAN_FC_STYPE_PSPOLL)
12905b9c547cSRui Paulo 		C2S(WLAN_FC_STYPE_RTS)
12915b9c547cSRui Paulo 		C2S(WLAN_FC_STYPE_CTS)
12925b9c547cSRui Paulo 		C2S(WLAN_FC_STYPE_ACK)
12935b9c547cSRui Paulo 		C2S(WLAN_FC_STYPE_CFEND)
12945b9c547cSRui Paulo 		C2S(WLAN_FC_STYPE_CFENDACK)
12955b9c547cSRui Paulo 		}
12965b9c547cSRui Paulo 		break;
12975b9c547cSRui Paulo 	case WLAN_FC_TYPE_DATA:
12985b9c547cSRui Paulo 		switch (stype) {
12995b9c547cSRui Paulo 		C2S(WLAN_FC_STYPE_DATA)
13005b9c547cSRui Paulo 		C2S(WLAN_FC_STYPE_DATA_CFACK)
13015b9c547cSRui Paulo 		C2S(WLAN_FC_STYPE_DATA_CFPOLL)
13025b9c547cSRui Paulo 		C2S(WLAN_FC_STYPE_DATA_CFACKPOLL)
13035b9c547cSRui Paulo 		C2S(WLAN_FC_STYPE_NULLFUNC)
13045b9c547cSRui Paulo 		C2S(WLAN_FC_STYPE_CFACK)
13055b9c547cSRui Paulo 		C2S(WLAN_FC_STYPE_CFPOLL)
13065b9c547cSRui Paulo 		C2S(WLAN_FC_STYPE_CFACKPOLL)
13075b9c547cSRui Paulo 		C2S(WLAN_FC_STYPE_QOS_DATA)
13085b9c547cSRui Paulo 		C2S(WLAN_FC_STYPE_QOS_DATA_CFACK)
13095b9c547cSRui Paulo 		C2S(WLAN_FC_STYPE_QOS_DATA_CFPOLL)
13105b9c547cSRui Paulo 		C2S(WLAN_FC_STYPE_QOS_DATA_CFACKPOLL)
13115b9c547cSRui Paulo 		C2S(WLAN_FC_STYPE_QOS_NULL)
13125b9c547cSRui Paulo 		C2S(WLAN_FC_STYPE_QOS_CFPOLL)
13135b9c547cSRui Paulo 		C2S(WLAN_FC_STYPE_QOS_CFACKPOLL)
13145b9c547cSRui Paulo 		}
13155b9c547cSRui Paulo 		break;
13165b9c547cSRui Paulo 	}
13175b9c547cSRui Paulo 	return "WLAN_FC_TYPE_UNKNOWN";
13185b9c547cSRui Paulo #undef C2S
13195b9c547cSRui Paulo }
1320325151a3SRui Paulo 
1321325151a3SRui Paulo 
1322*206b73d0SCy Schubert const char * reason2str(u16 reason)
1323*206b73d0SCy Schubert {
1324*206b73d0SCy Schubert #define R2S(r) case WLAN_REASON_ ## r: return #r;
1325*206b73d0SCy Schubert 	switch (reason) {
1326*206b73d0SCy Schubert 	R2S(UNSPECIFIED)
1327*206b73d0SCy Schubert 	R2S(PREV_AUTH_NOT_VALID)
1328*206b73d0SCy Schubert 	R2S(DEAUTH_LEAVING)
1329*206b73d0SCy Schubert 	R2S(DISASSOC_DUE_TO_INACTIVITY)
1330*206b73d0SCy Schubert 	R2S(DISASSOC_AP_BUSY)
1331*206b73d0SCy Schubert 	R2S(CLASS2_FRAME_FROM_NONAUTH_STA)
1332*206b73d0SCy Schubert 	R2S(CLASS3_FRAME_FROM_NONASSOC_STA)
1333*206b73d0SCy Schubert 	R2S(DISASSOC_STA_HAS_LEFT)
1334*206b73d0SCy Schubert 	R2S(STA_REQ_ASSOC_WITHOUT_AUTH)
1335*206b73d0SCy Schubert 	R2S(PWR_CAPABILITY_NOT_VALID)
1336*206b73d0SCy Schubert 	R2S(SUPPORTED_CHANNEL_NOT_VALID)
1337*206b73d0SCy Schubert 	R2S(BSS_TRANSITION_DISASSOC)
1338*206b73d0SCy Schubert 	R2S(INVALID_IE)
1339*206b73d0SCy Schubert 	R2S(MICHAEL_MIC_FAILURE)
1340*206b73d0SCy Schubert 	R2S(4WAY_HANDSHAKE_TIMEOUT)
1341*206b73d0SCy Schubert 	R2S(GROUP_KEY_UPDATE_TIMEOUT)
1342*206b73d0SCy Schubert 	R2S(IE_IN_4WAY_DIFFERS)
1343*206b73d0SCy Schubert 	R2S(GROUP_CIPHER_NOT_VALID)
1344*206b73d0SCy Schubert 	R2S(PAIRWISE_CIPHER_NOT_VALID)
1345*206b73d0SCy Schubert 	R2S(AKMP_NOT_VALID)
1346*206b73d0SCy Schubert 	R2S(UNSUPPORTED_RSN_IE_VERSION)
1347*206b73d0SCy Schubert 	R2S(INVALID_RSN_IE_CAPAB)
1348*206b73d0SCy Schubert 	R2S(IEEE_802_1X_AUTH_FAILED)
1349*206b73d0SCy Schubert 	R2S(CIPHER_SUITE_REJECTED)
1350*206b73d0SCy Schubert 	R2S(TDLS_TEARDOWN_UNREACHABLE)
1351*206b73d0SCy Schubert 	R2S(TDLS_TEARDOWN_UNSPECIFIED)
1352*206b73d0SCy Schubert 	R2S(SSP_REQUESTED_DISASSOC)
1353*206b73d0SCy Schubert 	R2S(NO_SSP_ROAMING_AGREEMENT)
1354*206b73d0SCy Schubert 	R2S(BAD_CIPHER_OR_AKM)
1355*206b73d0SCy Schubert 	R2S(NOT_AUTHORIZED_THIS_LOCATION)
1356*206b73d0SCy Schubert 	R2S(SERVICE_CHANGE_PRECLUDES_TS)
1357*206b73d0SCy Schubert 	R2S(UNSPECIFIED_QOS_REASON)
1358*206b73d0SCy Schubert 	R2S(NOT_ENOUGH_BANDWIDTH)
1359*206b73d0SCy Schubert 	R2S(DISASSOC_LOW_ACK)
1360*206b73d0SCy Schubert 	R2S(EXCEEDED_TXOP)
1361*206b73d0SCy Schubert 	R2S(STA_LEAVING)
1362*206b73d0SCy Schubert 	R2S(END_TS_BA_DLS)
1363*206b73d0SCy Schubert 	R2S(UNKNOWN_TS_BA)
1364*206b73d0SCy Schubert 	R2S(TIMEOUT)
1365*206b73d0SCy Schubert 	R2S(PEERKEY_MISMATCH)
1366*206b73d0SCy Schubert 	R2S(AUTHORIZED_ACCESS_LIMIT_REACHED)
1367*206b73d0SCy Schubert 	R2S(EXTERNAL_SERVICE_REQUIREMENTS)
1368*206b73d0SCy Schubert 	R2S(INVALID_FT_ACTION_FRAME_COUNT)
1369*206b73d0SCy Schubert 	R2S(INVALID_PMKID)
1370*206b73d0SCy Schubert 	R2S(INVALID_MDE)
1371*206b73d0SCy Schubert 	R2S(INVALID_FTE)
1372*206b73d0SCy Schubert 	R2S(MESH_PEERING_CANCELLED)
1373*206b73d0SCy Schubert 	R2S(MESH_MAX_PEERS)
1374*206b73d0SCy Schubert 	R2S(MESH_CONFIG_POLICY_VIOLATION)
1375*206b73d0SCy Schubert 	R2S(MESH_CLOSE_RCVD)
1376*206b73d0SCy Schubert 	R2S(MESH_MAX_RETRIES)
1377*206b73d0SCy Schubert 	R2S(MESH_CONFIRM_TIMEOUT)
1378*206b73d0SCy Schubert 	R2S(MESH_INVALID_GTK)
1379*206b73d0SCy Schubert 	R2S(MESH_INCONSISTENT_PARAMS)
1380*206b73d0SCy Schubert 	R2S(MESH_INVALID_SECURITY_CAP)
1381*206b73d0SCy Schubert 	R2S(MESH_PATH_ERROR_NO_PROXY_INFO)
1382*206b73d0SCy Schubert 	R2S(MESH_PATH_ERROR_NO_FORWARDING_INFO)
1383*206b73d0SCy Schubert 	R2S(MESH_PATH_ERROR_DEST_UNREACHABLE)
1384*206b73d0SCy Schubert 	R2S(MAC_ADDRESS_ALREADY_EXISTS_IN_MBSS)
1385*206b73d0SCy Schubert 	R2S(MESH_CHANNEL_SWITCH_REGULATORY_REQ)
1386*206b73d0SCy Schubert 	R2S(MESH_CHANNEL_SWITCH_UNSPECIFIED)
1387*206b73d0SCy Schubert 	}
1388*206b73d0SCy Schubert 	return "UNKNOWN";
1389*206b73d0SCy Schubert #undef R2S
1390*206b73d0SCy Schubert }
1391*206b73d0SCy Schubert 
1392*206b73d0SCy Schubert 
1393*206b73d0SCy Schubert const char * status2str(u16 status)
1394*206b73d0SCy Schubert {
1395*206b73d0SCy Schubert #define S2S(s) case WLAN_STATUS_ ## s: return #s;
1396*206b73d0SCy Schubert 	switch (status) {
1397*206b73d0SCy Schubert 	S2S(SUCCESS)
1398*206b73d0SCy Schubert 	S2S(UNSPECIFIED_FAILURE)
1399*206b73d0SCy Schubert 	S2S(TDLS_WAKEUP_ALTERNATE)
1400*206b73d0SCy Schubert 	S2S(TDLS_WAKEUP_REJECT)
1401*206b73d0SCy Schubert 	S2S(SECURITY_DISABLED)
1402*206b73d0SCy Schubert 	S2S(UNACCEPTABLE_LIFETIME)
1403*206b73d0SCy Schubert 	S2S(NOT_IN_SAME_BSS)
1404*206b73d0SCy Schubert 	S2S(CAPS_UNSUPPORTED)
1405*206b73d0SCy Schubert 	S2S(REASSOC_NO_ASSOC)
1406*206b73d0SCy Schubert 	S2S(ASSOC_DENIED_UNSPEC)
1407*206b73d0SCy Schubert 	S2S(NOT_SUPPORTED_AUTH_ALG)
1408*206b73d0SCy Schubert 	S2S(UNKNOWN_AUTH_TRANSACTION)
1409*206b73d0SCy Schubert 	S2S(CHALLENGE_FAIL)
1410*206b73d0SCy Schubert 	S2S(AUTH_TIMEOUT)
1411*206b73d0SCy Schubert 	S2S(AP_UNABLE_TO_HANDLE_NEW_STA)
1412*206b73d0SCy Schubert 	S2S(ASSOC_DENIED_RATES)
1413*206b73d0SCy Schubert 	S2S(ASSOC_DENIED_NOSHORT)
1414*206b73d0SCy Schubert 	S2S(SPEC_MGMT_REQUIRED)
1415*206b73d0SCy Schubert 	S2S(PWR_CAPABILITY_NOT_VALID)
1416*206b73d0SCy Schubert 	S2S(SUPPORTED_CHANNEL_NOT_VALID)
1417*206b73d0SCy Schubert 	S2S(ASSOC_DENIED_NO_SHORT_SLOT_TIME)
1418*206b73d0SCy Schubert 	S2S(ASSOC_DENIED_NO_HT)
1419*206b73d0SCy Schubert 	S2S(R0KH_UNREACHABLE)
1420*206b73d0SCy Schubert 	S2S(ASSOC_DENIED_NO_PCO)
1421*206b73d0SCy Schubert 	S2S(ASSOC_REJECTED_TEMPORARILY)
1422*206b73d0SCy Schubert 	S2S(ROBUST_MGMT_FRAME_POLICY_VIOLATION)
1423*206b73d0SCy Schubert 	S2S(UNSPECIFIED_QOS_FAILURE)
1424*206b73d0SCy Schubert 	S2S(DENIED_INSUFFICIENT_BANDWIDTH)
1425*206b73d0SCy Schubert 	S2S(DENIED_POOR_CHANNEL_CONDITIONS)
1426*206b73d0SCy Schubert 	S2S(DENIED_QOS_NOT_SUPPORTED)
1427*206b73d0SCy Schubert 	S2S(REQUEST_DECLINED)
1428*206b73d0SCy Schubert 	S2S(INVALID_PARAMETERS)
1429*206b73d0SCy Schubert 	S2S(REJECTED_WITH_SUGGESTED_CHANGES)
1430*206b73d0SCy Schubert 	S2S(INVALID_IE)
1431*206b73d0SCy Schubert 	S2S(GROUP_CIPHER_NOT_VALID)
1432*206b73d0SCy Schubert 	S2S(PAIRWISE_CIPHER_NOT_VALID)
1433*206b73d0SCy Schubert 	S2S(AKMP_NOT_VALID)
1434*206b73d0SCy Schubert 	S2S(UNSUPPORTED_RSN_IE_VERSION)
1435*206b73d0SCy Schubert 	S2S(INVALID_RSN_IE_CAPAB)
1436*206b73d0SCy Schubert 	S2S(CIPHER_REJECTED_PER_POLICY)
1437*206b73d0SCy Schubert 	S2S(TS_NOT_CREATED)
1438*206b73d0SCy Schubert 	S2S(DIRECT_LINK_NOT_ALLOWED)
1439*206b73d0SCy Schubert 	S2S(DEST_STA_NOT_PRESENT)
1440*206b73d0SCy Schubert 	S2S(DEST_STA_NOT_QOS_STA)
1441*206b73d0SCy Schubert 	S2S(ASSOC_DENIED_LISTEN_INT_TOO_LARGE)
1442*206b73d0SCy Schubert 	S2S(INVALID_FT_ACTION_FRAME_COUNT)
1443*206b73d0SCy Schubert 	S2S(INVALID_PMKID)
1444*206b73d0SCy Schubert 	S2S(INVALID_MDIE)
1445*206b73d0SCy Schubert 	S2S(INVALID_FTIE)
1446*206b73d0SCy Schubert 	S2S(REQUESTED_TCLAS_NOT_SUPPORTED)
1447*206b73d0SCy Schubert 	S2S(INSUFFICIENT_TCLAS_PROCESSING_RESOURCES)
1448*206b73d0SCy Schubert 	S2S(TRY_ANOTHER_BSS)
1449*206b73d0SCy Schubert 	S2S(GAS_ADV_PROTO_NOT_SUPPORTED)
1450*206b73d0SCy Schubert 	S2S(NO_OUTSTANDING_GAS_REQ)
1451*206b73d0SCy Schubert 	S2S(GAS_RESP_NOT_RECEIVED)
1452*206b73d0SCy Schubert 	S2S(STA_TIMED_OUT_WAITING_FOR_GAS_RESP)
1453*206b73d0SCy Schubert 	S2S(GAS_RESP_LARGER_THAN_LIMIT)
1454*206b73d0SCy Schubert 	S2S(REQ_REFUSED_HOME)
1455*206b73d0SCy Schubert 	S2S(ADV_SRV_UNREACHABLE)
1456*206b73d0SCy Schubert 	S2S(REQ_REFUSED_SSPN)
1457*206b73d0SCy Schubert 	S2S(REQ_REFUSED_UNAUTH_ACCESS)
1458*206b73d0SCy Schubert 	S2S(INVALID_RSNIE)
1459*206b73d0SCy Schubert 	S2S(U_APSD_COEX_NOT_SUPPORTED)
1460*206b73d0SCy Schubert 	S2S(U_APSD_COEX_MODE_NOT_SUPPORTED)
1461*206b73d0SCy Schubert 	S2S(BAD_INTERVAL_WITH_U_APSD_COEX)
1462*206b73d0SCy Schubert 	S2S(ANTI_CLOGGING_TOKEN_REQ)
1463*206b73d0SCy Schubert 	S2S(FINITE_CYCLIC_GROUP_NOT_SUPPORTED)
1464*206b73d0SCy Schubert 	S2S(CANNOT_FIND_ALT_TBTT)
1465*206b73d0SCy Schubert 	S2S(TRANSMISSION_FAILURE)
1466*206b73d0SCy Schubert 	S2S(REQ_TCLAS_NOT_SUPPORTED)
1467*206b73d0SCy Schubert 	S2S(TCLAS_RESOURCES_EXCHAUSTED)
1468*206b73d0SCy Schubert 	S2S(REJECTED_WITH_SUGGESTED_BSS_TRANSITION)
1469*206b73d0SCy Schubert 	S2S(REJECT_WITH_SCHEDULE)
1470*206b73d0SCy Schubert 	S2S(REJECT_NO_WAKEUP_SPECIFIED)
1471*206b73d0SCy Schubert 	S2S(SUCCESS_POWER_SAVE_MODE)
1472*206b73d0SCy Schubert 	S2S(PENDING_ADMITTING_FST_SESSION)
1473*206b73d0SCy Schubert 	S2S(PERFORMING_FST_NOW)
1474*206b73d0SCy Schubert 	S2S(PENDING_GAP_IN_BA_WINDOW)
1475*206b73d0SCy Schubert 	S2S(REJECT_U_PID_SETTING)
1476*206b73d0SCy Schubert 	S2S(REFUSED_EXTERNAL_REASON)
1477*206b73d0SCy Schubert 	S2S(REFUSED_AP_OUT_OF_MEMORY)
1478*206b73d0SCy Schubert 	S2S(REJECTED_EMERGENCY_SERVICE_NOT_SUPPORTED)
1479*206b73d0SCy Schubert 	S2S(QUERY_RESP_OUTSTANDING)
1480*206b73d0SCy Schubert 	S2S(REJECT_DSE_BAND)
1481*206b73d0SCy Schubert 	S2S(TCLAS_PROCESSING_TERMINATED)
1482*206b73d0SCy Schubert 	S2S(TS_SCHEDULE_CONFLICT)
1483*206b73d0SCy Schubert 	S2S(DENIED_WITH_SUGGESTED_BAND_AND_CHANNEL)
1484*206b73d0SCy Schubert 	S2S(MCCAOP_RESERVATION_CONFLICT)
1485*206b73d0SCy Schubert 	S2S(MAF_LIMIT_EXCEEDED)
1486*206b73d0SCy Schubert 	S2S(MCCA_TRACK_LIMIT_EXCEEDED)
1487*206b73d0SCy Schubert 	S2S(DENIED_DUE_TO_SPECTRUM_MANAGEMENT)
1488*206b73d0SCy Schubert 	S2S(ASSOC_DENIED_NO_VHT)
1489*206b73d0SCy Schubert 	S2S(ENABLEMENT_DENIED)
1490*206b73d0SCy Schubert 	S2S(RESTRICTION_FROM_AUTHORIZED_GDB)
1491*206b73d0SCy Schubert 	S2S(AUTHORIZATION_DEENABLED)
1492*206b73d0SCy Schubert 	S2S(FILS_AUTHENTICATION_FAILURE)
1493*206b73d0SCy Schubert 	S2S(UNKNOWN_AUTHENTICATION_SERVER)
1494*206b73d0SCy Schubert 	S2S(UNKNOWN_PASSWORD_IDENTIFIER)
1495*206b73d0SCy Schubert 	}
1496*206b73d0SCy Schubert 	return "UNKNOWN";
1497*206b73d0SCy Schubert #undef S2S
1498*206b73d0SCy Schubert }
1499*206b73d0SCy Schubert 
1500*206b73d0SCy Schubert 
1501325151a3SRui Paulo int mb_ies_info_by_ies(struct mb_ies_info *info, const u8 *ies_buf,
1502325151a3SRui Paulo 		       size_t ies_len)
1503325151a3SRui Paulo {
15044bc52338SCy Schubert 	const struct element *elem;
15054bc52338SCy Schubert 
1506325151a3SRui Paulo 	os_memset(info, 0, sizeof(*info));
1507325151a3SRui Paulo 
15084bc52338SCy Schubert 	if (!ies_buf)
15094bc52338SCy Schubert 		return 0;
1510325151a3SRui Paulo 
15114bc52338SCy Schubert 	for_each_element_id(elem, WLAN_EID_MULTI_BAND, ies_buf, ies_len) {
15124bc52338SCy Schubert 		if (info->nof_ies >= MAX_NOF_MB_IES_SUPPORTED)
15134bc52338SCy Schubert 			return 0;
1514325151a3SRui Paulo 
15154bc52338SCy Schubert 		wpa_printf(MSG_DEBUG, "MB IE of %u bytes found",
15164bc52338SCy Schubert 			   elem->datalen + 2);
15174bc52338SCy Schubert 		info->ies[info->nof_ies].ie = elem->data;
15184bc52338SCy Schubert 		info->ies[info->nof_ies].ie_len = elem->datalen;
1519325151a3SRui Paulo 		info->nof_ies++;
1520325151a3SRui Paulo 	}
1521325151a3SRui Paulo 
15224bc52338SCy Schubert 	if (!for_each_element_completed(elem, ies_buf, ies_len)) {
15234bc52338SCy Schubert 		wpa_hexdump(MSG_DEBUG, "Truncated IEs", ies_buf, ies_len);
15244bc52338SCy Schubert 		return -1;
1525325151a3SRui Paulo 	}
1526325151a3SRui Paulo 
1527325151a3SRui Paulo 	return 0;
1528325151a3SRui Paulo }
1529325151a3SRui Paulo 
1530325151a3SRui Paulo 
1531325151a3SRui Paulo struct wpabuf * mb_ies_by_info(struct mb_ies_info *info)
1532325151a3SRui Paulo {
1533325151a3SRui Paulo 	struct wpabuf *mb_ies = NULL;
1534325151a3SRui Paulo 
1535325151a3SRui Paulo 	WPA_ASSERT(info != NULL);
1536325151a3SRui Paulo 
1537325151a3SRui Paulo 	if (info->nof_ies) {
1538325151a3SRui Paulo 		u8 i;
1539325151a3SRui Paulo 		size_t mb_ies_size = 0;
1540325151a3SRui Paulo 
1541325151a3SRui Paulo 		for (i = 0; i < info->nof_ies; i++)
1542325151a3SRui Paulo 			mb_ies_size += 2 + info->ies[i].ie_len;
1543325151a3SRui Paulo 
1544325151a3SRui Paulo 		mb_ies = wpabuf_alloc(mb_ies_size);
1545325151a3SRui Paulo 		if (mb_ies) {
1546325151a3SRui Paulo 			for (i = 0; i < info->nof_ies; i++) {
1547325151a3SRui Paulo 				wpabuf_put_u8(mb_ies, WLAN_EID_MULTI_BAND);
1548325151a3SRui Paulo 				wpabuf_put_u8(mb_ies, info->ies[i].ie_len);
1549325151a3SRui Paulo 				wpabuf_put_data(mb_ies,
1550325151a3SRui Paulo 						info->ies[i].ie,
1551325151a3SRui Paulo 						info->ies[i].ie_len);
1552325151a3SRui Paulo 			}
1553325151a3SRui Paulo 		}
1554325151a3SRui Paulo 	}
1555325151a3SRui Paulo 
1556325151a3SRui Paulo 	return mb_ies;
1557325151a3SRui Paulo }
1558780fb4a2SCy Schubert 
1559780fb4a2SCy Schubert 
1560780fb4a2SCy Schubert const struct oper_class_map global_op_class[] = {
1561780fb4a2SCy Schubert 	{ HOSTAPD_MODE_IEEE80211G, 81, 1, 13, 1, BW20, P2P_SUPP },
1562780fb4a2SCy Schubert 	{ HOSTAPD_MODE_IEEE80211G, 82, 14, 14, 1, BW20, NO_P2P_SUPP },
1563780fb4a2SCy Schubert 
1564780fb4a2SCy Schubert 	/* Do not enable HT40 on 2.4 GHz for P2P use for now */
1565780fb4a2SCy Schubert 	{ HOSTAPD_MODE_IEEE80211G, 83, 1, 9, 1, BW40PLUS, NO_P2P_SUPP },
1566780fb4a2SCy Schubert 	{ HOSTAPD_MODE_IEEE80211G, 84, 5, 13, 1, BW40MINUS, NO_P2P_SUPP },
1567780fb4a2SCy Schubert 
1568780fb4a2SCy Schubert 	{ HOSTAPD_MODE_IEEE80211A, 115, 36, 48, 4, BW20, P2P_SUPP },
1569780fb4a2SCy Schubert 	{ HOSTAPD_MODE_IEEE80211A, 116, 36, 44, 8, BW40PLUS, P2P_SUPP },
1570780fb4a2SCy Schubert 	{ HOSTAPD_MODE_IEEE80211A, 117, 40, 48, 8, BW40MINUS, P2P_SUPP },
1571780fb4a2SCy Schubert 	{ HOSTAPD_MODE_IEEE80211A, 118, 52, 64, 4, BW20, NO_P2P_SUPP },
1572780fb4a2SCy Schubert 	{ HOSTAPD_MODE_IEEE80211A, 119, 52, 60, 8, BW40PLUS, NO_P2P_SUPP },
1573780fb4a2SCy Schubert 	{ HOSTAPD_MODE_IEEE80211A, 120, 56, 64, 8, BW40MINUS, NO_P2P_SUPP },
1574780fb4a2SCy Schubert 	{ HOSTAPD_MODE_IEEE80211A, 121, 100, 140, 4, BW20, NO_P2P_SUPP },
1575780fb4a2SCy Schubert 	{ HOSTAPD_MODE_IEEE80211A, 122, 100, 132, 8, BW40PLUS, NO_P2P_SUPP },
1576780fb4a2SCy Schubert 	{ HOSTAPD_MODE_IEEE80211A, 123, 104, 136, 8, BW40MINUS, NO_P2P_SUPP },
1577780fb4a2SCy Schubert 	{ HOSTAPD_MODE_IEEE80211A, 124, 149, 161, 4, BW20, P2P_SUPP },
1578780fb4a2SCy Schubert 	{ HOSTAPD_MODE_IEEE80211A, 125, 149, 169, 4, BW20, P2P_SUPP },
1579780fb4a2SCy Schubert 	{ HOSTAPD_MODE_IEEE80211A, 126, 149, 157, 8, BW40PLUS, P2P_SUPP },
1580780fb4a2SCy Schubert 	{ HOSTAPD_MODE_IEEE80211A, 127, 153, 161, 8, BW40MINUS, P2P_SUPP },
1581780fb4a2SCy Schubert 
1582780fb4a2SCy Schubert 	/*
1583780fb4a2SCy Schubert 	 * IEEE P802.11ac/D7.0 Table E-4 actually talks about channel center
1584780fb4a2SCy Schubert 	 * frequency index 42, 58, 106, 122, 138, 155 with channel spacing of
1585780fb4a2SCy Schubert 	 * 80 MHz, but currently use the following definition for simplicity
1586780fb4a2SCy Schubert 	 * (these center frequencies are not actual channels, which makes
1587780fb4a2SCy Schubert 	 * wpas_p2p_allow_channel() fail). wpas_p2p_verify_80mhz() should take
1588780fb4a2SCy Schubert 	 * care of removing invalid channels.
1589780fb4a2SCy Schubert 	 */
1590780fb4a2SCy Schubert 	{ HOSTAPD_MODE_IEEE80211A, 128, 36, 161, 4, BW80, P2P_SUPP },
1591780fb4a2SCy Schubert 	{ HOSTAPD_MODE_IEEE80211A, 129, 50, 114, 16, BW160, P2P_SUPP },
1592780fb4a2SCy Schubert 	{ HOSTAPD_MODE_IEEE80211A, 130, 36, 161, 4, BW80P80, P2P_SUPP },
1593780fb4a2SCy Schubert 	{ HOSTAPD_MODE_IEEE80211AD, 180, 1, 4, 1, BW2160, P2P_SUPP },
1594780fb4a2SCy Schubert 	{ -1, 0, 0, 0, 0, BW20, NO_P2P_SUPP }
1595780fb4a2SCy Schubert };
1596780fb4a2SCy Schubert 
1597780fb4a2SCy Schubert 
1598780fb4a2SCy Schubert static enum phy_type ieee80211_phy_type_by_freq(int freq)
1599780fb4a2SCy Schubert {
1600780fb4a2SCy Schubert 	enum hostapd_hw_mode hw_mode;
1601780fb4a2SCy Schubert 	u8 channel;
1602780fb4a2SCy Schubert 
1603780fb4a2SCy Schubert 	hw_mode = ieee80211_freq_to_chan(freq, &channel);
1604780fb4a2SCy Schubert 
1605780fb4a2SCy Schubert 	switch (hw_mode) {
1606780fb4a2SCy Schubert 	case HOSTAPD_MODE_IEEE80211A:
1607780fb4a2SCy Schubert 		return PHY_TYPE_OFDM;
1608780fb4a2SCy Schubert 	case HOSTAPD_MODE_IEEE80211B:
1609780fb4a2SCy Schubert 		return PHY_TYPE_HRDSSS;
1610780fb4a2SCy Schubert 	case HOSTAPD_MODE_IEEE80211G:
1611780fb4a2SCy Schubert 		return PHY_TYPE_ERP;
1612780fb4a2SCy Schubert 	case HOSTAPD_MODE_IEEE80211AD:
1613780fb4a2SCy Schubert 		return PHY_TYPE_DMG;
1614780fb4a2SCy Schubert 	default:
1615780fb4a2SCy Schubert 		return PHY_TYPE_UNSPECIFIED;
1616780fb4a2SCy Schubert 	};
1617780fb4a2SCy Schubert }
1618780fb4a2SCy Schubert 
1619780fb4a2SCy Schubert 
1620780fb4a2SCy Schubert /* ieee80211_get_phy_type - Derive the phy type by freq and bandwidth */
1621780fb4a2SCy Schubert enum phy_type ieee80211_get_phy_type(int freq, int ht, int vht)
1622780fb4a2SCy Schubert {
1623780fb4a2SCy Schubert 	if (vht)
1624780fb4a2SCy Schubert 		return PHY_TYPE_VHT;
1625780fb4a2SCy Schubert 	if (ht)
1626780fb4a2SCy Schubert 		return PHY_TYPE_HT;
1627780fb4a2SCy Schubert 
1628780fb4a2SCy Schubert 	return ieee80211_phy_type_by_freq(freq);
1629780fb4a2SCy Schubert }
1630780fb4a2SCy Schubert 
1631780fb4a2SCy Schubert 
1632780fb4a2SCy Schubert size_t global_op_class_size = ARRAY_SIZE(global_op_class);
1633780fb4a2SCy Schubert 
1634780fb4a2SCy Schubert 
1635780fb4a2SCy Schubert /**
1636780fb4a2SCy Schubert  * get_ie - Fetch a specified information element from IEs buffer
1637780fb4a2SCy Schubert  * @ies: Information elements buffer
1638780fb4a2SCy Schubert  * @len: Information elements buffer length
1639780fb4a2SCy Schubert  * @eid: Information element identifier (WLAN_EID_*)
1640780fb4a2SCy Schubert  * Returns: Pointer to the information element (id field) or %NULL if not found
1641780fb4a2SCy Schubert  *
1642780fb4a2SCy Schubert  * This function returns the first matching information element in the IEs
1643780fb4a2SCy Schubert  * buffer or %NULL in case the element is not found.
1644780fb4a2SCy Schubert  */
1645780fb4a2SCy Schubert const u8 * get_ie(const u8 *ies, size_t len, u8 eid)
1646780fb4a2SCy Schubert {
16474bc52338SCy Schubert 	const struct element *elem;
1648780fb4a2SCy Schubert 
1649780fb4a2SCy Schubert 	if (!ies)
1650780fb4a2SCy Schubert 		return NULL;
1651780fb4a2SCy Schubert 
16524bc52338SCy Schubert 	for_each_element_id(elem, eid, ies, len)
16534bc52338SCy Schubert 		return &elem->id;
1654780fb4a2SCy Schubert 
1655780fb4a2SCy Schubert 	return NULL;
1656780fb4a2SCy Schubert }
1657780fb4a2SCy Schubert 
1658780fb4a2SCy Schubert 
165985732ac8SCy Schubert /**
166085732ac8SCy Schubert  * get_ie_ext - Fetch a specified extended information element from IEs buffer
166185732ac8SCy Schubert  * @ies: Information elements buffer
166285732ac8SCy Schubert  * @len: Information elements buffer length
166385732ac8SCy Schubert  * @ext: Information element extension identifier (WLAN_EID_EXT_*)
166485732ac8SCy Schubert  * Returns: Pointer to the information element (id field) or %NULL if not found
166585732ac8SCy Schubert  *
166685732ac8SCy Schubert  * This function returns the first matching information element in the IEs
166785732ac8SCy Schubert  * buffer or %NULL in case the element is not found.
166885732ac8SCy Schubert  */
166985732ac8SCy Schubert const u8 * get_ie_ext(const u8 *ies, size_t len, u8 ext)
167085732ac8SCy Schubert {
16714bc52338SCy Schubert 	const struct element *elem;
167285732ac8SCy Schubert 
167385732ac8SCy Schubert 	if (!ies)
167485732ac8SCy Schubert 		return NULL;
167585732ac8SCy Schubert 
16764bc52338SCy Schubert 	for_each_element_extid(elem, ext, ies, len)
16774bc52338SCy Schubert 		return &elem->id;
167885732ac8SCy Schubert 
16794bc52338SCy Schubert 	return NULL;
16804bc52338SCy Schubert }
168185732ac8SCy Schubert 
168285732ac8SCy Schubert 
16834bc52338SCy Schubert const u8 * get_vendor_ie(const u8 *ies, size_t len, u32 vendor_type)
16844bc52338SCy Schubert {
16854bc52338SCy Schubert 	const struct element *elem;
16864bc52338SCy Schubert 
16874bc52338SCy Schubert 	for_each_element_id(elem, WLAN_EID_VENDOR_SPECIFIC, ies, len) {
16884bc52338SCy Schubert 		if (elem->datalen >= 4 &&
16894bc52338SCy Schubert 		    vendor_type == WPA_GET_BE32(elem->data))
16904bc52338SCy Schubert 			return &elem->id;
169185732ac8SCy Schubert 	}
169285732ac8SCy Schubert 
169385732ac8SCy Schubert 	return NULL;
169485732ac8SCy Schubert }
169585732ac8SCy Schubert 
169685732ac8SCy Schubert 
1697780fb4a2SCy Schubert size_t mbo_add_ie(u8 *buf, size_t len, const u8 *attr, size_t attr_len)
1698780fb4a2SCy Schubert {
1699780fb4a2SCy Schubert 	/*
1700780fb4a2SCy Schubert 	 * MBO IE requires 6 bytes without the attributes: EID (1), length (1),
1701780fb4a2SCy Schubert 	 * OUI (3), OUI type (1).
1702780fb4a2SCy Schubert 	 */
1703780fb4a2SCy Schubert 	if (len < 6 + attr_len) {
1704780fb4a2SCy Schubert 		wpa_printf(MSG_DEBUG,
1705780fb4a2SCy Schubert 			   "MBO: Not enough room in buffer for MBO IE: buf len = %zu, attr_len = %zu",
1706780fb4a2SCy Schubert 			   len, attr_len);
1707780fb4a2SCy Schubert 		return 0;
1708780fb4a2SCy Schubert 	}
1709780fb4a2SCy Schubert 
1710780fb4a2SCy Schubert 	*buf++ = WLAN_EID_VENDOR_SPECIFIC;
1711780fb4a2SCy Schubert 	*buf++ = attr_len + 4;
1712780fb4a2SCy Schubert 	WPA_PUT_BE24(buf, OUI_WFA);
1713780fb4a2SCy Schubert 	buf += 3;
1714780fb4a2SCy Schubert 	*buf++ = MBO_OUI_TYPE;
1715780fb4a2SCy Schubert 	os_memcpy(buf, attr, attr_len);
1716780fb4a2SCy Schubert 
1717780fb4a2SCy Schubert 	return 6 + attr_len;
1718780fb4a2SCy Schubert }
171985732ac8SCy Schubert 
172085732ac8SCy Schubert 
17214bc52338SCy Schubert size_t add_multi_ap_ie(u8 *buf, size_t len, u8 value)
17224bc52338SCy Schubert {
17234bc52338SCy Schubert 	u8 *pos = buf;
17244bc52338SCy Schubert 
17254bc52338SCy Schubert 	if (len < 9)
17264bc52338SCy Schubert 		return 0;
17274bc52338SCy Schubert 
17284bc52338SCy Schubert 	*pos++ = WLAN_EID_VENDOR_SPECIFIC;
17294bc52338SCy Schubert 	*pos++ = 7; /* len */
17304bc52338SCy Schubert 	WPA_PUT_BE24(pos, OUI_WFA);
17314bc52338SCy Schubert 	pos += 3;
17324bc52338SCy Schubert 	*pos++ = MULTI_AP_OUI_TYPE;
17334bc52338SCy Schubert 	*pos++ = MULTI_AP_SUB_ELEM_TYPE;
17344bc52338SCy Schubert 	*pos++ = 1; /* len */
17354bc52338SCy Schubert 	*pos++ = value;
17364bc52338SCy Schubert 
17374bc52338SCy Schubert 	return pos - buf;
17384bc52338SCy Schubert }
17394bc52338SCy Schubert 
17404bc52338SCy Schubert 
174185732ac8SCy Schubert static const struct country_op_class us_op_class[] = {
174285732ac8SCy Schubert 	{ 1, 115 },
174385732ac8SCy Schubert 	{ 2, 118 },
174485732ac8SCy Schubert 	{ 3, 124 },
174585732ac8SCy Schubert 	{ 4, 121 },
174685732ac8SCy Schubert 	{ 5, 125 },
174785732ac8SCy Schubert 	{ 12, 81 },
174885732ac8SCy Schubert 	{ 22, 116 },
174985732ac8SCy Schubert 	{ 23, 119 },
175085732ac8SCy Schubert 	{ 24, 122 },
175185732ac8SCy Schubert 	{ 25, 126 },
175285732ac8SCy Schubert 	{ 26, 126 },
175385732ac8SCy Schubert 	{ 27, 117 },
175485732ac8SCy Schubert 	{ 28, 120 },
175585732ac8SCy Schubert 	{ 29, 123 },
175685732ac8SCy Schubert 	{ 30, 127 },
175785732ac8SCy Schubert 	{ 31, 127 },
175885732ac8SCy Schubert 	{ 32, 83 },
175985732ac8SCy Schubert 	{ 33, 84 },
176085732ac8SCy Schubert 	{ 34, 180 },
176185732ac8SCy Schubert };
176285732ac8SCy Schubert 
176385732ac8SCy Schubert static const struct country_op_class eu_op_class[] = {
176485732ac8SCy Schubert 	{ 1, 115 },
176585732ac8SCy Schubert 	{ 2, 118 },
176685732ac8SCy Schubert 	{ 3, 121 },
176785732ac8SCy Schubert 	{ 4, 81 },
176885732ac8SCy Schubert 	{ 5, 116 },
176985732ac8SCy Schubert 	{ 6, 119 },
177085732ac8SCy Schubert 	{ 7, 122 },
177185732ac8SCy Schubert 	{ 8, 117 },
177285732ac8SCy Schubert 	{ 9, 120 },
177385732ac8SCy Schubert 	{ 10, 123 },
177485732ac8SCy Schubert 	{ 11, 83 },
177585732ac8SCy Schubert 	{ 12, 84 },
177685732ac8SCy Schubert 	{ 17, 125 },
177785732ac8SCy Schubert 	{ 18, 180 },
177885732ac8SCy Schubert };
177985732ac8SCy Schubert 
178085732ac8SCy Schubert static const struct country_op_class jp_op_class[] = {
178185732ac8SCy Schubert 	{ 1, 115 },
178285732ac8SCy Schubert 	{ 30, 81 },
178385732ac8SCy Schubert 	{ 31, 82 },
178485732ac8SCy Schubert 	{ 32, 118 },
178585732ac8SCy Schubert 	{ 33, 118 },
178685732ac8SCy Schubert 	{ 34, 121 },
178785732ac8SCy Schubert 	{ 35, 121 },
178885732ac8SCy Schubert 	{ 36, 116 },
178985732ac8SCy Schubert 	{ 37, 119 },
179085732ac8SCy Schubert 	{ 38, 119 },
179185732ac8SCy Schubert 	{ 39, 122 },
179285732ac8SCy Schubert 	{ 40, 122 },
179385732ac8SCy Schubert 	{ 41, 117 },
179485732ac8SCy Schubert 	{ 42, 120 },
179585732ac8SCy Schubert 	{ 43, 120 },
179685732ac8SCy Schubert 	{ 44, 123 },
179785732ac8SCy Schubert 	{ 45, 123 },
179885732ac8SCy Schubert 	{ 56, 83 },
179985732ac8SCy Schubert 	{ 57, 84 },
180085732ac8SCy Schubert 	{ 58, 121 },
180185732ac8SCy Schubert 	{ 59, 180 },
180285732ac8SCy Schubert };
180385732ac8SCy Schubert 
180485732ac8SCy Schubert static const struct country_op_class cn_op_class[] = {
180585732ac8SCy Schubert 	{ 1, 115 },
180685732ac8SCy Schubert 	{ 2, 118 },
180785732ac8SCy Schubert 	{ 3, 125 },
180885732ac8SCy Schubert 	{ 4, 116 },
180985732ac8SCy Schubert 	{ 5, 119 },
181085732ac8SCy Schubert 	{ 6, 126 },
181185732ac8SCy Schubert 	{ 7, 81 },
181285732ac8SCy Schubert 	{ 8, 83 },
181385732ac8SCy Schubert 	{ 9, 84 },
181485732ac8SCy Schubert };
181585732ac8SCy Schubert 
181685732ac8SCy Schubert static u8
181785732ac8SCy Schubert global_op_class_from_country_array(u8 op_class, size_t array_size,
181885732ac8SCy Schubert 				   const struct country_op_class *country_array)
181985732ac8SCy Schubert {
182085732ac8SCy Schubert 	size_t i;
182185732ac8SCy Schubert 
182285732ac8SCy Schubert 	for (i = 0; i < array_size; i++) {
182385732ac8SCy Schubert 		if (country_array[i].country_op_class == op_class)
182485732ac8SCy Schubert 			return country_array[i].global_op_class;
182585732ac8SCy Schubert 	}
182685732ac8SCy Schubert 
182785732ac8SCy Schubert 	return 0;
182885732ac8SCy Schubert }
182985732ac8SCy Schubert 
183085732ac8SCy Schubert 
183185732ac8SCy Schubert u8 country_to_global_op_class(const char *country, u8 op_class)
183285732ac8SCy Schubert {
183385732ac8SCy Schubert 	const struct country_op_class *country_array;
183485732ac8SCy Schubert 	size_t size;
183585732ac8SCy Schubert 	u8 g_op_class;
183685732ac8SCy Schubert 
183785732ac8SCy Schubert 	if (country_match(us_op_class_cc, country)) {
183885732ac8SCy Schubert 		country_array = us_op_class;
183985732ac8SCy Schubert 		size = ARRAY_SIZE(us_op_class);
184085732ac8SCy Schubert 	} else if (country_match(eu_op_class_cc, country)) {
184185732ac8SCy Schubert 		country_array = eu_op_class;
184285732ac8SCy Schubert 		size = ARRAY_SIZE(eu_op_class);
184385732ac8SCy Schubert 	} else if (country_match(jp_op_class_cc, country)) {
184485732ac8SCy Schubert 		country_array = jp_op_class;
184585732ac8SCy Schubert 		size = ARRAY_SIZE(jp_op_class);
184685732ac8SCy Schubert 	} else if (country_match(cn_op_class_cc, country)) {
184785732ac8SCy Schubert 		country_array = cn_op_class;
184885732ac8SCy Schubert 		size = ARRAY_SIZE(cn_op_class);
184985732ac8SCy Schubert 	} else {
185085732ac8SCy Schubert 		/*
185185732ac8SCy Schubert 		 * Countries that do not match any of the above countries use
185285732ac8SCy Schubert 		 * global operating classes
185385732ac8SCy Schubert 		 */
185485732ac8SCy Schubert 		return op_class;
185585732ac8SCy Schubert 	}
185685732ac8SCy Schubert 
185785732ac8SCy Schubert 	g_op_class = global_op_class_from_country_array(op_class, size,
185885732ac8SCy Schubert 							country_array);
185985732ac8SCy Schubert 
186085732ac8SCy Schubert 	/*
186185732ac8SCy Schubert 	 * If the given operating class did not match any of the country's
186285732ac8SCy Schubert 	 * operating classes, assume that global operating class is used.
186385732ac8SCy Schubert 	 */
186485732ac8SCy Schubert 	return g_op_class ? g_op_class : op_class;
186585732ac8SCy Schubert }
186685732ac8SCy Schubert 
186785732ac8SCy Schubert 
186885732ac8SCy Schubert const struct oper_class_map * get_oper_class(const char *country, u8 op_class)
186985732ac8SCy Schubert {
187085732ac8SCy Schubert 	const struct oper_class_map *op;
187185732ac8SCy Schubert 
187285732ac8SCy Schubert 	if (country)
187385732ac8SCy Schubert 		op_class = country_to_global_op_class(country, op_class);
187485732ac8SCy Schubert 
187585732ac8SCy Schubert 	op = &global_op_class[0];
187685732ac8SCy Schubert 	while (op->op_class && op->op_class != op_class)
187785732ac8SCy Schubert 		op++;
187885732ac8SCy Schubert 
187985732ac8SCy Schubert 	if (!op->op_class)
188085732ac8SCy Schubert 		return NULL;
188185732ac8SCy Schubert 
188285732ac8SCy Schubert 	return op;
188385732ac8SCy Schubert }
188485732ac8SCy Schubert 
188585732ac8SCy Schubert 
18864bc52338SCy Schubert int oper_class_bw_to_int(const struct oper_class_map *map)
18874bc52338SCy Schubert {
18884bc52338SCy Schubert 	switch (map->bw) {
18894bc52338SCy Schubert 	case BW20:
18904bc52338SCy Schubert 		return 20;
18914bc52338SCy Schubert 	case BW40PLUS:
18924bc52338SCy Schubert 	case BW40MINUS:
18934bc52338SCy Schubert 		return 40;
18944bc52338SCy Schubert 	case BW80:
18954bc52338SCy Schubert 		return 80;
18964bc52338SCy Schubert 	case BW80P80:
18974bc52338SCy Schubert 	case BW160:
18984bc52338SCy Schubert 		return 160;
18994bc52338SCy Schubert 	case BW2160:
19004bc52338SCy Schubert 		return 2160;
19014bc52338SCy Schubert 	default:
19024bc52338SCy Schubert 		return 0;
19034bc52338SCy Schubert 	}
19044bc52338SCy Schubert }
19054bc52338SCy Schubert 
19064bc52338SCy Schubert 
190785732ac8SCy Schubert int ieee802_11_parse_candidate_list(const char *pos, u8 *nei_rep,
190885732ac8SCy Schubert 				    size_t nei_rep_len)
190985732ac8SCy Schubert {
191085732ac8SCy Schubert 	u8 *nei_pos = nei_rep;
191185732ac8SCy Schubert 	const char *end;
191285732ac8SCy Schubert 
191385732ac8SCy Schubert 	/*
191485732ac8SCy Schubert 	 * BSS Transition Candidate List Entries - Neighbor Report elements
191585732ac8SCy Schubert 	 * neighbor=<BSSID>,<BSSID Information>,<Operating Class>,
191685732ac8SCy Schubert 	 * <Channel Number>,<PHY Type>[,<hexdump of Optional Subelements>]
191785732ac8SCy Schubert 	 */
191885732ac8SCy Schubert 	while (pos) {
191985732ac8SCy Schubert 		u8 *nei_start;
192085732ac8SCy Schubert 		long int val;
192185732ac8SCy Schubert 		char *endptr, *tmp;
192285732ac8SCy Schubert 
192385732ac8SCy Schubert 		pos = os_strstr(pos, " neighbor=");
192485732ac8SCy Schubert 		if (!pos)
192585732ac8SCy Schubert 			break;
192685732ac8SCy Schubert 		if (nei_pos + 15 > nei_rep + nei_rep_len) {
192785732ac8SCy Schubert 			wpa_printf(MSG_DEBUG,
192885732ac8SCy Schubert 				   "Not enough room for additional neighbor");
192985732ac8SCy Schubert 			return -1;
193085732ac8SCy Schubert 		}
193185732ac8SCy Schubert 		pos += 10;
193285732ac8SCy Schubert 
193385732ac8SCy Schubert 		nei_start = nei_pos;
193485732ac8SCy Schubert 		*nei_pos++ = WLAN_EID_NEIGHBOR_REPORT;
193585732ac8SCy Schubert 		nei_pos++; /* length to be filled in */
193685732ac8SCy Schubert 
193785732ac8SCy Schubert 		if (hwaddr_aton(pos, nei_pos)) {
193885732ac8SCy Schubert 			wpa_printf(MSG_DEBUG, "Invalid BSSID");
193985732ac8SCy Schubert 			return -1;
194085732ac8SCy Schubert 		}
194185732ac8SCy Schubert 		nei_pos += ETH_ALEN;
194285732ac8SCy Schubert 		pos += 17;
194385732ac8SCy Schubert 		if (*pos != ',') {
194485732ac8SCy Schubert 			wpa_printf(MSG_DEBUG, "Missing BSSID Information");
194585732ac8SCy Schubert 			return -1;
194685732ac8SCy Schubert 		}
194785732ac8SCy Schubert 		pos++;
194885732ac8SCy Schubert 
194985732ac8SCy Schubert 		val = strtol(pos, &endptr, 0);
195085732ac8SCy Schubert 		WPA_PUT_LE32(nei_pos, val);
195185732ac8SCy Schubert 		nei_pos += 4;
195285732ac8SCy Schubert 		if (*endptr != ',') {
195385732ac8SCy Schubert 			wpa_printf(MSG_DEBUG, "Missing Operating Class");
195485732ac8SCy Schubert 			return -1;
195585732ac8SCy Schubert 		}
195685732ac8SCy Schubert 		pos = endptr + 1;
195785732ac8SCy Schubert 
195885732ac8SCy Schubert 		*nei_pos++ = atoi(pos); /* Operating Class */
195985732ac8SCy Schubert 		pos = os_strchr(pos, ',');
196085732ac8SCy Schubert 		if (pos == NULL) {
196185732ac8SCy Schubert 			wpa_printf(MSG_DEBUG, "Missing Channel Number");
196285732ac8SCy Schubert 			return -1;
196385732ac8SCy Schubert 		}
196485732ac8SCy Schubert 		pos++;
196585732ac8SCy Schubert 
196685732ac8SCy Schubert 		*nei_pos++ = atoi(pos); /* Channel Number */
196785732ac8SCy Schubert 		pos = os_strchr(pos, ',');
196885732ac8SCy Schubert 		if (pos == NULL) {
196985732ac8SCy Schubert 			wpa_printf(MSG_DEBUG, "Missing PHY Type");
197085732ac8SCy Schubert 			return -1;
197185732ac8SCy Schubert 		}
197285732ac8SCy Schubert 		pos++;
197385732ac8SCy Schubert 
197485732ac8SCy Schubert 		*nei_pos++ = atoi(pos); /* PHY Type */
197585732ac8SCy Schubert 		end = os_strchr(pos, ' ');
197685732ac8SCy Schubert 		tmp = os_strchr(pos, ',');
197785732ac8SCy Schubert 		if (tmp && (!end || tmp < end)) {
197885732ac8SCy Schubert 			/* Optional Subelements (hexdump) */
197985732ac8SCy Schubert 			size_t len;
198085732ac8SCy Schubert 
198185732ac8SCy Schubert 			pos = tmp + 1;
198285732ac8SCy Schubert 			end = os_strchr(pos, ' ');
198385732ac8SCy Schubert 			if (end)
198485732ac8SCy Schubert 				len = end - pos;
198585732ac8SCy Schubert 			else
198685732ac8SCy Schubert 				len = os_strlen(pos);
198785732ac8SCy Schubert 			if (nei_pos + len / 2 > nei_rep + nei_rep_len) {
198885732ac8SCy Schubert 				wpa_printf(MSG_DEBUG,
198985732ac8SCy Schubert 					   "Not enough room for neighbor subelements");
199085732ac8SCy Schubert 				return -1;
199185732ac8SCy Schubert 			}
199285732ac8SCy Schubert 			if (len & 0x01 ||
199385732ac8SCy Schubert 			    hexstr2bin(pos, nei_pos, len / 2) < 0) {
199485732ac8SCy Schubert 				wpa_printf(MSG_DEBUG,
199585732ac8SCy Schubert 					   "Invalid neighbor subelement info");
199685732ac8SCy Schubert 				return -1;
199785732ac8SCy Schubert 			}
199885732ac8SCy Schubert 			nei_pos += len / 2;
199985732ac8SCy Schubert 			pos = end;
200085732ac8SCy Schubert 		}
200185732ac8SCy Schubert 
200285732ac8SCy Schubert 		nei_start[1] = nei_pos - nei_start - 2;
200385732ac8SCy Schubert 	}
200485732ac8SCy Schubert 
200585732ac8SCy Schubert 	return nei_pos - nei_rep;
200685732ac8SCy Schubert }
20074bc52338SCy Schubert 
20084bc52338SCy Schubert 
20094bc52338SCy Schubert int ieee802_11_ext_capab(const u8 *ie, unsigned int capab)
20104bc52338SCy Schubert {
20114bc52338SCy Schubert 	if (!ie || ie[1] <= capab / 8)
20124bc52338SCy Schubert 		return 0;
20134bc52338SCy Schubert 	return !!(ie[2 + capab / 8] & BIT(capab % 8));
20144bc52338SCy Schubert }
2015