13070Spst/*
23070Spst * IEEE 802.11 Common routines
33070Spst * Copyright (c) 2002-2012, Jouni Malinen <j@w1.fi>
43070Spst *
53070Spst * This software may be distributed under the terms of the BSD license.
63070Spst * See README for more details.
73070Spst */
83070Spst
93070Spst#include "includes.h"
103070Spst
113070Spst#include "common.h"
123070Spst#include "ieee802_11_defs.h"
133070Spst#include "ieee802_11_common.h"
143070Spst
153070Spst
163070Spststatic int ieee802_11_parse_vendor_specific(const u8 *pos, size_t elen,
173070Spst					    struct ieee802_11_elems *elems,
183070Spst					    int show_errors)
193070Spst{
203070Spst	unsigned int oui;
213070Spst
223070Spst	/* first 3 bytes in vendor specific information element are the IEEE
233070Spst	 * OUI of the vendor. The following byte is used a vendor specific
243070Spst	 * sub-type. */
253070Spst	if (elen < 4) {
263070Spst		if (show_errors) {
273070Spst			wpa_printf(MSG_MSGDUMP, "short vendor specific "
283070Spst				   "information element ignored (len=%lu)",
293070Spst				   (unsigned long) elen);
308870Srgrimes		}
313070Spst		return -1;
323070Spst	}
333070Spst
343070Spst	oui = WPA_GET_BE24(pos);
353070Spst	switch (oui) {
363070Spst	case OUI_MICROSOFT:
378870Srgrimes		/* Microsoft/Wi-Fi information elements are further typed and
383070Spst		 * subtyped */
393070Spst		switch (pos[3]) {
403070Spst		case 1:
413070Spst			/* Microsoft OUI (00:50:F2) with OUI Type 1:
423070Spst			 * real WPA information element */
433070Spst			elems->wpa_ie = pos;
443070Spst			elems->wpa_ie_len = elen;
453070Spst			break;
463070Spst		case WMM_OUI_TYPE:
473070Spst			/* WMM information element */
483070Spst			if (elen < 5) {
493070Spst				wpa_printf(MSG_MSGDUMP, "short WMM "
503070Spst					   "information element ignored "
513070Spst					   "(len=%lu)",
523070Spst					   (unsigned long) elen);
5392986Sobrien				return -1;
5492986Sobrien			}
553070Spst			switch (pos[4]) {
563070Spst			case WMM_OUI_SUBTYPE_INFORMATION_ELEMENT:
573070Spst			case WMM_OUI_SUBTYPE_PARAMETER_ELEMENT:
583070Spst				/*
593070Spst				 * Share same pointer since only one of these
603070Spst				 * is used and they start with same data.
613070Spst				 * Length field can be used to distinguish the
623070Spst				 * IEs.
63211276Sume				 */
643070Spst				elems->wmm = pos;
6565532Snectar				elems->wmm_len = elen;
6665532Snectar				break;
6717903Speter			case WMM_OUI_SUBTYPE_TSPEC_ELEMENT:
6817903Speter				elems->wmm_tspec = pos;
69145602Sume				elems->wmm_tspec_len = elen;
703070Spst				break;
713070Spst			default:
72145633Sume				wpa_printf(MSG_EXCESSIVE, "unknown WMM "
733070Spst					   "information element ignored "
74145633Sume					   "(subtype=%d len=%lu)",
75254700Sjilles					   pos[4], (unsigned long) elen);
763070Spst				return -1;
77145633Sume			}
78145633Sume			break;
793070Spst		case 4:
803070Spst			/* Wi-Fi Protected Setup (WPS) IE */
813070Spst			elems->wps_ie = pos;
82145633Sume			elems->wps_ie_len = elen;
833070Spst			break;
84145633Sume		default:
85145633Sume			wpa_printf(MSG_EXCESSIVE, "Unknown Microsoft "
86145633Sume				   "information element ignored "
873070Spst				   "(type=%d len=%lu)",
883070Spst				   pos[3], (unsigned long) elen);
893070Spst			return -1;
90145715Sume		}
91157779Sume		break;
92157779Sume
933070Spst	case OUI_WFA:
94145633Sume		switch (pos[3]) {
9592889Sobrien		case P2P_OUI_TYPE:
9617903Speter			/* Wi-Fi Alliance - P2P IE */
97145633Sume			elems->p2p = pos;
983070Spst			elems->p2p_len = elen;
99254700Sjilles			break;
100157779Sume		case WFD_OUI_TYPE:
101157779Sume			/* Wi-Fi Alliance - WFD IE */
10213408Speter			elems->wfd = pos;
10317903Speter			elems->wfd_len = elen;
104145633Sume			break;
105157779Sume		case HS20_INDICATION_OUI_TYPE:
106157779Sume			/* Hotspot 2.0 */
10713408Speter			elems->hs20 = pos;
1083070Spst			elems->hs20_len = elen;
1093070Spst			break;
110139612Ssobomax		default:
111139612Ssobomax			wpa_printf(MSG_MSGDUMP, "Unknown WFA "
112139612Ssobomax				   "information element ignored "
11313408Speter				   "(type=%d len=%lu)\n",
1143070Spst				   pos[3], (unsigned long) elen);
1153070Spst			return -1;
116145633Sume		}
11717903Speter		break;
11817903Speter
119145633Sume	case OUI_BROADCOM:
120145715Sume		switch (pos[3]) {
121145633Sume		case VENDOR_HT_CAPAB_OUI_TYPE:
122145633Sume			elems->vendor_ht_cap = pos;
12317903Speter			elems->vendor_ht_cap_len = elen;
12417903Speter			break;
12517903Speter		default:
12617903Speter			wpa_printf(MSG_EXCESSIVE, "Unknown Broadcom "
12717903Speter				   "information element ignored "
12817903Speter				   "(type=%d len=%lu)",
12917903Speter				   pos[3], (unsigned long) elen);
13013408Speter			return -1;
13117903Speter		}
132145633Sume		break;
133145633Sume
134145633Sume	default:
135145633Sume		wpa_printf(MSG_EXCESSIVE, "unknown vendor specific "
136145633Sume			   "information element ignored (vendor OUI "
1373070Spst			   "%02x:%02x:%02x len=%lu)",
1383070Spst			   pos[0], pos[1], pos[2], (unsigned long) elen);
139145633Sume		return -1;
140145633Sume	}
141145633Sume
142145633Sume	return 0;
143145633Sume}
144145633Sume
145145633Sume
146145633Sume/**
147157779Sume * ieee802_11_parse_elems - Parse information elements in management frames
148157779Sume * @start: Pointer to the start of IEs
149145633Sume * @len: Length of IE buffer in octets
150145633Sume * @elems: Data structure for parsed elements
151145633Sume * @show_errors: Whether to show parsing errors in debug log
152145633Sume * Returns: Parsing result
1533070Spst */
1543070SpstParseRes ieee802_11_parse_elems(const u8 *start, size_t len,
1553070Spst				struct ieee802_11_elems *elems,
1563070Spst				int show_errors)
1573070Spst{
158145633Sume	size_t left = len;
159145633Sume	const u8 *pos = start;
160145633Sume	int unknown = 0;
161145633Sume
162145633Sume	os_memset(elems, 0, sizeof(*elems));
163145633Sume
164145633Sume	while (left >= 2) {
165145633Sume		u8 id, elen;
166145633Sume
167145633Sume		id = *pos++;
168145633Sume		elen = *pos++;
1693070Spst		left -= 2;
1703070Spst
171157779Sume		if (elen > left) {
172157779Sume			if (show_errors) {
1733070Spst				wpa_printf(MSG_DEBUG, "IEEE 802.11 element "
1743070Spst					   "parse failed (id=%d elen=%d "
175145715Sume					   "left=%lu)",
176157779Sume					   id, elen, (unsigned long) left);
177157779Sume				wpa_hexdump(MSG_MSGDUMP, "IEs", start, len);
178145715Sume			}
179157779Sume			return ParseFailed;
180157779Sume		}
181157779Sume
182157779Sume		switch (id) {
183157779Sume		case WLAN_EID_SSID:
184157779Sume			elems->ssid = pos;
185157779Sume			elems->ssid_len = elen;
186157779Sume			break;
187157779Sume		case WLAN_EID_SUPP_RATES:
188156960Sume			elems->supp_rates = pos;
189157779Sume			elems->supp_rates_len = elen;
190157779Sume			break;
191157779Sume		case WLAN_EID_FH_PARAMS:
192157779Sume			elems->fh_params = pos;
193157779Sume			elems->fh_params_len = elen;
194157779Sume			break;
195157779Sume		case WLAN_EID_DS_PARAMS:
196211276Sume			elems->ds_params = pos;
197211276Sume			elems->ds_params_len = elen;
198211276Sume			break;
199211276Sume		case WLAN_EID_CF_PARAMS:
200211276Sume			elems->cf_params = pos;
201157779Sume			elems->cf_params_len = elen;
202157779Sume			break;
203145715Sume		case WLAN_EID_TIM:
204145715Sume			elems->tim = pos;
205145633Sume			elems->tim_len = elen;
206145633Sume			break;
207145633Sume		case WLAN_EID_IBSS_PARAMS:
208145633Sume			elems->ibss_params = pos;
209157779Sume			elems->ibss_params_len = elen;
210157779Sume			break;
211145633Sume		case WLAN_EID_CHALLENGE:
212145633Sume			elems->challenge = pos;
213157779Sume			elems->challenge_len = elen;
214157779Sume			break;
215157779Sume		case WLAN_EID_ERP_INFO:
216157779Sume			elems->erp_info = pos;
217157779Sume			elems->erp_info_len = elen;
218145633Sume			break;
219145633Sume		case WLAN_EID_EXT_SUPP_RATES:
22065532Snectar			elems->ext_supp_rates = pos;
221145715Sume			elems->ext_supp_rates_len = elen;
22265532Snectar			break;
22317922Speter		case WLAN_EID_VENDOR_SPECIFIC:
22417903Speter			if (ieee802_11_parse_vendor_specific(pos, elen,
225157779Sume							     elems,
226157779Sume							     show_errors))
227157779Sume				unknown++;
228157779Sume			break;
229145633Sume		case WLAN_EID_RSN:
23092889Sobrien			elems->rsn_ie = pos;
231157779Sume			elems->rsn_ie_len = elen;
232145633Sume			break;
23365532Snectar		case WLAN_EID_PWR_CAPABILITY:
23465532Snectar			elems->power_cap = pos;
23565532Snectar			elems->power_cap_len = elen;
236157779Sume			break;
237157779Sume		case WLAN_EID_SUPPORTED_CHANNELS:
238157779Sume			elems->supp_channels = pos;
239157779Sume			elems->supp_channels_len = elen;
240157779Sume			break;
241145633Sume		case WLAN_EID_MOBILITY_DOMAIN:
242157779Sume			elems->mdie = pos;
243157779Sume			elems->mdie_len = elen;
244157779Sume			break;
245157779Sume		case WLAN_EID_FAST_BSS_TRANSITION:
246157779Sume			elems->ftie = pos;
247157779Sume			elems->ftie_len = elen;
248157779Sume			break;
249157779Sume		case WLAN_EID_TIMEOUT_INTERVAL:
250157779Sume			elems->timeout_int = pos;
251157779Sume			elems->timeout_int_len = elen;
252157779Sume			break;
253157779Sume		case WLAN_EID_HT_CAP:
25417903Speter			elems->ht_capabilities = pos;
255157779Sume			elems->ht_capabilities_len = elen;
256157779Sume			break;
257157779Sume		case WLAN_EID_HT_OPERATION:
258157779Sume			elems->ht_operation = pos;
259157779Sume			elems->ht_operation_len = elen;
260145715Sume			break;
261157779Sume		case WLAN_EID_VHT_CAP:
2623070Spst			elems->vht_capabilities = pos;
263157779Sume			elems->vht_capabilities_len = elen;
2643070Spst			break;
2653070Spst		case WLAN_EID_VHT_OPERATION:
2663070Spst			elems->vht_operation = pos;
2673070Spst			elems->vht_operation_len = elen;
268157779Sume			break;
269145633Sume		case WLAN_EID_LINK_ID:
270157779Sume			if (elen < 18)
271157779Sume				break;
272157779Sume			elems->link_id = pos;
273157779Sume			break;
274157779Sume		case WLAN_EID_INTERWORKING:
275211276Sume			elems->interworking = pos;
276211276Sume			elems->interworking_len = elen;
277157779Sume			break;
278211276Sume		case WLAN_EID_EXT_CAPAB:
279157779Sume			elems->ext_capab = pos;
280157779Sume			elems->ext_capab_len = elen;
281157779Sume			break;
2823070Spst		case WLAN_EID_BSS_MAX_IDLE_PERIOD:
2833070Spst			if (elen < 3)
284145715Sume				break;
28565532Snectar			elems->bss_max_idle_period = pos;
28665532Snectar			break;
287158477Sume		case WLAN_EID_SSID_LIST:
288158477Sume			elems->ssid_list = pos;
289158477Sume			elems->ssid_list_len = elen;
290157779Sume			break;
291157779Sume		default:
292157779Sume			unknown++;
293157779Sume			if (!show_errors)
294145633Sume				break;
295157779Sume			wpa_printf(MSG_MSGDUMP, "IEEE 802.11 element parse "
296145633Sume				   "ignored unknown element (id=%d elen=%d)",
2973070Spst				   id, elen);
298158477Sume			break;
299158477Sume		}
30065532Snectar
301157779Sume		left -= elen;
302157779Sume		pos += elen;
303157779Sume	}
304157779Sume
305157779Sume	if (left)
30665532Snectar		return ParseFailed;
307157779Sume
308157779Sume	return unknown ? ParseUnknown : ParseOK;
309157779Sume}
310157779Sume
311157779Sume
312157779Sumeint ieee802_11_ie_count(const u8 *ies, size_t ies_len)
313157779Sume{
314157779Sume	int count = 0;
315157779Sume	const u8 *pos, *end;
316157779Sume
317157779Sume	if (ies == NULL)
318157779Sume		return 0;
319157779Sume
320157779Sume	pos = ies;
321157779Sume	end = ies + ies_len;
322157779Sume
323157779Sume	while (pos + 2 <= end) {
324145728Sume		if (pos + 2 + pos[1] > end)
3253070Spst			break;
326145728Sume		count++;
327157779Sume		pos += 2 + pos[1];
32865532Snectar	}
329157779Sume
330157779Sume	return count;
331157779Sume}
332211276Sume
333211276Sume
334157779Sumestruct wpabuf * ieee802_11_vendor_ie_concat(const u8 *ies, size_t ies_len,
335211276Sume					    u32 oui_type)
336157779Sume{
337157779Sume	struct wpabuf *buf;
338157779Sume	const u8 *end, *pos, *ie;
3393070Spst
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
379const 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
419int 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