1189251Ssam/*
2189251Ssam * IEEE 802.11 Common routines
3346981Scy * Copyright (c) 2002-2019, Jouni Malinen <j@w1.fi>
4189251Ssam *
5252726Srpaulo * This software may be distributed under the terms of the BSD license.
6252726Srpaulo * See README for more details.
7189251Ssam */
8189251Ssam
9189251Ssam#include "includes.h"
10189251Ssam
11189251Ssam#include "common.h"
12281806Srpaulo#include "defs.h"
13289549Srpaulo#include "wpa_common.h"
14346981Scy#include "drivers/driver.h"
15289549Srpaulo#include "qca-vendor.h"
16189251Ssam#include "ieee802_11_defs.h"
17189251Ssam#include "ieee802_11_common.h"
18189251Ssam
19189251Ssam
20214734Srpaulostatic int ieee802_11_parse_vendor_specific(const u8 *pos, size_t elen,
21189251Ssam					    struct ieee802_11_elems *elems,
22189251Ssam					    int show_errors)
23189251Ssam{
24189251Ssam	unsigned int oui;
25189251Ssam
26189251Ssam	/* first 3 bytes in vendor specific information element are the IEEE
27189251Ssam	 * OUI of the vendor. The following byte is used a vendor specific
28189251Ssam	 * sub-type. */
29189251Ssam	if (elen < 4) {
30189251Ssam		if (show_errors) {
31189251Ssam			wpa_printf(MSG_MSGDUMP, "short vendor specific "
32189251Ssam				   "information element ignored (len=%lu)",
33189251Ssam				   (unsigned long) elen);
34189251Ssam		}
35189251Ssam		return -1;
36189251Ssam	}
37189251Ssam
38189251Ssam	oui = WPA_GET_BE24(pos);
39189251Ssam	switch (oui) {
40189251Ssam	case OUI_MICROSOFT:
41189251Ssam		/* Microsoft/Wi-Fi information elements are further typed and
42189251Ssam		 * subtyped */
43189251Ssam		switch (pos[3]) {
44189251Ssam		case 1:
45189251Ssam			/* Microsoft OUI (00:50:F2) with OUI Type 1:
46189251Ssam			 * real WPA information element */
47189251Ssam			elems->wpa_ie = pos;
48189251Ssam			elems->wpa_ie_len = elen;
49189251Ssam			break;
50209158Srpaulo		case WMM_OUI_TYPE:
51209158Srpaulo			/* WMM information element */
52189251Ssam			if (elen < 5) {
53209158Srpaulo				wpa_printf(MSG_MSGDUMP, "short WMM "
54189251Ssam					   "information element ignored "
55189251Ssam					   "(len=%lu)",
56189251Ssam					   (unsigned long) elen);
57189251Ssam				return -1;
58189251Ssam			}
59189251Ssam			switch (pos[4]) {
60209158Srpaulo			case WMM_OUI_SUBTYPE_INFORMATION_ELEMENT:
61209158Srpaulo			case WMM_OUI_SUBTYPE_PARAMETER_ELEMENT:
62209158Srpaulo				/*
63209158Srpaulo				 * Share same pointer since only one of these
64209158Srpaulo				 * is used and they start with same data.
65209158Srpaulo				 * Length field can be used to distinguish the
66209158Srpaulo				 * IEs.
67209158Srpaulo				 */
68209158Srpaulo				elems->wmm = pos;
69209158Srpaulo				elems->wmm_len = elen;
70189251Ssam				break;
71209158Srpaulo			case WMM_OUI_SUBTYPE_TSPEC_ELEMENT:
72209158Srpaulo				elems->wmm_tspec = pos;
73209158Srpaulo				elems->wmm_tspec_len = elen;
74189251Ssam				break;
75189251Ssam			default:
76252726Srpaulo				wpa_printf(MSG_EXCESSIVE, "unknown WMM "
77189251Ssam					   "information element ignored "
78189251Ssam					   "(subtype=%d len=%lu)",
79189251Ssam					   pos[4], (unsigned long) elen);
80189251Ssam				return -1;
81189251Ssam			}
82189251Ssam			break;
83189251Ssam		case 4:
84189251Ssam			/* Wi-Fi Protected Setup (WPS) IE */
85189251Ssam			elems->wps_ie = pos;
86189251Ssam			elems->wps_ie_len = elen;
87189251Ssam			break;
88189251Ssam		default:
89252726Srpaulo			wpa_printf(MSG_EXCESSIVE, "Unknown Microsoft "
90189251Ssam				   "information element ignored "
91252726Srpaulo				   "(type=%d len=%lu)",
92252726Srpaulo				   pos[3], (unsigned long) elen);
93252726Srpaulo			return -1;
94252726Srpaulo		}
95252726Srpaulo		break;
96252726Srpaulo
97252726Srpaulo	case OUI_WFA:
98252726Srpaulo		switch (pos[3]) {
99252726Srpaulo		case P2P_OUI_TYPE:
100252726Srpaulo			/* Wi-Fi Alliance - P2P IE */
101252726Srpaulo			elems->p2p = pos;
102252726Srpaulo			elems->p2p_len = elen;
103252726Srpaulo			break;
104252726Srpaulo		case WFD_OUI_TYPE:
105252726Srpaulo			/* Wi-Fi Alliance - WFD IE */
106252726Srpaulo			elems->wfd = pos;
107252726Srpaulo			elems->wfd_len = elen;
108252726Srpaulo			break;
109252726Srpaulo		case HS20_INDICATION_OUI_TYPE:
110252726Srpaulo			/* Hotspot 2.0 */
111252726Srpaulo			elems->hs20 = pos;
112252726Srpaulo			elems->hs20_len = elen;
113252726Srpaulo			break;
114281806Srpaulo		case HS20_OSEN_OUI_TYPE:
115281806Srpaulo			/* Hotspot 2.0 OSEN */
116281806Srpaulo			elems->osen = pos;
117281806Srpaulo			elems->osen_len = elen;
118281806Srpaulo			break;
119337817Scy		case MBO_OUI_TYPE:
120337817Scy			/* MBO-OCE */
121337817Scy			elems->mbo = pos;
122337817Scy			elems->mbo_len = elen;
123337817Scy			break;
124346981Scy		case HS20_ROAMING_CONS_SEL_OUI_TYPE:
125346981Scy			/* Hotspot 2.0 Roaming Consortium Selection */
126346981Scy			elems->roaming_cons_sel = pos;
127346981Scy			elems->roaming_cons_sel_len = elen;
128346981Scy			break;
129346981Scy		case MULTI_AP_OUI_TYPE:
130346981Scy			elems->multi_ap = pos;
131346981Scy			elems->multi_ap_len = elen;
132346981Scy			break;
133252726Srpaulo		default:
134252726Srpaulo			wpa_printf(MSG_MSGDUMP, "Unknown WFA "
135252726Srpaulo				   "information element ignored "
136281806Srpaulo				   "(type=%d len=%lu)",
137189251Ssam				   pos[3], (unsigned long) elen);
138189251Ssam			return -1;
139189251Ssam		}
140189251Ssam		break;
141189251Ssam
142189251Ssam	case OUI_BROADCOM:
143189251Ssam		switch (pos[3]) {
144189251Ssam		case VENDOR_HT_CAPAB_OUI_TYPE:
145189251Ssam			elems->vendor_ht_cap = pos;
146189251Ssam			elems->vendor_ht_cap_len = elen;
147189251Ssam			break;
148281806Srpaulo		case VENDOR_VHT_TYPE:
149281806Srpaulo			if (elen > 4 &&
150281806Srpaulo			    (pos[4] == VENDOR_VHT_SUBTYPE ||
151281806Srpaulo			     pos[4] == VENDOR_VHT_SUBTYPE2)) {
152281806Srpaulo				elems->vendor_vht = pos;
153281806Srpaulo				elems->vendor_vht_len = elen;
154281806Srpaulo			} else
155281806Srpaulo				return -1;
156281806Srpaulo			break;
157189251Ssam		default:
158252726Srpaulo			wpa_printf(MSG_EXCESSIVE, "Unknown Broadcom "
159189251Ssam				   "information element ignored "
160252726Srpaulo				   "(type=%d len=%lu)",
161189251Ssam				   pos[3], (unsigned long) elen);
162189251Ssam			return -1;
163189251Ssam		}
164189251Ssam		break;
165189251Ssam
166289549Srpaulo	case OUI_QCA:
167289549Srpaulo		switch (pos[3]) {
168289549Srpaulo		case QCA_VENDOR_ELEM_P2P_PREF_CHAN_LIST:
169289549Srpaulo			elems->pref_freq_list = pos;
170289549Srpaulo			elems->pref_freq_list_len = elen;
171289549Srpaulo			break;
172289549Srpaulo		default:
173289549Srpaulo			wpa_printf(MSG_EXCESSIVE,
174289549Srpaulo				   "Unknown QCA information element ignored (type=%d len=%lu)",
175289549Srpaulo				   pos[3], (unsigned long) elen);
176289549Srpaulo			return -1;
177289549Srpaulo		}
178289549Srpaulo		break;
179289549Srpaulo
180189251Ssam	default:
181252726Srpaulo		wpa_printf(MSG_EXCESSIVE, "unknown vendor specific "
182252726Srpaulo			   "information element ignored (vendor OUI "
183252726Srpaulo			   "%02x:%02x:%02x len=%lu)",
184189251Ssam			   pos[0], pos[1], pos[2], (unsigned long) elen);
185189251Ssam		return -1;
186189251Ssam	}
187189251Ssam
188189251Ssam	return 0;
189189251Ssam}
190189251Ssam
191189251Ssam
192346981Scystatic int ieee802_11_parse_extension(const u8 *pos, size_t elen,
193346981Scy				      struct ieee802_11_elems *elems,
194346981Scy				      int show_errors)
195346981Scy{
196346981Scy	u8 ext_id;
197346981Scy
198346981Scy	if (elen < 1) {
199346981Scy		if (show_errors) {
200346981Scy			wpa_printf(MSG_MSGDUMP,
201346981Scy				   "short information element (Ext)");
202346981Scy		}
203346981Scy		return -1;
204346981Scy	}
205346981Scy
206346981Scy	ext_id = *pos++;
207346981Scy	elen--;
208346981Scy
209346981Scy	switch (ext_id) {
210346981Scy	case WLAN_EID_EXT_ASSOC_DELAY_INFO:
211346981Scy		if (elen != 1)
212346981Scy			break;
213346981Scy		elems->assoc_delay_info = pos;
214346981Scy		break;
215346981Scy	case WLAN_EID_EXT_FILS_REQ_PARAMS:
216346981Scy		if (elen < 3)
217346981Scy			break;
218346981Scy		elems->fils_req_params = pos;
219346981Scy		elems->fils_req_params_len = elen;
220346981Scy		break;
221346981Scy	case WLAN_EID_EXT_FILS_KEY_CONFIRM:
222346981Scy		elems->fils_key_confirm = pos;
223346981Scy		elems->fils_key_confirm_len = elen;
224346981Scy		break;
225346981Scy	case WLAN_EID_EXT_FILS_SESSION:
226346981Scy		if (elen != FILS_SESSION_LEN)
227346981Scy			break;
228346981Scy		elems->fils_session = pos;
229346981Scy		break;
230346981Scy	case WLAN_EID_EXT_FILS_HLP_CONTAINER:
231346981Scy		if (elen < 2 * ETH_ALEN)
232346981Scy			break;
233346981Scy		elems->fils_hlp = pos;
234346981Scy		elems->fils_hlp_len = elen;
235346981Scy		break;
236346981Scy	case WLAN_EID_EXT_FILS_IP_ADDR_ASSIGN:
237346981Scy		if (elen < 1)
238346981Scy			break;
239346981Scy		elems->fils_ip_addr_assign = pos;
240346981Scy		elems->fils_ip_addr_assign_len = elen;
241346981Scy		break;
242346981Scy	case WLAN_EID_EXT_KEY_DELIVERY:
243346981Scy		if (elen < WPA_KEY_RSC_LEN)
244346981Scy			break;
245346981Scy		elems->key_delivery = pos;
246346981Scy		elems->key_delivery_len = elen;
247346981Scy		break;
248346981Scy	case WLAN_EID_EXT_FILS_WRAPPED_DATA:
249346981Scy		elems->fils_wrapped_data = pos;
250346981Scy		elems->fils_wrapped_data_len = elen;
251346981Scy		break;
252346981Scy	case WLAN_EID_EXT_FILS_PUBLIC_KEY:
253346981Scy		if (elen < 1)
254346981Scy			break;
255346981Scy		elems->fils_pk = pos;
256346981Scy		elems->fils_pk_len = elen;
257346981Scy		break;
258346981Scy	case WLAN_EID_EXT_FILS_NONCE:
259346981Scy		if (elen != FILS_NONCE_LEN)
260346981Scy			break;
261346981Scy		elems->fils_nonce = pos;
262346981Scy		break;
263346981Scy	case WLAN_EID_EXT_OWE_DH_PARAM:
264346981Scy		if (elen < 2)
265346981Scy			break;
266346981Scy		elems->owe_dh = pos;
267346981Scy		elems->owe_dh_len = elen;
268346981Scy		break;
269346981Scy	case WLAN_EID_EXT_PASSWORD_IDENTIFIER:
270346981Scy		elems->password_id = pos;
271346981Scy		elems->password_id_len = elen;
272346981Scy		break;
273346981Scy	case WLAN_EID_EXT_HE_CAPABILITIES:
274346981Scy		elems->he_capabilities = pos;
275346981Scy		elems->he_capabilities_len = elen;
276346981Scy		break;
277351611Scy	case WLAN_EID_EXT_HE_OPERATION:
278351611Scy		elems->he_operation = pos;
279351611Scy		elems->he_operation_len = elen;
280351611Scy		break;
281346981Scy	case WLAN_EID_EXT_OCV_OCI:
282346981Scy		elems->oci = pos;
283346981Scy		elems->oci_len = elen;
284346981Scy		break;
285346981Scy	default:
286346981Scy		if (show_errors) {
287346981Scy			wpa_printf(MSG_MSGDUMP,
288346981Scy				   "IEEE 802.11 element parsing ignored unknown element extension (ext_id=%u elen=%u)",
289346981Scy				   ext_id, (unsigned int) elen);
290346981Scy		}
291346981Scy		return -1;
292346981Scy	}
293346981Scy
294346981Scy	return 0;
295346981Scy}
296346981Scy
297346981Scy
298189251Ssam/**
299189251Ssam * ieee802_11_parse_elems - Parse information elements in management frames
300189251Ssam * @start: Pointer to the start of IEs
301189251Ssam * @len: Length of IE buffer in octets
302189251Ssam * @elems: Data structure for parsed elements
303189251Ssam * @show_errors: Whether to show parsing errors in debug log
304189251Ssam * Returns: Parsing result
305189251Ssam */
306214734SrpauloParseRes ieee802_11_parse_elems(const u8 *start, size_t len,
307189251Ssam				struct ieee802_11_elems *elems,
308189251Ssam				int show_errors)
309189251Ssam{
310346981Scy	const struct element *elem;
311189251Ssam	int unknown = 0;
312189251Ssam
313189251Ssam	os_memset(elems, 0, sizeof(*elems));
314189251Ssam
315346981Scy	if (!start)
316346981Scy		return ParseOK;
317189251Ssam
318346981Scy	for_each_element(elem, start, len) {
319346981Scy		u8 id = elem->id, elen = elem->datalen;
320346981Scy		const u8 *pos = elem->data;
321189251Ssam
322189251Ssam		switch (id) {
323189251Ssam		case WLAN_EID_SSID:
324289549Srpaulo			if (elen > SSID_MAX_LEN) {
325289549Srpaulo				wpa_printf(MSG_DEBUG,
326289549Srpaulo					   "Ignored too long SSID element (elen=%u)",
327289549Srpaulo					   elen);
328289549Srpaulo				break;
329289549Srpaulo			}
330189251Ssam			elems->ssid = pos;
331189251Ssam			elems->ssid_len = elen;
332189251Ssam			break;
333189251Ssam		case WLAN_EID_SUPP_RATES:
334189251Ssam			elems->supp_rates = pos;
335189251Ssam			elems->supp_rates_len = elen;
336189251Ssam			break;
337189251Ssam		case WLAN_EID_DS_PARAMS:
338289549Srpaulo			if (elen < 1)
339289549Srpaulo				break;
340189251Ssam			elems->ds_params = pos;
341189251Ssam			break;
342189251Ssam		case WLAN_EID_CF_PARAMS:
343189251Ssam		case WLAN_EID_TIM:
344189251Ssam			break;
345189251Ssam		case WLAN_EID_CHALLENGE:
346189251Ssam			elems->challenge = pos;
347189251Ssam			elems->challenge_len = elen;
348189251Ssam			break;
349189251Ssam		case WLAN_EID_ERP_INFO:
350289549Srpaulo			if (elen < 1)
351289549Srpaulo				break;
352189251Ssam			elems->erp_info = pos;
353189251Ssam			break;
354189251Ssam		case WLAN_EID_EXT_SUPP_RATES:
355189251Ssam			elems->ext_supp_rates = pos;
356189251Ssam			elems->ext_supp_rates_len = elen;
357189251Ssam			break;
358189251Ssam		case WLAN_EID_VENDOR_SPECIFIC:
359189251Ssam			if (ieee802_11_parse_vendor_specific(pos, elen,
360189251Ssam							     elems,
361189251Ssam							     show_errors))
362189251Ssam				unknown++;
363189251Ssam			break;
364189251Ssam		case WLAN_EID_RSN:
365189251Ssam			elems->rsn_ie = pos;
366189251Ssam			elems->rsn_ie_len = elen;
367189251Ssam			break;
368189251Ssam		case WLAN_EID_PWR_CAPABILITY:
369346981Scy			if (elen < 2)
370346981Scy				break;
371346981Scy			elems->power_capab = pos;
372346981Scy			elems->power_capab_len = elen;
373189251Ssam			break;
374189251Ssam		case WLAN_EID_SUPPORTED_CHANNELS:
375189251Ssam			elems->supp_channels = pos;
376189251Ssam			elems->supp_channels_len = elen;
377189251Ssam			break;
378189251Ssam		case WLAN_EID_MOBILITY_DOMAIN:
379289549Srpaulo			if (elen < sizeof(struct rsn_mdie))
380289549Srpaulo				break;
381189251Ssam			elems->mdie = pos;
382189251Ssam			elems->mdie_len = elen;
383189251Ssam			break;
384189251Ssam		case WLAN_EID_FAST_BSS_TRANSITION:
385289549Srpaulo			if (elen < sizeof(struct rsn_ftie))
386289549Srpaulo				break;
387189251Ssam			elems->ftie = pos;
388189251Ssam			elems->ftie_len = elen;
389189251Ssam			break;
390189251Ssam		case WLAN_EID_TIMEOUT_INTERVAL:
391289549Srpaulo			if (elen != 5)
392289549Srpaulo				break;
393189251Ssam			elems->timeout_int = pos;
394189251Ssam			break;
395189251Ssam		case WLAN_EID_HT_CAP:
396289549Srpaulo			if (elen < sizeof(struct ieee80211_ht_capabilities))
397289549Srpaulo				break;
398189251Ssam			elems->ht_capabilities = pos;
399189251Ssam			break;
400189251Ssam		case WLAN_EID_HT_OPERATION:
401289549Srpaulo			if (elen < sizeof(struct ieee80211_ht_operation))
402289549Srpaulo				break;
403189251Ssam			elems->ht_operation = pos;
404189251Ssam			break;
405281806Srpaulo		case WLAN_EID_MESH_CONFIG:
406281806Srpaulo			elems->mesh_config = pos;
407281806Srpaulo			elems->mesh_config_len = elen;
408281806Srpaulo			break;
409281806Srpaulo		case WLAN_EID_MESH_ID:
410281806Srpaulo			elems->mesh_id = pos;
411281806Srpaulo			elems->mesh_id_len = elen;
412281806Srpaulo			break;
413281806Srpaulo		case WLAN_EID_PEER_MGMT:
414281806Srpaulo			elems->peer_mgmt = pos;
415281806Srpaulo			elems->peer_mgmt_len = elen;
416281806Srpaulo			break;
417252726Srpaulo		case WLAN_EID_VHT_CAP:
418289549Srpaulo			if (elen < sizeof(struct ieee80211_vht_capabilities))
419289549Srpaulo				break;
420252726Srpaulo			elems->vht_capabilities = pos;
421252726Srpaulo			break;
422252726Srpaulo		case WLAN_EID_VHT_OPERATION:
423289549Srpaulo			if (elen < sizeof(struct ieee80211_vht_operation))
424289549Srpaulo				break;
425252726Srpaulo			elems->vht_operation = pos;
426252726Srpaulo			break;
427281806Srpaulo		case WLAN_EID_VHT_OPERATING_MODE_NOTIFICATION:
428281806Srpaulo			if (elen != 1)
429281806Srpaulo				break;
430281806Srpaulo			elems->vht_opmode_notif = pos;
431281806Srpaulo			break;
432252726Srpaulo		case WLAN_EID_LINK_ID:
433252726Srpaulo			if (elen < 18)
434252726Srpaulo				break;
435252726Srpaulo			elems->link_id = pos;
436252726Srpaulo			break;
437252726Srpaulo		case WLAN_EID_INTERWORKING:
438252726Srpaulo			elems->interworking = pos;
439252726Srpaulo			elems->interworking_len = elen;
440252726Srpaulo			break;
441281806Srpaulo		case WLAN_EID_QOS_MAP_SET:
442281806Srpaulo			if (elen < 16)
443281806Srpaulo				break;
444281806Srpaulo			elems->qos_map_set = pos;
445281806Srpaulo			elems->qos_map_set_len = elen;
446281806Srpaulo			break;
447252726Srpaulo		case WLAN_EID_EXT_CAPAB:
448252726Srpaulo			elems->ext_capab = pos;
449252726Srpaulo			elems->ext_capab_len = elen;
450252726Srpaulo			break;
451252726Srpaulo		case WLAN_EID_BSS_MAX_IDLE_PERIOD:
452252726Srpaulo			if (elen < 3)
453252726Srpaulo				break;
454252726Srpaulo			elems->bss_max_idle_period = pos;
455252726Srpaulo			break;
456252726Srpaulo		case WLAN_EID_SSID_LIST:
457252726Srpaulo			elems->ssid_list = pos;
458252726Srpaulo			elems->ssid_list_len = elen;
459252726Srpaulo			break;
460281806Srpaulo		case WLAN_EID_AMPE:
461281806Srpaulo			elems->ampe = pos;
462281806Srpaulo			elems->ampe_len = elen;
463281806Srpaulo			break;
464281806Srpaulo		case WLAN_EID_MIC:
465281806Srpaulo			elems->mic = pos;
466281806Srpaulo			elems->mic_len = elen;
467281806Srpaulo			/* after mic everything is encrypted, so stop. */
468346981Scy			goto done;
469289549Srpaulo		case WLAN_EID_MULTI_BAND:
470289549Srpaulo			if (elems->mb_ies.nof_ies >= MAX_NOF_MB_IES_SUPPORTED) {
471289549Srpaulo				wpa_printf(MSG_MSGDUMP,
472289549Srpaulo					   "IEEE 802.11 element parse ignored MB IE (id=%d elen=%d)",
473289549Srpaulo					   id, elen);
474289549Srpaulo				break;
475289549Srpaulo			}
476289549Srpaulo
477289549Srpaulo			elems->mb_ies.ies[elems->mb_ies.nof_ies].ie = pos;
478289549Srpaulo			elems->mb_ies.ies[elems->mb_ies.nof_ies].ie_len = elen;
479289549Srpaulo			elems->mb_ies.nof_ies++;
480289549Srpaulo			break;
481337817Scy		case WLAN_EID_SUPPORTED_OPERATING_CLASSES:
482337817Scy			elems->supp_op_classes = pos;
483337817Scy			elems->supp_op_classes_len = elen;
484337817Scy			break;
485337817Scy		case WLAN_EID_RRM_ENABLED_CAPABILITIES:
486337817Scy			elems->rrm_enabled = pos;
487337817Scy			elems->rrm_enabled_len = elen;
488337817Scy			break;
489346981Scy		case WLAN_EID_CAG_NUMBER:
490346981Scy			elems->cag_number = pos;
491346981Scy			elems->cag_number_len = elen;
492346981Scy			break;
493346981Scy		case WLAN_EID_AP_CSN:
494346981Scy			if (elen < 1)
495346981Scy				break;
496346981Scy			elems->ap_csn = pos;
497346981Scy			break;
498346981Scy		case WLAN_EID_FILS_INDICATION:
499346981Scy			if (elen < 2)
500346981Scy				break;
501346981Scy			elems->fils_indic = pos;
502346981Scy			elems->fils_indic_len = elen;
503346981Scy			break;
504346981Scy		case WLAN_EID_DILS:
505346981Scy			if (elen < 2)
506346981Scy				break;
507346981Scy			elems->dils = pos;
508346981Scy			elems->dils_len = elen;
509346981Scy			break;
510346981Scy		case WLAN_EID_FRAGMENT:
511346981Scy			/* TODO */
512346981Scy			break;
513346981Scy		case WLAN_EID_EXTENSION:
514346981Scy			if (ieee802_11_parse_extension(pos, elen, elems,
515346981Scy						       show_errors))
516346981Scy				unknown++;
517346981Scy			break;
518189251Ssam		default:
519189251Ssam			unknown++;
520189251Ssam			if (!show_errors)
521189251Ssam				break;
522189251Ssam			wpa_printf(MSG_MSGDUMP, "IEEE 802.11 element parse "
523189251Ssam				   "ignored unknown element (id=%d elen=%d)",
524189251Ssam				   id, elen);
525189251Ssam			break;
526189251Ssam		}
527189251Ssam	}
528189251Ssam
529346981Scy	if (!for_each_element_completed(elem, start, len)) {
530346981Scy		if (show_errors) {
531346981Scy			wpa_printf(MSG_DEBUG,
532346981Scy				   "IEEE 802.11 element parse failed @%d",
533346981Scy				   (int) (start + len - (const u8 *) elem));
534346981Scy			wpa_hexdump(MSG_MSGDUMP, "IEs", start, len);
535346981Scy		}
536189251Ssam		return ParseFailed;
537346981Scy	}
538189251Ssam
539346981Scydone:
540189251Ssam	return unknown ? ParseUnknown : ParseOK;
541189251Ssam}
542214734Srpaulo
543214734Srpaulo
544214734Srpauloint ieee802_11_ie_count(const u8 *ies, size_t ies_len)
545214734Srpaulo{
546346981Scy	const struct element *elem;
547214734Srpaulo	int count = 0;
548214734Srpaulo
549214734Srpaulo	if (ies == NULL)
550214734Srpaulo		return 0;
551214734Srpaulo
552346981Scy	for_each_element(elem, ies, ies_len)
553214734Srpaulo		count++;
554214734Srpaulo
555214734Srpaulo	return count;
556214734Srpaulo}
557214734Srpaulo
558214734Srpaulo
559214734Srpaulostruct wpabuf * ieee802_11_vendor_ie_concat(const u8 *ies, size_t ies_len,
560214734Srpaulo					    u32 oui_type)
561214734Srpaulo{
562214734Srpaulo	struct wpabuf *buf;
563346981Scy	const struct element *elem, *found = NULL;
564214734Srpaulo
565346981Scy	for_each_element_id(elem, WLAN_EID_VENDOR_SPECIFIC, ies, ies_len) {
566346981Scy		if (elem->datalen >= 4 &&
567346981Scy		    WPA_GET_BE32(elem->data) == oui_type) {
568346981Scy			found = elem;
569214734Srpaulo			break;
570214734Srpaulo		}
571214734Srpaulo	}
572214734Srpaulo
573346981Scy	if (!found)
574214734Srpaulo		return NULL; /* No specified vendor IE found */
575214734Srpaulo
576214734Srpaulo	buf = wpabuf_alloc(ies_len);
577214734Srpaulo	if (buf == NULL)
578214734Srpaulo		return NULL;
579214734Srpaulo
580214734Srpaulo	/*
581214734Srpaulo	 * There may be multiple vendor IEs in the message, so need to
582214734Srpaulo	 * concatenate their data fields.
583214734Srpaulo	 */
584346981Scy	for_each_element_id(elem, WLAN_EID_VENDOR_SPECIFIC, ies, ies_len) {
585346981Scy		if (elem->datalen >= 4 && WPA_GET_BE32(elem->data) == oui_type)
586346981Scy			wpabuf_put_data(buf, elem->data + 4, elem->datalen - 4);
587214734Srpaulo	}
588214734Srpaulo
589214734Srpaulo	return buf;
590214734Srpaulo}
591252726Srpaulo
592252726Srpaulo
593252726Srpauloconst u8 * get_hdr_bssid(const struct ieee80211_hdr *hdr, size_t len)
594252726Srpaulo{
595252726Srpaulo	u16 fc, type, stype;
596252726Srpaulo
597252726Srpaulo	/*
598252726Srpaulo	 * PS-Poll frames are 16 bytes. All other frames are
599252726Srpaulo	 * 24 bytes or longer.
600252726Srpaulo	 */
601252726Srpaulo	if (len < 16)
602252726Srpaulo		return NULL;
603252726Srpaulo
604252726Srpaulo	fc = le_to_host16(hdr->frame_control);
605252726Srpaulo	type = WLAN_FC_GET_TYPE(fc);
606252726Srpaulo	stype = WLAN_FC_GET_STYPE(fc);
607252726Srpaulo
608252726Srpaulo	switch (type) {
609252726Srpaulo	case WLAN_FC_TYPE_DATA:
610252726Srpaulo		if (len < 24)
611252726Srpaulo			return NULL;
612252726Srpaulo		switch (fc & (WLAN_FC_FROMDS | WLAN_FC_TODS)) {
613252726Srpaulo		case WLAN_FC_FROMDS | WLAN_FC_TODS:
614252726Srpaulo		case WLAN_FC_TODS:
615252726Srpaulo			return hdr->addr1;
616252726Srpaulo		case WLAN_FC_FROMDS:
617252726Srpaulo			return hdr->addr2;
618252726Srpaulo		default:
619252726Srpaulo			return NULL;
620252726Srpaulo		}
621252726Srpaulo	case WLAN_FC_TYPE_CTRL:
622252726Srpaulo		if (stype != WLAN_FC_STYPE_PSPOLL)
623252726Srpaulo			return NULL;
624252726Srpaulo		return hdr->addr1;
625252726Srpaulo	case WLAN_FC_TYPE_MGMT:
626252726Srpaulo		return hdr->addr3;
627252726Srpaulo	default:
628252726Srpaulo		return NULL;
629252726Srpaulo	}
630252726Srpaulo}
631252726Srpaulo
632252726Srpaulo
633252726Srpauloint hostapd_config_wmm_ac(struct hostapd_wmm_ac_params wmm_ac_params[],
634252726Srpaulo			  const char *name, const char *val)
635252726Srpaulo{
636252726Srpaulo	int num, v;
637252726Srpaulo	const char *pos;
638252726Srpaulo	struct hostapd_wmm_ac_params *ac;
639252726Srpaulo
640252726Srpaulo	/* skip 'wme_ac_' or 'wmm_ac_' prefix */
641252726Srpaulo	pos = name + 7;
642252726Srpaulo	if (os_strncmp(pos, "be_", 3) == 0) {
643252726Srpaulo		num = 0;
644252726Srpaulo		pos += 3;
645252726Srpaulo	} else if (os_strncmp(pos, "bk_", 3) == 0) {
646252726Srpaulo		num = 1;
647252726Srpaulo		pos += 3;
648252726Srpaulo	} else if (os_strncmp(pos, "vi_", 3) == 0) {
649252726Srpaulo		num = 2;
650252726Srpaulo		pos += 3;
651252726Srpaulo	} else if (os_strncmp(pos, "vo_", 3) == 0) {
652252726Srpaulo		num = 3;
653252726Srpaulo		pos += 3;
654252726Srpaulo	} else {
655252726Srpaulo		wpa_printf(MSG_ERROR, "Unknown WMM name '%s'", pos);
656252726Srpaulo		return -1;
657252726Srpaulo	}
658252726Srpaulo
659252726Srpaulo	ac = &wmm_ac_params[num];
660252726Srpaulo
661252726Srpaulo	if (os_strcmp(pos, "aifs") == 0) {
662252726Srpaulo		v = atoi(val);
663252726Srpaulo		if (v < 1 || v > 255) {
664252726Srpaulo			wpa_printf(MSG_ERROR, "Invalid AIFS value %d", v);
665252726Srpaulo			return -1;
666252726Srpaulo		}
667252726Srpaulo		ac->aifs = v;
668252726Srpaulo	} else if (os_strcmp(pos, "cwmin") == 0) {
669252726Srpaulo		v = atoi(val);
670289549Srpaulo		if (v < 0 || v > 15) {
671252726Srpaulo			wpa_printf(MSG_ERROR, "Invalid cwMin value %d", v);
672252726Srpaulo			return -1;
673252726Srpaulo		}
674252726Srpaulo		ac->cwmin = v;
675252726Srpaulo	} else if (os_strcmp(pos, "cwmax") == 0) {
676252726Srpaulo		v = atoi(val);
677289549Srpaulo		if (v < 0 || v > 15) {
678252726Srpaulo			wpa_printf(MSG_ERROR, "Invalid cwMax value %d", v);
679252726Srpaulo			return -1;
680252726Srpaulo		}
681252726Srpaulo		ac->cwmax = v;
682252726Srpaulo	} else if (os_strcmp(pos, "txop_limit") == 0) {
683252726Srpaulo		v = atoi(val);
684252726Srpaulo		if (v < 0 || v > 0xffff) {
685252726Srpaulo			wpa_printf(MSG_ERROR, "Invalid txop value %d", v);
686252726Srpaulo			return -1;
687252726Srpaulo		}
688252726Srpaulo		ac->txop_limit = v;
689252726Srpaulo	} else if (os_strcmp(pos, "acm") == 0) {
690252726Srpaulo		v = atoi(val);
691252726Srpaulo		if (v < 0 || v > 1) {
692252726Srpaulo			wpa_printf(MSG_ERROR, "Invalid acm value %d", v);
693252726Srpaulo			return -1;
694252726Srpaulo		}
695252726Srpaulo		ac->admission_control_mandatory = v;
696252726Srpaulo	} else {
697252726Srpaulo		wpa_printf(MSG_ERROR, "Unknown wmm_ac_ field '%s'", pos);
698252726Srpaulo		return -1;
699252726Srpaulo	}
700252726Srpaulo
701252726Srpaulo	return 0;
702252726Srpaulo}
703281806Srpaulo
704281806Srpaulo
705281806Srpauloenum hostapd_hw_mode ieee80211_freq_to_chan(int freq, u8 *channel)
706281806Srpaulo{
707289549Srpaulo	u8 op_class;
708281806Srpaulo
709351611Scy	return ieee80211_freq_to_channel_ext(freq, 0, CHANWIDTH_USE_HT,
710337817Scy					     &op_class, channel);
711289549Srpaulo}
712289549Srpaulo
713289549Srpaulo
714289549Srpaulo/**
715289549Srpaulo * ieee80211_freq_to_channel_ext - Convert frequency into channel info
716289549Srpaulo * for HT40 and VHT. DFS channels are not covered.
717289549Srpaulo * @freq: Frequency (MHz) to convert
718289549Srpaulo * @sec_channel: 0 = non-HT40, 1 = sec. channel above, -1 = sec. channel below
719351611Scy * @vht: VHT channel width (CHANWIDTH_*)
720289549Srpaulo * @op_class: Buffer for returning operating class
721289549Srpaulo * @channel: Buffer for returning channel number
722289549Srpaulo * Returns: hw_mode on success, NUM_HOSTAPD_MODES on failure
723289549Srpaulo */
724289549Srpauloenum hostapd_hw_mode ieee80211_freq_to_channel_ext(unsigned int freq,
725289549Srpaulo						   int sec_channel, int vht,
726289549Srpaulo						   u8 *op_class, u8 *channel)
727289549Srpaulo{
728337817Scy	u8 vht_opclass;
729337817Scy
730289549Srpaulo	/* TODO: more operating classes */
731289549Srpaulo
732289549Srpaulo	if (sec_channel > 1 || sec_channel < -1)
733289549Srpaulo		return NUM_HOSTAPD_MODES;
734289549Srpaulo
735281806Srpaulo	if (freq >= 2412 && freq <= 2472) {
736289549Srpaulo		if ((freq - 2407) % 5)
737289549Srpaulo			return NUM_HOSTAPD_MODES;
738289549Srpaulo
739289549Srpaulo		if (vht)
740289549Srpaulo			return NUM_HOSTAPD_MODES;
741289549Srpaulo
742289549Srpaulo		/* 2.407 GHz, channels 1..13 */
743289549Srpaulo		if (sec_channel == 1)
744289549Srpaulo			*op_class = 83;
745289549Srpaulo		else if (sec_channel == -1)
746289549Srpaulo			*op_class = 84;
747289549Srpaulo		else
748289549Srpaulo			*op_class = 81;
749289549Srpaulo
750281806Srpaulo		*channel = (freq - 2407) / 5;
751289549Srpaulo
752289549Srpaulo		return HOSTAPD_MODE_IEEE80211G;
753289549Srpaulo	}
754289549Srpaulo
755289549Srpaulo	if (freq == 2484) {
756289549Srpaulo		if (sec_channel || vht)
757289549Srpaulo			return NUM_HOSTAPD_MODES;
758289549Srpaulo
759289549Srpaulo		*op_class = 82; /* channel 14 */
760281806Srpaulo		*channel = 14;
761289549Srpaulo
762289549Srpaulo		return HOSTAPD_MODE_IEEE80211B;
763289549Srpaulo	}
764289549Srpaulo
765289549Srpaulo	if (freq >= 4900 && freq < 5000) {
766289549Srpaulo		if ((freq - 4000) % 5)
767289549Srpaulo			return NUM_HOSTAPD_MODES;
768281806Srpaulo		*channel = (freq - 4000) / 5;
769289549Srpaulo		*op_class = 0; /* TODO */
770289549Srpaulo		return HOSTAPD_MODE_IEEE80211A;
771289549Srpaulo	}
772289549Srpaulo
773337817Scy	switch (vht) {
774351611Scy	case CHANWIDTH_80MHZ:
775337817Scy		vht_opclass = 128;
776337817Scy		break;
777351611Scy	case CHANWIDTH_160MHZ:
778337817Scy		vht_opclass = 129;
779337817Scy		break;
780351611Scy	case CHANWIDTH_80P80MHZ:
781337817Scy		vht_opclass = 130;
782337817Scy		break;
783337817Scy	default:
784337817Scy		vht_opclass = 0;
785337817Scy		break;
786337817Scy	}
787337817Scy
788289549Srpaulo	/* 5 GHz, channels 36..48 */
789289549Srpaulo	if (freq >= 5180 && freq <= 5240) {
790289549Srpaulo		if ((freq - 5000) % 5)
791289549Srpaulo			return NUM_HOSTAPD_MODES;
792289549Srpaulo
793337817Scy		if (vht_opclass)
794337817Scy			*op_class = vht_opclass;
795337817Scy		else if (sec_channel == 1)
796289549Srpaulo			*op_class = 116;
797289549Srpaulo		else if (sec_channel == -1)
798289549Srpaulo			*op_class = 117;
799289549Srpaulo		else
800289549Srpaulo			*op_class = 115;
801289549Srpaulo
802281806Srpaulo		*channel = (freq - 5000) / 5;
803289549Srpaulo
804289549Srpaulo		return HOSTAPD_MODE_IEEE80211A;
805289549Srpaulo	}
806289549Srpaulo
807346981Scy	/* 5 GHz, channels 52..64 */
808346981Scy	if (freq >= 5260 && freq <= 5320) {
809346981Scy		if ((freq - 5000) % 5)
810346981Scy			return NUM_HOSTAPD_MODES;
811346981Scy
812346981Scy		if (vht_opclass)
813346981Scy			*op_class = vht_opclass;
814346981Scy		else if (sec_channel == 1)
815346981Scy			*op_class = 119;
816346981Scy		else if (sec_channel == -1)
817346981Scy			*op_class = 120;
818346981Scy		else
819346981Scy			*op_class = 118;
820346981Scy
821346981Scy		*channel = (freq - 5000) / 5;
822346981Scy
823346981Scy		return HOSTAPD_MODE_IEEE80211A;
824346981Scy	}
825346981Scy
826337817Scy	/* 5 GHz, channels 149..169 */
827337817Scy	if (freq >= 5745 && freq <= 5845) {
828289549Srpaulo		if ((freq - 5000) % 5)
829289549Srpaulo			return NUM_HOSTAPD_MODES;
830289549Srpaulo
831337817Scy		if (vht_opclass)
832337817Scy			*op_class = vht_opclass;
833337817Scy		else if (sec_channel == 1)
834289549Srpaulo			*op_class = 126;
835289549Srpaulo		else if (sec_channel == -1)
836289549Srpaulo			*op_class = 127;
837337817Scy		else if (freq <= 5805)
838337817Scy			*op_class = 124;
839289549Srpaulo		else
840337817Scy			*op_class = 125;
841289549Srpaulo
842289549Srpaulo		*channel = (freq - 5000) / 5;
843289549Srpaulo
844289549Srpaulo		return HOSTAPD_MODE_IEEE80211A;
845289549Srpaulo	}
846289549Srpaulo
847337817Scy	/* 5 GHz, channels 100..140 */
848337817Scy	if (freq >= 5000 && freq <= 5700) {
849289549Srpaulo		if ((freq - 5000) % 5)
850289549Srpaulo			return NUM_HOSTAPD_MODES;
851289549Srpaulo
852337817Scy		if (vht_opclass)
853337817Scy			*op_class = vht_opclass;
854337817Scy		else if (sec_channel == 1)
855337817Scy			*op_class = 122;
856337817Scy		else if (sec_channel == -1)
857337817Scy			*op_class = 123;
858337817Scy		else
859337817Scy			*op_class = 121;
860289549Srpaulo
861289549Srpaulo		*channel = (freq - 5000) / 5;
862289549Srpaulo
863289549Srpaulo		return HOSTAPD_MODE_IEEE80211A;
864289549Srpaulo	}
865289549Srpaulo
866289549Srpaulo	if (freq >= 5000 && freq < 5900) {
867289549Srpaulo		if ((freq - 5000) % 5)
868289549Srpaulo			return NUM_HOSTAPD_MODES;
869289549Srpaulo		*channel = (freq - 5000) / 5;
870289549Srpaulo		*op_class = 0; /* TODO */
871289549Srpaulo		return HOSTAPD_MODE_IEEE80211A;
872289549Srpaulo	}
873289549Srpaulo
874289549Srpaulo	/* 56.16 GHz, channel 1..4 */
875289549Srpaulo	if (freq >= 56160 + 2160 * 1 && freq <= 56160 + 2160 * 4) {
876289549Srpaulo		if (sec_channel || vht)
877289549Srpaulo			return NUM_HOSTAPD_MODES;
878289549Srpaulo
879281806Srpaulo		*channel = (freq - 56160) / 2160;
880289549Srpaulo		*op_class = 180;
881289549Srpaulo
882289549Srpaulo		return HOSTAPD_MODE_IEEE80211AD;
883281806Srpaulo	}
884281806Srpaulo
885289549Srpaulo	return NUM_HOSTAPD_MODES;
886281806Srpaulo}
887281806Srpaulo
888281806Srpaulo
889346981Scyint ieee80211_chaninfo_to_channel(unsigned int freq, enum chan_width chanwidth,
890346981Scy				  int sec_channel, u8 *op_class, u8 *channel)
891346981Scy{
892346981Scy	int vht = CHAN_WIDTH_UNKNOWN;
893346981Scy
894346981Scy	switch (chanwidth) {
895346981Scy	case CHAN_WIDTH_UNKNOWN:
896346981Scy	case CHAN_WIDTH_20_NOHT:
897346981Scy	case CHAN_WIDTH_20:
898346981Scy	case CHAN_WIDTH_40:
899351611Scy		vht = CHANWIDTH_USE_HT;
900346981Scy		break;
901346981Scy	case CHAN_WIDTH_80:
902351611Scy		vht = CHANWIDTH_80MHZ;
903346981Scy		break;
904346981Scy	case CHAN_WIDTH_80P80:
905351611Scy		vht = CHANWIDTH_80P80MHZ;
906346981Scy		break;
907346981Scy	case CHAN_WIDTH_160:
908351611Scy		vht = CHANWIDTH_160MHZ;
909346981Scy		break;
910346981Scy	}
911346981Scy
912346981Scy	if (ieee80211_freq_to_channel_ext(freq, sec_channel, vht, op_class,
913346981Scy					  channel) == NUM_HOSTAPD_MODES) {
914346981Scy		wpa_printf(MSG_WARNING,
915346981Scy			   "Cannot determine operating class and channel (freq=%u chanwidth=%d sec_channel=%d)",
916346981Scy			   freq, chanwidth, sec_channel);
917346981Scy		return -1;
918346981Scy	}
919346981Scy
920346981Scy	return 0;
921346981Scy}
922346981Scy
923346981Scy
924289549Srpaulostatic const char *const us_op_class_cc[] = {
925281806Srpaulo	"US", "CA", NULL
926281806Srpaulo};
927281806Srpaulo
928289549Srpaulostatic const char *const eu_op_class_cc[] = {
929281806Srpaulo	"AL", "AM", "AT", "AZ", "BA", "BE", "BG", "BY", "CH", "CY", "CZ", "DE",
930281806Srpaulo	"DK", "EE", "EL", "ES", "FI", "FR", "GE", "HR", "HU", "IE", "IS", "IT",
931281806Srpaulo	"LI", "LT", "LU", "LV", "MD", "ME", "MK", "MT", "NL", "NO", "PL", "PT",
932281806Srpaulo	"RO", "RS", "RU", "SE", "SI", "SK", "TR", "UA", "UK", NULL
933281806Srpaulo};
934281806Srpaulo
935289549Srpaulostatic const char *const jp_op_class_cc[] = {
936281806Srpaulo	"JP", NULL
937281806Srpaulo};
938281806Srpaulo
939289549Srpaulostatic const char *const cn_op_class_cc[] = {
940289549Srpaulo	"CN", NULL
941281806Srpaulo};
942281806Srpaulo
943281806Srpaulo
944289549Srpaulostatic int country_match(const char *const cc[], const char *const country)
945281806Srpaulo{
946281806Srpaulo	int i;
947281806Srpaulo
948281806Srpaulo	if (country == NULL)
949281806Srpaulo		return 0;
950281806Srpaulo	for (i = 0; cc[i]; i++) {
951281806Srpaulo		if (cc[i][0] == country[0] && cc[i][1] == country[1])
952281806Srpaulo			return 1;
953281806Srpaulo	}
954281806Srpaulo
955281806Srpaulo	return 0;
956281806Srpaulo}
957281806Srpaulo
958281806Srpaulo
959281806Srpaulostatic int ieee80211_chan_to_freq_us(u8 op_class, u8 chan)
960281806Srpaulo{
961281806Srpaulo	switch (op_class) {
962281806Srpaulo	case 12: /* channels 1..11 */
963281806Srpaulo	case 32: /* channels 1..7; 40 MHz */
964281806Srpaulo	case 33: /* channels 5..11; 40 MHz */
965281806Srpaulo		if (chan < 1 || chan > 11)
966281806Srpaulo			return -1;
967281806Srpaulo		return 2407 + 5 * chan;
968281806Srpaulo	case 1: /* channels 36,40,44,48 */
969281806Srpaulo	case 2: /* channels 52,56,60,64; dfs */
970281806Srpaulo	case 22: /* channels 36,44; 40 MHz */
971281806Srpaulo	case 23: /* channels 52,60; 40 MHz */
972281806Srpaulo	case 27: /* channels 40,48; 40 MHz */
973281806Srpaulo	case 28: /* channels 56,64; 40 MHz */
974281806Srpaulo		if (chan < 36 || chan > 64)
975281806Srpaulo			return -1;
976281806Srpaulo		return 5000 + 5 * chan;
977281806Srpaulo	case 4: /* channels 100-144 */
978281806Srpaulo	case 24: /* channels 100-140; 40 MHz */
979281806Srpaulo		if (chan < 100 || chan > 144)
980281806Srpaulo			return -1;
981281806Srpaulo		return 5000 + 5 * chan;
982281806Srpaulo	case 3: /* channels 149,153,157,161 */
983281806Srpaulo	case 25: /* channels 149,157; 40 MHz */
984281806Srpaulo	case 26: /* channels 149,157; 40 MHz */
985281806Srpaulo	case 30: /* channels 153,161; 40 MHz */
986281806Srpaulo	case 31: /* channels 153,161; 40 MHz */
987281806Srpaulo		if (chan < 149 || chan > 161)
988281806Srpaulo			return -1;
989281806Srpaulo		return 5000 + 5 * chan;
990289549Srpaulo	case 5: /* channels 149,153,157,161,165 */
991289549Srpaulo		if (chan < 149 || chan > 165)
992289549Srpaulo			return -1;
993289549Srpaulo		return 5000 + 5 * chan;
994281806Srpaulo	case 34: /* 60 GHz band, channels 1..3 */
995281806Srpaulo		if (chan < 1 || chan > 3)
996281806Srpaulo			return -1;
997281806Srpaulo		return 56160 + 2160 * chan;
998281806Srpaulo	}
999281806Srpaulo	return -1;
1000281806Srpaulo}
1001281806Srpaulo
1002281806Srpaulo
1003281806Srpaulostatic int ieee80211_chan_to_freq_eu(u8 op_class, u8 chan)
1004281806Srpaulo{
1005281806Srpaulo	switch (op_class) {
1006281806Srpaulo	case 4: /* channels 1..13 */
1007281806Srpaulo	case 11: /* channels 1..9; 40 MHz */
1008281806Srpaulo	case 12: /* channels 5..13; 40 MHz */
1009281806Srpaulo		if (chan < 1 || chan > 13)
1010281806Srpaulo			return -1;
1011281806Srpaulo		return 2407 + 5 * chan;
1012281806Srpaulo	case 1: /* channels 36,40,44,48 */
1013281806Srpaulo	case 2: /* channels 52,56,60,64; dfs */
1014281806Srpaulo	case 5: /* channels 36,44; 40 MHz */
1015281806Srpaulo	case 6: /* channels 52,60; 40 MHz */
1016281806Srpaulo	case 8: /* channels 40,48; 40 MHz */
1017281806Srpaulo	case 9: /* channels 56,64; 40 MHz */
1018281806Srpaulo		if (chan < 36 || chan > 64)
1019281806Srpaulo			return -1;
1020281806Srpaulo		return 5000 + 5 * chan;
1021281806Srpaulo	case 3: /* channels 100-140 */
1022281806Srpaulo	case 7: /* channels 100-132; 40 MHz */
1023281806Srpaulo	case 10: /* channels 104-136; 40 MHz */
1024281806Srpaulo	case 16: /* channels 100-140 */
1025281806Srpaulo		if (chan < 100 || chan > 140)
1026281806Srpaulo			return -1;
1027281806Srpaulo		return 5000 + 5 * chan;
1028281806Srpaulo	case 17: /* channels 149,153,157,161,165,169 */
1029281806Srpaulo		if (chan < 149 || chan > 169)
1030281806Srpaulo			return -1;
1031281806Srpaulo		return 5000 + 5 * chan;
1032281806Srpaulo	case 18: /* 60 GHz band, channels 1..4 */
1033281806Srpaulo		if (chan < 1 || chan > 4)
1034281806Srpaulo			return -1;
1035281806Srpaulo		return 56160 + 2160 * chan;
1036281806Srpaulo	}
1037281806Srpaulo	return -1;
1038281806Srpaulo}
1039281806Srpaulo
1040281806Srpaulo
1041281806Srpaulostatic int ieee80211_chan_to_freq_jp(u8 op_class, u8 chan)
1042281806Srpaulo{
1043281806Srpaulo	switch (op_class) {
1044281806Srpaulo	case 30: /* channels 1..13 */
1045281806Srpaulo	case 56: /* channels 1..9; 40 MHz */
1046281806Srpaulo	case 57: /* channels 5..13; 40 MHz */
1047281806Srpaulo		if (chan < 1 || chan > 13)
1048281806Srpaulo			return -1;
1049281806Srpaulo		return 2407 + 5 * chan;
1050281806Srpaulo	case 31: /* channel 14 */
1051281806Srpaulo		if (chan != 14)
1052281806Srpaulo			return -1;
1053281806Srpaulo		return 2414 + 5 * chan;
1054281806Srpaulo	case 1: /* channels 34,38,42,46(old) or 36,40,44,48 */
1055281806Srpaulo	case 32: /* channels 52,56,60,64 */
1056281806Srpaulo	case 33: /* channels 52,56,60,64 */
1057281806Srpaulo	case 36: /* channels 36,44; 40 MHz */
1058281806Srpaulo	case 37: /* channels 52,60; 40 MHz */
1059281806Srpaulo	case 38: /* channels 52,60; 40 MHz */
1060281806Srpaulo	case 41: /* channels 40,48; 40 MHz */
1061281806Srpaulo	case 42: /* channels 56,64; 40 MHz */
1062281806Srpaulo	case 43: /* channels 56,64; 40 MHz */
1063281806Srpaulo		if (chan < 34 || chan > 64)
1064281806Srpaulo			return -1;
1065281806Srpaulo		return 5000 + 5 * chan;
1066281806Srpaulo	case 34: /* channels 100-140 */
1067281806Srpaulo	case 35: /* channels 100-140 */
1068281806Srpaulo	case 39: /* channels 100-132; 40 MHz */
1069281806Srpaulo	case 40: /* channels 100-132; 40 MHz */
1070281806Srpaulo	case 44: /* channels 104-136; 40 MHz */
1071281806Srpaulo	case 45: /* channels 104-136; 40 MHz */
1072281806Srpaulo	case 58: /* channels 100-140 */
1073281806Srpaulo		if (chan < 100 || chan > 140)
1074281806Srpaulo			return -1;
1075281806Srpaulo		return 5000 + 5 * chan;
1076281806Srpaulo	case 59: /* 60 GHz band, channels 1..4 */
1077281806Srpaulo		if (chan < 1 || chan > 3)
1078281806Srpaulo			return -1;
1079281806Srpaulo		return 56160 + 2160 * chan;
1080281806Srpaulo	}
1081281806Srpaulo	return -1;
1082281806Srpaulo}
1083281806Srpaulo
1084281806Srpaulo
1085281806Srpaulostatic int ieee80211_chan_to_freq_cn(u8 op_class, u8 chan)
1086281806Srpaulo{
1087281806Srpaulo	switch (op_class) {
1088281806Srpaulo	case 7: /* channels 1..13 */
1089281806Srpaulo	case 8: /* channels 1..9; 40 MHz */
1090281806Srpaulo	case 9: /* channels 5..13; 40 MHz */
1091281806Srpaulo		if (chan < 1 || chan > 13)
1092281806Srpaulo			return -1;
1093281806Srpaulo		return 2407 + 5 * chan;
1094281806Srpaulo	case 1: /* channels 36,40,44,48 */
1095281806Srpaulo	case 2: /* channels 52,56,60,64; dfs */
1096281806Srpaulo	case 4: /* channels 36,44; 40 MHz */
1097281806Srpaulo	case 5: /* channels 52,60; 40 MHz */
1098281806Srpaulo		if (chan < 36 || chan > 64)
1099281806Srpaulo			return -1;
1100281806Srpaulo		return 5000 + 5 * chan;
1101281806Srpaulo	case 3: /* channels 149,153,157,161,165 */
1102281806Srpaulo	case 6: /* channels 149,157; 40 MHz */
1103281806Srpaulo		if (chan < 149 || chan > 165)
1104281806Srpaulo			return -1;
1105281806Srpaulo		return 5000 + 5 * chan;
1106281806Srpaulo	}
1107281806Srpaulo	return -1;
1108281806Srpaulo}
1109281806Srpaulo
1110281806Srpaulo
1111281806Srpaulostatic int ieee80211_chan_to_freq_global(u8 op_class, u8 chan)
1112281806Srpaulo{
1113281806Srpaulo	/* Table E-4 in IEEE Std 802.11-2012 - Global operating classes */
1114281806Srpaulo	switch (op_class) {
1115281806Srpaulo	case 81:
1116281806Srpaulo		/* channels 1..13 */
1117281806Srpaulo		if (chan < 1 || chan > 13)
1118281806Srpaulo			return -1;
1119281806Srpaulo		return 2407 + 5 * chan;
1120281806Srpaulo	case 82:
1121281806Srpaulo		/* channel 14 */
1122281806Srpaulo		if (chan != 14)
1123281806Srpaulo			return -1;
1124281806Srpaulo		return 2414 + 5 * chan;
1125281806Srpaulo	case 83: /* channels 1..9; 40 MHz */
1126281806Srpaulo	case 84: /* channels 5..13; 40 MHz */
1127281806Srpaulo		if (chan < 1 || chan > 13)
1128281806Srpaulo			return -1;
1129281806Srpaulo		return 2407 + 5 * chan;
1130281806Srpaulo	case 115: /* channels 36,40,44,48; indoor only */
1131281806Srpaulo	case 116: /* channels 36,44; 40 MHz; indoor only */
1132281806Srpaulo	case 117: /* channels 40,48; 40 MHz; indoor only */
1133281806Srpaulo	case 118: /* channels 52,56,60,64; dfs */
1134281806Srpaulo	case 119: /* channels 52,60; 40 MHz; dfs */
1135281806Srpaulo	case 120: /* channels 56,64; 40 MHz; dfs */
1136281806Srpaulo		if (chan < 36 || chan > 64)
1137281806Srpaulo			return -1;
1138281806Srpaulo		return 5000 + 5 * chan;
1139281806Srpaulo	case 121: /* channels 100-140 */
1140281806Srpaulo	case 122: /* channels 100-142; 40 MHz */
1141281806Srpaulo	case 123: /* channels 104-136; 40 MHz */
1142281806Srpaulo		if (chan < 100 || chan > 140)
1143281806Srpaulo			return -1;
1144281806Srpaulo		return 5000 + 5 * chan;
1145281806Srpaulo	case 124: /* channels 149,153,157,161 */
1146281806Srpaulo	case 126: /* channels 149,157; 40 MHz */
1147281806Srpaulo	case 127: /* channels 153,161; 40 MHz */
1148281806Srpaulo		if (chan < 149 || chan > 161)
1149281806Srpaulo			return -1;
1150281806Srpaulo		return 5000 + 5 * chan;
1151289549Srpaulo	case 125: /* channels 149,153,157,161,165,169 */
1152289549Srpaulo		if (chan < 149 || chan > 169)
1153289549Srpaulo			return -1;
1154289549Srpaulo		return 5000 + 5 * chan;
1155281806Srpaulo	case 128: /* center freqs 42, 58, 106, 122, 138, 155; 80 MHz */
1156281806Srpaulo	case 130: /* center freqs 42, 58, 106, 122, 138, 155; 80 MHz */
1157281806Srpaulo		if (chan < 36 || chan > 161)
1158281806Srpaulo			return -1;
1159281806Srpaulo		return 5000 + 5 * chan;
1160281806Srpaulo	case 129: /* center freqs 50, 114; 160 MHz */
1161346981Scy		if (chan < 36 || chan > 128)
1162281806Srpaulo			return -1;
1163281806Srpaulo		return 5000 + 5 * chan;
1164281806Srpaulo	case 180: /* 60 GHz band, channels 1..4 */
1165281806Srpaulo		if (chan < 1 || chan > 4)
1166281806Srpaulo			return -1;
1167281806Srpaulo		return 56160 + 2160 * chan;
1168281806Srpaulo	}
1169281806Srpaulo	return -1;
1170281806Srpaulo}
1171281806Srpaulo
1172281806Srpaulo/**
1173281806Srpaulo * ieee80211_chan_to_freq - Convert channel info to frequency
1174281806Srpaulo * @country: Country code, if known; otherwise, global operating class is used
1175281806Srpaulo * @op_class: Operating class
1176281806Srpaulo * @chan: Channel number
1177281806Srpaulo * Returns: Frequency in MHz or -1 if the specified channel is unknown
1178281806Srpaulo */
1179281806Srpauloint ieee80211_chan_to_freq(const char *country, u8 op_class, u8 chan)
1180281806Srpaulo{
1181281806Srpaulo	int freq;
1182281806Srpaulo
1183281806Srpaulo	if (country_match(us_op_class_cc, country)) {
1184281806Srpaulo		freq = ieee80211_chan_to_freq_us(op_class, chan);
1185281806Srpaulo		if (freq > 0)
1186281806Srpaulo			return freq;
1187281806Srpaulo	}
1188281806Srpaulo
1189281806Srpaulo	if (country_match(eu_op_class_cc, country)) {
1190281806Srpaulo		freq = ieee80211_chan_to_freq_eu(op_class, chan);
1191281806Srpaulo		if (freq > 0)
1192281806Srpaulo			return freq;
1193281806Srpaulo	}
1194281806Srpaulo
1195281806Srpaulo	if (country_match(jp_op_class_cc, country)) {
1196281806Srpaulo		freq = ieee80211_chan_to_freq_jp(op_class, chan);
1197281806Srpaulo		if (freq > 0)
1198281806Srpaulo			return freq;
1199281806Srpaulo	}
1200281806Srpaulo
1201281806Srpaulo	if (country_match(cn_op_class_cc, country)) {
1202281806Srpaulo		freq = ieee80211_chan_to_freq_cn(op_class, chan);
1203281806Srpaulo		if (freq > 0)
1204281806Srpaulo			return freq;
1205281806Srpaulo	}
1206281806Srpaulo
1207281806Srpaulo	return ieee80211_chan_to_freq_global(op_class, chan);
1208281806Srpaulo}
1209281806Srpaulo
1210281806Srpaulo
1211346981Scyint ieee80211_is_dfs(int freq, const struct hostapd_hw_modes *modes,
1212346981Scy		     u16 num_modes)
1213281806Srpaulo{
1214346981Scy	int i, j;
1215346981Scy
1216346981Scy	if (!modes || !num_modes)
1217346981Scy		return (freq >= 5260 && freq <= 5320) ||
1218346981Scy			(freq >= 5500 && freq <= 5700);
1219346981Scy
1220346981Scy	for (i = 0; i < num_modes; i++) {
1221346981Scy		for (j = 0; j < modes[i].num_channels; j++) {
1222346981Scy			if (modes[i].channels[j].freq == freq &&
1223346981Scy			    (modes[i].channels[j].flag & HOSTAPD_CHAN_RADAR))
1224346981Scy				return 1;
1225346981Scy		}
1226346981Scy	}
1227346981Scy
1228346981Scy	return 0;
1229281806Srpaulo}
1230281806Srpaulo
1231281806Srpaulo
1232281806Srpaulostatic int is_11b(u8 rate)
1233281806Srpaulo{
1234281806Srpaulo	return rate == 0x02 || rate == 0x04 || rate == 0x0b || rate == 0x16;
1235281806Srpaulo}
1236281806Srpaulo
1237281806Srpaulo
1238281806Srpauloint supp_rates_11b_only(struct ieee802_11_elems *elems)
1239281806Srpaulo{
1240281806Srpaulo	int num_11b = 0, num_others = 0;
1241281806Srpaulo	int i;
1242281806Srpaulo
1243281806Srpaulo	if (elems->supp_rates == NULL && elems->ext_supp_rates == NULL)
1244281806Srpaulo		return 0;
1245281806Srpaulo
1246281806Srpaulo	for (i = 0; elems->supp_rates && i < elems->supp_rates_len; i++) {
1247281806Srpaulo		if (is_11b(elems->supp_rates[i]))
1248281806Srpaulo			num_11b++;
1249281806Srpaulo		else
1250281806Srpaulo			num_others++;
1251281806Srpaulo	}
1252281806Srpaulo
1253281806Srpaulo	for (i = 0; elems->ext_supp_rates && i < elems->ext_supp_rates_len;
1254281806Srpaulo	     i++) {
1255281806Srpaulo		if (is_11b(elems->ext_supp_rates[i]))
1256281806Srpaulo			num_11b++;
1257281806Srpaulo		else
1258281806Srpaulo			num_others++;
1259281806Srpaulo	}
1260281806Srpaulo
1261281806Srpaulo	return num_11b > 0 && num_others == 0;
1262281806Srpaulo}
1263281806Srpaulo
1264281806Srpaulo
1265281806Srpauloconst char * fc2str(u16 fc)
1266281806Srpaulo{
1267281806Srpaulo	u16 stype = WLAN_FC_GET_STYPE(fc);
1268281806Srpaulo#define C2S(x) case x: return #x;
1269281806Srpaulo
1270281806Srpaulo	switch (WLAN_FC_GET_TYPE(fc)) {
1271281806Srpaulo	case WLAN_FC_TYPE_MGMT:
1272281806Srpaulo		switch (stype) {
1273281806Srpaulo		C2S(WLAN_FC_STYPE_ASSOC_REQ)
1274281806Srpaulo		C2S(WLAN_FC_STYPE_ASSOC_RESP)
1275281806Srpaulo		C2S(WLAN_FC_STYPE_REASSOC_REQ)
1276281806Srpaulo		C2S(WLAN_FC_STYPE_REASSOC_RESP)
1277281806Srpaulo		C2S(WLAN_FC_STYPE_PROBE_REQ)
1278281806Srpaulo		C2S(WLAN_FC_STYPE_PROBE_RESP)
1279281806Srpaulo		C2S(WLAN_FC_STYPE_BEACON)
1280281806Srpaulo		C2S(WLAN_FC_STYPE_ATIM)
1281281806Srpaulo		C2S(WLAN_FC_STYPE_DISASSOC)
1282281806Srpaulo		C2S(WLAN_FC_STYPE_AUTH)
1283281806Srpaulo		C2S(WLAN_FC_STYPE_DEAUTH)
1284281806Srpaulo		C2S(WLAN_FC_STYPE_ACTION)
1285281806Srpaulo		}
1286281806Srpaulo		break;
1287281806Srpaulo	case WLAN_FC_TYPE_CTRL:
1288281806Srpaulo		switch (stype) {
1289281806Srpaulo		C2S(WLAN_FC_STYPE_PSPOLL)
1290281806Srpaulo		C2S(WLAN_FC_STYPE_RTS)
1291281806Srpaulo		C2S(WLAN_FC_STYPE_CTS)
1292281806Srpaulo		C2S(WLAN_FC_STYPE_ACK)
1293281806Srpaulo		C2S(WLAN_FC_STYPE_CFEND)
1294281806Srpaulo		C2S(WLAN_FC_STYPE_CFENDACK)
1295281806Srpaulo		}
1296281806Srpaulo		break;
1297281806Srpaulo	case WLAN_FC_TYPE_DATA:
1298281806Srpaulo		switch (stype) {
1299281806Srpaulo		C2S(WLAN_FC_STYPE_DATA)
1300281806Srpaulo		C2S(WLAN_FC_STYPE_DATA_CFACK)
1301281806Srpaulo		C2S(WLAN_FC_STYPE_DATA_CFPOLL)
1302281806Srpaulo		C2S(WLAN_FC_STYPE_DATA_CFACKPOLL)
1303281806Srpaulo		C2S(WLAN_FC_STYPE_NULLFUNC)
1304281806Srpaulo		C2S(WLAN_FC_STYPE_CFACK)
1305281806Srpaulo		C2S(WLAN_FC_STYPE_CFPOLL)
1306281806Srpaulo		C2S(WLAN_FC_STYPE_CFACKPOLL)
1307281806Srpaulo		C2S(WLAN_FC_STYPE_QOS_DATA)
1308281806Srpaulo		C2S(WLAN_FC_STYPE_QOS_DATA_CFACK)
1309281806Srpaulo		C2S(WLAN_FC_STYPE_QOS_DATA_CFPOLL)
1310281806Srpaulo		C2S(WLAN_FC_STYPE_QOS_DATA_CFACKPOLL)
1311281806Srpaulo		C2S(WLAN_FC_STYPE_QOS_NULL)
1312281806Srpaulo		C2S(WLAN_FC_STYPE_QOS_CFPOLL)
1313281806Srpaulo		C2S(WLAN_FC_STYPE_QOS_CFACKPOLL)
1314281806Srpaulo		}
1315281806Srpaulo		break;
1316281806Srpaulo	}
1317281806Srpaulo	return "WLAN_FC_TYPE_UNKNOWN";
1318281806Srpaulo#undef C2S
1319281806Srpaulo}
1320289549Srpaulo
1321289549Srpaulo
1322351611Scyconst char * reason2str(u16 reason)
1323351611Scy{
1324351611Scy#define R2S(r) case WLAN_REASON_ ## r: return #r;
1325351611Scy	switch (reason) {
1326351611Scy	R2S(UNSPECIFIED)
1327351611Scy	R2S(PREV_AUTH_NOT_VALID)
1328351611Scy	R2S(DEAUTH_LEAVING)
1329351611Scy	R2S(DISASSOC_DUE_TO_INACTIVITY)
1330351611Scy	R2S(DISASSOC_AP_BUSY)
1331351611Scy	R2S(CLASS2_FRAME_FROM_NONAUTH_STA)
1332351611Scy	R2S(CLASS3_FRAME_FROM_NONASSOC_STA)
1333351611Scy	R2S(DISASSOC_STA_HAS_LEFT)
1334351611Scy	R2S(STA_REQ_ASSOC_WITHOUT_AUTH)
1335351611Scy	R2S(PWR_CAPABILITY_NOT_VALID)
1336351611Scy	R2S(SUPPORTED_CHANNEL_NOT_VALID)
1337351611Scy	R2S(BSS_TRANSITION_DISASSOC)
1338351611Scy	R2S(INVALID_IE)
1339351611Scy	R2S(MICHAEL_MIC_FAILURE)
1340351611Scy	R2S(4WAY_HANDSHAKE_TIMEOUT)
1341351611Scy	R2S(GROUP_KEY_UPDATE_TIMEOUT)
1342351611Scy	R2S(IE_IN_4WAY_DIFFERS)
1343351611Scy	R2S(GROUP_CIPHER_NOT_VALID)
1344351611Scy	R2S(PAIRWISE_CIPHER_NOT_VALID)
1345351611Scy	R2S(AKMP_NOT_VALID)
1346351611Scy	R2S(UNSUPPORTED_RSN_IE_VERSION)
1347351611Scy	R2S(INVALID_RSN_IE_CAPAB)
1348351611Scy	R2S(IEEE_802_1X_AUTH_FAILED)
1349351611Scy	R2S(CIPHER_SUITE_REJECTED)
1350351611Scy	R2S(TDLS_TEARDOWN_UNREACHABLE)
1351351611Scy	R2S(TDLS_TEARDOWN_UNSPECIFIED)
1352351611Scy	R2S(SSP_REQUESTED_DISASSOC)
1353351611Scy	R2S(NO_SSP_ROAMING_AGREEMENT)
1354351611Scy	R2S(BAD_CIPHER_OR_AKM)
1355351611Scy	R2S(NOT_AUTHORIZED_THIS_LOCATION)
1356351611Scy	R2S(SERVICE_CHANGE_PRECLUDES_TS)
1357351611Scy	R2S(UNSPECIFIED_QOS_REASON)
1358351611Scy	R2S(NOT_ENOUGH_BANDWIDTH)
1359351611Scy	R2S(DISASSOC_LOW_ACK)
1360351611Scy	R2S(EXCEEDED_TXOP)
1361351611Scy	R2S(STA_LEAVING)
1362351611Scy	R2S(END_TS_BA_DLS)
1363351611Scy	R2S(UNKNOWN_TS_BA)
1364351611Scy	R2S(TIMEOUT)
1365351611Scy	R2S(PEERKEY_MISMATCH)
1366351611Scy	R2S(AUTHORIZED_ACCESS_LIMIT_REACHED)
1367351611Scy	R2S(EXTERNAL_SERVICE_REQUIREMENTS)
1368351611Scy	R2S(INVALID_FT_ACTION_FRAME_COUNT)
1369351611Scy	R2S(INVALID_PMKID)
1370351611Scy	R2S(INVALID_MDE)
1371351611Scy	R2S(INVALID_FTE)
1372351611Scy	R2S(MESH_PEERING_CANCELLED)
1373351611Scy	R2S(MESH_MAX_PEERS)
1374351611Scy	R2S(MESH_CONFIG_POLICY_VIOLATION)
1375351611Scy	R2S(MESH_CLOSE_RCVD)
1376351611Scy	R2S(MESH_MAX_RETRIES)
1377351611Scy	R2S(MESH_CONFIRM_TIMEOUT)
1378351611Scy	R2S(MESH_INVALID_GTK)
1379351611Scy	R2S(MESH_INCONSISTENT_PARAMS)
1380351611Scy	R2S(MESH_INVALID_SECURITY_CAP)
1381351611Scy	R2S(MESH_PATH_ERROR_NO_PROXY_INFO)
1382351611Scy	R2S(MESH_PATH_ERROR_NO_FORWARDING_INFO)
1383351611Scy	R2S(MESH_PATH_ERROR_DEST_UNREACHABLE)
1384351611Scy	R2S(MAC_ADDRESS_ALREADY_EXISTS_IN_MBSS)
1385351611Scy	R2S(MESH_CHANNEL_SWITCH_REGULATORY_REQ)
1386351611Scy	R2S(MESH_CHANNEL_SWITCH_UNSPECIFIED)
1387351611Scy	}
1388351611Scy	return "UNKNOWN";
1389351611Scy#undef R2S
1390351611Scy}
1391351611Scy
1392351611Scy
1393351611Scyconst char * status2str(u16 status)
1394351611Scy{
1395351611Scy#define S2S(s) case WLAN_STATUS_ ## s: return #s;
1396351611Scy	switch (status) {
1397351611Scy	S2S(SUCCESS)
1398351611Scy	S2S(UNSPECIFIED_FAILURE)
1399351611Scy	S2S(TDLS_WAKEUP_ALTERNATE)
1400351611Scy	S2S(TDLS_WAKEUP_REJECT)
1401351611Scy	S2S(SECURITY_DISABLED)
1402351611Scy	S2S(UNACCEPTABLE_LIFETIME)
1403351611Scy	S2S(NOT_IN_SAME_BSS)
1404351611Scy	S2S(CAPS_UNSUPPORTED)
1405351611Scy	S2S(REASSOC_NO_ASSOC)
1406351611Scy	S2S(ASSOC_DENIED_UNSPEC)
1407351611Scy	S2S(NOT_SUPPORTED_AUTH_ALG)
1408351611Scy	S2S(UNKNOWN_AUTH_TRANSACTION)
1409351611Scy	S2S(CHALLENGE_FAIL)
1410351611Scy	S2S(AUTH_TIMEOUT)
1411351611Scy	S2S(AP_UNABLE_TO_HANDLE_NEW_STA)
1412351611Scy	S2S(ASSOC_DENIED_RATES)
1413351611Scy	S2S(ASSOC_DENIED_NOSHORT)
1414351611Scy	S2S(SPEC_MGMT_REQUIRED)
1415351611Scy	S2S(PWR_CAPABILITY_NOT_VALID)
1416351611Scy	S2S(SUPPORTED_CHANNEL_NOT_VALID)
1417351611Scy	S2S(ASSOC_DENIED_NO_SHORT_SLOT_TIME)
1418351611Scy	S2S(ASSOC_DENIED_NO_HT)
1419351611Scy	S2S(R0KH_UNREACHABLE)
1420351611Scy	S2S(ASSOC_DENIED_NO_PCO)
1421351611Scy	S2S(ASSOC_REJECTED_TEMPORARILY)
1422351611Scy	S2S(ROBUST_MGMT_FRAME_POLICY_VIOLATION)
1423351611Scy	S2S(UNSPECIFIED_QOS_FAILURE)
1424351611Scy	S2S(DENIED_INSUFFICIENT_BANDWIDTH)
1425351611Scy	S2S(DENIED_POOR_CHANNEL_CONDITIONS)
1426351611Scy	S2S(DENIED_QOS_NOT_SUPPORTED)
1427351611Scy	S2S(REQUEST_DECLINED)
1428351611Scy	S2S(INVALID_PARAMETERS)
1429351611Scy	S2S(REJECTED_WITH_SUGGESTED_CHANGES)
1430351611Scy	S2S(INVALID_IE)
1431351611Scy	S2S(GROUP_CIPHER_NOT_VALID)
1432351611Scy	S2S(PAIRWISE_CIPHER_NOT_VALID)
1433351611Scy	S2S(AKMP_NOT_VALID)
1434351611Scy	S2S(UNSUPPORTED_RSN_IE_VERSION)
1435351611Scy	S2S(INVALID_RSN_IE_CAPAB)
1436351611Scy	S2S(CIPHER_REJECTED_PER_POLICY)
1437351611Scy	S2S(TS_NOT_CREATED)
1438351611Scy	S2S(DIRECT_LINK_NOT_ALLOWED)
1439351611Scy	S2S(DEST_STA_NOT_PRESENT)
1440351611Scy	S2S(DEST_STA_NOT_QOS_STA)
1441351611Scy	S2S(ASSOC_DENIED_LISTEN_INT_TOO_LARGE)
1442351611Scy	S2S(INVALID_FT_ACTION_FRAME_COUNT)
1443351611Scy	S2S(INVALID_PMKID)
1444351611Scy	S2S(INVALID_MDIE)
1445351611Scy	S2S(INVALID_FTIE)
1446351611Scy	S2S(REQUESTED_TCLAS_NOT_SUPPORTED)
1447351611Scy	S2S(INSUFFICIENT_TCLAS_PROCESSING_RESOURCES)
1448351611Scy	S2S(TRY_ANOTHER_BSS)
1449351611Scy	S2S(GAS_ADV_PROTO_NOT_SUPPORTED)
1450351611Scy	S2S(NO_OUTSTANDING_GAS_REQ)
1451351611Scy	S2S(GAS_RESP_NOT_RECEIVED)
1452351611Scy	S2S(STA_TIMED_OUT_WAITING_FOR_GAS_RESP)
1453351611Scy	S2S(GAS_RESP_LARGER_THAN_LIMIT)
1454351611Scy	S2S(REQ_REFUSED_HOME)
1455351611Scy	S2S(ADV_SRV_UNREACHABLE)
1456351611Scy	S2S(REQ_REFUSED_SSPN)
1457351611Scy	S2S(REQ_REFUSED_UNAUTH_ACCESS)
1458351611Scy	S2S(INVALID_RSNIE)
1459351611Scy	S2S(U_APSD_COEX_NOT_SUPPORTED)
1460351611Scy	S2S(U_APSD_COEX_MODE_NOT_SUPPORTED)
1461351611Scy	S2S(BAD_INTERVAL_WITH_U_APSD_COEX)
1462351611Scy	S2S(ANTI_CLOGGING_TOKEN_REQ)
1463351611Scy	S2S(FINITE_CYCLIC_GROUP_NOT_SUPPORTED)
1464351611Scy	S2S(CANNOT_FIND_ALT_TBTT)
1465351611Scy	S2S(TRANSMISSION_FAILURE)
1466351611Scy	S2S(REQ_TCLAS_NOT_SUPPORTED)
1467351611Scy	S2S(TCLAS_RESOURCES_EXCHAUSTED)
1468351611Scy	S2S(REJECTED_WITH_SUGGESTED_BSS_TRANSITION)
1469351611Scy	S2S(REJECT_WITH_SCHEDULE)
1470351611Scy	S2S(REJECT_NO_WAKEUP_SPECIFIED)
1471351611Scy	S2S(SUCCESS_POWER_SAVE_MODE)
1472351611Scy	S2S(PENDING_ADMITTING_FST_SESSION)
1473351611Scy	S2S(PERFORMING_FST_NOW)
1474351611Scy	S2S(PENDING_GAP_IN_BA_WINDOW)
1475351611Scy	S2S(REJECT_U_PID_SETTING)
1476351611Scy	S2S(REFUSED_EXTERNAL_REASON)
1477351611Scy	S2S(REFUSED_AP_OUT_OF_MEMORY)
1478351611Scy	S2S(REJECTED_EMERGENCY_SERVICE_NOT_SUPPORTED)
1479351611Scy	S2S(QUERY_RESP_OUTSTANDING)
1480351611Scy	S2S(REJECT_DSE_BAND)
1481351611Scy	S2S(TCLAS_PROCESSING_TERMINATED)
1482351611Scy	S2S(TS_SCHEDULE_CONFLICT)
1483351611Scy	S2S(DENIED_WITH_SUGGESTED_BAND_AND_CHANNEL)
1484351611Scy	S2S(MCCAOP_RESERVATION_CONFLICT)
1485351611Scy	S2S(MAF_LIMIT_EXCEEDED)
1486351611Scy	S2S(MCCA_TRACK_LIMIT_EXCEEDED)
1487351611Scy	S2S(DENIED_DUE_TO_SPECTRUM_MANAGEMENT)
1488351611Scy	S2S(ASSOC_DENIED_NO_VHT)
1489351611Scy	S2S(ENABLEMENT_DENIED)
1490351611Scy	S2S(RESTRICTION_FROM_AUTHORIZED_GDB)
1491351611Scy	S2S(AUTHORIZATION_DEENABLED)
1492351611Scy	S2S(FILS_AUTHENTICATION_FAILURE)
1493351611Scy	S2S(UNKNOWN_AUTHENTICATION_SERVER)
1494351611Scy	S2S(UNKNOWN_PASSWORD_IDENTIFIER)
1495351611Scy	}
1496351611Scy	return "UNKNOWN";
1497351611Scy#undef S2S
1498351611Scy}
1499351611Scy
1500351611Scy
1501289549Srpauloint mb_ies_info_by_ies(struct mb_ies_info *info, const u8 *ies_buf,
1502289549Srpaulo		       size_t ies_len)
1503289549Srpaulo{
1504346981Scy	const struct element *elem;
1505346981Scy
1506289549Srpaulo	os_memset(info, 0, sizeof(*info));
1507289549Srpaulo
1508346981Scy	if (!ies_buf)
1509346981Scy		return 0;
1510289549Srpaulo
1511346981Scy	for_each_element_id(elem, WLAN_EID_MULTI_BAND, ies_buf, ies_len) {
1512346981Scy		if (info->nof_ies >= MAX_NOF_MB_IES_SUPPORTED)
1513346981Scy			return 0;
1514289549Srpaulo
1515346981Scy		wpa_printf(MSG_DEBUG, "MB IE of %u bytes found",
1516346981Scy			   elem->datalen + 2);
1517346981Scy		info->ies[info->nof_ies].ie = elem->data;
1518346981Scy		info->ies[info->nof_ies].ie_len = elem->datalen;
1519346981Scy		info->nof_ies++;
1520346981Scy	}
1521289549Srpaulo
1522346981Scy	if (!for_each_element_completed(elem, ies_buf, ies_len)) {
1523346981Scy		wpa_hexdump(MSG_DEBUG, "Truncated IEs", ies_buf, ies_len);
1524346981Scy		return -1;
1525289549Srpaulo	}
1526289549Srpaulo
1527289549Srpaulo	return 0;
1528289549Srpaulo}
1529289549Srpaulo
1530289549Srpaulo
1531289549Srpaulostruct wpabuf * mb_ies_by_info(struct mb_ies_info *info)
1532289549Srpaulo{
1533289549Srpaulo	struct wpabuf *mb_ies = NULL;
1534289549Srpaulo
1535289549Srpaulo	WPA_ASSERT(info != NULL);
1536289549Srpaulo
1537289549Srpaulo	if (info->nof_ies) {
1538289549Srpaulo		u8 i;
1539289549Srpaulo		size_t mb_ies_size = 0;
1540289549Srpaulo
1541289549Srpaulo		for (i = 0; i < info->nof_ies; i++)
1542289549Srpaulo			mb_ies_size += 2 + info->ies[i].ie_len;
1543289549Srpaulo
1544289549Srpaulo		mb_ies = wpabuf_alloc(mb_ies_size);
1545289549Srpaulo		if (mb_ies) {
1546289549Srpaulo			for (i = 0; i < info->nof_ies; i++) {
1547289549Srpaulo				wpabuf_put_u8(mb_ies, WLAN_EID_MULTI_BAND);
1548289549Srpaulo				wpabuf_put_u8(mb_ies, info->ies[i].ie_len);
1549289549Srpaulo				wpabuf_put_data(mb_ies,
1550289549Srpaulo						info->ies[i].ie,
1551289549Srpaulo						info->ies[i].ie_len);
1552289549Srpaulo			}
1553289549Srpaulo		}
1554289549Srpaulo	}
1555289549Srpaulo
1556289549Srpaulo	return mb_ies;
1557289549Srpaulo}
1558337817Scy
1559337817Scy
1560337817Scyconst struct oper_class_map global_op_class[] = {
1561337817Scy	{ HOSTAPD_MODE_IEEE80211G, 81, 1, 13, 1, BW20, P2P_SUPP },
1562337817Scy	{ HOSTAPD_MODE_IEEE80211G, 82, 14, 14, 1, BW20, NO_P2P_SUPP },
1563337817Scy
1564337817Scy	/* Do not enable HT40 on 2.4 GHz for P2P use for now */
1565337817Scy	{ HOSTAPD_MODE_IEEE80211G, 83, 1, 9, 1, BW40PLUS, NO_P2P_SUPP },
1566337817Scy	{ HOSTAPD_MODE_IEEE80211G, 84, 5, 13, 1, BW40MINUS, NO_P2P_SUPP },
1567337817Scy
1568337817Scy	{ HOSTAPD_MODE_IEEE80211A, 115, 36, 48, 4, BW20, P2P_SUPP },
1569337817Scy	{ HOSTAPD_MODE_IEEE80211A, 116, 36, 44, 8, BW40PLUS, P2P_SUPP },
1570337817Scy	{ HOSTAPD_MODE_IEEE80211A, 117, 40, 48, 8, BW40MINUS, P2P_SUPP },
1571337817Scy	{ HOSTAPD_MODE_IEEE80211A, 118, 52, 64, 4, BW20, NO_P2P_SUPP },
1572337817Scy	{ HOSTAPD_MODE_IEEE80211A, 119, 52, 60, 8, BW40PLUS, NO_P2P_SUPP },
1573337817Scy	{ HOSTAPD_MODE_IEEE80211A, 120, 56, 64, 8, BW40MINUS, NO_P2P_SUPP },
1574337817Scy	{ HOSTAPD_MODE_IEEE80211A, 121, 100, 140, 4, BW20, NO_P2P_SUPP },
1575337817Scy	{ HOSTAPD_MODE_IEEE80211A, 122, 100, 132, 8, BW40PLUS, NO_P2P_SUPP },
1576337817Scy	{ HOSTAPD_MODE_IEEE80211A, 123, 104, 136, 8, BW40MINUS, NO_P2P_SUPP },
1577337817Scy	{ HOSTAPD_MODE_IEEE80211A, 124, 149, 161, 4, BW20, P2P_SUPP },
1578337817Scy	{ HOSTAPD_MODE_IEEE80211A, 125, 149, 169, 4, BW20, P2P_SUPP },
1579337817Scy	{ HOSTAPD_MODE_IEEE80211A, 126, 149, 157, 8, BW40PLUS, P2P_SUPP },
1580337817Scy	{ HOSTAPD_MODE_IEEE80211A, 127, 153, 161, 8, BW40MINUS, P2P_SUPP },
1581337817Scy
1582337817Scy	/*
1583337817Scy	 * IEEE P802.11ac/D7.0 Table E-4 actually talks about channel center
1584337817Scy	 * frequency index 42, 58, 106, 122, 138, 155 with channel spacing of
1585337817Scy	 * 80 MHz, but currently use the following definition for simplicity
1586337817Scy	 * (these center frequencies are not actual channels, which makes
1587337817Scy	 * wpas_p2p_allow_channel() fail). wpas_p2p_verify_80mhz() should take
1588337817Scy	 * care of removing invalid channels.
1589337817Scy	 */
1590337817Scy	{ HOSTAPD_MODE_IEEE80211A, 128, 36, 161, 4, BW80, P2P_SUPP },
1591337817Scy	{ HOSTAPD_MODE_IEEE80211A, 129, 50, 114, 16, BW160, P2P_SUPP },
1592337817Scy	{ HOSTAPD_MODE_IEEE80211A, 130, 36, 161, 4, BW80P80, P2P_SUPP },
1593337817Scy	{ HOSTAPD_MODE_IEEE80211AD, 180, 1, 4, 1, BW2160, P2P_SUPP },
1594337817Scy	{ -1, 0, 0, 0, 0, BW20, NO_P2P_SUPP }
1595337817Scy};
1596337817Scy
1597337817Scy
1598337817Scystatic enum phy_type ieee80211_phy_type_by_freq(int freq)
1599337817Scy{
1600337817Scy	enum hostapd_hw_mode hw_mode;
1601337817Scy	u8 channel;
1602337817Scy
1603337817Scy	hw_mode = ieee80211_freq_to_chan(freq, &channel);
1604337817Scy
1605337817Scy	switch (hw_mode) {
1606337817Scy	case HOSTAPD_MODE_IEEE80211A:
1607337817Scy		return PHY_TYPE_OFDM;
1608337817Scy	case HOSTAPD_MODE_IEEE80211B:
1609337817Scy		return PHY_TYPE_HRDSSS;
1610337817Scy	case HOSTAPD_MODE_IEEE80211G:
1611337817Scy		return PHY_TYPE_ERP;
1612337817Scy	case HOSTAPD_MODE_IEEE80211AD:
1613337817Scy		return PHY_TYPE_DMG;
1614337817Scy	default:
1615337817Scy		return PHY_TYPE_UNSPECIFIED;
1616337817Scy	};
1617337817Scy}
1618337817Scy
1619337817Scy
1620337817Scy/* ieee80211_get_phy_type - Derive the phy type by freq and bandwidth */
1621337817Scyenum phy_type ieee80211_get_phy_type(int freq, int ht, int vht)
1622337817Scy{
1623337817Scy	if (vht)
1624337817Scy		return PHY_TYPE_VHT;
1625337817Scy	if (ht)
1626337817Scy		return PHY_TYPE_HT;
1627337817Scy
1628337817Scy	return ieee80211_phy_type_by_freq(freq);
1629337817Scy}
1630337817Scy
1631337817Scy
1632337817Scysize_t global_op_class_size = ARRAY_SIZE(global_op_class);
1633337817Scy
1634337817Scy
1635337817Scy/**
1636337817Scy * get_ie - Fetch a specified information element from IEs buffer
1637337817Scy * @ies: Information elements buffer
1638337817Scy * @len: Information elements buffer length
1639337817Scy * @eid: Information element identifier (WLAN_EID_*)
1640337817Scy * Returns: Pointer to the information element (id field) or %NULL if not found
1641337817Scy *
1642337817Scy * This function returns the first matching information element in the IEs
1643337817Scy * buffer or %NULL in case the element is not found.
1644337817Scy */
1645337817Scyconst u8 * get_ie(const u8 *ies, size_t len, u8 eid)
1646337817Scy{
1647346981Scy	const struct element *elem;
1648337817Scy
1649337817Scy	if (!ies)
1650337817Scy		return NULL;
1651337817Scy
1652346981Scy	for_each_element_id(elem, eid, ies, len)
1653346981Scy		return &elem->id;
1654337817Scy
1655346981Scy	return NULL;
1656346981Scy}
1657337817Scy
1658337817Scy
1659346981Scy/**
1660346981Scy * get_ie_ext - Fetch a specified extended information element from IEs buffer
1661346981Scy * @ies: Information elements buffer
1662346981Scy * @len: Information elements buffer length
1663346981Scy * @ext: Information element extension identifier (WLAN_EID_EXT_*)
1664346981Scy * Returns: Pointer to the information element (id field) or %NULL if not found
1665346981Scy *
1666346981Scy * This function returns the first matching information element in the IEs
1667346981Scy * buffer or %NULL in case the element is not found.
1668346981Scy */
1669346981Scyconst u8 * get_ie_ext(const u8 *ies, size_t len, u8 ext)
1670346981Scy{
1671346981Scy	const struct element *elem;
1672346981Scy
1673346981Scy	if (!ies)
1674346981Scy		return NULL;
1675346981Scy
1676346981Scy	for_each_element_extid(elem, ext, ies, len)
1677346981Scy		return &elem->id;
1678346981Scy
1679346981Scy	return NULL;
1680346981Scy}
1681346981Scy
1682346981Scy
1683346981Scyconst u8 * get_vendor_ie(const u8 *ies, size_t len, u32 vendor_type)
1684346981Scy{
1685346981Scy	const struct element *elem;
1686346981Scy
1687346981Scy	for_each_element_id(elem, WLAN_EID_VENDOR_SPECIFIC, ies, len) {
1688346981Scy		if (elem->datalen >= 4 &&
1689346981Scy		    vendor_type == WPA_GET_BE32(elem->data))
1690346981Scy			return &elem->id;
1691337817Scy	}
1692337817Scy
1693337817Scy	return NULL;
1694337817Scy}
1695337817Scy
1696337817Scy
1697337817Scysize_t mbo_add_ie(u8 *buf, size_t len, const u8 *attr, size_t attr_len)
1698337817Scy{
1699337817Scy	/*
1700337817Scy	 * MBO IE requires 6 bytes without the attributes: EID (1), length (1),
1701337817Scy	 * OUI (3), OUI type (1).
1702337817Scy	 */
1703337817Scy	if (len < 6 + attr_len) {
1704337817Scy		wpa_printf(MSG_DEBUG,
1705337817Scy			   "MBO: Not enough room in buffer for MBO IE: buf len = %zu, attr_len = %zu",
1706337817Scy			   len, attr_len);
1707337817Scy		return 0;
1708337817Scy	}
1709337817Scy
1710337817Scy	*buf++ = WLAN_EID_VENDOR_SPECIFIC;
1711337817Scy	*buf++ = attr_len + 4;
1712337817Scy	WPA_PUT_BE24(buf, OUI_WFA);
1713337817Scy	buf += 3;
1714337817Scy	*buf++ = MBO_OUI_TYPE;
1715337817Scy	os_memcpy(buf, attr, attr_len);
1716337817Scy
1717337817Scy	return 6 + attr_len;
1718337817Scy}
1719346981Scy
1720346981Scy
1721346981Scysize_t add_multi_ap_ie(u8 *buf, size_t len, u8 value)
1722346981Scy{
1723346981Scy	u8 *pos = buf;
1724346981Scy
1725346981Scy	if (len < 9)
1726346981Scy		return 0;
1727346981Scy
1728346981Scy	*pos++ = WLAN_EID_VENDOR_SPECIFIC;
1729346981Scy	*pos++ = 7; /* len */
1730346981Scy	WPA_PUT_BE24(pos, OUI_WFA);
1731346981Scy	pos += 3;
1732346981Scy	*pos++ = MULTI_AP_OUI_TYPE;
1733346981Scy	*pos++ = MULTI_AP_SUB_ELEM_TYPE;
1734346981Scy	*pos++ = 1; /* len */
1735346981Scy	*pos++ = value;
1736346981Scy
1737346981Scy	return pos - buf;
1738346981Scy}
1739346981Scy
1740346981Scy
1741346981Scystatic const struct country_op_class us_op_class[] = {
1742346981Scy	{ 1, 115 },
1743346981Scy	{ 2, 118 },
1744346981Scy	{ 3, 124 },
1745346981Scy	{ 4, 121 },
1746346981Scy	{ 5, 125 },
1747346981Scy	{ 12, 81 },
1748346981Scy	{ 22, 116 },
1749346981Scy	{ 23, 119 },
1750346981Scy	{ 24, 122 },
1751346981Scy	{ 25, 126 },
1752346981Scy	{ 26, 126 },
1753346981Scy	{ 27, 117 },
1754346981Scy	{ 28, 120 },
1755346981Scy	{ 29, 123 },
1756346981Scy	{ 30, 127 },
1757346981Scy	{ 31, 127 },
1758346981Scy	{ 32, 83 },
1759346981Scy	{ 33, 84 },
1760346981Scy	{ 34, 180 },
1761346981Scy};
1762346981Scy
1763346981Scystatic const struct country_op_class eu_op_class[] = {
1764346981Scy	{ 1, 115 },
1765346981Scy	{ 2, 118 },
1766346981Scy	{ 3, 121 },
1767346981Scy	{ 4, 81 },
1768346981Scy	{ 5, 116 },
1769346981Scy	{ 6, 119 },
1770346981Scy	{ 7, 122 },
1771346981Scy	{ 8, 117 },
1772346981Scy	{ 9, 120 },
1773346981Scy	{ 10, 123 },
1774346981Scy	{ 11, 83 },
1775346981Scy	{ 12, 84 },
1776346981Scy	{ 17, 125 },
1777346981Scy	{ 18, 180 },
1778346981Scy};
1779346981Scy
1780346981Scystatic const struct country_op_class jp_op_class[] = {
1781346981Scy	{ 1, 115 },
1782346981Scy	{ 30, 81 },
1783346981Scy	{ 31, 82 },
1784346981Scy	{ 32, 118 },
1785346981Scy	{ 33, 118 },
1786346981Scy	{ 34, 121 },
1787346981Scy	{ 35, 121 },
1788346981Scy	{ 36, 116 },
1789346981Scy	{ 37, 119 },
1790346981Scy	{ 38, 119 },
1791346981Scy	{ 39, 122 },
1792346981Scy	{ 40, 122 },
1793346981Scy	{ 41, 117 },
1794346981Scy	{ 42, 120 },
1795346981Scy	{ 43, 120 },
1796346981Scy	{ 44, 123 },
1797346981Scy	{ 45, 123 },
1798346981Scy	{ 56, 83 },
1799346981Scy	{ 57, 84 },
1800346981Scy	{ 58, 121 },
1801346981Scy	{ 59, 180 },
1802346981Scy};
1803346981Scy
1804346981Scystatic const struct country_op_class cn_op_class[] = {
1805346981Scy	{ 1, 115 },
1806346981Scy	{ 2, 118 },
1807346981Scy	{ 3, 125 },
1808346981Scy	{ 4, 116 },
1809346981Scy	{ 5, 119 },
1810346981Scy	{ 6, 126 },
1811346981Scy	{ 7, 81 },
1812346981Scy	{ 8, 83 },
1813346981Scy	{ 9, 84 },
1814346981Scy};
1815346981Scy
1816346981Scystatic u8
1817346981Scyglobal_op_class_from_country_array(u8 op_class, size_t array_size,
1818346981Scy				   const struct country_op_class *country_array)
1819346981Scy{
1820346981Scy	size_t i;
1821346981Scy
1822346981Scy	for (i = 0; i < array_size; i++) {
1823346981Scy		if (country_array[i].country_op_class == op_class)
1824346981Scy			return country_array[i].global_op_class;
1825346981Scy	}
1826346981Scy
1827346981Scy	return 0;
1828346981Scy}
1829346981Scy
1830346981Scy
1831346981Scyu8 country_to_global_op_class(const char *country, u8 op_class)
1832346981Scy{
1833346981Scy	const struct country_op_class *country_array;
1834346981Scy	size_t size;
1835346981Scy	u8 g_op_class;
1836346981Scy
1837346981Scy	if (country_match(us_op_class_cc, country)) {
1838346981Scy		country_array = us_op_class;
1839346981Scy		size = ARRAY_SIZE(us_op_class);
1840346981Scy	} else if (country_match(eu_op_class_cc, country)) {
1841346981Scy		country_array = eu_op_class;
1842346981Scy		size = ARRAY_SIZE(eu_op_class);
1843346981Scy	} else if (country_match(jp_op_class_cc, country)) {
1844346981Scy		country_array = jp_op_class;
1845346981Scy		size = ARRAY_SIZE(jp_op_class);
1846346981Scy	} else if (country_match(cn_op_class_cc, country)) {
1847346981Scy		country_array = cn_op_class;
1848346981Scy		size = ARRAY_SIZE(cn_op_class);
1849346981Scy	} else {
1850346981Scy		/*
1851346981Scy		 * Countries that do not match any of the above countries use
1852346981Scy		 * global operating classes
1853346981Scy		 */
1854346981Scy		return op_class;
1855346981Scy	}
1856346981Scy
1857346981Scy	g_op_class = global_op_class_from_country_array(op_class, size,
1858346981Scy							country_array);
1859346981Scy
1860346981Scy	/*
1861346981Scy	 * If the given operating class did not match any of the country's
1862346981Scy	 * operating classes, assume that global operating class is used.
1863346981Scy	 */
1864346981Scy	return g_op_class ? g_op_class : op_class;
1865346981Scy}
1866346981Scy
1867346981Scy
1868346981Scyconst struct oper_class_map * get_oper_class(const char *country, u8 op_class)
1869346981Scy{
1870346981Scy	const struct oper_class_map *op;
1871346981Scy
1872346981Scy	if (country)
1873346981Scy		op_class = country_to_global_op_class(country, op_class);
1874346981Scy
1875346981Scy	op = &global_op_class[0];
1876346981Scy	while (op->op_class && op->op_class != op_class)
1877346981Scy		op++;
1878346981Scy
1879346981Scy	if (!op->op_class)
1880346981Scy		return NULL;
1881346981Scy
1882346981Scy	return op;
1883346981Scy}
1884346981Scy
1885346981Scy
1886346981Scyint oper_class_bw_to_int(const struct oper_class_map *map)
1887346981Scy{
1888346981Scy	switch (map->bw) {
1889346981Scy	case BW20:
1890346981Scy		return 20;
1891346981Scy	case BW40PLUS:
1892346981Scy	case BW40MINUS:
1893346981Scy		return 40;
1894346981Scy	case BW80:
1895346981Scy		return 80;
1896346981Scy	case BW80P80:
1897346981Scy	case BW160:
1898346981Scy		return 160;
1899346981Scy	case BW2160:
1900346981Scy		return 2160;
1901346981Scy	default:
1902346981Scy		return 0;
1903346981Scy	}
1904346981Scy}
1905346981Scy
1906346981Scy
1907346981Scyint ieee802_11_parse_candidate_list(const char *pos, u8 *nei_rep,
1908346981Scy				    size_t nei_rep_len)
1909346981Scy{
1910346981Scy	u8 *nei_pos = nei_rep;
1911346981Scy	const char *end;
1912346981Scy
1913346981Scy	/*
1914346981Scy	 * BSS Transition Candidate List Entries - Neighbor Report elements
1915346981Scy	 * neighbor=<BSSID>,<BSSID Information>,<Operating Class>,
1916346981Scy	 * <Channel Number>,<PHY Type>[,<hexdump of Optional Subelements>]
1917346981Scy	 */
1918346981Scy	while (pos) {
1919346981Scy		u8 *nei_start;
1920346981Scy		long int val;
1921346981Scy		char *endptr, *tmp;
1922346981Scy
1923346981Scy		pos = os_strstr(pos, " neighbor=");
1924346981Scy		if (!pos)
1925346981Scy			break;
1926346981Scy		if (nei_pos + 15 > nei_rep + nei_rep_len) {
1927346981Scy			wpa_printf(MSG_DEBUG,
1928346981Scy				   "Not enough room for additional neighbor");
1929346981Scy			return -1;
1930346981Scy		}
1931346981Scy		pos += 10;
1932346981Scy
1933346981Scy		nei_start = nei_pos;
1934346981Scy		*nei_pos++ = WLAN_EID_NEIGHBOR_REPORT;
1935346981Scy		nei_pos++; /* length to be filled in */
1936346981Scy
1937346981Scy		if (hwaddr_aton(pos, nei_pos)) {
1938346981Scy			wpa_printf(MSG_DEBUG, "Invalid BSSID");
1939346981Scy			return -1;
1940346981Scy		}
1941346981Scy		nei_pos += ETH_ALEN;
1942346981Scy		pos += 17;
1943346981Scy		if (*pos != ',') {
1944346981Scy			wpa_printf(MSG_DEBUG, "Missing BSSID Information");
1945346981Scy			return -1;
1946346981Scy		}
1947346981Scy		pos++;
1948346981Scy
1949346981Scy		val = strtol(pos, &endptr, 0);
1950346981Scy		WPA_PUT_LE32(nei_pos, val);
1951346981Scy		nei_pos += 4;
1952346981Scy		if (*endptr != ',') {
1953346981Scy			wpa_printf(MSG_DEBUG, "Missing Operating Class");
1954346981Scy			return -1;
1955346981Scy		}
1956346981Scy		pos = endptr + 1;
1957346981Scy
1958346981Scy		*nei_pos++ = atoi(pos); /* Operating Class */
1959346981Scy		pos = os_strchr(pos, ',');
1960346981Scy		if (pos == NULL) {
1961346981Scy			wpa_printf(MSG_DEBUG, "Missing Channel Number");
1962346981Scy			return -1;
1963346981Scy		}
1964346981Scy		pos++;
1965346981Scy
1966346981Scy		*nei_pos++ = atoi(pos); /* Channel Number */
1967346981Scy		pos = os_strchr(pos, ',');
1968346981Scy		if (pos == NULL) {
1969346981Scy			wpa_printf(MSG_DEBUG, "Missing PHY Type");
1970346981Scy			return -1;
1971346981Scy		}
1972346981Scy		pos++;
1973346981Scy
1974346981Scy		*nei_pos++ = atoi(pos); /* PHY Type */
1975346981Scy		end = os_strchr(pos, ' ');
1976346981Scy		tmp = os_strchr(pos, ',');
1977346981Scy		if (tmp && (!end || tmp < end)) {
1978346981Scy			/* Optional Subelements (hexdump) */
1979346981Scy			size_t len;
1980346981Scy
1981346981Scy			pos = tmp + 1;
1982346981Scy			end = os_strchr(pos, ' ');
1983346981Scy			if (end)
1984346981Scy				len = end - pos;
1985346981Scy			else
1986346981Scy				len = os_strlen(pos);
1987346981Scy			if (nei_pos + len / 2 > nei_rep + nei_rep_len) {
1988346981Scy				wpa_printf(MSG_DEBUG,
1989346981Scy					   "Not enough room for neighbor subelements");
1990346981Scy				return -1;
1991346981Scy			}
1992346981Scy			if (len & 0x01 ||
1993346981Scy			    hexstr2bin(pos, nei_pos, len / 2) < 0) {
1994346981Scy				wpa_printf(MSG_DEBUG,
1995346981Scy					   "Invalid neighbor subelement info");
1996346981Scy				return -1;
1997346981Scy			}
1998346981Scy			nei_pos += len / 2;
1999346981Scy			pos = end;
2000346981Scy		}
2001346981Scy
2002346981Scy		nei_start[1] = nei_pos - nei_start - 2;
2003346981Scy	}
2004346981Scy
2005346981Scy	return nei_pos - nei_rep;
2006346981Scy}
2007346981Scy
2008346981Scy
2009346981Scyint ieee802_11_ext_capab(const u8 *ie, unsigned int capab)
2010346981Scy{
2011346981Scy	if (!ie || ie[1] <= capab / 8)
2012346981Scy		return 0;
2013346981Scy	return !!(ie[2 + capab / 8] & BIT(capab % 8));
2014346981Scy}
2015