1189251Ssam/*
2189251Ssam * IEEE 802.11 Common routines
3289549Srpaulo * Copyright (c) 2002-2015, 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"
14289549Srpaulo#include "qca-vendor.h"
15189251Ssam#include "ieee802_11_defs.h"
16189251Ssam#include "ieee802_11_common.h"
17189251Ssam
18189251Ssam
19214734Srpaulostatic int ieee802_11_parse_vendor_specific(const u8 *pos, size_t elen,
20189251Ssam					    struct ieee802_11_elems *elems,
21189251Ssam					    int show_errors)
22189251Ssam{
23189251Ssam	unsigned int oui;
24189251Ssam
25189251Ssam	/* first 3 bytes in vendor specific information element are the IEEE
26189251Ssam	 * OUI of the vendor. The following byte is used a vendor specific
27189251Ssam	 * sub-type. */
28189251Ssam	if (elen < 4) {
29189251Ssam		if (show_errors) {
30189251Ssam			wpa_printf(MSG_MSGDUMP, "short vendor specific "
31189251Ssam				   "information element ignored (len=%lu)",
32189251Ssam				   (unsigned long) elen);
33189251Ssam		}
34189251Ssam		return -1;
35189251Ssam	}
36189251Ssam
37189251Ssam	oui = WPA_GET_BE24(pos);
38189251Ssam	switch (oui) {
39189251Ssam	case OUI_MICROSOFT:
40189251Ssam		/* Microsoft/Wi-Fi information elements are further typed and
41189251Ssam		 * subtyped */
42189251Ssam		switch (pos[3]) {
43189251Ssam		case 1:
44189251Ssam			/* Microsoft OUI (00:50:F2) with OUI Type 1:
45189251Ssam			 * real WPA information element */
46189251Ssam			elems->wpa_ie = pos;
47189251Ssam			elems->wpa_ie_len = elen;
48189251Ssam			break;
49209158Srpaulo		case WMM_OUI_TYPE:
50209158Srpaulo			/* WMM information element */
51189251Ssam			if (elen < 5) {
52209158Srpaulo				wpa_printf(MSG_MSGDUMP, "short WMM "
53189251Ssam					   "information element ignored "
54189251Ssam					   "(len=%lu)",
55189251Ssam					   (unsigned long) elen);
56189251Ssam				return -1;
57189251Ssam			}
58189251Ssam			switch (pos[4]) {
59209158Srpaulo			case WMM_OUI_SUBTYPE_INFORMATION_ELEMENT:
60209158Srpaulo			case WMM_OUI_SUBTYPE_PARAMETER_ELEMENT:
61209158Srpaulo				/*
62209158Srpaulo				 * Share same pointer since only one of these
63209158Srpaulo				 * is used and they start with same data.
64209158Srpaulo				 * Length field can be used to distinguish the
65209158Srpaulo				 * IEs.
66209158Srpaulo				 */
67209158Srpaulo				elems->wmm = pos;
68209158Srpaulo				elems->wmm_len = elen;
69189251Ssam				break;
70209158Srpaulo			case WMM_OUI_SUBTYPE_TSPEC_ELEMENT:
71209158Srpaulo				elems->wmm_tspec = pos;
72209158Srpaulo				elems->wmm_tspec_len = elen;
73189251Ssam				break;
74189251Ssam			default:
75252726Srpaulo				wpa_printf(MSG_EXCESSIVE, "unknown WMM "
76189251Ssam					   "information element ignored "
77189251Ssam					   "(subtype=%d len=%lu)",
78189251Ssam					   pos[4], (unsigned long) elen);
79189251Ssam				return -1;
80189251Ssam			}
81189251Ssam			break;
82189251Ssam		case 4:
83189251Ssam			/* Wi-Fi Protected Setup (WPS) IE */
84189251Ssam			elems->wps_ie = pos;
85189251Ssam			elems->wps_ie_len = elen;
86189251Ssam			break;
87189251Ssam		default:
88252726Srpaulo			wpa_printf(MSG_EXCESSIVE, "Unknown Microsoft "
89189251Ssam				   "information element ignored "
90252726Srpaulo				   "(type=%d len=%lu)",
91252726Srpaulo				   pos[3], (unsigned long) elen);
92252726Srpaulo			return -1;
93252726Srpaulo		}
94252726Srpaulo		break;
95252726Srpaulo
96252726Srpaulo	case OUI_WFA:
97252726Srpaulo		switch (pos[3]) {
98252726Srpaulo		case P2P_OUI_TYPE:
99252726Srpaulo			/* Wi-Fi Alliance - P2P IE */
100252726Srpaulo			elems->p2p = pos;
101252726Srpaulo			elems->p2p_len = elen;
102252726Srpaulo			break;
103252726Srpaulo		case WFD_OUI_TYPE:
104252726Srpaulo			/* Wi-Fi Alliance - WFD IE */
105252726Srpaulo			elems->wfd = pos;
106252726Srpaulo			elems->wfd_len = elen;
107252726Srpaulo			break;
108252726Srpaulo		case HS20_INDICATION_OUI_TYPE:
109252726Srpaulo			/* Hotspot 2.0 */
110252726Srpaulo			elems->hs20 = pos;
111252726Srpaulo			elems->hs20_len = elen;
112252726Srpaulo			break;
113281806Srpaulo		case HS20_OSEN_OUI_TYPE:
114281806Srpaulo			/* Hotspot 2.0 OSEN */
115281806Srpaulo			elems->osen = pos;
116281806Srpaulo			elems->osen_len = elen;
117281806Srpaulo			break;
118252726Srpaulo		default:
119252726Srpaulo			wpa_printf(MSG_MSGDUMP, "Unknown WFA "
120252726Srpaulo				   "information element ignored "
121281806Srpaulo				   "(type=%d len=%lu)",
122189251Ssam				   pos[3], (unsigned long) elen);
123189251Ssam			return -1;
124189251Ssam		}
125189251Ssam		break;
126189251Ssam
127189251Ssam	case OUI_BROADCOM:
128189251Ssam		switch (pos[3]) {
129189251Ssam		case VENDOR_HT_CAPAB_OUI_TYPE:
130189251Ssam			elems->vendor_ht_cap = pos;
131189251Ssam			elems->vendor_ht_cap_len = elen;
132189251Ssam			break;
133281806Srpaulo		case VENDOR_VHT_TYPE:
134281806Srpaulo			if (elen > 4 &&
135281806Srpaulo			    (pos[4] == VENDOR_VHT_SUBTYPE ||
136281806Srpaulo			     pos[4] == VENDOR_VHT_SUBTYPE2)) {
137281806Srpaulo				elems->vendor_vht = pos;
138281806Srpaulo				elems->vendor_vht_len = elen;
139281806Srpaulo			} else
140281806Srpaulo				return -1;
141281806Srpaulo			break;
142189251Ssam		default:
143252726Srpaulo			wpa_printf(MSG_EXCESSIVE, "Unknown Broadcom "
144189251Ssam				   "information element ignored "
145252726Srpaulo				   "(type=%d len=%lu)",
146189251Ssam				   pos[3], (unsigned long) elen);
147189251Ssam			return -1;
148189251Ssam		}
149189251Ssam		break;
150189251Ssam
151289549Srpaulo	case OUI_QCA:
152289549Srpaulo		switch (pos[3]) {
153289549Srpaulo		case QCA_VENDOR_ELEM_P2P_PREF_CHAN_LIST:
154289549Srpaulo			elems->pref_freq_list = pos;
155289549Srpaulo			elems->pref_freq_list_len = elen;
156289549Srpaulo			break;
157289549Srpaulo		default:
158289549Srpaulo			wpa_printf(MSG_EXCESSIVE,
159289549Srpaulo				   "Unknown QCA information element ignored (type=%d len=%lu)",
160289549Srpaulo				   pos[3], (unsigned long) elen);
161289549Srpaulo			return -1;
162289549Srpaulo		}
163289549Srpaulo		break;
164289549Srpaulo
165189251Ssam	default:
166252726Srpaulo		wpa_printf(MSG_EXCESSIVE, "unknown vendor specific "
167252726Srpaulo			   "information element ignored (vendor OUI "
168252726Srpaulo			   "%02x:%02x:%02x len=%lu)",
169189251Ssam			   pos[0], pos[1], pos[2], (unsigned long) elen);
170189251Ssam		return -1;
171189251Ssam	}
172189251Ssam
173189251Ssam	return 0;
174189251Ssam}
175189251Ssam
176189251Ssam
177189251Ssam/**
178189251Ssam * ieee802_11_parse_elems - Parse information elements in management frames
179189251Ssam * @start: Pointer to the start of IEs
180189251Ssam * @len: Length of IE buffer in octets
181189251Ssam * @elems: Data structure for parsed elements
182189251Ssam * @show_errors: Whether to show parsing errors in debug log
183189251Ssam * Returns: Parsing result
184189251Ssam */
185214734SrpauloParseRes ieee802_11_parse_elems(const u8 *start, size_t len,
186189251Ssam				struct ieee802_11_elems *elems,
187189251Ssam				int show_errors)
188189251Ssam{
189189251Ssam	size_t left = len;
190214734Srpaulo	const u8 *pos = start;
191189251Ssam	int unknown = 0;
192189251Ssam
193189251Ssam	os_memset(elems, 0, sizeof(*elems));
194189251Ssam
195189251Ssam	while (left >= 2) {
196189251Ssam		u8 id, elen;
197189251Ssam
198189251Ssam		id = *pos++;
199189251Ssam		elen = *pos++;
200189251Ssam		left -= 2;
201189251Ssam
202189251Ssam		if (elen > left) {
203189251Ssam			if (show_errors) {
204189251Ssam				wpa_printf(MSG_DEBUG, "IEEE 802.11 element "
205189251Ssam					   "parse failed (id=%d elen=%d "
206189251Ssam					   "left=%lu)",
207189251Ssam					   id, elen, (unsigned long) left);
208189251Ssam				wpa_hexdump(MSG_MSGDUMP, "IEs", start, len);
209189251Ssam			}
210189251Ssam			return ParseFailed;
211189251Ssam		}
212189251Ssam
213189251Ssam		switch (id) {
214189251Ssam		case WLAN_EID_SSID:
215289549Srpaulo			if (elen > SSID_MAX_LEN) {
216289549Srpaulo				wpa_printf(MSG_DEBUG,
217289549Srpaulo					   "Ignored too long SSID element (elen=%u)",
218289549Srpaulo					   elen);
219289549Srpaulo				break;
220289549Srpaulo			}
221189251Ssam			elems->ssid = pos;
222189251Ssam			elems->ssid_len = elen;
223189251Ssam			break;
224189251Ssam		case WLAN_EID_SUPP_RATES:
225189251Ssam			elems->supp_rates = pos;
226189251Ssam			elems->supp_rates_len = elen;
227189251Ssam			break;
228189251Ssam		case WLAN_EID_DS_PARAMS:
229289549Srpaulo			if (elen < 1)
230289549Srpaulo				break;
231189251Ssam			elems->ds_params = pos;
232189251Ssam			break;
233189251Ssam		case WLAN_EID_CF_PARAMS:
234189251Ssam		case WLAN_EID_TIM:
235189251Ssam			break;
236189251Ssam		case WLAN_EID_CHALLENGE:
237189251Ssam			elems->challenge = pos;
238189251Ssam			elems->challenge_len = elen;
239189251Ssam			break;
240189251Ssam		case WLAN_EID_ERP_INFO:
241289549Srpaulo			if (elen < 1)
242289549Srpaulo				break;
243189251Ssam			elems->erp_info = pos;
244189251Ssam			break;
245189251Ssam		case WLAN_EID_EXT_SUPP_RATES:
246189251Ssam			elems->ext_supp_rates = pos;
247189251Ssam			elems->ext_supp_rates_len = elen;
248189251Ssam			break;
249189251Ssam		case WLAN_EID_VENDOR_SPECIFIC:
250189251Ssam			if (ieee802_11_parse_vendor_specific(pos, elen,
251189251Ssam							     elems,
252189251Ssam							     show_errors))
253189251Ssam				unknown++;
254189251Ssam			break;
255189251Ssam		case WLAN_EID_RSN:
256189251Ssam			elems->rsn_ie = pos;
257189251Ssam			elems->rsn_ie_len = elen;
258189251Ssam			break;
259189251Ssam		case WLAN_EID_PWR_CAPABILITY:
260189251Ssam			break;
261189251Ssam		case WLAN_EID_SUPPORTED_CHANNELS:
262189251Ssam			elems->supp_channels = pos;
263189251Ssam			elems->supp_channels_len = elen;
264189251Ssam			break;
265189251Ssam		case WLAN_EID_MOBILITY_DOMAIN:
266289549Srpaulo			if (elen < sizeof(struct rsn_mdie))
267289549Srpaulo				break;
268189251Ssam			elems->mdie = pos;
269189251Ssam			elems->mdie_len = elen;
270189251Ssam			break;
271189251Ssam		case WLAN_EID_FAST_BSS_TRANSITION:
272289549Srpaulo			if (elen < sizeof(struct rsn_ftie))
273289549Srpaulo				break;
274189251Ssam			elems->ftie = pos;
275189251Ssam			elems->ftie_len = elen;
276189251Ssam			break;
277189251Ssam		case WLAN_EID_TIMEOUT_INTERVAL:
278289549Srpaulo			if (elen != 5)
279289549Srpaulo				break;
280189251Ssam			elems->timeout_int = pos;
281189251Ssam			break;
282189251Ssam		case WLAN_EID_HT_CAP:
283289549Srpaulo			if (elen < sizeof(struct ieee80211_ht_capabilities))
284289549Srpaulo				break;
285189251Ssam			elems->ht_capabilities = pos;
286189251Ssam			break;
287189251Ssam		case WLAN_EID_HT_OPERATION:
288289549Srpaulo			if (elen < sizeof(struct ieee80211_ht_operation))
289289549Srpaulo				break;
290189251Ssam			elems->ht_operation = pos;
291189251Ssam			break;
292281806Srpaulo		case WLAN_EID_MESH_CONFIG:
293281806Srpaulo			elems->mesh_config = pos;
294281806Srpaulo			elems->mesh_config_len = elen;
295281806Srpaulo			break;
296281806Srpaulo		case WLAN_EID_MESH_ID:
297281806Srpaulo			elems->mesh_id = pos;
298281806Srpaulo			elems->mesh_id_len = elen;
299281806Srpaulo			break;
300281806Srpaulo		case WLAN_EID_PEER_MGMT:
301281806Srpaulo			elems->peer_mgmt = pos;
302281806Srpaulo			elems->peer_mgmt_len = elen;
303281806Srpaulo			break;
304252726Srpaulo		case WLAN_EID_VHT_CAP:
305289549Srpaulo			if (elen < sizeof(struct ieee80211_vht_capabilities))
306289549Srpaulo				break;
307252726Srpaulo			elems->vht_capabilities = pos;
308252726Srpaulo			break;
309252726Srpaulo		case WLAN_EID_VHT_OPERATION:
310289549Srpaulo			if (elen < sizeof(struct ieee80211_vht_operation))
311289549Srpaulo				break;
312252726Srpaulo			elems->vht_operation = pos;
313252726Srpaulo			break;
314281806Srpaulo		case WLAN_EID_VHT_OPERATING_MODE_NOTIFICATION:
315281806Srpaulo			if (elen != 1)
316281806Srpaulo				break;
317281806Srpaulo			elems->vht_opmode_notif = pos;
318281806Srpaulo			break;
319252726Srpaulo		case WLAN_EID_LINK_ID:
320252726Srpaulo			if (elen < 18)
321252726Srpaulo				break;
322252726Srpaulo			elems->link_id = pos;
323252726Srpaulo			break;
324252726Srpaulo		case WLAN_EID_INTERWORKING:
325252726Srpaulo			elems->interworking = pos;
326252726Srpaulo			elems->interworking_len = elen;
327252726Srpaulo			break;
328281806Srpaulo		case WLAN_EID_QOS_MAP_SET:
329281806Srpaulo			if (elen < 16)
330281806Srpaulo				break;
331281806Srpaulo			elems->qos_map_set = pos;
332281806Srpaulo			elems->qos_map_set_len = elen;
333281806Srpaulo			break;
334252726Srpaulo		case WLAN_EID_EXT_CAPAB:
335252726Srpaulo			elems->ext_capab = pos;
336252726Srpaulo			elems->ext_capab_len = elen;
337252726Srpaulo			break;
338252726Srpaulo		case WLAN_EID_BSS_MAX_IDLE_PERIOD:
339252726Srpaulo			if (elen < 3)
340252726Srpaulo				break;
341252726Srpaulo			elems->bss_max_idle_period = pos;
342252726Srpaulo			break;
343252726Srpaulo		case WLAN_EID_SSID_LIST:
344252726Srpaulo			elems->ssid_list = pos;
345252726Srpaulo			elems->ssid_list_len = elen;
346252726Srpaulo			break;
347281806Srpaulo		case WLAN_EID_AMPE:
348281806Srpaulo			elems->ampe = pos;
349281806Srpaulo			elems->ampe_len = elen;
350281806Srpaulo			break;
351281806Srpaulo		case WLAN_EID_MIC:
352281806Srpaulo			elems->mic = pos;
353281806Srpaulo			elems->mic_len = elen;
354281806Srpaulo			/* after mic everything is encrypted, so stop. */
355281806Srpaulo			left = elen;
356281806Srpaulo			break;
357289549Srpaulo		case WLAN_EID_MULTI_BAND:
358289549Srpaulo			if (elems->mb_ies.nof_ies >= MAX_NOF_MB_IES_SUPPORTED) {
359289549Srpaulo				wpa_printf(MSG_MSGDUMP,
360289549Srpaulo					   "IEEE 802.11 element parse ignored MB IE (id=%d elen=%d)",
361289549Srpaulo					   id, elen);
362289549Srpaulo				break;
363289549Srpaulo			}
364289549Srpaulo
365289549Srpaulo			elems->mb_ies.ies[elems->mb_ies.nof_ies].ie = pos;
366289549Srpaulo			elems->mb_ies.ies[elems->mb_ies.nof_ies].ie_len = elen;
367289549Srpaulo			elems->mb_ies.nof_ies++;
368289549Srpaulo			break;
369189251Ssam		default:
370189251Ssam			unknown++;
371189251Ssam			if (!show_errors)
372189251Ssam				break;
373189251Ssam			wpa_printf(MSG_MSGDUMP, "IEEE 802.11 element parse "
374189251Ssam				   "ignored unknown element (id=%d elen=%d)",
375189251Ssam				   id, elen);
376189251Ssam			break;
377189251Ssam		}
378189251Ssam
379189251Ssam		left -= elen;
380189251Ssam		pos += elen;
381189251Ssam	}
382189251Ssam
383189251Ssam	if (left)
384189251Ssam		return ParseFailed;
385189251Ssam
386189251Ssam	return unknown ? ParseUnknown : ParseOK;
387189251Ssam}
388214734Srpaulo
389214734Srpaulo
390214734Srpauloint ieee802_11_ie_count(const u8 *ies, size_t ies_len)
391214734Srpaulo{
392214734Srpaulo	int count = 0;
393214734Srpaulo	const u8 *pos, *end;
394214734Srpaulo
395214734Srpaulo	if (ies == NULL)
396214734Srpaulo		return 0;
397214734Srpaulo
398214734Srpaulo	pos = ies;
399214734Srpaulo	end = ies + ies_len;
400214734Srpaulo
401214734Srpaulo	while (pos + 2 <= end) {
402214734Srpaulo		if (pos + 2 + pos[1] > end)
403214734Srpaulo			break;
404214734Srpaulo		count++;
405214734Srpaulo		pos += 2 + pos[1];
406214734Srpaulo	}
407214734Srpaulo
408214734Srpaulo	return count;
409214734Srpaulo}
410214734Srpaulo
411214734Srpaulo
412214734Srpaulostruct wpabuf * ieee802_11_vendor_ie_concat(const u8 *ies, size_t ies_len,
413214734Srpaulo					    u32 oui_type)
414214734Srpaulo{
415214734Srpaulo	struct wpabuf *buf;
416214734Srpaulo	const u8 *end, *pos, *ie;
417214734Srpaulo
418214734Srpaulo	pos = ies;
419214734Srpaulo	end = ies + ies_len;
420214734Srpaulo	ie = NULL;
421214734Srpaulo
422214734Srpaulo	while (pos + 1 < end) {
423214734Srpaulo		if (pos + 2 + pos[1] > end)
424214734Srpaulo			return NULL;
425214734Srpaulo		if (pos[0] == WLAN_EID_VENDOR_SPECIFIC && pos[1] >= 4 &&
426214734Srpaulo		    WPA_GET_BE32(&pos[2]) == oui_type) {
427214734Srpaulo			ie = pos;
428214734Srpaulo			break;
429214734Srpaulo		}
430214734Srpaulo		pos += 2 + pos[1];
431214734Srpaulo	}
432214734Srpaulo
433214734Srpaulo	if (ie == NULL)
434214734Srpaulo		return NULL; /* No specified vendor IE found */
435214734Srpaulo
436214734Srpaulo	buf = wpabuf_alloc(ies_len);
437214734Srpaulo	if (buf == NULL)
438214734Srpaulo		return NULL;
439214734Srpaulo
440214734Srpaulo	/*
441214734Srpaulo	 * There may be multiple vendor IEs in the message, so need to
442214734Srpaulo	 * concatenate their data fields.
443214734Srpaulo	 */
444214734Srpaulo	while (pos + 1 < end) {
445214734Srpaulo		if (pos + 2 + pos[1] > end)
446214734Srpaulo			break;
447214734Srpaulo		if (pos[0] == WLAN_EID_VENDOR_SPECIFIC && pos[1] >= 4 &&
448214734Srpaulo		    WPA_GET_BE32(&pos[2]) == oui_type)
449214734Srpaulo			wpabuf_put_data(buf, pos + 6, pos[1] - 4);
450214734Srpaulo		pos += 2 + pos[1];
451214734Srpaulo	}
452214734Srpaulo
453214734Srpaulo	return buf;
454214734Srpaulo}
455252726Srpaulo
456252726Srpaulo
457252726Srpauloconst u8 * get_hdr_bssid(const struct ieee80211_hdr *hdr, size_t len)
458252726Srpaulo{
459252726Srpaulo	u16 fc, type, stype;
460252726Srpaulo
461252726Srpaulo	/*
462252726Srpaulo	 * PS-Poll frames are 16 bytes. All other frames are
463252726Srpaulo	 * 24 bytes or longer.
464252726Srpaulo	 */
465252726Srpaulo	if (len < 16)
466252726Srpaulo		return NULL;
467252726Srpaulo
468252726Srpaulo	fc = le_to_host16(hdr->frame_control);
469252726Srpaulo	type = WLAN_FC_GET_TYPE(fc);
470252726Srpaulo	stype = WLAN_FC_GET_STYPE(fc);
471252726Srpaulo
472252726Srpaulo	switch (type) {
473252726Srpaulo	case WLAN_FC_TYPE_DATA:
474252726Srpaulo		if (len < 24)
475252726Srpaulo			return NULL;
476252726Srpaulo		switch (fc & (WLAN_FC_FROMDS | WLAN_FC_TODS)) {
477252726Srpaulo		case WLAN_FC_FROMDS | WLAN_FC_TODS:
478252726Srpaulo		case WLAN_FC_TODS:
479252726Srpaulo			return hdr->addr1;
480252726Srpaulo		case WLAN_FC_FROMDS:
481252726Srpaulo			return hdr->addr2;
482252726Srpaulo		default:
483252726Srpaulo			return NULL;
484252726Srpaulo		}
485252726Srpaulo	case WLAN_FC_TYPE_CTRL:
486252726Srpaulo		if (stype != WLAN_FC_STYPE_PSPOLL)
487252726Srpaulo			return NULL;
488252726Srpaulo		return hdr->addr1;
489252726Srpaulo	case WLAN_FC_TYPE_MGMT:
490252726Srpaulo		return hdr->addr3;
491252726Srpaulo	default:
492252726Srpaulo		return NULL;
493252726Srpaulo	}
494252726Srpaulo}
495252726Srpaulo
496252726Srpaulo
497252726Srpauloint hostapd_config_wmm_ac(struct hostapd_wmm_ac_params wmm_ac_params[],
498252726Srpaulo			  const char *name, const char *val)
499252726Srpaulo{
500252726Srpaulo	int num, v;
501252726Srpaulo	const char *pos;
502252726Srpaulo	struct hostapd_wmm_ac_params *ac;
503252726Srpaulo
504252726Srpaulo	/* skip 'wme_ac_' or 'wmm_ac_' prefix */
505252726Srpaulo	pos = name + 7;
506252726Srpaulo	if (os_strncmp(pos, "be_", 3) == 0) {
507252726Srpaulo		num = 0;
508252726Srpaulo		pos += 3;
509252726Srpaulo	} else if (os_strncmp(pos, "bk_", 3) == 0) {
510252726Srpaulo		num = 1;
511252726Srpaulo		pos += 3;
512252726Srpaulo	} else if (os_strncmp(pos, "vi_", 3) == 0) {
513252726Srpaulo		num = 2;
514252726Srpaulo		pos += 3;
515252726Srpaulo	} else if (os_strncmp(pos, "vo_", 3) == 0) {
516252726Srpaulo		num = 3;
517252726Srpaulo		pos += 3;
518252726Srpaulo	} else {
519252726Srpaulo		wpa_printf(MSG_ERROR, "Unknown WMM name '%s'", pos);
520252726Srpaulo		return -1;
521252726Srpaulo	}
522252726Srpaulo
523252726Srpaulo	ac = &wmm_ac_params[num];
524252726Srpaulo
525252726Srpaulo	if (os_strcmp(pos, "aifs") == 0) {
526252726Srpaulo		v = atoi(val);
527252726Srpaulo		if (v < 1 || v > 255) {
528252726Srpaulo			wpa_printf(MSG_ERROR, "Invalid AIFS value %d", v);
529252726Srpaulo			return -1;
530252726Srpaulo		}
531252726Srpaulo		ac->aifs = v;
532252726Srpaulo	} else if (os_strcmp(pos, "cwmin") == 0) {
533252726Srpaulo		v = atoi(val);
534289549Srpaulo		if (v < 0 || v > 15) {
535252726Srpaulo			wpa_printf(MSG_ERROR, "Invalid cwMin value %d", v);
536252726Srpaulo			return -1;
537252726Srpaulo		}
538252726Srpaulo		ac->cwmin = v;
539252726Srpaulo	} else if (os_strcmp(pos, "cwmax") == 0) {
540252726Srpaulo		v = atoi(val);
541289549Srpaulo		if (v < 0 || v > 15) {
542252726Srpaulo			wpa_printf(MSG_ERROR, "Invalid cwMax value %d", v);
543252726Srpaulo			return -1;
544252726Srpaulo		}
545252726Srpaulo		ac->cwmax = v;
546252726Srpaulo	} else if (os_strcmp(pos, "txop_limit") == 0) {
547252726Srpaulo		v = atoi(val);
548252726Srpaulo		if (v < 0 || v > 0xffff) {
549252726Srpaulo			wpa_printf(MSG_ERROR, "Invalid txop value %d", v);
550252726Srpaulo			return -1;
551252726Srpaulo		}
552252726Srpaulo		ac->txop_limit = v;
553252726Srpaulo	} else if (os_strcmp(pos, "acm") == 0) {
554252726Srpaulo		v = atoi(val);
555252726Srpaulo		if (v < 0 || v > 1) {
556252726Srpaulo			wpa_printf(MSG_ERROR, "Invalid acm value %d", v);
557252726Srpaulo			return -1;
558252726Srpaulo		}
559252726Srpaulo		ac->admission_control_mandatory = v;
560252726Srpaulo	} else {
561252726Srpaulo		wpa_printf(MSG_ERROR, "Unknown wmm_ac_ field '%s'", pos);
562252726Srpaulo		return -1;
563252726Srpaulo	}
564252726Srpaulo
565252726Srpaulo	return 0;
566252726Srpaulo}
567281806Srpaulo
568281806Srpaulo
569281806Srpauloenum hostapd_hw_mode ieee80211_freq_to_chan(int freq, u8 *channel)
570281806Srpaulo{
571289549Srpaulo	u8 op_class;
572281806Srpaulo
573289549Srpaulo	return ieee80211_freq_to_channel_ext(freq, 0, 0, &op_class, channel);
574289549Srpaulo}
575289549Srpaulo
576289549Srpaulo
577289549Srpaulo/**
578289549Srpaulo * ieee80211_freq_to_channel_ext - Convert frequency into channel info
579289549Srpaulo * for HT40 and VHT. DFS channels are not covered.
580289549Srpaulo * @freq: Frequency (MHz) to convert
581289549Srpaulo * @sec_channel: 0 = non-HT40, 1 = sec. channel above, -1 = sec. channel below
582289549Srpaulo * @vht: 0 - non-VHT, 1 - 80 MHz
583289549Srpaulo * @op_class: Buffer for returning operating class
584289549Srpaulo * @channel: Buffer for returning channel number
585289549Srpaulo * Returns: hw_mode on success, NUM_HOSTAPD_MODES on failure
586289549Srpaulo */
587289549Srpauloenum hostapd_hw_mode ieee80211_freq_to_channel_ext(unsigned int freq,
588289549Srpaulo						   int sec_channel, int vht,
589289549Srpaulo						   u8 *op_class, u8 *channel)
590289549Srpaulo{
591289549Srpaulo	/* TODO: more operating classes */
592289549Srpaulo
593289549Srpaulo	if (sec_channel > 1 || sec_channel < -1)
594289549Srpaulo		return NUM_HOSTAPD_MODES;
595289549Srpaulo
596281806Srpaulo	if (freq >= 2412 && freq <= 2472) {
597289549Srpaulo		if ((freq - 2407) % 5)
598289549Srpaulo			return NUM_HOSTAPD_MODES;
599289549Srpaulo
600289549Srpaulo		if (vht)
601289549Srpaulo			return NUM_HOSTAPD_MODES;
602289549Srpaulo
603289549Srpaulo		/* 2.407 GHz, channels 1..13 */
604289549Srpaulo		if (sec_channel == 1)
605289549Srpaulo			*op_class = 83;
606289549Srpaulo		else if (sec_channel == -1)
607289549Srpaulo			*op_class = 84;
608289549Srpaulo		else
609289549Srpaulo			*op_class = 81;
610289549Srpaulo
611281806Srpaulo		*channel = (freq - 2407) / 5;
612289549Srpaulo
613289549Srpaulo		return HOSTAPD_MODE_IEEE80211G;
614289549Srpaulo	}
615289549Srpaulo
616289549Srpaulo	if (freq == 2484) {
617289549Srpaulo		if (sec_channel || vht)
618289549Srpaulo			return NUM_HOSTAPD_MODES;
619289549Srpaulo
620289549Srpaulo		*op_class = 82; /* channel 14 */
621281806Srpaulo		*channel = 14;
622289549Srpaulo
623289549Srpaulo		return HOSTAPD_MODE_IEEE80211B;
624289549Srpaulo	}
625289549Srpaulo
626289549Srpaulo	if (freq >= 4900 && freq < 5000) {
627289549Srpaulo		if ((freq - 4000) % 5)
628289549Srpaulo			return NUM_HOSTAPD_MODES;
629281806Srpaulo		*channel = (freq - 4000) / 5;
630289549Srpaulo		*op_class = 0; /* TODO */
631289549Srpaulo		return HOSTAPD_MODE_IEEE80211A;
632289549Srpaulo	}
633289549Srpaulo
634289549Srpaulo	/* 5 GHz, channels 36..48 */
635289549Srpaulo	if (freq >= 5180 && freq <= 5240) {
636289549Srpaulo		if ((freq - 5000) % 5)
637289549Srpaulo			return NUM_HOSTAPD_MODES;
638289549Srpaulo
639289549Srpaulo		if (sec_channel == 1)
640289549Srpaulo			*op_class = 116;
641289549Srpaulo		else if (sec_channel == -1)
642289549Srpaulo			*op_class = 117;
643289549Srpaulo		else if (vht)
644289549Srpaulo			*op_class = 128;
645289549Srpaulo		else
646289549Srpaulo			*op_class = 115;
647289549Srpaulo
648281806Srpaulo		*channel = (freq - 5000) / 5;
649289549Srpaulo
650289549Srpaulo		return HOSTAPD_MODE_IEEE80211A;
651289549Srpaulo	}
652289549Srpaulo
653289549Srpaulo	/* 5 GHz, channels 149..161 */
654289549Srpaulo	if (freq >= 5745 && freq <= 5805) {
655289549Srpaulo		if ((freq - 5000) % 5)
656289549Srpaulo			return NUM_HOSTAPD_MODES;
657289549Srpaulo
658289549Srpaulo		if (sec_channel == 1)
659289549Srpaulo			*op_class = 126;
660289549Srpaulo		else if (sec_channel == -1)
661289549Srpaulo			*op_class = 127;
662289549Srpaulo		else if (vht)
663289549Srpaulo			*op_class = 128;
664289549Srpaulo		else
665289549Srpaulo			*op_class = 124;
666289549Srpaulo
667289549Srpaulo		*channel = (freq - 5000) / 5;
668289549Srpaulo
669289549Srpaulo		return HOSTAPD_MODE_IEEE80211A;
670289549Srpaulo	}
671289549Srpaulo
672289549Srpaulo	/* 5 GHz, channels 149..169 */
673289549Srpaulo	if (freq >= 5745 && freq <= 5845) {
674289549Srpaulo		if ((freq - 5000) % 5)
675289549Srpaulo			return NUM_HOSTAPD_MODES;
676289549Srpaulo
677289549Srpaulo		*op_class = 125;
678289549Srpaulo
679289549Srpaulo		*channel = (freq - 5000) / 5;
680289549Srpaulo
681289549Srpaulo		return HOSTAPD_MODE_IEEE80211A;
682289549Srpaulo	}
683289549Srpaulo
684289549Srpaulo	if (freq >= 5000 && freq < 5900) {
685289549Srpaulo		if ((freq - 5000) % 5)
686289549Srpaulo			return NUM_HOSTAPD_MODES;
687289549Srpaulo		*channel = (freq - 5000) / 5;
688289549Srpaulo		*op_class = 0; /* TODO */
689289549Srpaulo		return HOSTAPD_MODE_IEEE80211A;
690289549Srpaulo	}
691289549Srpaulo
692289549Srpaulo	/* 56.16 GHz, channel 1..4 */
693289549Srpaulo	if (freq >= 56160 + 2160 * 1 && freq <= 56160 + 2160 * 4) {
694289549Srpaulo		if (sec_channel || vht)
695289549Srpaulo			return NUM_HOSTAPD_MODES;
696289549Srpaulo
697281806Srpaulo		*channel = (freq - 56160) / 2160;
698289549Srpaulo		*op_class = 180;
699289549Srpaulo
700289549Srpaulo		return HOSTAPD_MODE_IEEE80211AD;
701281806Srpaulo	}
702281806Srpaulo
703289549Srpaulo	return NUM_HOSTAPD_MODES;
704281806Srpaulo}
705281806Srpaulo
706281806Srpaulo
707289549Srpaulostatic const char *const us_op_class_cc[] = {
708281806Srpaulo	"US", "CA", NULL
709281806Srpaulo};
710281806Srpaulo
711289549Srpaulostatic const char *const eu_op_class_cc[] = {
712281806Srpaulo	"AL", "AM", "AT", "AZ", "BA", "BE", "BG", "BY", "CH", "CY", "CZ", "DE",
713281806Srpaulo	"DK", "EE", "EL", "ES", "FI", "FR", "GE", "HR", "HU", "IE", "IS", "IT",
714281806Srpaulo	"LI", "LT", "LU", "LV", "MD", "ME", "MK", "MT", "NL", "NO", "PL", "PT",
715281806Srpaulo	"RO", "RS", "RU", "SE", "SI", "SK", "TR", "UA", "UK", NULL
716281806Srpaulo};
717281806Srpaulo
718289549Srpaulostatic const char *const jp_op_class_cc[] = {
719281806Srpaulo	"JP", NULL
720281806Srpaulo};
721281806Srpaulo
722289549Srpaulostatic const char *const cn_op_class_cc[] = {
723289549Srpaulo	"CN", NULL
724281806Srpaulo};
725281806Srpaulo
726281806Srpaulo
727289549Srpaulostatic int country_match(const char *const cc[], const char *const country)
728281806Srpaulo{
729281806Srpaulo	int i;
730281806Srpaulo
731281806Srpaulo	if (country == NULL)
732281806Srpaulo		return 0;
733281806Srpaulo	for (i = 0; cc[i]; i++) {
734281806Srpaulo		if (cc[i][0] == country[0] && cc[i][1] == country[1])
735281806Srpaulo			return 1;
736281806Srpaulo	}
737281806Srpaulo
738281806Srpaulo	return 0;
739281806Srpaulo}
740281806Srpaulo
741281806Srpaulo
742281806Srpaulostatic int ieee80211_chan_to_freq_us(u8 op_class, u8 chan)
743281806Srpaulo{
744281806Srpaulo	switch (op_class) {
745281806Srpaulo	case 12: /* channels 1..11 */
746281806Srpaulo	case 32: /* channels 1..7; 40 MHz */
747281806Srpaulo	case 33: /* channels 5..11; 40 MHz */
748281806Srpaulo		if (chan < 1 || chan > 11)
749281806Srpaulo			return -1;
750281806Srpaulo		return 2407 + 5 * chan;
751281806Srpaulo	case 1: /* channels 36,40,44,48 */
752281806Srpaulo	case 2: /* channels 52,56,60,64; dfs */
753281806Srpaulo	case 22: /* channels 36,44; 40 MHz */
754281806Srpaulo	case 23: /* channels 52,60; 40 MHz */
755281806Srpaulo	case 27: /* channels 40,48; 40 MHz */
756281806Srpaulo	case 28: /* channels 56,64; 40 MHz */
757281806Srpaulo		if (chan < 36 || chan > 64)
758281806Srpaulo			return -1;
759281806Srpaulo		return 5000 + 5 * chan;
760281806Srpaulo	case 4: /* channels 100-144 */
761281806Srpaulo	case 24: /* channels 100-140; 40 MHz */
762281806Srpaulo		if (chan < 100 || chan > 144)
763281806Srpaulo			return -1;
764281806Srpaulo		return 5000 + 5 * chan;
765281806Srpaulo	case 3: /* channels 149,153,157,161 */
766281806Srpaulo	case 25: /* channels 149,157; 40 MHz */
767281806Srpaulo	case 26: /* channels 149,157; 40 MHz */
768281806Srpaulo	case 30: /* channels 153,161; 40 MHz */
769281806Srpaulo	case 31: /* channels 153,161; 40 MHz */
770281806Srpaulo		if (chan < 149 || chan > 161)
771281806Srpaulo			return -1;
772281806Srpaulo		return 5000 + 5 * chan;
773289549Srpaulo	case 5: /* channels 149,153,157,161,165 */
774289549Srpaulo		if (chan < 149 || chan > 165)
775289549Srpaulo			return -1;
776289549Srpaulo		return 5000 + 5 * chan;
777281806Srpaulo	case 34: /* 60 GHz band, channels 1..3 */
778281806Srpaulo		if (chan < 1 || chan > 3)
779281806Srpaulo			return -1;
780281806Srpaulo		return 56160 + 2160 * chan;
781281806Srpaulo	}
782281806Srpaulo	return -1;
783281806Srpaulo}
784281806Srpaulo
785281806Srpaulo
786281806Srpaulostatic int ieee80211_chan_to_freq_eu(u8 op_class, u8 chan)
787281806Srpaulo{
788281806Srpaulo	switch (op_class) {
789281806Srpaulo	case 4: /* channels 1..13 */
790281806Srpaulo	case 11: /* channels 1..9; 40 MHz */
791281806Srpaulo	case 12: /* channels 5..13; 40 MHz */
792281806Srpaulo		if (chan < 1 || chan > 13)
793281806Srpaulo			return -1;
794281806Srpaulo		return 2407 + 5 * chan;
795281806Srpaulo	case 1: /* channels 36,40,44,48 */
796281806Srpaulo	case 2: /* channels 52,56,60,64; dfs */
797281806Srpaulo	case 5: /* channels 36,44; 40 MHz */
798281806Srpaulo	case 6: /* channels 52,60; 40 MHz */
799281806Srpaulo	case 8: /* channels 40,48; 40 MHz */
800281806Srpaulo	case 9: /* channels 56,64; 40 MHz */
801281806Srpaulo		if (chan < 36 || chan > 64)
802281806Srpaulo			return -1;
803281806Srpaulo		return 5000 + 5 * chan;
804281806Srpaulo	case 3: /* channels 100-140 */
805281806Srpaulo	case 7: /* channels 100-132; 40 MHz */
806281806Srpaulo	case 10: /* channels 104-136; 40 MHz */
807281806Srpaulo	case 16: /* channels 100-140 */
808281806Srpaulo		if (chan < 100 || chan > 140)
809281806Srpaulo			return -1;
810281806Srpaulo		return 5000 + 5 * chan;
811281806Srpaulo	case 17: /* channels 149,153,157,161,165,169 */
812281806Srpaulo		if (chan < 149 || chan > 169)
813281806Srpaulo			return -1;
814281806Srpaulo		return 5000 + 5 * chan;
815281806Srpaulo	case 18: /* 60 GHz band, channels 1..4 */
816281806Srpaulo		if (chan < 1 || chan > 4)
817281806Srpaulo			return -1;
818281806Srpaulo		return 56160 + 2160 * chan;
819281806Srpaulo	}
820281806Srpaulo	return -1;
821281806Srpaulo}
822281806Srpaulo
823281806Srpaulo
824281806Srpaulostatic int ieee80211_chan_to_freq_jp(u8 op_class, u8 chan)
825281806Srpaulo{
826281806Srpaulo	switch (op_class) {
827281806Srpaulo	case 30: /* channels 1..13 */
828281806Srpaulo	case 56: /* channels 1..9; 40 MHz */
829281806Srpaulo	case 57: /* channels 5..13; 40 MHz */
830281806Srpaulo		if (chan < 1 || chan > 13)
831281806Srpaulo			return -1;
832281806Srpaulo		return 2407 + 5 * chan;
833281806Srpaulo	case 31: /* channel 14 */
834281806Srpaulo		if (chan != 14)
835281806Srpaulo			return -1;
836281806Srpaulo		return 2414 + 5 * chan;
837281806Srpaulo	case 1: /* channels 34,38,42,46(old) or 36,40,44,48 */
838281806Srpaulo	case 32: /* channels 52,56,60,64 */
839281806Srpaulo	case 33: /* channels 52,56,60,64 */
840281806Srpaulo	case 36: /* channels 36,44; 40 MHz */
841281806Srpaulo	case 37: /* channels 52,60; 40 MHz */
842281806Srpaulo	case 38: /* channels 52,60; 40 MHz */
843281806Srpaulo	case 41: /* channels 40,48; 40 MHz */
844281806Srpaulo	case 42: /* channels 56,64; 40 MHz */
845281806Srpaulo	case 43: /* channels 56,64; 40 MHz */
846281806Srpaulo		if (chan < 34 || chan > 64)
847281806Srpaulo			return -1;
848281806Srpaulo		return 5000 + 5 * chan;
849281806Srpaulo	case 34: /* channels 100-140 */
850281806Srpaulo	case 35: /* channels 100-140 */
851281806Srpaulo	case 39: /* channels 100-132; 40 MHz */
852281806Srpaulo	case 40: /* channels 100-132; 40 MHz */
853281806Srpaulo	case 44: /* channels 104-136; 40 MHz */
854281806Srpaulo	case 45: /* channels 104-136; 40 MHz */
855281806Srpaulo	case 58: /* channels 100-140 */
856281806Srpaulo		if (chan < 100 || chan > 140)
857281806Srpaulo			return -1;
858281806Srpaulo		return 5000 + 5 * chan;
859281806Srpaulo	case 59: /* 60 GHz band, channels 1..4 */
860281806Srpaulo		if (chan < 1 || chan > 3)
861281806Srpaulo			return -1;
862281806Srpaulo		return 56160 + 2160 * chan;
863281806Srpaulo	}
864281806Srpaulo	return -1;
865281806Srpaulo}
866281806Srpaulo
867281806Srpaulo
868281806Srpaulostatic int ieee80211_chan_to_freq_cn(u8 op_class, u8 chan)
869281806Srpaulo{
870281806Srpaulo	switch (op_class) {
871281806Srpaulo	case 7: /* channels 1..13 */
872281806Srpaulo	case 8: /* channels 1..9; 40 MHz */
873281806Srpaulo	case 9: /* channels 5..13; 40 MHz */
874281806Srpaulo		if (chan < 1 || chan > 13)
875281806Srpaulo			return -1;
876281806Srpaulo		return 2407 + 5 * chan;
877281806Srpaulo	case 1: /* channels 36,40,44,48 */
878281806Srpaulo	case 2: /* channels 52,56,60,64; dfs */
879281806Srpaulo	case 4: /* channels 36,44; 40 MHz */
880281806Srpaulo	case 5: /* channels 52,60; 40 MHz */
881281806Srpaulo		if (chan < 36 || chan > 64)
882281806Srpaulo			return -1;
883281806Srpaulo		return 5000 + 5 * chan;
884281806Srpaulo	case 3: /* channels 149,153,157,161,165 */
885281806Srpaulo	case 6: /* channels 149,157; 40 MHz */
886281806Srpaulo		if (chan < 149 || chan > 165)
887281806Srpaulo			return -1;
888281806Srpaulo		return 5000 + 5 * chan;
889281806Srpaulo	}
890281806Srpaulo	return -1;
891281806Srpaulo}
892281806Srpaulo
893281806Srpaulo
894281806Srpaulostatic int ieee80211_chan_to_freq_global(u8 op_class, u8 chan)
895281806Srpaulo{
896281806Srpaulo	/* Table E-4 in IEEE Std 802.11-2012 - Global operating classes */
897281806Srpaulo	switch (op_class) {
898281806Srpaulo	case 81:
899281806Srpaulo		/* channels 1..13 */
900281806Srpaulo		if (chan < 1 || chan > 13)
901281806Srpaulo			return -1;
902281806Srpaulo		return 2407 + 5 * chan;
903281806Srpaulo	case 82:
904281806Srpaulo		/* channel 14 */
905281806Srpaulo		if (chan != 14)
906281806Srpaulo			return -1;
907281806Srpaulo		return 2414 + 5 * chan;
908281806Srpaulo	case 83: /* channels 1..9; 40 MHz */
909281806Srpaulo	case 84: /* channels 5..13; 40 MHz */
910281806Srpaulo		if (chan < 1 || chan > 13)
911281806Srpaulo			return -1;
912281806Srpaulo		return 2407 + 5 * chan;
913281806Srpaulo	case 115: /* channels 36,40,44,48; indoor only */
914281806Srpaulo	case 116: /* channels 36,44; 40 MHz; indoor only */
915281806Srpaulo	case 117: /* channels 40,48; 40 MHz; indoor only */
916281806Srpaulo	case 118: /* channels 52,56,60,64; dfs */
917281806Srpaulo	case 119: /* channels 52,60; 40 MHz; dfs */
918281806Srpaulo	case 120: /* channels 56,64; 40 MHz; dfs */
919281806Srpaulo		if (chan < 36 || chan > 64)
920281806Srpaulo			return -1;
921281806Srpaulo		return 5000 + 5 * chan;
922281806Srpaulo	case 121: /* channels 100-140 */
923281806Srpaulo	case 122: /* channels 100-142; 40 MHz */
924281806Srpaulo	case 123: /* channels 104-136; 40 MHz */
925281806Srpaulo		if (chan < 100 || chan > 140)
926281806Srpaulo			return -1;
927281806Srpaulo		return 5000 + 5 * chan;
928281806Srpaulo	case 124: /* channels 149,153,157,161 */
929281806Srpaulo	case 126: /* channels 149,157; 40 MHz */
930281806Srpaulo	case 127: /* channels 153,161; 40 MHz */
931281806Srpaulo		if (chan < 149 || chan > 161)
932281806Srpaulo			return -1;
933281806Srpaulo		return 5000 + 5 * chan;
934289549Srpaulo	case 125: /* channels 149,153,157,161,165,169 */
935289549Srpaulo		if (chan < 149 || chan > 169)
936289549Srpaulo			return -1;
937289549Srpaulo		return 5000 + 5 * chan;
938281806Srpaulo	case 128: /* center freqs 42, 58, 106, 122, 138, 155; 80 MHz */
939281806Srpaulo	case 130: /* center freqs 42, 58, 106, 122, 138, 155; 80 MHz */
940281806Srpaulo		if (chan < 36 || chan > 161)
941281806Srpaulo			return -1;
942281806Srpaulo		return 5000 + 5 * chan;
943281806Srpaulo	case 129: /* center freqs 50, 114; 160 MHz */
944281806Srpaulo		if (chan < 50 || chan > 114)
945281806Srpaulo			return -1;
946281806Srpaulo		return 5000 + 5 * chan;
947281806Srpaulo	case 180: /* 60 GHz band, channels 1..4 */
948281806Srpaulo		if (chan < 1 || chan > 4)
949281806Srpaulo			return -1;
950281806Srpaulo		return 56160 + 2160 * chan;
951281806Srpaulo	}
952281806Srpaulo	return -1;
953281806Srpaulo}
954281806Srpaulo
955281806Srpaulo/**
956281806Srpaulo * ieee80211_chan_to_freq - Convert channel info to frequency
957281806Srpaulo * @country: Country code, if known; otherwise, global operating class is used
958281806Srpaulo * @op_class: Operating class
959281806Srpaulo * @chan: Channel number
960281806Srpaulo * Returns: Frequency in MHz or -1 if the specified channel is unknown
961281806Srpaulo */
962281806Srpauloint ieee80211_chan_to_freq(const char *country, u8 op_class, u8 chan)
963281806Srpaulo{
964281806Srpaulo	int freq;
965281806Srpaulo
966281806Srpaulo	if (country_match(us_op_class_cc, country)) {
967281806Srpaulo		freq = ieee80211_chan_to_freq_us(op_class, chan);
968281806Srpaulo		if (freq > 0)
969281806Srpaulo			return freq;
970281806Srpaulo	}
971281806Srpaulo
972281806Srpaulo	if (country_match(eu_op_class_cc, country)) {
973281806Srpaulo		freq = ieee80211_chan_to_freq_eu(op_class, chan);
974281806Srpaulo		if (freq > 0)
975281806Srpaulo			return freq;
976281806Srpaulo	}
977281806Srpaulo
978281806Srpaulo	if (country_match(jp_op_class_cc, country)) {
979281806Srpaulo		freq = ieee80211_chan_to_freq_jp(op_class, chan);
980281806Srpaulo		if (freq > 0)
981281806Srpaulo			return freq;
982281806Srpaulo	}
983281806Srpaulo
984281806Srpaulo	if (country_match(cn_op_class_cc, country)) {
985281806Srpaulo		freq = ieee80211_chan_to_freq_cn(op_class, chan);
986281806Srpaulo		if (freq > 0)
987281806Srpaulo			return freq;
988281806Srpaulo	}
989281806Srpaulo
990281806Srpaulo	return ieee80211_chan_to_freq_global(op_class, chan);
991281806Srpaulo}
992281806Srpaulo
993281806Srpaulo
994281806Srpauloint ieee80211_is_dfs(int freq)
995281806Srpaulo{
996281806Srpaulo	/* TODO: this could be more accurate to better cover all domains */
997281806Srpaulo	return (freq >= 5260 && freq <= 5320) || (freq >= 5500 && freq <= 5700);
998281806Srpaulo}
999281806Srpaulo
1000281806Srpaulo
1001281806Srpaulostatic int is_11b(u8 rate)
1002281806Srpaulo{
1003281806Srpaulo	return rate == 0x02 || rate == 0x04 || rate == 0x0b || rate == 0x16;
1004281806Srpaulo}
1005281806Srpaulo
1006281806Srpaulo
1007281806Srpauloint supp_rates_11b_only(struct ieee802_11_elems *elems)
1008281806Srpaulo{
1009281806Srpaulo	int num_11b = 0, num_others = 0;
1010281806Srpaulo	int i;
1011281806Srpaulo
1012281806Srpaulo	if (elems->supp_rates == NULL && elems->ext_supp_rates == NULL)
1013281806Srpaulo		return 0;
1014281806Srpaulo
1015281806Srpaulo	for (i = 0; elems->supp_rates && i < elems->supp_rates_len; i++) {
1016281806Srpaulo		if (is_11b(elems->supp_rates[i]))
1017281806Srpaulo			num_11b++;
1018281806Srpaulo		else
1019281806Srpaulo			num_others++;
1020281806Srpaulo	}
1021281806Srpaulo
1022281806Srpaulo	for (i = 0; elems->ext_supp_rates && i < elems->ext_supp_rates_len;
1023281806Srpaulo	     i++) {
1024281806Srpaulo		if (is_11b(elems->ext_supp_rates[i]))
1025281806Srpaulo			num_11b++;
1026281806Srpaulo		else
1027281806Srpaulo			num_others++;
1028281806Srpaulo	}
1029281806Srpaulo
1030281806Srpaulo	return num_11b > 0 && num_others == 0;
1031281806Srpaulo}
1032281806Srpaulo
1033281806Srpaulo
1034281806Srpauloconst char * fc2str(u16 fc)
1035281806Srpaulo{
1036281806Srpaulo	u16 stype = WLAN_FC_GET_STYPE(fc);
1037281806Srpaulo#define C2S(x) case x: return #x;
1038281806Srpaulo
1039281806Srpaulo	switch (WLAN_FC_GET_TYPE(fc)) {
1040281806Srpaulo	case WLAN_FC_TYPE_MGMT:
1041281806Srpaulo		switch (stype) {
1042281806Srpaulo		C2S(WLAN_FC_STYPE_ASSOC_REQ)
1043281806Srpaulo		C2S(WLAN_FC_STYPE_ASSOC_RESP)
1044281806Srpaulo		C2S(WLAN_FC_STYPE_REASSOC_REQ)
1045281806Srpaulo		C2S(WLAN_FC_STYPE_REASSOC_RESP)
1046281806Srpaulo		C2S(WLAN_FC_STYPE_PROBE_REQ)
1047281806Srpaulo		C2S(WLAN_FC_STYPE_PROBE_RESP)
1048281806Srpaulo		C2S(WLAN_FC_STYPE_BEACON)
1049281806Srpaulo		C2S(WLAN_FC_STYPE_ATIM)
1050281806Srpaulo		C2S(WLAN_FC_STYPE_DISASSOC)
1051281806Srpaulo		C2S(WLAN_FC_STYPE_AUTH)
1052281806Srpaulo		C2S(WLAN_FC_STYPE_DEAUTH)
1053281806Srpaulo		C2S(WLAN_FC_STYPE_ACTION)
1054281806Srpaulo		}
1055281806Srpaulo		break;
1056281806Srpaulo	case WLAN_FC_TYPE_CTRL:
1057281806Srpaulo		switch (stype) {
1058281806Srpaulo		C2S(WLAN_FC_STYPE_PSPOLL)
1059281806Srpaulo		C2S(WLAN_FC_STYPE_RTS)
1060281806Srpaulo		C2S(WLAN_FC_STYPE_CTS)
1061281806Srpaulo		C2S(WLAN_FC_STYPE_ACK)
1062281806Srpaulo		C2S(WLAN_FC_STYPE_CFEND)
1063281806Srpaulo		C2S(WLAN_FC_STYPE_CFENDACK)
1064281806Srpaulo		}
1065281806Srpaulo		break;
1066281806Srpaulo	case WLAN_FC_TYPE_DATA:
1067281806Srpaulo		switch (stype) {
1068281806Srpaulo		C2S(WLAN_FC_STYPE_DATA)
1069281806Srpaulo		C2S(WLAN_FC_STYPE_DATA_CFACK)
1070281806Srpaulo		C2S(WLAN_FC_STYPE_DATA_CFPOLL)
1071281806Srpaulo		C2S(WLAN_FC_STYPE_DATA_CFACKPOLL)
1072281806Srpaulo		C2S(WLAN_FC_STYPE_NULLFUNC)
1073281806Srpaulo		C2S(WLAN_FC_STYPE_CFACK)
1074281806Srpaulo		C2S(WLAN_FC_STYPE_CFPOLL)
1075281806Srpaulo		C2S(WLAN_FC_STYPE_CFACKPOLL)
1076281806Srpaulo		C2S(WLAN_FC_STYPE_QOS_DATA)
1077281806Srpaulo		C2S(WLAN_FC_STYPE_QOS_DATA_CFACK)
1078281806Srpaulo		C2S(WLAN_FC_STYPE_QOS_DATA_CFPOLL)
1079281806Srpaulo		C2S(WLAN_FC_STYPE_QOS_DATA_CFACKPOLL)
1080281806Srpaulo		C2S(WLAN_FC_STYPE_QOS_NULL)
1081281806Srpaulo		C2S(WLAN_FC_STYPE_QOS_CFPOLL)
1082281806Srpaulo		C2S(WLAN_FC_STYPE_QOS_CFACKPOLL)
1083281806Srpaulo		}
1084281806Srpaulo		break;
1085281806Srpaulo	}
1086281806Srpaulo	return "WLAN_FC_TYPE_UNKNOWN";
1087281806Srpaulo#undef C2S
1088281806Srpaulo}
1089289549Srpaulo
1090289549Srpaulo
1091289549Srpauloint mb_ies_info_by_ies(struct mb_ies_info *info, const u8 *ies_buf,
1092289549Srpaulo		       size_t ies_len)
1093289549Srpaulo{
1094289549Srpaulo	os_memset(info, 0, sizeof(*info));
1095289549Srpaulo
1096289549Srpaulo	while (ies_buf && ies_len >= 2 &&
1097289549Srpaulo	       info->nof_ies < MAX_NOF_MB_IES_SUPPORTED) {
1098289549Srpaulo		size_t len = 2 + ies_buf[1];
1099289549Srpaulo
1100289549Srpaulo		if (len > ies_len) {
1101289549Srpaulo			wpa_hexdump(MSG_DEBUG, "Truncated IEs",
1102289549Srpaulo				    ies_buf, ies_len);
1103289549Srpaulo			return -1;
1104289549Srpaulo		}
1105289549Srpaulo
1106289549Srpaulo		if (ies_buf[0] == WLAN_EID_MULTI_BAND) {
1107289549Srpaulo			wpa_printf(MSG_DEBUG, "MB IE of %zu bytes found", len);
1108289549Srpaulo			info->ies[info->nof_ies].ie = ies_buf + 2;
1109289549Srpaulo			info->ies[info->nof_ies].ie_len = ies_buf[1];
1110289549Srpaulo			info->nof_ies++;
1111289549Srpaulo		}
1112289549Srpaulo
1113289549Srpaulo		ies_len -= len;
1114289549Srpaulo		ies_buf += len;
1115289549Srpaulo	}
1116289549Srpaulo
1117289549Srpaulo	return 0;
1118289549Srpaulo}
1119289549Srpaulo
1120289549Srpaulo
1121289549Srpaulostruct wpabuf * mb_ies_by_info(struct mb_ies_info *info)
1122289549Srpaulo{
1123289549Srpaulo	struct wpabuf *mb_ies = NULL;
1124289549Srpaulo
1125289549Srpaulo	WPA_ASSERT(info != NULL);
1126289549Srpaulo
1127289549Srpaulo	if (info->nof_ies) {
1128289549Srpaulo		u8 i;
1129289549Srpaulo		size_t mb_ies_size = 0;
1130289549Srpaulo
1131289549Srpaulo		for (i = 0; i < info->nof_ies; i++)
1132289549Srpaulo			mb_ies_size += 2 + info->ies[i].ie_len;
1133289549Srpaulo
1134289549Srpaulo		mb_ies = wpabuf_alloc(mb_ies_size);
1135289549Srpaulo		if (mb_ies) {
1136289549Srpaulo			for (i = 0; i < info->nof_ies; i++) {
1137289549Srpaulo				wpabuf_put_u8(mb_ies, WLAN_EID_MULTI_BAND);
1138289549Srpaulo				wpabuf_put_u8(mb_ies, info->ies[i].ie_len);
1139289549Srpaulo				wpabuf_put_data(mb_ies,
1140289549Srpaulo						info->ies[i].ie,
1141289549Srpaulo						info->ies[i].ie_len);
1142289549Srpaulo			}
1143289549Srpaulo		}
1144289549Srpaulo	}
1145289549Srpaulo
1146289549Srpaulo	return mb_ies;
1147289549Srpaulo}
1148