• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * IEEE 802.11 Common routines
3  * Copyright (c) 2002-2013, 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 "defs.h"
13 #include "ieee802_11_defs.h"
14 #include "ieee802_11_common.h"
15 
16 
ieee802_11_parse_vendor_specific(const u8 * pos,size_t elen,struct ieee802_11_elems * elems,int show_errors)17 static int ieee802_11_parse_vendor_specific(const u8 *pos, size_t elen,
18 					    struct ieee802_11_elems *elems,
19 					    int show_errors)
20 {
21 	unsigned int oui;
22 
23 	/* first 3 bytes in vendor specific information element are the IEEE
24 	 * OUI of the vendor. The following byte is used a vendor specific
25 	 * sub-type. */
26 	if (elen < 4) {
27 		if (show_errors) {
28 			wpa_printf(MSG_MSGDUMP, "short vendor specific "
29 				   "information element ignored (len=%lu)",
30 				   (unsigned long) elen);
31 		}
32 		return -1;
33 	}
34 
35 	oui = WPA_GET_BE24(pos);
36 	switch (oui) {
37 	case OUI_MICROSOFT:
38 		/* Microsoft/Wi-Fi information elements are further typed and
39 		 * subtyped */
40 		switch (pos[3]) {
41 		case 1:
42 			/* Microsoft OUI (00:50:F2) with OUI Type 1:
43 			 * real WPA information element */
44 			elems->wpa_ie = pos;
45 			elems->wpa_ie_len = elen;
46 			break;
47 		case WMM_OUI_TYPE:
48 			/* WMM information element */
49 			if (elen < 5) {
50 				wpa_printf(MSG_MSGDUMP, "short WMM "
51 					   "information element ignored "
52 					   "(len=%lu)",
53 					   (unsigned long) elen);
54 				return -1;
55 			}
56 			switch (pos[4]) {
57 			case WMM_OUI_SUBTYPE_INFORMATION_ELEMENT:
58 			case WMM_OUI_SUBTYPE_PARAMETER_ELEMENT:
59 				/*
60 				 * Share same pointer since only one of these
61 				 * is used and they start with same data.
62 				 * Length field can be used to distinguish the
63 				 * IEs.
64 				 */
65 				elems->wmm = pos;
66 				elems->wmm_len = elen;
67 				break;
68 			case WMM_OUI_SUBTYPE_TSPEC_ELEMENT:
69 				elems->wmm_tspec = pos;
70 				elems->wmm_tspec_len = elen;
71 				break;
72 			default:
73 				wpa_printf(MSG_EXCESSIVE, "unknown WMM "
74 					   "information element ignored "
75 					   "(subtype=%d len=%lu)",
76 					   pos[4], (unsigned long) elen);
77 				return -1;
78 			}
79 			break;
80 		case 4:
81 			/* Wi-Fi Protected Setup (WPS) IE */
82 			elems->wps_ie = pos;
83 			elems->wps_ie_len = elen;
84 			break;
85 		default:
86 			wpa_printf(MSG_EXCESSIVE, "Unknown Microsoft "
87 				   "information element ignored "
88 				   "(type=%d len=%lu)",
89 				   pos[3], (unsigned long) elen);
90 			return -1;
91 		}
92 		break;
93 
94 	case OUI_WFA:
95 		switch (pos[3]) {
96 		case P2P_OUI_TYPE:
97 			/* Wi-Fi Alliance - P2P IE */
98 			elems->p2p = pos;
99 			elems->p2p_len = elen;
100 			break;
101 		case WFD_OUI_TYPE:
102 			/* Wi-Fi Alliance - WFD IE */
103 			elems->wfd = pos;
104 			elems->wfd_len = elen;
105 			break;
106 		case HS20_INDICATION_OUI_TYPE:
107 			/* Hotspot 2.0 */
108 			elems->hs20 = pos;
109 			elems->hs20_len = elen;
110 			break;
111 		case HS20_OSEN_OUI_TYPE:
112 			/* Hotspot 2.0 OSEN */
113 			elems->osen = pos;
114 			elems->osen_len = elen;
115 			break;
116 		default:
117 			wpa_printf(MSG_MSGDUMP, "Unknown WFA "
118 				   "information element ignored "
119 				   "(type=%d len=%lu)",
120 				   pos[3], (unsigned long) elen);
121 			return -1;
122 		}
123 		break;
124 
125 	case OUI_BROADCOM:
126 		switch (pos[3]) {
127 		case VENDOR_HT_CAPAB_OUI_TYPE:
128 			elems->vendor_ht_cap = pos;
129 			elems->vendor_ht_cap_len = elen;
130 			break;
131 		default:
132 			wpa_printf(MSG_EXCESSIVE, "Unknown Broadcom "
133 				   "information element ignored "
134 				   "(type=%d len=%lu)",
135 				   pos[3], (unsigned long) elen);
136 			return -1;
137 		}
138 		break;
139 
140 	default:
141 		wpa_printf(MSG_EXCESSIVE, "unknown vendor specific "
142 			   "information element ignored (vendor OUI "
143 			   "%02x:%02x:%02x len=%lu)",
144 			   pos[0], pos[1], pos[2], (unsigned long) elen);
145 		return -1;
146 	}
147 
148 	return 0;
149 }
150 
151 
152 /**
153  * ieee802_11_parse_elems - Parse information elements in management frames
154  * @start: Pointer to the start of IEs
155  * @len: Length of IE buffer in octets
156  * @elems: Data structure for parsed elements
157  * @show_errors: Whether to show parsing errors in debug log
158  * Returns: Parsing result
159  */
ieee802_11_parse_elems(const u8 * start,size_t len,struct ieee802_11_elems * elems,int show_errors)160 ParseRes ieee802_11_parse_elems(const u8 *start, size_t len,
161 				struct ieee802_11_elems *elems,
162 				int show_errors)
163 {
164 	size_t left = len;
165 	const u8 *pos = start;
166 	int unknown = 0;
167 
168 	os_memset(elems, 0, sizeof(*elems));
169 
170 	while (left >= 2) {
171 		u8 id, elen;
172 
173 		id = *pos++;
174 		elen = *pos++;
175 		left -= 2;
176 
177 		if (elen > left) {
178 			if (show_errors) {
179 				wpa_printf(MSG_DEBUG, "IEEE 802.11 element "
180 					   "parse failed (id=%d elen=%d "
181 					   "left=%lu)",
182 					   id, elen, (unsigned long) left);
183 				wpa_hexdump(MSG_MSGDUMP, "IEs", start, len);
184 			}
185 			return ParseFailed;
186 		}
187 
188 		switch (id) {
189 		case WLAN_EID_SSID:
190 			elems->ssid = pos;
191 			elems->ssid_len = elen;
192 			break;
193 		case WLAN_EID_SUPP_RATES:
194 			elems->supp_rates = pos;
195 			elems->supp_rates_len = elen;
196 			break;
197 		case WLAN_EID_DS_PARAMS:
198 			elems->ds_params = pos;
199 			elems->ds_params_len = elen;
200 			break;
201 		case WLAN_EID_CF_PARAMS:
202 		case WLAN_EID_TIM:
203 			break;
204 		case WLAN_EID_CHALLENGE:
205 			elems->challenge = pos;
206 			elems->challenge_len = elen;
207 			break;
208 		case WLAN_EID_ERP_INFO:
209 			elems->erp_info = pos;
210 			elems->erp_info_len = elen;
211 			break;
212 		case WLAN_EID_EXT_SUPP_RATES:
213 			elems->ext_supp_rates = pos;
214 			elems->ext_supp_rates_len = elen;
215 			break;
216 		case WLAN_EID_VENDOR_SPECIFIC:
217 			if (ieee802_11_parse_vendor_specific(pos, elen,
218 							     elems,
219 							     show_errors))
220 				unknown++;
221 			break;
222 		case WLAN_EID_RSN:
223 			elems->rsn_ie = pos;
224 			elems->rsn_ie_len = elen;
225 			break;
226 		case WLAN_EID_PWR_CAPABILITY:
227 			break;
228 		case WLAN_EID_SUPPORTED_CHANNELS:
229 			elems->supp_channels = pos;
230 			elems->supp_channels_len = elen;
231 			break;
232 		case WLAN_EID_MOBILITY_DOMAIN:
233 			elems->mdie = pos;
234 			elems->mdie_len = elen;
235 			break;
236 		case WLAN_EID_FAST_BSS_TRANSITION:
237 			elems->ftie = pos;
238 			elems->ftie_len = elen;
239 			break;
240 		case WLAN_EID_TIMEOUT_INTERVAL:
241 			elems->timeout_int = pos;
242 			elems->timeout_int_len = elen;
243 			break;
244 		case WLAN_EID_HT_CAP:
245 			elems->ht_capabilities = pos;
246 			elems->ht_capabilities_len = elen;
247 			break;
248 		case WLAN_EID_HT_OPERATION:
249 			elems->ht_operation = pos;
250 			elems->ht_operation_len = elen;
251 			break;
252 		case WLAN_EID_VHT_CAP:
253 			elems->vht_capabilities = pos;
254 			elems->vht_capabilities_len = elen;
255 			break;
256 		case WLAN_EID_VHT_OPERATION:
257 			elems->vht_operation = pos;
258 			elems->vht_operation_len = elen;
259 			break;
260 		case WLAN_EID_VHT_OPERATING_MODE_NOTIFICATION:
261 			if (elen != 1)
262 				break;
263 			elems->vht_opmode_notif = pos;
264 			break;
265 		case WLAN_EID_LINK_ID:
266 			if (elen < 18)
267 				break;
268 			elems->link_id = pos;
269 			break;
270 		case WLAN_EID_INTERWORKING:
271 			elems->interworking = pos;
272 			elems->interworking_len = elen;
273 			break;
274 		case WLAN_EID_QOS_MAP_SET:
275 			if (elen < 16)
276 				break;
277 			elems->qos_map_set = pos;
278 			elems->qos_map_set_len = elen;
279 			break;
280 		case WLAN_EID_EXT_CAPAB:
281 			elems->ext_capab = pos;
282 			elems->ext_capab_len = elen;
283 			break;
284 		case WLAN_EID_BSS_MAX_IDLE_PERIOD:
285 			if (elen < 3)
286 				break;
287 			elems->bss_max_idle_period = pos;
288 			break;
289 		case WLAN_EID_SSID_LIST:
290 			elems->ssid_list = pos;
291 			elems->ssid_list_len = elen;
292 			break;
293 		default:
294 			unknown++;
295 			if (!show_errors)
296 				break;
297 			wpa_printf(MSG_MSGDUMP, "IEEE 802.11 element parse "
298 				   "ignored unknown element (id=%d elen=%d)",
299 				   id, elen);
300 			break;
301 		}
302 
303 		left -= elen;
304 		pos += elen;
305 	}
306 
307 	if (left)
308 		return ParseFailed;
309 
310 	return unknown ? ParseUnknown : ParseOK;
311 }
312 
313 
ieee802_11_ie_count(const u8 * ies,size_t ies_len)314 int ieee802_11_ie_count(const u8 *ies, size_t ies_len)
315 {
316 	int count = 0;
317 	const u8 *pos, *end;
318 
319 	if (ies == NULL)
320 		return 0;
321 
322 	pos = ies;
323 	end = ies + ies_len;
324 
325 	while (pos + 2 <= end) {
326 		if (pos + 2 + pos[1] > end)
327 			break;
328 		count++;
329 		pos += 2 + pos[1];
330 	}
331 
332 	return count;
333 }
334 
335 
ieee802_11_vendor_ie_concat(const u8 * ies,size_t ies_len,u32 oui_type)336 struct wpabuf * ieee802_11_vendor_ie_concat(const u8 *ies, size_t ies_len,
337 					    u32 oui_type)
338 {
339 	struct wpabuf *buf;
340 	const u8 *end, *pos, *ie;
341 
342 	pos = ies;
343 	end = ies + ies_len;
344 	ie = NULL;
345 
346 	while (pos + 1 < end) {
347 		if (pos + 2 + pos[1] > end)
348 			return NULL;
349 		if (pos[0] == WLAN_EID_VENDOR_SPECIFIC && pos[1] >= 4 &&
350 		    WPA_GET_BE32(&pos[2]) == oui_type) {
351 			ie = pos;
352 			break;
353 		}
354 		pos += 2 + pos[1];
355 	}
356 
357 	if (ie == NULL)
358 		return NULL; /* No specified vendor IE found */
359 
360 	buf = wpabuf_alloc(ies_len);
361 	if (buf == NULL)
362 		return NULL;
363 
364 	/*
365 	 * There may be multiple vendor IEs in the message, so need to
366 	 * concatenate their data fields.
367 	 */
368 	while (pos + 1 < end) {
369 		if (pos + 2 + pos[1] > end)
370 			break;
371 		if (pos[0] == WLAN_EID_VENDOR_SPECIFIC && pos[1] >= 4 &&
372 		    WPA_GET_BE32(&pos[2]) == oui_type)
373 			wpabuf_put_data(buf, pos + 6, pos[1] - 4);
374 		pos += 2 + pos[1];
375 	}
376 
377 	return buf;
378 }
379 
380 
get_hdr_bssid(const struct ieee80211_hdr * hdr,size_t len)381 const u8 * get_hdr_bssid(const struct ieee80211_hdr *hdr, size_t len)
382 {
383 	u16 fc, type, stype;
384 
385 	/*
386 	 * PS-Poll frames are 16 bytes. All other frames are
387 	 * 24 bytes or longer.
388 	 */
389 	if (len < 16)
390 		return NULL;
391 
392 	fc = le_to_host16(hdr->frame_control);
393 	type = WLAN_FC_GET_TYPE(fc);
394 	stype = WLAN_FC_GET_STYPE(fc);
395 
396 	switch (type) {
397 	case WLAN_FC_TYPE_DATA:
398 		if (len < 24)
399 			return NULL;
400 		switch (fc & (WLAN_FC_FROMDS | WLAN_FC_TODS)) {
401 		case WLAN_FC_FROMDS | WLAN_FC_TODS:
402 		case WLAN_FC_TODS:
403 			return hdr->addr1;
404 		case WLAN_FC_FROMDS:
405 			return hdr->addr2;
406 		default:
407 			return NULL;
408 		}
409 	case WLAN_FC_TYPE_CTRL:
410 		if (stype != WLAN_FC_STYPE_PSPOLL)
411 			return NULL;
412 		return hdr->addr1;
413 	case WLAN_FC_TYPE_MGMT:
414 		return hdr->addr3;
415 	default:
416 		return NULL;
417 	}
418 }
419 
420 
hostapd_config_wmm_ac(struct hostapd_wmm_ac_params wmm_ac_params[],const char * name,const char * val)421 int hostapd_config_wmm_ac(struct hostapd_wmm_ac_params wmm_ac_params[],
422 			  const char *name, const char *val)
423 {
424 	int num, v;
425 	const char *pos;
426 	struct hostapd_wmm_ac_params *ac;
427 
428 	/* skip 'wme_ac_' or 'wmm_ac_' prefix */
429 	pos = name + 7;
430 	if (os_strncmp(pos, "be_", 3) == 0) {
431 		num = 0;
432 		pos += 3;
433 	} else if (os_strncmp(pos, "bk_", 3) == 0) {
434 		num = 1;
435 		pos += 3;
436 	} else if (os_strncmp(pos, "vi_", 3) == 0) {
437 		num = 2;
438 		pos += 3;
439 	} else if (os_strncmp(pos, "vo_", 3) == 0) {
440 		num = 3;
441 		pos += 3;
442 	} else {
443 		wpa_printf(MSG_ERROR, "Unknown WMM name '%s'", pos);
444 		return -1;
445 	}
446 
447 	ac = &wmm_ac_params[num];
448 
449 	if (os_strcmp(pos, "aifs") == 0) {
450 		v = atoi(val);
451 		if (v < 1 || v > 255) {
452 			wpa_printf(MSG_ERROR, "Invalid AIFS value %d", v);
453 			return -1;
454 		}
455 		ac->aifs = v;
456 	} else if (os_strcmp(pos, "cwmin") == 0) {
457 		v = atoi(val);
458 		if (v < 0 || v > 12) {
459 			wpa_printf(MSG_ERROR, "Invalid cwMin value %d", v);
460 			return -1;
461 		}
462 		ac->cwmin = v;
463 	} else if (os_strcmp(pos, "cwmax") == 0) {
464 		v = atoi(val);
465 		if (v < 0 || v > 12) {
466 			wpa_printf(MSG_ERROR, "Invalid cwMax value %d", v);
467 			return -1;
468 		}
469 		ac->cwmax = v;
470 	} else if (os_strcmp(pos, "txop_limit") == 0) {
471 		v = atoi(val);
472 		if (v < 0 || v > 0xffff) {
473 			wpa_printf(MSG_ERROR, "Invalid txop value %d", v);
474 			return -1;
475 		}
476 		ac->txop_limit = v;
477 	} else if (os_strcmp(pos, "acm") == 0) {
478 		v = atoi(val);
479 		if (v < 0 || v > 1) {
480 			wpa_printf(MSG_ERROR, "Invalid acm value %d", v);
481 			return -1;
482 		}
483 		ac->admission_control_mandatory = v;
484 	} else {
485 		wpa_printf(MSG_ERROR, "Unknown wmm_ac_ field '%s'", pos);
486 		return -1;
487 	}
488 
489 	return 0;
490 }
491 
492 
ieee80211_freq_to_chan(int freq,u8 * channel)493 enum hostapd_hw_mode ieee80211_freq_to_chan(int freq, u8 *channel)
494 {
495 	enum hostapd_hw_mode mode = NUM_HOSTAPD_MODES;
496 
497 	if (freq >= 2412 && freq <= 2472) {
498 		mode = HOSTAPD_MODE_IEEE80211G;
499 		*channel = (freq - 2407) / 5;
500 	} else if (freq == 2484) {
501 		mode = HOSTAPD_MODE_IEEE80211B;
502 		*channel = 14;
503 	} else if (freq >= 4900 && freq < 5000) {
504 		mode = HOSTAPD_MODE_IEEE80211A;
505 		*channel = (freq - 4000) / 5;
506 	} else if (freq >= 5000 && freq < 5900) {
507 		mode = HOSTAPD_MODE_IEEE80211A;
508 		*channel = (freq - 5000) / 5;
509 	} else if (freq >= 56160 + 2160 * 1 && freq <= 56160 + 2160 * 4) {
510 		mode = HOSTAPD_MODE_IEEE80211AD;
511 		*channel = (freq - 56160) / 2160;
512 	}
513 
514 	return mode;
515 }
516 
517 
is_11b(u8 rate)518 static int is_11b(u8 rate)
519 {
520 	return rate == 0x02 || rate == 0x04 || rate == 0x0b || rate == 0x16;
521 }
522 
523 
supp_rates_11b_only(struct ieee802_11_elems * elems)524 int supp_rates_11b_only(struct ieee802_11_elems *elems)
525 {
526 	int num_11b = 0, num_others = 0;
527 	int i;
528 
529 	if (elems->supp_rates == NULL && elems->ext_supp_rates == NULL)
530 		return 0;
531 
532 	for (i = 0; elems->supp_rates && i < elems->supp_rates_len; i++) {
533 		if (is_11b(elems->supp_rates[i]))
534 			num_11b++;
535 		else
536 			num_others++;
537 	}
538 
539 	for (i = 0; elems->ext_supp_rates && i < elems->ext_supp_rates_len;
540 	     i++) {
541 		if (is_11b(elems->ext_supp_rates[i]))
542 			num_11b++;
543 		else
544 			num_others++;
545 	}
546 
547 	return num_11b > 0 && num_others == 0;
548 }
549 
550 
fc2str(u16 fc)551 const char * fc2str(u16 fc)
552 {
553 	u16 stype = WLAN_FC_GET_STYPE(fc);
554 #define C2S(x) case x: return #x;
555 
556 	switch (WLAN_FC_GET_TYPE(fc)) {
557 	case WLAN_FC_TYPE_MGMT:
558 		switch (stype) {
559 		C2S(WLAN_FC_STYPE_ASSOC_REQ)
560 		C2S(WLAN_FC_STYPE_ASSOC_RESP)
561 		C2S(WLAN_FC_STYPE_REASSOC_REQ)
562 		C2S(WLAN_FC_STYPE_REASSOC_RESP)
563 		C2S(WLAN_FC_STYPE_PROBE_REQ)
564 		C2S(WLAN_FC_STYPE_PROBE_RESP)
565 		C2S(WLAN_FC_STYPE_BEACON)
566 		C2S(WLAN_FC_STYPE_ATIM)
567 		C2S(WLAN_FC_STYPE_DISASSOC)
568 		C2S(WLAN_FC_STYPE_AUTH)
569 		C2S(WLAN_FC_STYPE_DEAUTH)
570 		C2S(WLAN_FC_STYPE_ACTION)
571 		}
572 		break;
573 	case WLAN_FC_TYPE_CTRL:
574 		switch (stype) {
575 		C2S(WLAN_FC_STYPE_PSPOLL)
576 		C2S(WLAN_FC_STYPE_RTS)
577 		C2S(WLAN_FC_STYPE_CTS)
578 		C2S(WLAN_FC_STYPE_ACK)
579 		C2S(WLAN_FC_STYPE_CFEND)
580 		C2S(WLAN_FC_STYPE_CFENDACK)
581 		}
582 		break;
583 	case WLAN_FC_TYPE_DATA:
584 		switch (stype) {
585 		C2S(WLAN_FC_STYPE_DATA)
586 		C2S(WLAN_FC_STYPE_DATA_CFACK)
587 		C2S(WLAN_FC_STYPE_DATA_CFPOLL)
588 		C2S(WLAN_FC_STYPE_DATA_CFACKPOLL)
589 		C2S(WLAN_FC_STYPE_NULLFUNC)
590 		C2S(WLAN_FC_STYPE_CFACK)
591 		C2S(WLAN_FC_STYPE_CFPOLL)
592 		C2S(WLAN_FC_STYPE_CFACKPOLL)
593 		C2S(WLAN_FC_STYPE_QOS_DATA)
594 		C2S(WLAN_FC_STYPE_QOS_DATA_CFACK)
595 		C2S(WLAN_FC_STYPE_QOS_DATA_CFPOLL)
596 		C2S(WLAN_FC_STYPE_QOS_DATA_CFACKPOLL)
597 		C2S(WLAN_FC_STYPE_QOS_NULL)
598 		C2S(WLAN_FC_STYPE_QOS_CFPOLL)
599 		C2S(WLAN_FC_STYPE_QOS_CFACKPOLL)
600 		}
601 		break;
602 	}
603 	return "WLAN_FC_TYPE_UNKNOWN";
604 #undef C2S
605 }
606