xref: /freebsd/contrib/wpa/src/common/ieee802_11_common.c (revision 8d20be1e22095c27faf8fe8b2f0d089739cc742e)
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 
16 static 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  */
154 ParseRes 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 		case WLAN_EID_SSID_LIST:
288 			elems->ssid_list = pos;
289 			elems->ssid_list_len = elen;
290 			break;
291 		default:
292 			unknown++;
293 			if (!show_errors)
294 				break;
295 			wpa_printf(MSG_MSGDUMP, "IEEE 802.11 element parse "
296 				   "ignored unknown element (id=%d elen=%d)",
297 				   id, elen);
298 			break;
299 		}
300 
301 		left -= elen;
302 		pos += elen;
303 	}
304 
305 	if (left)
306 		return ParseFailed;
307 
308 	return unknown ? ParseUnknown : ParseOK;
309 }
310 
311 
312 int ieee802_11_ie_count(const u8 *ies, size_t ies_len)
313 {
314 	int count = 0;
315 	const u8 *pos, *end;
316 
317 	if (ies == NULL)
318 		return 0;
319 
320 	pos = ies;
321 	end = ies + ies_len;
322 
323 	while (pos + 2 <= end) {
324 		if (pos + 2 + pos[1] > end)
325 			break;
326 		count++;
327 		pos += 2 + pos[1];
328 	}
329 
330 	return count;
331 }
332 
333 
334 struct wpabuf * ieee802_11_vendor_ie_concat(const u8 *ies, size_t ies_len,
335 					    u32 oui_type)
336 {
337 	struct wpabuf *buf;
338 	const u8 *end, *pos, *ie;
339 
340 	pos = ies;
341 	end = ies + ies_len;
342 	ie = NULL;
343 
344 	while (pos + 1 < end) {
345 		if (pos + 2 + pos[1] > end)
346 			return NULL;
347 		if (pos[0] == WLAN_EID_VENDOR_SPECIFIC && pos[1] >= 4 &&
348 		    WPA_GET_BE32(&pos[2]) == oui_type) {
349 			ie = pos;
350 			break;
351 		}
352 		pos += 2 + pos[1];
353 	}
354 
355 	if (ie == NULL)
356 		return NULL; /* No specified vendor IE found */
357 
358 	buf = wpabuf_alloc(ies_len);
359 	if (buf == NULL)
360 		return NULL;
361 
362 	/*
363 	 * There may be multiple vendor IEs in the message, so need to
364 	 * concatenate their data fields.
365 	 */
366 	while (pos + 1 < end) {
367 		if (pos + 2 + pos[1] > end)
368 			break;
369 		if (pos[0] == WLAN_EID_VENDOR_SPECIFIC && pos[1] >= 4 &&
370 		    WPA_GET_BE32(&pos[2]) == oui_type)
371 			wpabuf_put_data(buf, pos + 6, pos[1] - 4);
372 		pos += 2 + pos[1];
373 	}
374 
375 	return buf;
376 }
377 
378 
379 const u8 * get_hdr_bssid(const struct ieee80211_hdr *hdr, size_t len)
380 {
381 	u16 fc, type, stype;
382 
383 	/*
384 	 * PS-Poll frames are 16 bytes. All other frames are
385 	 * 24 bytes or longer.
386 	 */
387 	if (len < 16)
388 		return NULL;
389 
390 	fc = le_to_host16(hdr->frame_control);
391 	type = WLAN_FC_GET_TYPE(fc);
392 	stype = WLAN_FC_GET_STYPE(fc);
393 
394 	switch (type) {
395 	case WLAN_FC_TYPE_DATA:
396 		if (len < 24)
397 			return NULL;
398 		switch (fc & (WLAN_FC_FROMDS | WLAN_FC_TODS)) {
399 		case WLAN_FC_FROMDS | WLAN_FC_TODS:
400 		case WLAN_FC_TODS:
401 			return hdr->addr1;
402 		case WLAN_FC_FROMDS:
403 			return hdr->addr2;
404 		default:
405 			return NULL;
406 		}
407 	case WLAN_FC_TYPE_CTRL:
408 		if (stype != WLAN_FC_STYPE_PSPOLL)
409 			return NULL;
410 		return hdr->addr1;
411 	case WLAN_FC_TYPE_MGMT:
412 		return hdr->addr3;
413 	default:
414 		return NULL;
415 	}
416 }
417 
418 
419 int hostapd_config_wmm_ac(struct hostapd_wmm_ac_params wmm_ac_params[],
420 			  const char *name, const char *val)
421 {
422 	int num, v;
423 	const char *pos;
424 	struct hostapd_wmm_ac_params *ac;
425 
426 	/* skip 'wme_ac_' or 'wmm_ac_' prefix */
427 	pos = name + 7;
428 	if (os_strncmp(pos, "be_", 3) == 0) {
429 		num = 0;
430 		pos += 3;
431 	} else if (os_strncmp(pos, "bk_", 3) == 0) {
432 		num = 1;
433 		pos += 3;
434 	} else if (os_strncmp(pos, "vi_", 3) == 0) {
435 		num = 2;
436 		pos += 3;
437 	} else if (os_strncmp(pos, "vo_", 3) == 0) {
438 		num = 3;
439 		pos += 3;
440 	} else {
441 		wpa_printf(MSG_ERROR, "Unknown WMM name '%s'", pos);
442 		return -1;
443 	}
444 
445 	ac = &wmm_ac_params[num];
446 
447 	if (os_strcmp(pos, "aifs") == 0) {
448 		v = atoi(val);
449 		if (v < 1 || v > 255) {
450 			wpa_printf(MSG_ERROR, "Invalid AIFS value %d", v);
451 			return -1;
452 		}
453 		ac->aifs = v;
454 	} else if (os_strcmp(pos, "cwmin") == 0) {
455 		v = atoi(val);
456 		if (v < 0 || v > 12) {
457 			wpa_printf(MSG_ERROR, "Invalid cwMin value %d", v);
458 			return -1;
459 		}
460 		ac->cwmin = v;
461 	} else if (os_strcmp(pos, "cwmax") == 0) {
462 		v = atoi(val);
463 		if (v < 0 || v > 12) {
464 			wpa_printf(MSG_ERROR, "Invalid cwMax value %d", v);
465 			return -1;
466 		}
467 		ac->cwmax = v;
468 	} else if (os_strcmp(pos, "txop_limit") == 0) {
469 		v = atoi(val);
470 		if (v < 0 || v > 0xffff) {
471 			wpa_printf(MSG_ERROR, "Invalid txop value %d", v);
472 			return -1;
473 		}
474 		ac->txop_limit = v;
475 	} else if (os_strcmp(pos, "acm") == 0) {
476 		v = atoi(val);
477 		if (v < 0 || v > 1) {
478 			wpa_printf(MSG_ERROR, "Invalid acm value %d", v);
479 			return -1;
480 		}
481 		ac->admission_control_mandatory = v;
482 	} else {
483 		wpa_printf(MSG_ERROR, "Unknown wmm_ac_ field '%s'", pos);
484 		return -1;
485 	}
486 
487 	return 0;
488 }
489