Home | History | Annotate | Download | only in common
      1 /*
      2  * IEEE 802.11 Common routines
      3  * Copyright (c) 2002-2009, Jouni Malinen <j (at) 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 		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  */
    144 ParseRes 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 
    281 int 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 
    303 struct 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 
    348 const 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