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