1189251Ssam/*
2189251Ssam * IEEE 802.11 Common routines
3252726Srpaulo * Copyright (c) 2002-2012, 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"
12189251Ssam#include "ieee802_11_defs.h"
13189251Ssam#include "ieee802_11_common.h"
14189251Ssam
15189251Ssam
16214734Srpaulostatic int ieee802_11_parse_vendor_specific(const u8 *pos, size_t elen,
17189251Ssam					    struct ieee802_11_elems *elems,
18189251Ssam					    int show_errors)
19189251Ssam{
20189251Ssam	unsigned int oui;
21189251Ssam
22189251Ssam	/* first 3 bytes in vendor specific information element are the IEEE
23189251Ssam	 * OUI of the vendor. The following byte is used a vendor specific
24189251Ssam	 * sub-type. */
25189251Ssam	if (elen < 4) {
26189251Ssam		if (show_errors) {
27189251Ssam			wpa_printf(MSG_MSGDUMP, "short vendor specific "
28189251Ssam				   "information element ignored (len=%lu)",
29189251Ssam				   (unsigned long) elen);
30189251Ssam		}
31189251Ssam		return -1;
32189251Ssam	}
33189251Ssam
34189251Ssam	oui = WPA_GET_BE24(pos);
35189251Ssam	switch (oui) {
36189251Ssam	case OUI_MICROSOFT:
37189251Ssam		/* Microsoft/Wi-Fi information elements are further typed and
38189251Ssam		 * subtyped */
39189251Ssam		switch (pos[3]) {
40189251Ssam		case 1:
41189251Ssam			/* Microsoft OUI (00:50:F2) with OUI Type 1:
42189251Ssam			 * real WPA information element */
43189251Ssam			elems->wpa_ie = pos;
44189251Ssam			elems->wpa_ie_len = elen;
45189251Ssam			break;
46209158Srpaulo		case WMM_OUI_TYPE:
47209158Srpaulo			/* WMM information element */
48189251Ssam			if (elen < 5) {
49209158Srpaulo				wpa_printf(MSG_MSGDUMP, "short WMM "
50189251Ssam					   "information element ignored "
51189251Ssam					   "(len=%lu)",
52189251Ssam					   (unsigned long) elen);
53189251Ssam				return -1;
54189251Ssam			}
55189251Ssam			switch (pos[4]) {
56209158Srpaulo			case WMM_OUI_SUBTYPE_INFORMATION_ELEMENT:
57209158Srpaulo			case WMM_OUI_SUBTYPE_PARAMETER_ELEMENT:
58209158Srpaulo				/*
59209158Srpaulo				 * Share same pointer since only one of these
60209158Srpaulo				 * is used and they start with same data.
61209158Srpaulo				 * Length field can be used to distinguish the
62209158Srpaulo				 * IEs.
63209158Srpaulo				 */
64209158Srpaulo				elems->wmm = pos;
65209158Srpaulo				elems->wmm_len = elen;
66189251Ssam				break;
67209158Srpaulo			case WMM_OUI_SUBTYPE_TSPEC_ELEMENT:
68209158Srpaulo				elems->wmm_tspec = pos;
69209158Srpaulo				elems->wmm_tspec_len = elen;
70189251Ssam				break;
71189251Ssam			default:
72252726Srpaulo				wpa_printf(MSG_EXCESSIVE, "unknown WMM "
73189251Ssam					   "information element ignored "
74189251Ssam					   "(subtype=%d len=%lu)",
75189251Ssam					   pos[4], (unsigned long) elen);
76189251Ssam				return -1;
77189251Ssam			}
78189251Ssam			break;
79189251Ssam		case 4:
80189251Ssam			/* Wi-Fi Protected Setup (WPS) IE */
81189251Ssam			elems->wps_ie = pos;
82189251Ssam			elems->wps_ie_len = elen;
83189251Ssam			break;
84189251Ssam		default:
85252726Srpaulo			wpa_printf(MSG_EXCESSIVE, "Unknown Microsoft "
86189251Ssam				   "information element ignored "
87252726Srpaulo				   "(type=%d len=%lu)",
88252726Srpaulo				   pos[3], (unsigned long) elen);
89252726Srpaulo			return -1;
90252726Srpaulo		}
91252726Srpaulo		break;
92252726Srpaulo
93252726Srpaulo	case OUI_WFA:
94252726Srpaulo		switch (pos[3]) {
95252726Srpaulo		case P2P_OUI_TYPE:
96252726Srpaulo			/* Wi-Fi Alliance - P2P IE */
97252726Srpaulo			elems->p2p = pos;
98252726Srpaulo			elems->p2p_len = elen;
99252726Srpaulo			break;
100252726Srpaulo		case WFD_OUI_TYPE:
101252726Srpaulo			/* Wi-Fi Alliance - WFD IE */
102252726Srpaulo			elems->wfd = pos;
103252726Srpaulo			elems->wfd_len = elen;
104252726Srpaulo			break;
105252726Srpaulo		case HS20_INDICATION_OUI_TYPE:
106252726Srpaulo			/* Hotspot 2.0 */
107252726Srpaulo			elems->hs20 = pos;
108252726Srpaulo			elems->hs20_len = elen;
109252726Srpaulo			break;
110252726Srpaulo		default:
111252726Srpaulo			wpa_printf(MSG_MSGDUMP, "Unknown WFA "
112252726Srpaulo				   "information element ignored "
113189251Ssam				   "(type=%d len=%lu)\n",
114189251Ssam				   pos[3], (unsigned long) elen);
115189251Ssam			return -1;
116189251Ssam		}
117189251Ssam		break;
118189251Ssam
119189251Ssam	case OUI_BROADCOM:
120189251Ssam		switch (pos[3]) {
121189251Ssam		case VENDOR_HT_CAPAB_OUI_TYPE:
122189251Ssam			elems->vendor_ht_cap = pos;
123189251Ssam			elems->vendor_ht_cap_len = elen;
124189251Ssam			break;
125189251Ssam		default:
126252726Srpaulo			wpa_printf(MSG_EXCESSIVE, "Unknown Broadcom "
127189251Ssam				   "information element ignored "
128252726Srpaulo				   "(type=%d len=%lu)",
129189251Ssam				   pos[3], (unsigned long) elen);
130189251Ssam			return -1;
131189251Ssam		}
132189251Ssam		break;
133189251Ssam
134189251Ssam	default:
135252726Srpaulo		wpa_printf(MSG_EXCESSIVE, "unknown vendor specific "
136252726Srpaulo			   "information element ignored (vendor OUI "
137252726Srpaulo			   "%02x:%02x:%02x len=%lu)",
138189251Ssam			   pos[0], pos[1], pos[2], (unsigned long) elen);
139189251Ssam		return -1;
140189251Ssam	}
141189251Ssam
142189251Ssam	return 0;
143189251Ssam}
144189251Ssam
145189251Ssam
146189251Ssam/**
147189251Ssam * ieee802_11_parse_elems - Parse information elements in management frames
148189251Ssam * @start: Pointer to the start of IEs
149189251Ssam * @len: Length of IE buffer in octets
150189251Ssam * @elems: Data structure for parsed elements
151189251Ssam * @show_errors: Whether to show parsing errors in debug log
152189251Ssam * Returns: Parsing result
153189251Ssam */
154214734SrpauloParseRes ieee802_11_parse_elems(const u8 *start, size_t len,
155189251Ssam				struct ieee802_11_elems *elems,
156189251Ssam				int show_errors)
157189251Ssam{
158189251Ssam	size_t left = len;
159214734Srpaulo	const u8 *pos = start;
160189251Ssam	int unknown = 0;
161189251Ssam
162189251Ssam	os_memset(elems, 0, sizeof(*elems));
163189251Ssam
164189251Ssam	while (left >= 2) {
165189251Ssam		u8 id, elen;
166189251Ssam
167189251Ssam		id = *pos++;
168189251Ssam		elen = *pos++;
169189251Ssam		left -= 2;
170189251Ssam
171189251Ssam		if (elen > left) {
172189251Ssam			if (show_errors) {
173189251Ssam				wpa_printf(MSG_DEBUG, "IEEE 802.11 element "
174189251Ssam					   "parse failed (id=%d elen=%d "
175189251Ssam					   "left=%lu)",
176189251Ssam					   id, elen, (unsigned long) left);
177189251Ssam				wpa_hexdump(MSG_MSGDUMP, "IEs", start, len);
178189251Ssam			}
179189251Ssam			return ParseFailed;
180189251Ssam		}
181189251Ssam
182189251Ssam		switch (id) {
183189251Ssam		case WLAN_EID_SSID:
184189251Ssam			elems->ssid = pos;
185189251Ssam			elems->ssid_len = elen;
186189251Ssam			break;
187189251Ssam		case WLAN_EID_SUPP_RATES:
188189251Ssam			elems->supp_rates = pos;
189189251Ssam			elems->supp_rates_len = elen;
190189251Ssam			break;
191189251Ssam		case WLAN_EID_FH_PARAMS:
192189251Ssam			elems->fh_params = pos;
193189251Ssam			elems->fh_params_len = elen;
194189251Ssam			break;
195189251Ssam		case WLAN_EID_DS_PARAMS:
196189251Ssam			elems->ds_params = pos;
197189251Ssam			elems->ds_params_len = elen;
198189251Ssam			break;
199189251Ssam		case WLAN_EID_CF_PARAMS:
200189251Ssam			elems->cf_params = pos;
201189251Ssam			elems->cf_params_len = elen;
202189251Ssam			break;
203189251Ssam		case WLAN_EID_TIM:
204189251Ssam			elems->tim = pos;
205189251Ssam			elems->tim_len = elen;
206189251Ssam			break;
207189251Ssam		case WLAN_EID_IBSS_PARAMS:
208189251Ssam			elems->ibss_params = pos;
209189251Ssam			elems->ibss_params_len = elen;
210189251Ssam			break;
211189251Ssam		case WLAN_EID_CHALLENGE:
212189251Ssam			elems->challenge = pos;
213189251Ssam			elems->challenge_len = elen;
214189251Ssam			break;
215189251Ssam		case WLAN_EID_ERP_INFO:
216189251Ssam			elems->erp_info = pos;
217189251Ssam			elems->erp_info_len = elen;
218189251Ssam			break;
219189251Ssam		case WLAN_EID_EXT_SUPP_RATES:
220189251Ssam			elems->ext_supp_rates = pos;
221189251Ssam			elems->ext_supp_rates_len = elen;
222189251Ssam			break;
223189251Ssam		case WLAN_EID_VENDOR_SPECIFIC:
224189251Ssam			if (ieee802_11_parse_vendor_specific(pos, elen,
225189251Ssam							     elems,
226189251Ssam							     show_errors))
227189251Ssam				unknown++;
228189251Ssam			break;
229189251Ssam		case WLAN_EID_RSN:
230189251Ssam			elems->rsn_ie = pos;
231189251Ssam			elems->rsn_ie_len = elen;
232189251Ssam			break;
233189251Ssam		case WLAN_EID_PWR_CAPABILITY:
234189251Ssam			elems->power_cap = pos;
235189251Ssam			elems->power_cap_len = elen;
236189251Ssam			break;
237189251Ssam		case WLAN_EID_SUPPORTED_CHANNELS:
238189251Ssam			elems->supp_channels = pos;
239189251Ssam			elems->supp_channels_len = elen;
240189251Ssam			break;
241189251Ssam		case WLAN_EID_MOBILITY_DOMAIN:
242189251Ssam			elems->mdie = pos;
243189251Ssam			elems->mdie_len = elen;
244189251Ssam			break;
245189251Ssam		case WLAN_EID_FAST_BSS_TRANSITION:
246189251Ssam			elems->ftie = pos;
247189251Ssam			elems->ftie_len = elen;
248189251Ssam			break;
249189251Ssam		case WLAN_EID_TIMEOUT_INTERVAL:
250189251Ssam			elems->timeout_int = pos;
251189251Ssam			elems->timeout_int_len = elen;
252189251Ssam			break;
253189251Ssam		case WLAN_EID_HT_CAP:
254189251Ssam			elems->ht_capabilities = pos;
255189251Ssam			elems->ht_capabilities_len = elen;
256189251Ssam			break;
257189251Ssam		case WLAN_EID_HT_OPERATION:
258189251Ssam			elems->ht_operation = pos;
259189251Ssam			elems->ht_operation_len = elen;
260189251Ssam			break;
261252726Srpaulo		case WLAN_EID_VHT_CAP:
262252726Srpaulo			elems->vht_capabilities = pos;
263252726Srpaulo			elems->vht_capabilities_len = elen;
264252726Srpaulo			break;
265252726Srpaulo		case WLAN_EID_VHT_OPERATION:
266252726Srpaulo			elems->vht_operation = pos;
267252726Srpaulo			elems->vht_operation_len = elen;
268252726Srpaulo			break;
269252726Srpaulo		case WLAN_EID_LINK_ID:
270252726Srpaulo			if (elen < 18)
271252726Srpaulo				break;
272252726Srpaulo			elems->link_id = pos;
273252726Srpaulo			break;
274252726Srpaulo		case WLAN_EID_INTERWORKING:
275252726Srpaulo			elems->interworking = pos;
276252726Srpaulo			elems->interworking_len = elen;
277252726Srpaulo			break;
278252726Srpaulo		case WLAN_EID_EXT_CAPAB:
279252726Srpaulo			elems->ext_capab = pos;
280252726Srpaulo			elems->ext_capab_len = elen;
281252726Srpaulo			break;
282252726Srpaulo		case WLAN_EID_BSS_MAX_IDLE_PERIOD:
283252726Srpaulo			if (elen < 3)
284252726Srpaulo				break;
285252726Srpaulo			elems->bss_max_idle_period = pos;
286252726Srpaulo			break;
287252726Srpaulo		case WLAN_EID_SSID_LIST:
288252726Srpaulo			elems->ssid_list = pos;
289252726Srpaulo			elems->ssid_list_len = elen;
290252726Srpaulo			break;
291189251Ssam		default:
292189251Ssam			unknown++;
293189251Ssam			if (!show_errors)
294189251Ssam				break;
295189251Ssam			wpa_printf(MSG_MSGDUMP, "IEEE 802.11 element parse "
296189251Ssam				   "ignored unknown element (id=%d elen=%d)",
297189251Ssam				   id, elen);
298189251Ssam			break;
299189251Ssam		}
300189251Ssam
301189251Ssam		left -= elen;
302189251Ssam		pos += elen;
303189251Ssam	}
304189251Ssam
305189251Ssam	if (left)
306189251Ssam		return ParseFailed;
307189251Ssam
308189251Ssam	return unknown ? ParseUnknown : ParseOK;
309189251Ssam}
310214734Srpaulo
311214734Srpaulo
312214734Srpauloint ieee802_11_ie_count(const u8 *ies, size_t ies_len)
313214734Srpaulo{
314214734Srpaulo	int count = 0;
315214734Srpaulo	const u8 *pos, *end;
316214734Srpaulo
317214734Srpaulo	if (ies == NULL)
318214734Srpaulo		return 0;
319214734Srpaulo
320214734Srpaulo	pos = ies;
321214734Srpaulo	end = ies + ies_len;
322214734Srpaulo
323214734Srpaulo	while (pos + 2 <= end) {
324214734Srpaulo		if (pos + 2 + pos[1] > end)
325214734Srpaulo			break;
326214734Srpaulo		count++;
327214734Srpaulo		pos += 2 + pos[1];
328214734Srpaulo	}
329214734Srpaulo
330214734Srpaulo	return count;
331214734Srpaulo}
332214734Srpaulo
333214734Srpaulo
334214734Srpaulostruct wpabuf * ieee802_11_vendor_ie_concat(const u8 *ies, size_t ies_len,
335214734Srpaulo					    u32 oui_type)
336214734Srpaulo{
337214734Srpaulo	struct wpabuf *buf;
338214734Srpaulo	const u8 *end, *pos, *ie;
339214734Srpaulo
340214734Srpaulo	pos = ies;
341214734Srpaulo	end = ies + ies_len;
342214734Srpaulo	ie = NULL;
343214734Srpaulo
344214734Srpaulo	while (pos + 1 < end) {
345214734Srpaulo		if (pos + 2 + pos[1] > end)
346214734Srpaulo			return NULL;
347214734Srpaulo		if (pos[0] == WLAN_EID_VENDOR_SPECIFIC && pos[1] >= 4 &&
348214734Srpaulo		    WPA_GET_BE32(&pos[2]) == oui_type) {
349214734Srpaulo			ie = pos;
350214734Srpaulo			break;
351214734Srpaulo		}
352214734Srpaulo		pos += 2 + pos[1];
353214734Srpaulo	}
354214734Srpaulo
355214734Srpaulo	if (ie == NULL)
356214734Srpaulo		return NULL; /* No specified vendor IE found */
357214734Srpaulo
358214734Srpaulo	buf = wpabuf_alloc(ies_len);
359214734Srpaulo	if (buf == NULL)
360214734Srpaulo		return NULL;
361214734Srpaulo
362214734Srpaulo	/*
363214734Srpaulo	 * There may be multiple vendor IEs in the message, so need to
364214734Srpaulo	 * concatenate their data fields.
365214734Srpaulo	 */
366214734Srpaulo	while (pos + 1 < end) {
367214734Srpaulo		if (pos + 2 + pos[1] > end)
368214734Srpaulo			break;
369214734Srpaulo		if (pos[0] == WLAN_EID_VENDOR_SPECIFIC && pos[1] >= 4 &&
370214734Srpaulo		    WPA_GET_BE32(&pos[2]) == oui_type)
371214734Srpaulo			wpabuf_put_data(buf, pos + 6, pos[1] - 4);
372214734Srpaulo		pos += 2 + pos[1];
373214734Srpaulo	}
374214734Srpaulo
375214734Srpaulo	return buf;
376214734Srpaulo}
377252726Srpaulo
378252726Srpaulo
379252726Srpauloconst u8 * get_hdr_bssid(const struct ieee80211_hdr *hdr, size_t len)
380252726Srpaulo{
381252726Srpaulo	u16 fc, type, stype;
382252726Srpaulo
383252726Srpaulo	/*
384252726Srpaulo	 * PS-Poll frames are 16 bytes. All other frames are
385252726Srpaulo	 * 24 bytes or longer.
386252726Srpaulo	 */
387252726Srpaulo	if (len < 16)
388252726Srpaulo		return NULL;
389252726Srpaulo
390252726Srpaulo	fc = le_to_host16(hdr->frame_control);
391252726Srpaulo	type = WLAN_FC_GET_TYPE(fc);
392252726Srpaulo	stype = WLAN_FC_GET_STYPE(fc);
393252726Srpaulo
394252726Srpaulo	switch (type) {
395252726Srpaulo	case WLAN_FC_TYPE_DATA:
396252726Srpaulo		if (len < 24)
397252726Srpaulo			return NULL;
398252726Srpaulo		switch (fc & (WLAN_FC_FROMDS | WLAN_FC_TODS)) {
399252726Srpaulo		case WLAN_FC_FROMDS | WLAN_FC_TODS:
400252726Srpaulo		case WLAN_FC_TODS:
401252726Srpaulo			return hdr->addr1;
402252726Srpaulo		case WLAN_FC_FROMDS:
403252726Srpaulo			return hdr->addr2;
404252726Srpaulo		default:
405252726Srpaulo			return NULL;
406252726Srpaulo		}
407252726Srpaulo	case WLAN_FC_TYPE_CTRL:
408252726Srpaulo		if (stype != WLAN_FC_STYPE_PSPOLL)
409252726Srpaulo			return NULL;
410252726Srpaulo		return hdr->addr1;
411252726Srpaulo	case WLAN_FC_TYPE_MGMT:
412252726Srpaulo		return hdr->addr3;
413252726Srpaulo	default:
414252726Srpaulo		return NULL;
415252726Srpaulo	}
416252726Srpaulo}
417252726Srpaulo
418252726Srpaulo
419252726Srpauloint hostapd_config_wmm_ac(struct hostapd_wmm_ac_params wmm_ac_params[],
420252726Srpaulo			  const char *name, const char *val)
421252726Srpaulo{
422252726Srpaulo	int num, v;
423252726Srpaulo	const char *pos;
424252726Srpaulo	struct hostapd_wmm_ac_params *ac;
425252726Srpaulo
426252726Srpaulo	/* skip 'wme_ac_' or 'wmm_ac_' prefix */
427252726Srpaulo	pos = name + 7;
428252726Srpaulo	if (os_strncmp(pos, "be_", 3) == 0) {
429252726Srpaulo		num = 0;
430252726Srpaulo		pos += 3;
431252726Srpaulo	} else if (os_strncmp(pos, "bk_", 3) == 0) {
432252726Srpaulo		num = 1;
433252726Srpaulo		pos += 3;
434252726Srpaulo	} else if (os_strncmp(pos, "vi_", 3) == 0) {
435252726Srpaulo		num = 2;
436252726Srpaulo		pos += 3;
437252726Srpaulo	} else if (os_strncmp(pos, "vo_", 3) == 0) {
438252726Srpaulo		num = 3;
439252726Srpaulo		pos += 3;
440252726Srpaulo	} else {
441252726Srpaulo		wpa_printf(MSG_ERROR, "Unknown WMM name '%s'", pos);
442252726Srpaulo		return -1;
443252726Srpaulo	}
444252726Srpaulo
445252726Srpaulo	ac = &wmm_ac_params[num];
446252726Srpaulo
447252726Srpaulo	if (os_strcmp(pos, "aifs") == 0) {
448252726Srpaulo		v = atoi(val);
449252726Srpaulo		if (v < 1 || v > 255) {
450252726Srpaulo			wpa_printf(MSG_ERROR, "Invalid AIFS value %d", v);
451252726Srpaulo			return -1;
452252726Srpaulo		}
453252726Srpaulo		ac->aifs = v;
454252726Srpaulo	} else if (os_strcmp(pos, "cwmin") == 0) {
455252726Srpaulo		v = atoi(val);
456252726Srpaulo		if (v < 0 || v > 12) {
457252726Srpaulo			wpa_printf(MSG_ERROR, "Invalid cwMin value %d", v);
458252726Srpaulo			return -1;
459252726Srpaulo		}
460252726Srpaulo		ac->cwmin = v;
461252726Srpaulo	} else if (os_strcmp(pos, "cwmax") == 0) {
462252726Srpaulo		v = atoi(val);
463252726Srpaulo		if (v < 0 || v > 12) {
464252726Srpaulo			wpa_printf(MSG_ERROR, "Invalid cwMax value %d", v);
465252726Srpaulo			return -1;
466252726Srpaulo		}
467252726Srpaulo		ac->cwmax = v;
468252726Srpaulo	} else if (os_strcmp(pos, "txop_limit") == 0) {
469252726Srpaulo		v = atoi(val);
470252726Srpaulo		if (v < 0 || v > 0xffff) {
471252726Srpaulo			wpa_printf(MSG_ERROR, "Invalid txop value %d", v);
472252726Srpaulo			return -1;
473252726Srpaulo		}
474252726Srpaulo		ac->txop_limit = v;
475252726Srpaulo	} else if (os_strcmp(pos, "acm") == 0) {
476252726Srpaulo		v = atoi(val);
477252726Srpaulo		if (v < 0 || v > 1) {
478252726Srpaulo			wpa_printf(MSG_ERROR, "Invalid acm value %d", v);
479252726Srpaulo			return -1;
480252726Srpaulo		}
481252726Srpaulo		ac->admission_control_mandatory = v;
482252726Srpaulo	} else {
483252726Srpaulo		wpa_printf(MSG_ERROR, "Unknown wmm_ac_ field '%s'", pos);
484252726Srpaulo		return -1;
485252726Srpaulo	}
486252726Srpaulo
487252726Srpaulo	return 0;
488252726Srpaulo}
489