ieee802_11_common.c revision 8d520ff1dc2da35cdca849e982051b86468016d8
1/*
2 * IEEE 802.11 Common routines
3 * Copyright (c) 2002-2009, Jouni Malinen <j@w1.fi>
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License version 2 as
7 * published by the Free Software Foundation.
8 *
9 * Alternatively, this software may be distributed under the terms of BSD
10 * license.
11 *
12 * See README and COPYING for more details.
13 */
14
15#include "includes.h"
16
17#include "common.h"
18#include "ieee802_11_defs.h"
19#include "ieee802_11_common.h"
20
21
22static int ieee802_11_parse_vendor_specific(const u8 *pos, size_t elen,
23					    struct ieee802_11_elems *elems,
24					    int show_errors)
25{
26	unsigned int oui;
27
28	/* first 3 bytes in vendor specific information element are the IEEE
29	 * OUI of the vendor. The following byte is used a vendor specific
30	 * sub-type. */
31	if (elen < 4) {
32		if (show_errors) {
33			wpa_printf(MSG_MSGDUMP, "short vendor specific "
34				   "information element ignored (len=%lu)",
35				   (unsigned long) elen);
36		}
37		return -1;
38	}
39
40	oui = WPA_GET_BE24(pos);
41	switch (oui) {
42	case OUI_MICROSOFT:
43		/* Microsoft/Wi-Fi information elements are further typed and
44		 * subtyped */
45		switch (pos[3]) {
46		case 1:
47			/* Microsoft OUI (00:50:F2) with OUI Type 1:
48			 * real WPA information element */
49			elems->wpa_ie = pos;
50			elems->wpa_ie_len = elen;
51			break;
52		case WMM_OUI_TYPE:
53			/* WMM information element */
54			if (elen < 5) {
55				wpa_printf(MSG_MSGDUMP, "short WMM "
56					   "information element ignored "
57					   "(len=%lu)",
58					   (unsigned long) elen);
59				return -1;
60			}
61			switch (pos[4]) {
62			case WMM_OUI_SUBTYPE_INFORMATION_ELEMENT:
63			case WMM_OUI_SUBTYPE_PARAMETER_ELEMENT:
64				/*
65				 * Share same pointer since only one of these
66				 * is used and they start with same data.
67				 * Length field can be used to distinguish the
68				 * IEs.
69				 */
70				elems->wmm = pos;
71				elems->wmm_len = elen;
72				break;
73			case WMM_OUI_SUBTYPE_TSPEC_ELEMENT:
74				elems->wmm_tspec = pos;
75				elems->wmm_tspec_len = elen;
76				break;
77			default:
78				wpa_printf(MSG_EXCESSIVE, "unknown WMM "
79					   "information element ignored "
80					   "(subtype=%d len=%lu)",
81					   pos[4], (unsigned long) elen);
82				return -1;
83			}
84			break;
85		case 4:
86			/* Wi-Fi Protected Setup (WPS) IE */
87			elems->wps_ie = pos;
88			elems->wps_ie_len = elen;
89			break;
90		default:
91			wpa_printf(MSG_EXCESSIVE, "Unknown Microsoft "
92				   "information element ignored "
93				   "(type=%d len=%lu)",
94				   pos[3], (unsigned long) elen);
95			return -1;
96		}
97		break;
98
99	case OUI_WFA:
100		switch (pos[3]) {
101		case P2P_OUI_TYPE:
102			/* Wi-Fi Alliance - P2P IE */
103			elems->p2p = pos;
104			elems->p2p_len = elen;
105			break;
106		default:
107			wpa_printf(MSG_MSGDUMP, "Unknown WFA "
108				   "information element ignored "
109				   "(type=%d len=%lu)\n",
110				   pos[3], (unsigned long) elen);
111			return -1;
112		}
113		break;
114
115	case OUI_BROADCOM:
116		switch (pos[3]) {
117		case VENDOR_HT_CAPAB_OUI_TYPE:
118			elems->vendor_ht_cap = pos;
119			elems->vendor_ht_cap_len = elen;
120			break;
121		default:
122			wpa_printf(MSG_EXCESSIVE, "Unknown Broadcom "
123				   "information element ignored "
124				   "(type=%d len=%lu)",
125				   pos[3], (unsigned long) elen);
126			return -1;
127		}
128		break;
129
130	default:
131		wpa_printf(MSG_EXCESSIVE, "unknown vendor specific "
132			   "information element ignored (vendor OUI "
133			   "%02x:%02x:%02x len=%lu)",
134			   pos[0], pos[1], pos[2], (unsigned long) elen);
135		return -1;
136	}
137
138	return 0;
139}
140
141
142/**
143 * ieee802_11_parse_elems - Parse information elements in management frames
144 * @start: Pointer to the start of IEs
145 * @len: Length of IE buffer in octets
146 * @elems: Data structure for parsed elements
147 * @show_errors: Whether to show parsing errors in debug log
148 * Returns: Parsing result
149 */
150ParseRes ieee802_11_parse_elems(const u8 *start, size_t len,
151				struct ieee802_11_elems *elems,
152				int show_errors)
153{
154	size_t left = len;
155	const u8 *pos = start;
156	int unknown = 0;
157
158	os_memset(elems, 0, sizeof(*elems));
159
160	while (left >= 2) {
161		u8 id, elen;
162
163		id = *pos++;
164		elen = *pos++;
165		left -= 2;
166
167		if (elen > left) {
168			if (show_errors) {
169				wpa_printf(MSG_DEBUG, "IEEE 802.11 element "
170					   "parse failed (id=%d elen=%d "
171					   "left=%lu)",
172					   id, elen, (unsigned long) left);
173				wpa_hexdump(MSG_MSGDUMP, "IEs", start, len);
174			}
175			return ParseFailed;
176		}
177
178		switch (id) {
179		case WLAN_EID_SSID:
180			elems->ssid = pos;
181			elems->ssid_len = elen;
182			break;
183		case WLAN_EID_SUPP_RATES:
184			elems->supp_rates = pos;
185			elems->supp_rates_len = elen;
186			break;
187		case WLAN_EID_FH_PARAMS:
188			elems->fh_params = pos;
189			elems->fh_params_len = elen;
190			break;
191		case WLAN_EID_DS_PARAMS:
192			elems->ds_params = pos;
193			elems->ds_params_len = elen;
194			break;
195		case WLAN_EID_CF_PARAMS:
196			elems->cf_params = pos;
197			elems->cf_params_len = elen;
198			break;
199		case WLAN_EID_TIM:
200			elems->tim = pos;
201			elems->tim_len = elen;
202			break;
203		case WLAN_EID_IBSS_PARAMS:
204			elems->ibss_params = pos;
205			elems->ibss_params_len = elen;
206			break;
207		case WLAN_EID_CHALLENGE:
208			elems->challenge = pos;
209			elems->challenge_len = elen;
210			break;
211		case WLAN_EID_ERP_INFO:
212			elems->erp_info = pos;
213			elems->erp_info_len = elen;
214			break;
215		case WLAN_EID_EXT_SUPP_RATES:
216			elems->ext_supp_rates = pos;
217			elems->ext_supp_rates_len = elen;
218			break;
219		case WLAN_EID_VENDOR_SPECIFIC:
220			if (ieee802_11_parse_vendor_specific(pos, elen,
221							     elems,
222							     show_errors))
223				unknown++;
224			break;
225		case WLAN_EID_RSN:
226			elems->rsn_ie = pos;
227			elems->rsn_ie_len = elen;
228			break;
229		case WLAN_EID_PWR_CAPABILITY:
230			elems->power_cap = pos;
231			elems->power_cap_len = elen;
232			break;
233		case WLAN_EID_SUPPORTED_CHANNELS:
234			elems->supp_channels = pos;
235			elems->supp_channels_len = elen;
236			break;
237		case WLAN_EID_MOBILITY_DOMAIN:
238			elems->mdie = pos;
239			elems->mdie_len = elen;
240			break;
241		case WLAN_EID_FAST_BSS_TRANSITION:
242			elems->ftie = pos;
243			elems->ftie_len = elen;
244			break;
245		case WLAN_EID_TIMEOUT_INTERVAL:
246			elems->timeout_int = pos;
247			elems->timeout_int_len = elen;
248			break;
249		case WLAN_EID_HT_CAP:
250			elems->ht_capabilities = pos;
251			elems->ht_capabilities_len = elen;
252			break;
253		case WLAN_EID_HT_OPERATION:
254			elems->ht_operation = pos;
255			elems->ht_operation_len = elen;
256			break;
257		case WLAN_EID_LINK_ID:
258			if (elen < 18)
259				break;
260			elems->link_id = pos;
261			break;
262		default:
263			unknown++;
264			if (!show_errors)
265				break;
266			wpa_printf(MSG_MSGDUMP, "IEEE 802.11 element parse "
267				   "ignored unknown element (id=%d elen=%d)",
268				   id, elen);
269			break;
270		}
271
272		left -= elen;
273		pos += elen;
274	}
275
276	if (left)
277		return ParseFailed;
278
279	return unknown ? ParseUnknown : ParseOK;
280}
281
282
283int ieee802_11_ie_count(const u8 *ies, size_t ies_len)
284{
285	int count = 0;
286	const u8 *pos, *end;
287
288	if (ies == NULL)
289		return 0;
290
291	pos = ies;
292	end = ies + ies_len;
293
294	while (pos + 2 <= end) {
295		if (pos + 2 + pos[1] > end)
296			break;
297		count++;
298		pos += 2 + pos[1];
299	}
300
301	return count;
302}
303
304
305struct wpabuf * ieee802_11_vendor_ie_concat(const u8 *ies, size_t ies_len,
306					    u32 oui_type)
307{
308	struct wpabuf *buf;
309	const u8 *end, *pos, *ie;
310
311	pos = ies;
312	end = ies + ies_len;
313	ie = NULL;
314
315	while (pos + 1 < end) {
316		if (pos + 2 + pos[1] > end)
317			return NULL;
318		if (pos[0] == WLAN_EID_VENDOR_SPECIFIC && pos[1] >= 4 &&
319		    WPA_GET_BE32(&pos[2]) == oui_type) {
320			ie = pos;
321			break;
322		}
323		pos += 2 + pos[1];
324	}
325
326	if (ie == NULL)
327		return NULL; /* No specified vendor IE found */
328
329	buf = wpabuf_alloc(ies_len);
330	if (buf == NULL)
331		return NULL;
332
333	/*
334	 * There may be multiple vendor IEs in the message, so need to
335	 * concatenate their data fields.
336	 */
337	while (pos + 1 < end) {
338		if (pos + 2 + pos[1] > end)
339			break;
340		if (pos[0] == WLAN_EID_VENDOR_SPECIFIC && pos[1] >= 4 &&
341		    WPA_GET_BE32(&pos[2]) == oui_type)
342			wpabuf_put_data(buf, pos + 6, pos[1] - 4);
343		pos += 2 + pos[1];
344	}
345
346	return buf;
347}
348