ieee802_11_common.c revision 04949598a23f501be6eec21697465fd46a28840a
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 HS20_INDICATION_OUI_TYPE:
101			/* Hotspot 2.0 */
102			elems->hs20 = pos;
103			elems->hs20_len = elen;
104			break;
105		default:
106			wpa_printf(MSG_MSGDUMP, "Unknown WFA "
107				   "information element ignored "
108				   "(type=%d len=%lu)\n",
109				   pos[3], (unsigned long) elen);
110			return -1;
111		}
112		break;
113
114	case OUI_BROADCOM:
115		switch (pos[3]) {
116		case VENDOR_HT_CAPAB_OUI_TYPE:
117			elems->vendor_ht_cap = pos;
118			elems->vendor_ht_cap_len = elen;
119			break;
120		default:
121			wpa_printf(MSG_EXCESSIVE, "Unknown Broadcom "
122				   "information element ignored "
123				   "(type=%d len=%lu)",
124				   pos[3], (unsigned long) elen);
125			return -1;
126		}
127		break;
128
129	default:
130		wpa_printf(MSG_EXCESSIVE, "unknown vendor specific "
131			   "information element ignored (vendor OUI "
132			   "%02x:%02x:%02x len=%lu)",
133			   pos[0], pos[1], pos[2], (unsigned long) elen);
134		return -1;
135	}
136
137	return 0;
138}
139
140
141/**
142 * ieee802_11_parse_elems - Parse information elements in management frames
143 * @start: Pointer to the start of IEs
144 * @len: Length of IE buffer in octets
145 * @elems: Data structure for parsed elements
146 * @show_errors: Whether to show parsing errors in debug log
147 * Returns: Parsing result
148 */
149ParseRes ieee802_11_parse_elems(const u8 *start, size_t len,
150				struct ieee802_11_elems *elems,
151				int show_errors)
152{
153	size_t left = len;
154	const u8 *pos = start;
155	int unknown = 0;
156
157	os_memset(elems, 0, sizeof(*elems));
158
159	while (left >= 2) {
160		u8 id, elen;
161
162		id = *pos++;
163		elen = *pos++;
164		left -= 2;
165
166		if (elen > left) {
167			if (show_errors) {
168				wpa_printf(MSG_DEBUG, "IEEE 802.11 element "
169					   "parse failed (id=%d elen=%d "
170					   "left=%lu)",
171					   id, elen, (unsigned long) left);
172				wpa_hexdump(MSG_MSGDUMP, "IEs", start, len);
173			}
174			return ParseFailed;
175		}
176
177		switch (id) {
178		case WLAN_EID_SSID:
179			elems->ssid = pos;
180			elems->ssid_len = elen;
181			break;
182		case WLAN_EID_SUPP_RATES:
183			elems->supp_rates = pos;
184			elems->supp_rates_len = elen;
185			break;
186		case WLAN_EID_FH_PARAMS:
187			elems->fh_params = pos;
188			elems->fh_params_len = elen;
189			break;
190		case WLAN_EID_DS_PARAMS:
191			elems->ds_params = pos;
192			elems->ds_params_len = elen;
193			break;
194		case WLAN_EID_CF_PARAMS:
195			elems->cf_params = pos;
196			elems->cf_params_len = elen;
197			break;
198		case WLAN_EID_TIM:
199			elems->tim = pos;
200			elems->tim_len = elen;
201			break;
202		case WLAN_EID_IBSS_PARAMS:
203			elems->ibss_params = pos;
204			elems->ibss_params_len = elen;
205			break;
206		case WLAN_EID_CHALLENGE:
207			elems->challenge = pos;
208			elems->challenge_len = elen;
209			break;
210		case WLAN_EID_ERP_INFO:
211			elems->erp_info = pos;
212			elems->erp_info_len = elen;
213			break;
214		case WLAN_EID_EXT_SUPP_RATES:
215			elems->ext_supp_rates = pos;
216			elems->ext_supp_rates_len = elen;
217			break;
218		case WLAN_EID_VENDOR_SPECIFIC:
219			if (ieee802_11_parse_vendor_specific(pos, elen,
220							     elems,
221							     show_errors))
222				unknown++;
223			break;
224		case WLAN_EID_RSN:
225			elems->rsn_ie = pos;
226			elems->rsn_ie_len = elen;
227			break;
228		case WLAN_EID_PWR_CAPABILITY:
229			elems->power_cap = pos;
230			elems->power_cap_len = elen;
231			break;
232		case WLAN_EID_SUPPORTED_CHANNELS:
233			elems->supp_channels = pos;
234			elems->supp_channels_len = elen;
235			break;
236		case WLAN_EID_MOBILITY_DOMAIN:
237			elems->mdie = pos;
238			elems->mdie_len = elen;
239			break;
240		case WLAN_EID_FAST_BSS_TRANSITION:
241			elems->ftie = pos;
242			elems->ftie_len = elen;
243			break;
244		case WLAN_EID_TIMEOUT_INTERVAL:
245			elems->timeout_int = pos;
246			elems->timeout_int_len = elen;
247			break;
248		case WLAN_EID_HT_CAP:
249			elems->ht_capabilities = pos;
250			elems->ht_capabilities_len = elen;
251			break;
252		case WLAN_EID_HT_OPERATION:
253			elems->ht_operation = pos;
254			elems->ht_operation_len = elen;
255			break;
256		case WLAN_EID_LINK_ID:
257			if (elen < 18)
258				break;
259			elems->link_id = pos;
260			break;
261		case WLAN_EID_INTERWORKING:
262			elems->interworking = pos;
263			elems->interworking_len = elen;
264			break;
265		case WLAN_EID_EXT_CAPAB:
266			elems->ext_capab = pos;
267			elems->ext_capab_len = elen;
268			break;
269		case WLAN_EID_BSS_MAX_IDLE_PERIOD:
270			if (elen < 3)
271				break;
272			elems->bss_max_idle_period = pos;
273			break;
274		default:
275			unknown++;
276			if (!show_errors)
277				break;
278			wpa_printf(MSG_MSGDUMP, "IEEE 802.11 element parse "
279				   "ignored unknown element (id=%d elen=%d)",
280				   id, elen);
281			break;
282		}
283
284		left -= elen;
285		pos += elen;
286	}
287
288	if (left)
289		return ParseFailed;
290
291	return unknown ? ParseUnknown : ParseOK;
292}
293
294
295int ieee802_11_ie_count(const u8 *ies, size_t ies_len)
296{
297	int count = 0;
298	const u8 *pos, *end;
299
300	if (ies == NULL)
301		return 0;
302
303	pos = ies;
304	end = ies + ies_len;
305
306	while (pos + 2 <= end) {
307		if (pos + 2 + pos[1] > end)
308			break;
309		count++;
310		pos += 2 + pos[1];
311	}
312
313	return count;
314}
315
316
317struct wpabuf * ieee802_11_vendor_ie_concat(const u8 *ies, size_t ies_len,
318					    u32 oui_type)
319{
320	struct wpabuf *buf;
321	const u8 *end, *pos, *ie;
322
323	pos = ies;
324	end = ies + ies_len;
325	ie = NULL;
326
327	while (pos + 1 < end) {
328		if (pos + 2 + pos[1] > end)
329			return NULL;
330		if (pos[0] == WLAN_EID_VENDOR_SPECIFIC && pos[1] >= 4 &&
331		    WPA_GET_BE32(&pos[2]) == oui_type) {
332			ie = pos;
333			break;
334		}
335		pos += 2 + pos[1];
336	}
337
338	if (ie == NULL)
339		return NULL; /* No specified vendor IE found */
340
341	buf = wpabuf_alloc(ies_len);
342	if (buf == NULL)
343		return NULL;
344
345	/*
346	 * There may be multiple vendor IEs in the message, so need to
347	 * concatenate their data fields.
348	 */
349	while (pos + 1 < end) {
350		if (pos + 2 + pos[1] > end)
351			break;
352		if (pos[0] == WLAN_EID_VENDOR_SPECIFIC && pos[1] >= 4 &&
353		    WPA_GET_BE32(&pos[2]) == oui_type)
354			wpabuf_put_data(buf, pos + 6, pos[1] - 4);
355		pos += 2 + pos[1];
356	}
357
358	return buf;
359}
360
361
362const u8 * get_hdr_bssid(const struct ieee80211_hdr *hdr, size_t len)
363{
364	u16 fc, type, stype;
365
366	/*
367	 * PS-Poll frames are 16 bytes. All other frames are
368	 * 24 bytes or longer.
369	 */
370	if (len < 16)
371		return NULL;
372
373	fc = le_to_host16(hdr->frame_control);
374	type = WLAN_FC_GET_TYPE(fc);
375	stype = WLAN_FC_GET_STYPE(fc);
376
377	switch (type) {
378	case WLAN_FC_TYPE_DATA:
379		if (len < 24)
380			return NULL;
381		switch (fc & (WLAN_FC_FROMDS | WLAN_FC_TODS)) {
382		case WLAN_FC_FROMDS | WLAN_FC_TODS:
383		case WLAN_FC_TODS:
384			return hdr->addr1;
385		case WLAN_FC_FROMDS:
386			return hdr->addr2;
387		default:
388			return NULL;
389		}
390	case WLAN_FC_TYPE_CTRL:
391		if (stype != WLAN_FC_STYPE_PSPOLL)
392			return NULL;
393		return hdr->addr1;
394	case WLAN_FC_TYPE_MGMT:
395		return hdr->addr3;
396	default:
397		return NULL;
398	}
399}
400