1/*
2 * IEEE 802.11 Common routines
3 * Copyright (c) 2002-2012, Jouni Malinen <j@w1.fi>
4 *
5 * This software may be distributed under the terms of the BSD license.
6 * See README for more details.
7 */
8
9#include "includes.h"
10
11#include "common.h"
12#include "ieee802_11_defs.h"
13#include "ieee802_11_common.h"
14
15
16static int ieee802_11_parse_vendor_specific(const u8 *pos, size_t elen,
17					    struct ieee802_11_elems *elems,
18					    int show_errors)
19{
20	unsigned int oui;
21
22	/* first 3 bytes in vendor specific information element are the IEEE
23	 * OUI of the vendor. The following byte is used a vendor specific
24	 * sub-type. */
25	if (elen < 4) {
26		if (show_errors) {
27			wpa_printf(MSG_MSGDUMP, "short vendor specific "
28				   "information element ignored (len=%lu)",
29				   (unsigned long) elen);
30		}
31		return -1;
32	}
33
34	oui = WPA_GET_BE24(pos);
35	switch (oui) {
36	case OUI_MICROSOFT:
37		/* Microsoft/Wi-Fi information elements are further typed and
38		 * subtyped */
39		switch (pos[3]) {
40		case 1:
41			/* Microsoft OUI (00:50:F2) with OUI Type 1:
42			 * real WPA information element */
43			elems->wpa_ie = pos;
44			elems->wpa_ie_len = elen;
45			break;
46		case WMM_OUI_TYPE:
47			/* WMM information element */
48			if (elen < 5) {
49				wpa_printf(MSG_MSGDUMP, "short WMM "
50					   "information element ignored "
51					   "(len=%lu)",
52					   (unsigned long) elen);
53				return -1;
54			}
55			switch (pos[4]) {
56			case WMM_OUI_SUBTYPE_INFORMATION_ELEMENT:
57			case WMM_OUI_SUBTYPE_PARAMETER_ELEMENT:
58				/*
59				 * Share same pointer since only one of these
60				 * is used and they start with same data.
61				 * Length field can be used to distinguish the
62				 * IEs.
63				 */
64				elems->wmm = pos;
65				elems->wmm_len = elen;
66				break;
67			case WMM_OUI_SUBTYPE_TSPEC_ELEMENT:
68				elems->wmm_tspec = pos;
69				elems->wmm_tspec_len = elen;
70				break;
71			default:
72				wpa_printf(MSG_EXCESSIVE, "unknown WMM "
73					   "information element ignored "
74					   "(subtype=%d len=%lu)",
75					   pos[4], (unsigned long) elen);
76				return -1;
77			}
78			break;
79		case 4:
80			/* Wi-Fi Protected Setup (WPS) IE */
81			elems->wps_ie = pos;
82			elems->wps_ie_len = elen;
83			break;
84		default:
85			wpa_printf(MSG_EXCESSIVE, "Unknown Microsoft "
86				   "information element ignored "
87				   "(type=%d len=%lu)",
88				   pos[3], (unsigned long) elen);
89			return -1;
90		}
91		break;
92
93	case OUI_WFA:
94		switch (pos[3]) {
95		case P2P_OUI_TYPE:
96			/* Wi-Fi Alliance - P2P IE */
97			elems->p2p = pos;
98			elems->p2p_len = elen;
99			break;
100		case WFD_OUI_TYPE:
101			/* Wi-Fi Alliance - WFD IE */
102			elems->wfd = pos;
103			elems->wfd_len = elen;
104			break;
105		case HS20_INDICATION_OUI_TYPE:
106			/* Hotspot 2.0 */
107			elems->hs20 = pos;
108			elems->hs20_len = elen;
109			break;
110		default:
111			wpa_printf(MSG_MSGDUMP, "Unknown WFA "
112				   "information element ignored "
113				   "(type=%d len=%lu)\n",
114				   pos[3], (unsigned long) elen);
115			return -1;
116		}
117		break;
118
119	case OUI_BROADCOM:
120		switch (pos[3]) {
121		case VENDOR_HT_CAPAB_OUI_TYPE:
122			elems->vendor_ht_cap = pos;
123			elems->vendor_ht_cap_len = elen;
124			break;
125		default:
126			wpa_printf(MSG_EXCESSIVE, "Unknown Broadcom "
127				   "information element ignored "
128				   "(type=%d len=%lu)",
129				   pos[3], (unsigned long) elen);
130			return -1;
131		}
132		break;
133
134	default:
135		wpa_printf(MSG_EXCESSIVE, "unknown vendor specific "
136			   "information element ignored (vendor OUI "
137			   "%02x:%02x:%02x len=%lu)",
138			   pos[0], pos[1], pos[2], (unsigned long) elen);
139		return -1;
140	}
141
142	return 0;
143}
144
145
146/**
147 * ieee802_11_parse_elems - Parse information elements in management frames
148 * @start: Pointer to the start of IEs
149 * @len: Length of IE buffer in octets
150 * @elems: Data structure for parsed elements
151 * @show_errors: Whether to show parsing errors in debug log
152 * Returns: Parsing result
153 */
154ParseRes ieee802_11_parse_elems(const u8 *start, size_t len,
155				struct ieee802_11_elems *elems,
156				int show_errors)
157{
158	size_t left = len;
159	const u8 *pos = start;
160	int unknown = 0;
161
162	os_memset(elems, 0, sizeof(*elems));
163
164	while (left >= 2) {
165		u8 id, elen;
166
167		id = *pos++;
168		elen = *pos++;
169		left -= 2;
170
171		if (elen > left) {
172			if (show_errors) {
173				wpa_printf(MSG_DEBUG, "IEEE 802.11 element "
174					   "parse failed (id=%d elen=%d "
175					   "left=%lu)",
176					   id, elen, (unsigned long) left);
177				wpa_hexdump(MSG_MSGDUMP, "IEs", start, len);
178			}
179			return ParseFailed;
180		}
181
182		switch (id) {
183		case WLAN_EID_SSID:
184			elems->ssid = pos;
185			elems->ssid_len = elen;
186			break;
187		case WLAN_EID_SUPP_RATES:
188			elems->supp_rates = pos;
189			elems->supp_rates_len = elen;
190			break;
191		case WLAN_EID_FH_PARAMS:
192			elems->fh_params = pos;
193			elems->fh_params_len = elen;
194			break;
195		case WLAN_EID_DS_PARAMS:
196			elems->ds_params = pos;
197			elems->ds_params_len = elen;
198			break;
199		case WLAN_EID_CF_PARAMS:
200			elems->cf_params = pos;
201			elems->cf_params_len = elen;
202			break;
203		case WLAN_EID_TIM:
204			elems->tim = pos;
205			elems->tim_len = elen;
206			break;
207		case WLAN_EID_IBSS_PARAMS:
208			elems->ibss_params = pos;
209			elems->ibss_params_len = elen;
210			break;
211		case WLAN_EID_CHALLENGE:
212			elems->challenge = pos;
213			elems->challenge_len = elen;
214			break;
215		case WLAN_EID_ERP_INFO:
216			elems->erp_info = pos;
217			elems->erp_info_len = elen;
218			break;
219		case WLAN_EID_EXT_SUPP_RATES:
220			elems->ext_supp_rates = pos;
221			elems->ext_supp_rates_len = elen;
222			break;
223		case WLAN_EID_VENDOR_SPECIFIC:
224			if (ieee802_11_parse_vendor_specific(pos, elen,
225							     elems,
226							     show_errors))
227				unknown++;
228			break;
229		case WLAN_EID_RSN:
230			elems->rsn_ie = pos;
231			elems->rsn_ie_len = elen;
232			break;
233		case WLAN_EID_PWR_CAPABILITY:
234			elems->power_cap = pos;
235			elems->power_cap_len = elen;
236			break;
237		case WLAN_EID_SUPPORTED_CHANNELS:
238			elems->supp_channels = pos;
239			elems->supp_channels_len = elen;
240			break;
241		case WLAN_EID_MOBILITY_DOMAIN:
242			elems->mdie = pos;
243			elems->mdie_len = elen;
244			break;
245		case WLAN_EID_FAST_BSS_TRANSITION:
246			elems->ftie = pos;
247			elems->ftie_len = elen;
248			break;
249		case WLAN_EID_TIMEOUT_INTERVAL:
250			elems->timeout_int = pos;
251			elems->timeout_int_len = elen;
252			break;
253		case WLAN_EID_HT_CAP:
254			elems->ht_capabilities = pos;
255			elems->ht_capabilities_len = elen;
256			break;
257		case WLAN_EID_HT_OPERATION:
258			elems->ht_operation = pos;
259			elems->ht_operation_len = elen;
260			break;
261		case WLAN_EID_VHT_CAP:
262			elems->vht_capabilities = pos;
263			elems->vht_capabilities_len = elen;
264			break;
265		case WLAN_EID_VHT_OPERATION:
266			elems->vht_operation = pos;
267			elems->vht_operation_len = elen;
268			break;
269		case WLAN_EID_LINK_ID:
270			if (elen < 18)
271				break;
272			elems->link_id = pos;
273			break;
274		case WLAN_EID_INTERWORKING:
275			elems->interworking = pos;
276			elems->interworking_len = elen;
277			break;
278		case WLAN_EID_EXT_CAPAB:
279			elems->ext_capab = pos;
280			elems->ext_capab_len = elen;
281			break;
282		case WLAN_EID_BSS_MAX_IDLE_PERIOD:
283			if (elen < 3)
284				break;
285			elems->bss_max_idle_period = pos;
286			break;
287		default:
288			unknown++;
289			if (!show_errors)
290				break;
291			wpa_printf(MSG_MSGDUMP, "IEEE 802.11 element parse "
292				   "ignored unknown element (id=%d elen=%d)",
293				   id, elen);
294			break;
295		}
296
297		left -= elen;
298		pos += elen;
299	}
300
301	if (left)
302		return ParseFailed;
303
304	return unknown ? ParseUnknown : ParseOK;
305}
306
307
308int ieee802_11_ie_count(const u8 *ies, size_t ies_len)
309{
310	int count = 0;
311	const u8 *pos, *end;
312
313	if (ies == NULL)
314		return 0;
315
316	pos = ies;
317	end = ies + ies_len;
318
319	while (pos + 2 <= end) {
320		if (pos + 2 + pos[1] > end)
321			break;
322		count++;
323		pos += 2 + pos[1];
324	}
325
326	return count;
327}
328
329
330struct wpabuf * ieee802_11_vendor_ie_concat(const u8 *ies, size_t ies_len,
331					    u32 oui_type)
332{
333	struct wpabuf *buf;
334	const u8 *end, *pos, *ie;
335
336	pos = ies;
337	end = ies + ies_len;
338	ie = NULL;
339
340	while (pos + 1 < end) {
341		if (pos + 2 + pos[1] > end)
342			return NULL;
343		if (pos[0] == WLAN_EID_VENDOR_SPECIFIC && pos[1] >= 4 &&
344		    WPA_GET_BE32(&pos[2]) == oui_type) {
345			ie = pos;
346			break;
347		}
348		pos += 2 + pos[1];
349	}
350
351	if (ie == NULL)
352		return NULL; /* No specified vendor IE found */
353
354	buf = wpabuf_alloc(ies_len);
355	if (buf == NULL)
356		return NULL;
357
358	/*
359	 * There may be multiple vendor IEs in the message, so need to
360	 * concatenate their data fields.
361	 */
362	while (pos + 1 < end) {
363		if (pos + 2 + pos[1] > end)
364			break;
365		if (pos[0] == WLAN_EID_VENDOR_SPECIFIC && pos[1] >= 4 &&
366		    WPA_GET_BE32(&pos[2]) == oui_type)
367			wpabuf_put_data(buf, pos + 6, pos[1] - 4);
368		pos += 2 + pos[1];
369	}
370
371	return buf;
372}
373
374
375const u8 * get_hdr_bssid(const struct ieee80211_hdr *hdr, size_t len)
376{
377	u16 fc, type, stype;
378
379	/*
380	 * PS-Poll frames are 16 bytes. All other frames are
381	 * 24 bytes or longer.
382	 */
383	if (len < 16)
384		return NULL;
385
386	fc = le_to_host16(hdr->frame_control);
387	type = WLAN_FC_GET_TYPE(fc);
388	stype = WLAN_FC_GET_STYPE(fc);
389
390	switch (type) {
391	case WLAN_FC_TYPE_DATA:
392		if (len < 24)
393			return NULL;
394		switch (fc & (WLAN_FC_FROMDS | WLAN_FC_TODS)) {
395		case WLAN_FC_FROMDS | WLAN_FC_TODS:
396		case WLAN_FC_TODS:
397			return hdr->addr1;
398		case WLAN_FC_FROMDS:
399			return hdr->addr2;
400		default:
401			return NULL;
402		}
403	case WLAN_FC_TYPE_CTRL:
404		if (stype != WLAN_FC_STYPE_PSPOLL)
405			return NULL;
406		return hdr->addr1;
407	case WLAN_FC_TYPE_MGMT:
408		return hdr->addr3;
409	default:
410		return NULL;
411	}
412}
413
414
415int hostapd_config_wmm_ac(struct hostapd_wmm_ac_params wmm_ac_params[],
416			  const char *name, const char *val)
417{
418	int num, v;
419	const char *pos;
420	struct hostapd_wmm_ac_params *ac;
421
422	/* skip 'wme_ac_' or 'wmm_ac_' prefix */
423	pos = name + 7;
424	if (os_strncmp(pos, "be_", 3) == 0) {
425		num = 0;
426		pos += 3;
427	} else if (os_strncmp(pos, "bk_", 3) == 0) {
428		num = 1;
429		pos += 3;
430	} else if (os_strncmp(pos, "vi_", 3) == 0) {
431		num = 2;
432		pos += 3;
433	} else if (os_strncmp(pos, "vo_", 3) == 0) {
434		num = 3;
435		pos += 3;
436	} else {
437		wpa_printf(MSG_ERROR, "Unknown WMM name '%s'", pos);
438		return -1;
439	}
440
441	ac = &wmm_ac_params[num];
442
443	if (os_strcmp(pos, "aifs") == 0) {
444		v = atoi(val);
445		if (v < 1 || v > 255) {
446			wpa_printf(MSG_ERROR, "Invalid AIFS value %d", v);
447			return -1;
448		}
449		ac->aifs = v;
450	} else if (os_strcmp(pos, "cwmin") == 0) {
451		v = atoi(val);
452		if (v < 0 || v > 12) {
453			wpa_printf(MSG_ERROR, "Invalid cwMin value %d", v);
454			return -1;
455		}
456		ac->cwmin = v;
457	} else if (os_strcmp(pos, "cwmax") == 0) {
458		v = atoi(val);
459		if (v < 0 || v > 12) {
460			wpa_printf(MSG_ERROR, "Invalid cwMax value %d", v);
461			return -1;
462		}
463		ac->cwmax = v;
464	} else if (os_strcmp(pos, "txop_limit") == 0) {
465		v = atoi(val);
466		if (v < 0 || v > 0xffff) {
467			wpa_printf(MSG_ERROR, "Invalid txop value %d", v);
468			return -1;
469		}
470		ac->txop_limit = v;
471	} else if (os_strcmp(pos, "acm") == 0) {
472		v = atoi(val);
473		if (v < 0 || v > 1) {
474			wpa_printf(MSG_ERROR, "Invalid acm value %d", v);
475			return -1;
476		}
477		ac->admission_control_mandatory = v;
478	} else {
479		wpa_printf(MSG_ERROR, "Unknown wmm_ac_ field '%s'", pos);
480		return -1;
481	}
482
483	return 0;
484}
485