]>
Commit | Line | Data |
---|---|---|
06a05884 LF |
1 | /****************************************************************************** |
2 | * | |
3 | * Copyright(c) 2007 - 2011 Realtek Corporation. All rights reserved. | |
4 | * | |
5 | * This program is free software; you can redistribute it and/or modify it | |
6 | * under the terms of version 2 of the GNU General Public License as | |
7 | * published by the Free Software Foundation. | |
8 | * | |
9 | * This program is distributed in the hope that it will be useful, but WITHOUT | |
10 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | |
11 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | |
12 | * more details. | |
13 | * | |
14 | * You should have received a copy of the GNU General Public License along with | |
15 | * this program; if not, write to the Free Software Foundation, Inc., | |
16 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA | |
17 | * | |
18 | * | |
19 | ******************************************************************************/ | |
20 | #define _IEEE80211_C | |
21 | ||
22 | #include <drv_types.h> | |
0aba3f41 | 23 | #include <osdep_intf.h> |
06a05884 LF |
24 | #include <ieee80211.h> |
25 | #include <wifi.h> | |
26 | #include <osdep_service.h> | |
27 | #include <wlan_bssdef.h> | |
06a05884 LF |
28 | |
29 | u8 RTW_WPA_OUI_TYPE[] = { 0x00, 0x50, 0xf2, 1 }; | |
06a05884 LF |
30 | u8 WPA_AUTH_KEY_MGMT_NONE[] = { 0x00, 0x50, 0xf2, 0 }; |
31 | u8 WPA_AUTH_KEY_MGMT_UNSPEC_802_1X[] = { 0x00, 0x50, 0xf2, 1 }; | |
32 | u8 WPA_AUTH_KEY_MGMT_PSK_OVER_802_1X[] = { 0x00, 0x50, 0xf2, 2 }; | |
33 | u8 WPA_CIPHER_SUITE_NONE[] = { 0x00, 0x50, 0xf2, 0 }; | |
34 | u8 WPA_CIPHER_SUITE_WEP40[] = { 0x00, 0x50, 0xf2, 1 }; | |
35 | u8 WPA_CIPHER_SUITE_TKIP[] = { 0x00, 0x50, 0xf2, 2 }; | |
36 | u8 WPA_CIPHER_SUITE_WRAP[] = { 0x00, 0x50, 0xf2, 3 }; | |
37 | u8 WPA_CIPHER_SUITE_CCMP[] = { 0x00, 0x50, 0xf2, 4 }; | |
38 | u8 WPA_CIPHER_SUITE_WEP104[] = { 0x00, 0x50, 0xf2, 5 }; | |
39 | ||
40 | u16 RSN_VERSION_BSD = 1; | |
41 | u8 RSN_AUTH_KEY_MGMT_UNSPEC_802_1X[] = { 0x00, 0x0f, 0xac, 1 }; | |
42 | u8 RSN_AUTH_KEY_MGMT_PSK_OVER_802_1X[] = { 0x00, 0x0f, 0xac, 2 }; | |
43 | u8 RSN_CIPHER_SUITE_NONE[] = { 0x00, 0x0f, 0xac, 0 }; | |
44 | u8 RSN_CIPHER_SUITE_WEP40[] = { 0x00, 0x0f, 0xac, 1 }; | |
45 | u8 RSN_CIPHER_SUITE_TKIP[] = { 0x00, 0x0f, 0xac, 2 }; | |
46 | u8 RSN_CIPHER_SUITE_WRAP[] = { 0x00, 0x0f, 0xac, 3 }; | |
47 | u8 RSN_CIPHER_SUITE_CCMP[] = { 0x00, 0x0f, 0xac, 4 }; | |
48 | u8 RSN_CIPHER_SUITE_WEP104[] = { 0x00, 0x0f, 0xac, 5 }; | |
49 | /* */ | |
50 | /* for adhoc-master to generate ie and provide supported-rate to fw */ | |
51 | /* */ | |
52 | ||
53 | static u8 WIFI_CCKRATES[] = { | |
54 | (IEEE80211_CCK_RATE_1MB | IEEE80211_BASIC_RATE_MASK), | |
55 | (IEEE80211_CCK_RATE_2MB | IEEE80211_BASIC_RATE_MASK), | |
56 | (IEEE80211_CCK_RATE_5MB | IEEE80211_BASIC_RATE_MASK), | |
57 | (IEEE80211_CCK_RATE_11MB | IEEE80211_BASIC_RATE_MASK) | |
58 | }; | |
59 | ||
60 | static u8 WIFI_OFDMRATES[] = { | |
61 | (IEEE80211_OFDM_RATE_6MB), | |
62 | (IEEE80211_OFDM_RATE_9MB), | |
63 | (IEEE80211_OFDM_RATE_12MB), | |
64 | (IEEE80211_OFDM_RATE_18MB), | |
65 | (IEEE80211_OFDM_RATE_24MB), | |
66 | IEEE80211_OFDM_RATE_36MB, | |
67 | IEEE80211_OFDM_RATE_48MB, | |
68 | IEEE80211_OFDM_RATE_54MB | |
69 | }; | |
70 | ||
71 | ||
72 | int rtw_get_bit_value_from_ieee_value(u8 val) | |
73 | { | |
74 | unsigned char dot11_rate_table[] = { | |
75 | 2, 4, 11, 22, 12, 18, 24, 36, 48, | |
76 | 72, 96, 108, 0}; /* last element must be zero!! */ | |
77 | ||
78 | int i = 0; | |
79 | while (dot11_rate_table[i] != 0) { | |
80 | if (dot11_rate_table[i] == val) | |
81 | return BIT(i); | |
82 | i++; | |
83 | } | |
84 | return 0; | |
85 | } | |
86 | ||
87 | uint rtw_is_cckrates_included(u8 *rate) | |
88 | { | |
89 | u32 i = 0; | |
90 | ||
91 | while (rate[i] != 0) { | |
92 | if ((((rate[i]) & 0x7f) == 2) || (((rate[i]) & 0x7f) == 4) || | |
93 | (((rate[i]) & 0x7f) == 11) || (((rate[i]) & 0x7f) == 22)) | |
94 | return true; | |
95 | i++; | |
96 | } | |
97 | return false; | |
98 | } | |
99 | ||
100 | uint rtw_is_cckratesonly_included(u8 *rate) | |
101 | { | |
102 | u32 i = 0; | |
103 | ||
104 | while (rate[i] != 0) { | |
105 | if ((((rate[i]) & 0x7f) != 2) && (((rate[i]) & 0x7f) != 4) && | |
106 | (((rate[i]) & 0x7f) != 11) && (((rate[i]) & 0x7f) != 22)) | |
107 | return false; | |
108 | i++; | |
109 | } | |
110 | ||
111 | return true; | |
112 | } | |
113 | ||
114 | int rtw_check_network_type(unsigned char *rate, int ratelen, int channel) | |
115 | { | |
116 | if (channel > 14) { | |
117 | if ((rtw_is_cckrates_included(rate)) == true) | |
118 | return WIRELESS_INVALID; | |
119 | else | |
120 | return WIRELESS_11A; | |
121 | } else { /* could be pure B, pure G, or B/G */ | |
122 | if ((rtw_is_cckratesonly_included(rate)) == true) | |
123 | return WIRELESS_11B; | |
124 | else if ((rtw_is_cckrates_included(rate)) == true) | |
125 | return WIRELESS_11BG; | |
126 | else | |
127 | return WIRELESS_11G; | |
128 | } | |
129 | } | |
130 | ||
131 | u8 *rtw_set_fixed_ie(unsigned char *pbuf, unsigned int len, unsigned char *source, | |
132 | unsigned int *frlen) | |
133 | { | |
134 | memcpy((void *)pbuf, (void *)source, len); | |
135 | *frlen = *frlen + len; | |
136 | return pbuf + len; | |
137 | } | |
138 | ||
139 | /* rtw_set_ie will update frame length */ | |
140 | u8 *rtw_set_ie | |
141 | ( | |
142 | u8 *pbuf, | |
143 | int index, | |
144 | uint len, | |
145 | u8 *source, | |
146 | uint *frlen /* frame length */ | |
147 | ) | |
148 | { | |
06a05884 LF |
149 | *pbuf = (u8)index; |
150 | ||
151 | *(pbuf + 1) = (u8)len; | |
152 | ||
153 | if (len > 0) | |
154 | memcpy((void *)(pbuf + 2), (void *)source, len); | |
155 | ||
156 | *frlen = *frlen + (len + 2); | |
157 | ||
fbb57f6d | 158 | return pbuf + len + 2; |
06a05884 LF |
159 | } |
160 | ||
7be921a2 | 161 | inline u8 *rtw_set_ie_ch_switch(u8 *buf, u32 *buf_len, u8 ch_switch_mode, |
06a05884 LF |
162 | u8 new_ch, u8 ch_switch_cnt) |
163 | { | |
164 | u8 ie_data[3]; | |
165 | ||
166 | ie_data[0] = ch_switch_mode; | |
167 | ie_data[1] = new_ch; | |
168 | ie_data[2] = ch_switch_cnt; | |
169 | return rtw_set_ie(buf, WLAN_EID_CHANNEL_SWITCH, 3, ie_data, buf_len); | |
170 | } | |
171 | ||
172 | inline u8 secondary_ch_offset_to_hal_ch_offset(u8 ch_offset) | |
173 | { | |
174 | if (ch_offset == SCN) | |
175 | return HAL_PRIME_CHNL_OFFSET_DONT_CARE; | |
176 | else if (ch_offset == SCA) | |
177 | return HAL_PRIME_CHNL_OFFSET_UPPER; | |
178 | else if (ch_offset == SCB) | |
179 | return HAL_PRIME_CHNL_OFFSET_LOWER; | |
180 | ||
181 | return HAL_PRIME_CHNL_OFFSET_DONT_CARE; | |
182 | } | |
183 | ||
184 | inline u8 hal_ch_offset_to_secondary_ch_offset(u8 ch_offset) | |
185 | { | |
186 | if (ch_offset == HAL_PRIME_CHNL_OFFSET_DONT_CARE) | |
187 | return SCN; | |
188 | else if (ch_offset == HAL_PRIME_CHNL_OFFSET_LOWER) | |
189 | return SCB; | |
190 | else if (ch_offset == HAL_PRIME_CHNL_OFFSET_UPPER) | |
191 | return SCA; | |
192 | ||
193 | return SCN; | |
194 | } | |
195 | ||
196 | inline u8 *rtw_set_ie_secondary_ch_offset(u8 *buf, u32 *buf_len, u8 secondary_ch_offset) | |
197 | { | |
198 | return rtw_set_ie(buf, WLAN_EID_SECONDARY_CHANNEL_OFFSET, 1, &secondary_ch_offset, buf_len); | |
199 | } | |
200 | ||
201 | inline u8 *rtw_set_ie_mesh_ch_switch_parm(u8 *buf, u32 *buf_len, u8 ttl, | |
202 | u8 flags, u16 reason, u16 precedence) | |
203 | { | |
204 | u8 ie_data[6]; | |
205 | ||
206 | ie_data[0] = ttl; | |
207 | ie_data[1] = flags; | |
83a9b669 | 208 | *(u16 *)(ie_data+2) = cpu_to_le16(reason); |
209 | *(u16 *)(ie_data+4) = cpu_to_le16(precedence); | |
06a05884 LF |
210 | |
211 | return rtw_set_ie(buf, 0x118, 6, ie_data, buf_len); | |
212 | } | |
213 | ||
214 | /*---------------------------------------------------------------------------- | |
215 | index: the information element id index, limit is the limit for search | |
216 | -----------------------------------------------------------------------------*/ | |
217 | u8 *rtw_get_ie(u8 *pbuf, int index, int *len, int limit) | |
218 | { | |
219 | int tmp, i; | |
220 | u8 *p; | |
9cdddda5 | 221 | if (limit < 1) |
06a05884 | 222 | return NULL; |
06a05884 LF |
223 | |
224 | p = pbuf; | |
225 | i = 0; | |
226 | *len = 0; | |
227 | while (1) { | |
228 | if (*p == index) { | |
229 | *len = *(p + 1); | |
230 | return p; | |
231 | } else { | |
232 | tmp = *(p + 1); | |
233 | p += (tmp + 2); | |
234 | i += (tmp + 2); | |
235 | } | |
236 | if (i >= limit) | |
237 | break; | |
238 | } | |
06a05884 LF |
239 | return NULL; |
240 | } | |
241 | ||
242 | /** | |
243 | * rtw_get_ie_ex - Search specific IE from a series of IEs | |
244 | * @in_ie: Address of IEs to search | |
245 | * @in_len: Length limit from in_ie | |
246 | * @eid: Element ID to match | |
247 | * @oui: OUI to match | |
248 | * @oui_len: OUI length | |
249 | * @ie: If not NULL and the specific IE is found, the IE will be copied to the buf starting from the specific IE | |
250 | * @ielen: If not NULL and the specific IE is found, will set to the length of the entire IE | |
251 | * | |
252 | * Returns: The address of the specific IE found, or NULL | |
253 | */ | |
254 | u8 *rtw_get_ie_ex(u8 *in_ie, uint in_len, u8 eid, u8 *oui, u8 oui_len, u8 *ie, uint *ielen) | |
255 | { | |
256 | uint cnt; | |
257 | u8 *target_ie = NULL; | |
258 | ||
259 | ||
260 | if (ielen) | |
261 | *ielen = 0; | |
262 | ||
263 | if (!in_ie || in_len <= 0) | |
264 | return target_ie; | |
265 | ||
266 | cnt = 0; | |
267 | ||
268 | while (cnt < in_len) { | |
f42f52aa | 269 | if (eid == in_ie[cnt] && (!oui || !memcmp(&in_ie[cnt+2], oui, oui_len))) { |
06a05884 LF |
270 | target_ie = &in_ie[cnt]; |
271 | ||
272 | if (ie) | |
273 | memcpy(ie, &in_ie[cnt], in_ie[cnt+1]+2); | |
274 | ||
275 | if (ielen) | |
276 | *ielen = in_ie[cnt+1]+2; | |
277 | ||
278 | break; | |
279 | } else { | |
280 | cnt += in_ie[cnt+1]+2; /* goto next */ | |
281 | } | |
282 | } | |
283 | return target_ie; | |
284 | } | |
285 | ||
286 | /** | |
287 | * rtw_ies_remove_ie - Find matching IEs and remove | |
288 | * @ies: Address of IEs to search | |
289 | * @ies_len: Pointer of length of ies, will update to new length | |
290 | * @offset: The offset to start scarch | |
291 | * @eid: Element ID to match | |
292 | * @oui: OUI to match | |
293 | * @oui_len: OUI length | |
294 | * | |
295 | * Returns: _SUCCESS: ies is updated, _FAIL: not updated | |
296 | */ | |
297 | int rtw_ies_remove_ie(u8 *ies, uint *ies_len, uint offset, u8 eid, u8 *oui, u8 oui_len) | |
298 | { | |
299 | int ret = _FAIL; | |
300 | u8 *target_ie; | |
301 | u32 target_ielen; | |
302 | u8 *start; | |
303 | uint search_len; | |
304 | ||
305 | if (!ies || !ies_len || *ies_len <= offset) | |
306 | goto exit; | |
307 | ||
308 | start = ies + offset; | |
309 | search_len = *ies_len - offset; | |
310 | ||
311 | while (1) { | |
312 | target_ie = rtw_get_ie_ex(start, search_len, eid, oui, oui_len, NULL, &target_ielen); | |
313 | if (target_ie && target_ielen) { | |
314 | u8 buf[MAX_IE_SZ] = {0}; | |
315 | u8 *remain_ies = target_ie + target_ielen; | |
316 | uint remain_len = search_len - (remain_ies - start); | |
317 | ||
318 | memcpy(buf, remain_ies, remain_len); | |
319 | memcpy(target_ie, buf, remain_len); | |
320 | *ies_len = *ies_len - target_ielen; | |
321 | ret = _SUCCESS; | |
322 | ||
323 | start = target_ie; | |
324 | search_len = remain_len; | |
325 | } else { | |
326 | break; | |
327 | } | |
328 | } | |
329 | exit: | |
330 | return ret; | |
331 | } | |
332 | ||
333 | void rtw_set_supported_rate(u8 *SupportedRates, uint mode) | |
334 | { | |
06a05884 | 335 | |
1ce39848 | 336 | memset(SupportedRates, 0, NDIS_802_11_LENGTH_RATES_EX); |
06a05884 LF |
337 | |
338 | switch (mode) { | |
339 | case WIRELESS_11B: | |
340 | memcpy(SupportedRates, WIFI_CCKRATES, IEEE80211_CCK_RATE_LEN); | |
341 | break; | |
342 | case WIRELESS_11G: | |
343 | case WIRELESS_11A: | |
344 | case WIRELESS_11_5N: | |
345 | case WIRELESS_11A_5N:/* Todo: no basic rate for ofdm ? */ | |
346 | memcpy(SupportedRates, WIFI_OFDMRATES, IEEE80211_NUM_OFDM_RATESLEN); | |
347 | break; | |
348 | case WIRELESS_11BG: | |
349 | case WIRELESS_11G_24N: | |
350 | case WIRELESS_11_24N: | |
351 | case WIRELESS_11BG_24N: | |
352 | memcpy(SupportedRates, WIFI_CCKRATES, IEEE80211_CCK_RATE_LEN); | |
353 | memcpy(SupportedRates + IEEE80211_CCK_RATE_LEN, WIFI_OFDMRATES, IEEE80211_NUM_OFDM_RATESLEN); | |
354 | break; | |
355 | } | |
06a05884 LF |
356 | } |
357 | ||
358 | uint rtw_get_rateset_len(u8 *rateset) | |
359 | { | |
360 | uint i = 0; | |
06a05884 LF |
361 | while (1) { |
362 | if ((rateset[i]) == 0) | |
363 | break; | |
364 | if (i > 12) | |
365 | break; | |
366 | i++; | |
367 | } | |
06a05884 LF |
368 | return i; |
369 | } | |
370 | ||
371 | int rtw_generate_ie(struct registry_priv *pregistrypriv) | |
372 | { | |
373 | u8 wireless_mode; | |
374 | int sz = 0, rateLen; | |
375 | struct wlan_bssid_ex *pdev_network = &pregistrypriv->dev_network; | |
376 | u8 *ie = pdev_network->IEs; | |
377 | ||
06a05884 LF |
378 | |
379 | /* timestamp will be inserted by hardware */ | |
380 | sz += 8; | |
381 | ie += sz; | |
382 | ||
383 | /* beacon interval : 2bytes */ | |
384 | *(__le16 *)ie = cpu_to_le16((u16)pdev_network->Configuration.BeaconPeriod);/* BCN_INTERVAL; */ | |
385 | sz += 2; | |
386 | ie += 2; | |
387 | ||
388 | /* capability info */ | |
389 | *(u16 *)ie = 0; | |
390 | ||
391 | *(__le16 *)ie |= cpu_to_le16(cap_IBSS); | |
392 | ||
393 | if (pregistrypriv->preamble == PREAMBLE_SHORT) | |
394 | *(__le16 *)ie |= cpu_to_le16(cap_ShortPremble); | |
395 | ||
396 | if (pdev_network->Privacy) | |
397 | *(__le16 *)ie |= cpu_to_le16(cap_Privacy); | |
398 | ||
399 | sz += 2; | |
400 | ie += 2; | |
401 | ||
402 | /* SSID */ | |
403 | ie = rtw_set_ie(ie, _SSID_IE_, pdev_network->Ssid.SsidLength, pdev_network->Ssid.Ssid, &sz); | |
404 | ||
405 | /* supported rates */ | |
406 | if (pregistrypriv->wireless_mode == WIRELESS_11ABGN) { | |
407 | if (pdev_network->Configuration.DSConfig > 14) | |
408 | wireless_mode = WIRELESS_11A_5N; | |
409 | else | |
410 | wireless_mode = WIRELESS_11BG_24N; | |
411 | } else { | |
412 | wireless_mode = pregistrypriv->wireless_mode; | |
413 | } | |
414 | ||
415 | rtw_set_supported_rate(pdev_network->SupportedRates, wireless_mode); | |
416 | ||
417 | rateLen = rtw_get_rateset_len(pdev_network->SupportedRates); | |
418 | ||
419 | if (rateLen > 8) { | |
420 | ie = rtw_set_ie(ie, _SUPPORTEDRATES_IE_, 8, pdev_network->SupportedRates, &sz); | |
421 | /* ie = rtw_set_ie(ie, _EXT_SUPPORTEDRATES_IE_, (rateLen - 8), (pdev_network->SupportedRates + 8), &sz); */ | |
422 | } else { | |
423 | ie = rtw_set_ie(ie, _SUPPORTEDRATES_IE_, rateLen, pdev_network->SupportedRates, &sz); | |
424 | } | |
425 | ||
426 | /* DS parameter set */ | |
427 | ie = rtw_set_ie(ie, _DSSET_IE_, 1, (u8 *)&(pdev_network->Configuration.DSConfig), &sz); | |
428 | ||
429 | /* IBSS Parameter Set */ | |
430 | ||
431 | ie = rtw_set_ie(ie, _IBSS_PARA_IE_, 2, (u8 *)&(pdev_network->Configuration.ATIMWindow), &sz); | |
432 | ||
433 | if (rateLen > 8) | |
434 | ie = rtw_set_ie(ie, _EXT_SUPPORTEDRATES_IE_, (rateLen - 8), (pdev_network->SupportedRates + 8), &sz); | |
06a05884 LF |
435 | |
436 | return sz; | |
437 | } | |
438 | ||
439 | unsigned char *rtw_get_wpa_ie(unsigned char *pie, int *wpa_ie_len, int limit) | |
440 | { | |
441 | int len; | |
442 | u16 val16; | |
443 | __le16 le_tmp; | |
444 | unsigned char wpa_oui_type[] = {0x00, 0x50, 0xf2, 0x01}; | |
445 | u8 *pbuf = pie; | |
446 | int limit_new = limit; | |
447 | ||
448 | while (1) { | |
449 | pbuf = rtw_get_ie(pbuf, _WPA_IE_ID_, &len, limit_new); | |
450 | ||
451 | if (pbuf) { | |
452 | /* check if oui matches... */ | |
f42f52aa | 453 | if (!memcmp((pbuf + 2), wpa_oui_type, sizeof(wpa_oui_type)) == false) |
06a05884 LF |
454 | goto check_next_ie; |
455 | ||
456 | /* check version... */ | |
457 | memcpy((u8 *)&le_tmp, (pbuf + 6), sizeof(val16)); | |
458 | ||
459 | val16 = le16_to_cpu(le_tmp); | |
460 | if (val16 != 0x0001) | |
461 | goto check_next_ie; | |
462 | *wpa_ie_len = *(pbuf + 1); | |
463 | return pbuf; | |
464 | } else { | |
465 | *wpa_ie_len = 0; | |
466 | return NULL; | |
467 | } | |
468 | ||
469 | check_next_ie: | |
470 | limit_new = limit - (pbuf - pie) - 2 - len; | |
471 | if (limit_new <= 0) | |
472 | break; | |
473 | pbuf += (2 + len); | |
474 | } | |
475 | *wpa_ie_len = 0; | |
476 | return NULL; | |
477 | } | |
478 | ||
479 | unsigned char *rtw_get_wpa2_ie(unsigned char *pie, int *rsn_ie_len, int limit) | |
480 | { | |
481 | ||
482 | return rtw_get_ie(pie, _WPA2_IE_ID_, rsn_ie_len, limit); | |
483 | } | |
484 | ||
485 | int rtw_get_wpa_cipher_suite(u8 *s) | |
486 | { | |
05c9bc1f | 487 | if (!memcmp(s, WPA_CIPHER_SUITE_NONE, WPA_SELECTOR_LEN)) |
06a05884 | 488 | return WPA_CIPHER_NONE; |
05c9bc1f | 489 | if (!memcmp(s, WPA_CIPHER_SUITE_WEP40, WPA_SELECTOR_LEN)) |
06a05884 | 490 | return WPA_CIPHER_WEP40; |
05c9bc1f | 491 | if (!memcmp(s, WPA_CIPHER_SUITE_TKIP, WPA_SELECTOR_LEN)) |
06a05884 | 492 | return WPA_CIPHER_TKIP; |
05c9bc1f | 493 | if (!memcmp(s, WPA_CIPHER_SUITE_CCMP, WPA_SELECTOR_LEN)) |
06a05884 | 494 | return WPA_CIPHER_CCMP; |
05c9bc1f | 495 | if (!memcmp(s, WPA_CIPHER_SUITE_WEP104, WPA_SELECTOR_LEN)) |
06a05884 LF |
496 | return WPA_CIPHER_WEP104; |
497 | ||
498 | return 0; | |
499 | } | |
500 | ||
501 | int rtw_get_wpa2_cipher_suite(u8 *s) | |
502 | { | |
05c9bc1f | 503 | if (!memcmp(s, RSN_CIPHER_SUITE_NONE, RSN_SELECTOR_LEN)) |
06a05884 | 504 | return WPA_CIPHER_NONE; |
05c9bc1f | 505 | if (!memcmp(s, RSN_CIPHER_SUITE_WEP40, RSN_SELECTOR_LEN)) |
06a05884 | 506 | return WPA_CIPHER_WEP40; |
05c9bc1f | 507 | if (!memcmp(s, RSN_CIPHER_SUITE_TKIP, RSN_SELECTOR_LEN)) |
06a05884 | 508 | return WPA_CIPHER_TKIP; |
05c9bc1f | 509 | if (!memcmp(s, RSN_CIPHER_SUITE_CCMP, RSN_SELECTOR_LEN)) |
06a05884 | 510 | return WPA_CIPHER_CCMP; |
05c9bc1f | 511 | if (!memcmp(s, RSN_CIPHER_SUITE_WEP104, RSN_SELECTOR_LEN)) |
06a05884 LF |
512 | return WPA_CIPHER_WEP104; |
513 | ||
514 | return 0; | |
515 | } | |
516 | ||
517 | ||
518 | int rtw_parse_wpa_ie(u8 *wpa_ie, int wpa_ie_len, int *group_cipher, int *pairwise_cipher, int *is_8021x) | |
519 | { | |
520 | int i, ret = _SUCCESS; | |
521 | int left, count; | |
522 | u8 *pos; | |
523 | u8 SUITE_1X[4] = {0x00, 0x50, 0xf2, 1}; | |
524 | ||
525 | if (wpa_ie_len <= 0) { | |
526 | /* No WPA IE - fail silently */ | |
527 | return _FAIL; | |
528 | } | |
529 | ||
530 | ||
531 | if ((*wpa_ie != _WPA_IE_ID_) || (*(wpa_ie+1) != (u8)(wpa_ie_len - 2)) || | |
05c9bc1f | 532 | (memcmp(wpa_ie+2, RTW_WPA_OUI_TYPE, WPA_SELECTOR_LEN))) |
06a05884 LF |
533 | return _FAIL; |
534 | ||
535 | pos = wpa_ie; | |
536 | ||
537 | pos += 8; | |
538 | left = wpa_ie_len - 8; | |
539 | ||
540 | ||
541 | /* group_cipher */ | |
542 | if (left >= WPA_SELECTOR_LEN) { | |
543 | *group_cipher = rtw_get_wpa_cipher_suite(pos); | |
544 | pos += WPA_SELECTOR_LEN; | |
545 | left -= WPA_SELECTOR_LEN; | |
546 | } else if (left > 0) { | |
547 | RT_TRACE(_module_rtl871x_mlme_c_, _drv_err_, ("%s: ie length mismatch, %u too much", __func__, left)); | |
548 | return _FAIL; | |
549 | } | |
550 | ||
551 | /* pairwise_cipher */ | |
552 | if (left >= 2) { | |
4b49a5b3 | 553 | count = get_unaligned_le16(pos); |
06a05884 LF |
554 | pos += 2; |
555 | left -= 2; | |
556 | ||
557 | if (count == 0 || left < count * WPA_SELECTOR_LEN) { | |
558 | RT_TRACE(_module_rtl871x_mlme_c_, _drv_err_, ("%s: ie count botch (pairwise), " | |
559 | "count %u left %u", __func__, count, left)); | |
560 | return _FAIL; | |
561 | } | |
562 | ||
563 | for (i = 0; i < count; i++) { | |
564 | *pairwise_cipher |= rtw_get_wpa_cipher_suite(pos); | |
565 | ||
566 | pos += WPA_SELECTOR_LEN; | |
567 | left -= WPA_SELECTOR_LEN; | |
568 | } | |
569 | } else if (left == 1) { | |
570 | RT_TRACE(_module_rtl871x_mlme_c_, _drv_err_, ("%s: ie too short (for key mgmt)", __func__)); | |
571 | return _FAIL; | |
572 | } | |
573 | ||
574 | if (is_8021x) { | |
575 | if (left >= 6) { | |
576 | pos += 2; | |
05c9bc1f | 577 | if (!memcmp(pos, SUITE_1X, 4)) { |
06a05884 LF |
578 | RT_TRACE(_module_rtl871x_mlme_c_, _drv_info_, ("%s : there has 802.1x auth\n", __func__)); |
579 | *is_8021x = 1; | |
580 | } | |
581 | } | |
582 | } | |
583 | ||
584 | return ret; | |
585 | } | |
586 | ||
587 | int rtw_parse_wpa2_ie(u8 *rsn_ie, int rsn_ie_len, int *group_cipher, int *pairwise_cipher, int *is_8021x) | |
588 | { | |
589 | int i, ret = _SUCCESS; | |
590 | int left, count; | |
591 | u8 *pos; | |
592 | u8 SUITE_1X[4] = {0x00, 0x0f, 0xac, 0x01}; | |
593 | ||
594 | if (rsn_ie_len <= 0) { | |
595 | /* No RSN IE - fail silently */ | |
596 | return _FAIL; | |
597 | } | |
598 | ||
599 | ||
600 | if ((*rsn_ie != _WPA2_IE_ID_) || (*(rsn_ie+1) != (u8)(rsn_ie_len - 2))) | |
601 | return _FAIL; | |
602 | ||
603 | pos = rsn_ie; | |
604 | pos += 4; | |
605 | left = rsn_ie_len - 4; | |
606 | ||
607 | /* group_cipher */ | |
608 | if (left >= RSN_SELECTOR_LEN) { | |
609 | *group_cipher = rtw_get_wpa2_cipher_suite(pos); | |
610 | ||
611 | pos += RSN_SELECTOR_LEN; | |
612 | left -= RSN_SELECTOR_LEN; | |
613 | ||
614 | } else if (left > 0) { | |
615 | RT_TRACE(_module_rtl871x_mlme_c_, _drv_err_, ("%s: ie length mismatch, %u too much", __func__, left)); | |
616 | return _FAIL; | |
617 | } | |
618 | ||
619 | /* pairwise_cipher */ | |
620 | if (left >= 2) { | |
4b49a5b3 | 621 | count = get_unaligned_le16(pos); |
06a05884 LF |
622 | pos += 2; |
623 | left -= 2; | |
624 | ||
625 | if (count == 0 || left < count * RSN_SELECTOR_LEN) { | |
626 | RT_TRACE(_module_rtl871x_mlme_c_, _drv_err_, ("%s: ie count botch (pairwise), " | |
627 | "count %u left %u", __func__, count, left)); | |
628 | return _FAIL; | |
629 | } | |
630 | ||
631 | for (i = 0; i < count; i++) { | |
632 | *pairwise_cipher |= rtw_get_wpa2_cipher_suite(pos); | |
633 | ||
634 | pos += RSN_SELECTOR_LEN; | |
635 | left -= RSN_SELECTOR_LEN; | |
636 | } | |
637 | ||
638 | } else if (left == 1) { | |
639 | RT_TRACE(_module_rtl871x_mlme_c_, _drv_err_, ("%s: ie too short (for key mgmt)", __func__)); | |
640 | ||
641 | return _FAIL; | |
642 | } | |
643 | ||
644 | if (is_8021x) { | |
645 | if (left >= 6) { | |
646 | pos += 2; | |
05c9bc1f | 647 | if (!memcmp(pos, SUITE_1X, 4)) { |
06a05884 LF |
648 | RT_TRACE(_module_rtl871x_mlme_c_, _drv_info_, ("%s (): there has 802.1x auth\n", __func__)); |
649 | *is_8021x = 1; | |
650 | } | |
651 | } | |
652 | } | |
653 | return ret; | |
654 | } | |
655 | ||
656 | int rtw_get_sec_ie(u8 *in_ie, uint in_len, u8 *rsn_ie, u16 *rsn_len, u8 *wpa_ie, u16 *wpa_len) | |
657 | { | |
658 | u8 authmode, sec_idx, i; | |
659 | u8 wpa_oui[4] = {0x0, 0x50, 0xf2, 0x01}; | |
660 | uint cnt; | |
661 | ||
06a05884 LF |
662 | |
663 | /* Search required WPA or WPA2 IE and copy to sec_ie[] */ | |
664 | ||
adb3d770 | 665 | cnt = _TIMESTAMP_ + _BEACON_ITERVAL_ + _CAPABILITY_; |
06a05884 LF |
666 | |
667 | sec_idx = 0; | |
668 | ||
669 | while (cnt < in_len) { | |
670 | authmode = in_ie[cnt]; | |
671 | ||
f42f52aa | 672 | if ((authmode == _WPA_IE_ID_) && (!memcmp(&in_ie[cnt+2], &wpa_oui[0], 4))) { |
06a05884 LF |
673 | RT_TRACE(_module_rtl871x_mlme_c_, _drv_info_, |
674 | ("\n rtw_get_wpa_ie: sec_idx =%d in_ie[cnt+1]+2 =%d\n", | |
675 | sec_idx, in_ie[cnt+1]+2)); | |
676 | ||
677 | if (wpa_ie) { | |
678 | memcpy(wpa_ie, &in_ie[cnt], in_ie[cnt+1]+2); | |
679 | ||
680 | for (i = 0; i < (in_ie[cnt+1]+2); i += 8) { | |
681 | RT_TRACE(_module_rtl871x_mlme_c_, _drv_info_, | |
682 | ("\n %2x,%2x,%2x,%2x,%2x,%2x,%2x,%2x\n", | |
683 | wpa_ie[i], wpa_ie[i+1], wpa_ie[i+2], wpa_ie[i+3], wpa_ie[i+4], | |
684 | wpa_ie[i+5], wpa_ie[i+6], wpa_ie[i+7])); | |
685 | } | |
686 | } | |
687 | ||
688 | *wpa_len = in_ie[cnt+1]+2; | |
689 | cnt += in_ie[cnt+1]+2; /* get next */ | |
690 | } else { | |
691 | if (authmode == _WPA2_IE_ID_) { | |
692 | RT_TRACE(_module_rtl871x_mlme_c_, _drv_info_, | |
693 | ("\n get_rsn_ie: sec_idx =%d in_ie[cnt+1]+2 =%d\n", | |
694 | sec_idx, in_ie[cnt+1]+2)); | |
695 | ||
696 | if (rsn_ie) { | |
697 | memcpy(rsn_ie, &in_ie[cnt], in_ie[cnt+1]+2); | |
698 | ||
699 | for (i = 0; i < (in_ie[cnt+1]+2); i += 8) { | |
700 | RT_TRACE(_module_rtl871x_mlme_c_, _drv_info_, | |
701 | ("\n %2x,%2x,%2x,%2x,%2x,%2x,%2x,%2x\n", | |
702 | rsn_ie[i], rsn_ie[i+1], rsn_ie[i+2], rsn_ie[i+3], rsn_ie[i+4], | |
703 | rsn_ie[i+5], rsn_ie[i+6], rsn_ie[i+7])); | |
704 | } | |
705 | } | |
706 | ||
707 | *rsn_len = in_ie[cnt+1]+2; | |
708 | cnt += in_ie[cnt+1]+2; /* get next */ | |
709 | } else { | |
710 | cnt += in_ie[cnt+1]+2; /* get next */ | |
711 | } | |
712 | } | |
713 | } | |
714 | ||
06a05884 LF |
715 | |
716 | return *rsn_len + *wpa_len; | |
717 | } | |
718 | ||
719 | u8 rtw_is_wps_ie(u8 *ie_ptr, uint *wps_ielen) | |
720 | { | |
721 | u8 match = false; | |
722 | u8 eid, wps_oui[4] = {0x0, 0x50, 0xf2, 0x04}; | |
723 | ||
724 | if (ie_ptr == NULL) | |
725 | return match; | |
726 | ||
727 | eid = ie_ptr[0]; | |
728 | ||
f42f52aa | 729 | if ((eid == _WPA_IE_ID_) && (!memcmp(&ie_ptr[2], wps_oui, 4))) { |
06a05884 LF |
730 | *wps_ielen = ie_ptr[1]+2; |
731 | match = true; | |
732 | } | |
733 | return match; | |
734 | } | |
735 | ||
736 | /** | |
737 | * rtw_get_wps_ie - Search WPS IE from a series of IEs | |
738 | * @in_ie: Address of IEs to search | |
739 | * @in_len: Length limit from in_ie | |
740 | * @wps_ie: If not NULL and WPS IE is found, WPS IE will be copied to the buf starting from wps_ie | |
741 | * @wps_ielen: If not NULL and WPS IE is found, will set to the length of the entire WPS IE | |
742 | * | |
743 | * Returns: The address of the WPS IE found, or NULL | |
744 | */ | |
745 | u8 *rtw_get_wps_ie(u8 *in_ie, uint in_len, u8 *wps_ie, uint *wps_ielen) | |
746 | { | |
747 | uint cnt; | |
748 | u8 *wpsie_ptr = NULL; | |
749 | u8 eid, wps_oui[4] = {0x0, 0x50, 0xf2, 0x04}; | |
750 | ||
751 | if (wps_ielen) | |
752 | *wps_ielen = 0; | |
753 | ||
754 | if (!in_ie || in_len <= 0) | |
755 | return wpsie_ptr; | |
756 | ||
757 | cnt = 0; | |
758 | ||
759 | while (cnt < in_len) { | |
760 | eid = in_ie[cnt]; | |
761 | ||
f42f52aa | 762 | if ((eid == _WPA_IE_ID_) && (!memcmp(&in_ie[cnt+2], wps_oui, 4))) { |
06a05884 LF |
763 | wpsie_ptr = &in_ie[cnt]; |
764 | ||
765 | if (wps_ie) | |
766 | memcpy(wps_ie, &in_ie[cnt], in_ie[cnt+1]+2); | |
767 | ||
768 | if (wps_ielen) | |
769 | *wps_ielen = in_ie[cnt+1]+2; | |
770 | ||
771 | cnt += in_ie[cnt+1]+2; | |
772 | ||
773 | break; | |
774 | } else { | |
775 | cnt += in_ie[cnt+1]+2; /* goto next */ | |
776 | } | |
777 | } | |
778 | return wpsie_ptr; | |
779 | } | |
780 | ||
781 | /** | |
782 | * rtw_get_wps_attr - Search a specific WPS attribute from a given WPS IE | |
783 | * @wps_ie: Address of WPS IE to search | |
784 | * @wps_ielen: Length limit from wps_ie | |
785 | * @target_attr_id: The attribute ID of WPS attribute to search | |
786 | * @buf_attr: If not NULL and the WPS attribute is found, WPS attribute will be copied to the buf starting from buf_attr | |
787 | * @len_attr: If not NULL and the WPS attribute is found, will set to the length of the entire WPS attribute | |
788 | * | |
789 | * Returns: the address of the specific WPS attribute found, or NULL | |
790 | */ | |
4c0dbe0a | 791 | u8 *rtw_get_wps_attr(u8 *wps_ie, uint wps_ielen, u16 target_attr_id, u8 *buf_attr, u32 *len_attr) |
06a05884 LF |
792 | { |
793 | u8 *attr_ptr = NULL; | |
794 | u8 *target_attr_ptr = NULL; | |
795 | u8 wps_oui[4] = {0x00, 0x50, 0xF2, 0x04}; | |
796 | ||
797 | if (len_attr) | |
798 | *len_attr = 0; | |
799 | ||
800 | if ((wps_ie[0] != _VENDOR_SPECIFIC_IE_) || | |
4c0dbe0a | 801 | (memcmp(wps_ie + 2, wps_oui, 4))) |
06a05884 LF |
802 | return attr_ptr; |
803 | ||
804 | /* 6 = 1(Element ID) + 1(Length) + 4(WPS OUI) */ | |
805 | attr_ptr = wps_ie + 6; /* goto first attr */ | |
806 | ||
807 | while (attr_ptr - wps_ie < wps_ielen) { | |
808 | /* 4 = 2(Attribute ID) + 2(Length) */ | |
27c8aac7 | 809 | u16 attr_id = get_unaligned_be16(attr_ptr); |
810 | u16 attr_data_len = get_unaligned_be16(attr_ptr + 2); | |
06a05884 LF |
811 | u16 attr_len = attr_data_len + 4; |
812 | ||
813 | if (attr_id == target_attr_id) { | |
814 | target_attr_ptr = attr_ptr; | |
815 | if (buf_attr) | |
816 | memcpy(buf_attr, attr_ptr, attr_len); | |
817 | if (len_attr) | |
818 | *len_attr = attr_len; | |
819 | break; | |
820 | } else { | |
821 | attr_ptr += attr_len; /* goto next */ | |
822 | } | |
823 | } | |
824 | return target_attr_ptr; | |
825 | } | |
826 | ||
827 | /** | |
828 | * rtw_get_wps_attr_content - Search a specific WPS attribute content from a given WPS IE | |
829 | * @wps_ie: Address of WPS IE to search | |
830 | * @wps_ielen: Length limit from wps_ie | |
831 | * @target_attr_id: The attribute ID of WPS attribute to search | |
832 | * @buf_content: If not NULL and the WPS attribute is found, WPS attribute content will be copied to the buf starting from buf_content | |
833 | * @len_content: If not NULL and the WPS attribute is found, will set to the length of the WPS attribute content | |
834 | * | |
835 | * Returns: the address of the specific WPS attribute content found, or NULL | |
836 | */ | |
4c0dbe0a | 837 | u8 *rtw_get_wps_attr_content(u8 *wps_ie, uint wps_ielen, u16 target_attr_id, u8 *buf_content, uint *len_content) |
06a05884 LF |
838 | { |
839 | u8 *attr_ptr; | |
840 | u32 attr_len; | |
841 | ||
842 | if (len_content) | |
843 | *len_content = 0; | |
844 | ||
845 | attr_ptr = rtw_get_wps_attr(wps_ie, wps_ielen, target_attr_id, NULL, &attr_len); | |
846 | ||
847 | if (attr_ptr && attr_len) { | |
848 | if (buf_content) | |
849 | memcpy(buf_content, attr_ptr+4, attr_len-4); | |
850 | ||
851 | if (len_content) | |
852 | *len_content = attr_len-4; | |
853 | ||
854 | return attr_ptr+4; | |
855 | } | |
856 | ||
857 | return NULL; | |
858 | } | |
859 | ||
860 | static int rtw_ieee802_11_parse_vendor_specific(u8 *pos, uint elen, | |
861 | struct rtw_ieee802_11_elems *elems, | |
862 | int show_errors) | |
863 | { | |
864 | unsigned int oui; | |
865 | ||
866 | /* first 3 bytes in vendor specific information element are the IEEE | |
867 | * OUI of the vendor. The following byte is used a vendor specific | |
868 | * sub-type. */ | |
869 | if (elen < 4) { | |
870 | if (show_errors) { | |
871 | DBG_88E("short vendor specific information element ignored (len=%lu)\n", | |
7be921a2 | 872 | (unsigned long)elen); |
06a05884 LF |
873 | } |
874 | return -1; | |
875 | } | |
876 | ||
877 | oui = RTW_GET_BE24(pos); | |
878 | switch (oui) { | |
879 | case OUI_MICROSOFT: | |
880 | /* Microsoft/Wi-Fi information elements are further typed and | |
881 | * subtyped */ | |
882 | switch (pos[3]) { | |
883 | case 1: | |
884 | /* Microsoft OUI (00:50:F2) with OUI Type 1: | |
885 | * real WPA information element */ | |
886 | elems->wpa_ie = pos; | |
887 | elems->wpa_ie_len = elen; | |
888 | break; | |
889 | case WME_OUI_TYPE: /* this is a Wi-Fi WME info. element */ | |
890 | if (elen < 5) { | |
891 | DBG_88E("short WME information element ignored (len=%lu)\n", | |
7be921a2 | 892 | (unsigned long)elen); |
06a05884 LF |
893 | return -1; |
894 | } | |
895 | switch (pos[4]) { | |
896 | case WME_OUI_SUBTYPE_INFORMATION_ELEMENT: | |
897 | case WME_OUI_SUBTYPE_PARAMETER_ELEMENT: | |
898 | elems->wme = pos; | |
899 | elems->wme_len = elen; | |
900 | break; | |
901 | case WME_OUI_SUBTYPE_TSPEC_ELEMENT: | |
902 | elems->wme_tspec = pos; | |
903 | elems->wme_tspec_len = elen; | |
904 | break; | |
905 | default: | |
906 | DBG_88E("unknown WME information element ignored (subtype=%d len=%lu)\n", | |
7be921a2 | 907 | pos[4], (unsigned long)elen); |
06a05884 LF |
908 | return -1; |
909 | } | |
910 | break; | |
911 | case 4: | |
912 | /* Wi-Fi Protected Setup (WPS) IE */ | |
913 | elems->wps_ie = pos; | |
914 | elems->wps_ie_len = elen; | |
915 | break; | |
916 | default: | |
917 | DBG_88E("Unknown Microsoft information element ignored (type=%d len=%lu)\n", | |
7be921a2 | 918 | pos[3], (unsigned long)elen); |
06a05884 LF |
919 | return -1; |
920 | } | |
921 | break; | |
922 | ||
923 | case OUI_BROADCOM: | |
924 | switch (pos[3]) { | |
925 | case VENDOR_HT_CAPAB_OUI_TYPE: | |
926 | elems->vendor_ht_cap = pos; | |
927 | elems->vendor_ht_cap_len = elen; | |
928 | break; | |
929 | default: | |
930 | DBG_88E("Unknown Broadcom information element ignored (type=%d len=%lu)\n", | |
7be921a2 | 931 | pos[3], (unsigned long)elen); |
06a05884 LF |
932 | return -1; |
933 | } | |
934 | break; | |
935 | default: | |
936 | DBG_88E("unknown vendor specific information element ignored (vendor OUI %02x:%02x:%02x len=%lu)\n", | |
7be921a2 | 937 | pos[0], pos[1], pos[2], (unsigned long)elen); |
06a05884 LF |
938 | return -1; |
939 | } | |
940 | return 0; | |
941 | } | |
942 | ||
943 | /** | |
944 | * ieee802_11_parse_elems - Parse information elements in management frames | |
945 | * @start: Pointer to the start of IEs | |
946 | * @len: Length of IE buffer in octets | |
947 | * @elems: Data structure for parsed elements | |
948 | * @show_errors: Whether to show parsing errors in debug log | |
949 | * Returns: Parsing result | |
950 | */ | |
951 | enum parse_res rtw_ieee802_11_parse_elems(u8 *start, uint len, | |
952 | struct rtw_ieee802_11_elems *elems, | |
953 | int show_errors) | |
954 | { | |
955 | uint left = len; | |
956 | u8 *pos = start; | |
957 | int unknown = 0; | |
958 | ||
1ce39848 | 959 | memset(elems, 0, sizeof(*elems)); |
06a05884 LF |
960 | |
961 | while (left >= 2) { | |
962 | u8 id, elen; | |
963 | ||
964 | id = *pos++; | |
965 | elen = *pos++; | |
966 | left -= 2; | |
967 | ||
968 | if (elen > left) { | |
969 | if (show_errors) { | |
970 | DBG_88E("IEEE 802.11 element parse failed (id=%d elen=%d left=%lu)\n", | |
7be921a2 | 971 | id, elen, (unsigned long)left); |
06a05884 LF |
972 | } |
973 | return ParseFailed; | |
974 | } | |
975 | ||
976 | switch (id) { | |
977 | case WLAN_EID_SSID: | |
978 | elems->ssid = pos; | |
979 | elems->ssid_len = elen; | |
980 | break; | |
981 | case WLAN_EID_SUPP_RATES: | |
982 | elems->supp_rates = pos; | |
983 | elems->supp_rates_len = elen; | |
984 | break; | |
985 | case WLAN_EID_FH_PARAMS: | |
986 | elems->fh_params = pos; | |
987 | elems->fh_params_len = elen; | |
988 | break; | |
989 | case WLAN_EID_DS_PARAMS: | |
990 | elems->ds_params = pos; | |
991 | elems->ds_params_len = elen; | |
992 | break; | |
993 | case WLAN_EID_CF_PARAMS: | |
994 | elems->cf_params = pos; | |
995 | elems->cf_params_len = elen; | |
996 | break; | |
997 | case WLAN_EID_TIM: | |
998 | elems->tim = pos; | |
999 | elems->tim_len = elen; | |
1000 | break; | |
1001 | case WLAN_EID_IBSS_PARAMS: | |
1002 | elems->ibss_params = pos; | |
1003 | elems->ibss_params_len = elen; | |
1004 | break; | |
1005 | case WLAN_EID_CHALLENGE: | |
1006 | elems->challenge = pos; | |
1007 | elems->challenge_len = elen; | |
1008 | break; | |
1009 | case WLAN_EID_ERP_INFO: | |
1010 | elems->erp_info = pos; | |
1011 | elems->erp_info_len = elen; | |
1012 | break; | |
1013 | case WLAN_EID_EXT_SUPP_RATES: | |
1014 | elems->ext_supp_rates = pos; | |
1015 | elems->ext_supp_rates_len = elen; | |
1016 | break; | |
1017 | case WLAN_EID_VENDOR_SPECIFIC: | |
1018 | if (rtw_ieee802_11_parse_vendor_specific(pos, elen, elems, show_errors)) | |
1019 | unknown++; | |
1020 | break; | |
1021 | case WLAN_EID_RSN: | |
1022 | elems->rsn_ie = pos; | |
1023 | elems->rsn_ie_len = elen; | |
1024 | break; | |
1025 | case WLAN_EID_PWR_CAPABILITY: | |
1026 | elems->power_cap = pos; | |
1027 | elems->power_cap_len = elen; | |
1028 | break; | |
1029 | case WLAN_EID_SUPPORTED_CHANNELS: | |
1030 | elems->supp_channels = pos; | |
1031 | elems->supp_channels_len = elen; | |
1032 | break; | |
1033 | case WLAN_EID_MOBILITY_DOMAIN: | |
1034 | elems->mdie = pos; | |
1035 | elems->mdie_len = elen; | |
1036 | break; | |
1037 | case WLAN_EID_FAST_BSS_TRANSITION: | |
1038 | elems->ftie = pos; | |
1039 | elems->ftie_len = elen; | |
1040 | break; | |
1041 | case WLAN_EID_TIMEOUT_INTERVAL: | |
1042 | elems->timeout_int = pos; | |
1043 | elems->timeout_int_len = elen; | |
1044 | break; | |
1045 | case WLAN_EID_HT_CAP: | |
1046 | elems->ht_capabilities = pos; | |
1047 | elems->ht_capabilities_len = elen; | |
1048 | break; | |
1049 | case WLAN_EID_HT_OPERATION: | |
1050 | elems->ht_operation = pos; | |
1051 | elems->ht_operation_len = elen; | |
1052 | break; | |
1053 | default: | |
1054 | unknown++; | |
1055 | if (!show_errors) | |
1056 | break; | |
1057 | DBG_88E("IEEE 802.11 element parse ignored unknown element (id=%d elen=%d)\n", | |
1058 | id, elen); | |
1059 | break; | |
1060 | } | |
1061 | left -= elen; | |
1062 | pos += elen; | |
1063 | } | |
1064 | if (left) | |
1065 | return ParseFailed; | |
1066 | return unknown ? ParseUnknown : ParseOK; | |
1067 | } | |
1068 | ||
06a05884 LF |
1069 | void rtw_macaddr_cfg(u8 *mac_addr) |
1070 | { | |
1071 | u8 mac[ETH_ALEN]; | |
79374daf | 1072 | |
06a05884 LF |
1073 | if (mac_addr == NULL) |
1074 | return; | |
1075 | ||
79374daf AS |
1076 | if (rtw_initmac && mac_pton(rtw_initmac, mac)) { |
1077 | /* Users specify the mac address */ | |
06a05884 | 1078 | memcpy(mac_addr, mac, ETH_ALEN); |
79374daf AS |
1079 | } else { |
1080 | /* Use the mac address stored in the Efuse */ | |
06a05884 LF |
1081 | memcpy(mac, mac_addr, ETH_ALEN); |
1082 | } | |
1083 | ||
1084 | if (((mac[0] == 0xff) && (mac[1] == 0xff) && (mac[2] == 0xff) && | |
1085 | (mac[3] == 0xff) && (mac[4] == 0xff) && (mac[5] == 0xff)) || | |
1086 | ((mac[0] == 0x0) && (mac[1] == 0x0) && (mac[2] == 0x0) && | |
1087 | (mac[3] == 0x0) && (mac[4] == 0x0) && (mac[5] == 0x0))) { | |
1088 | mac[0] = 0x00; | |
1089 | mac[1] = 0xe0; | |
1090 | mac[2] = 0x4c; | |
1091 | mac[3] = 0x87; | |
1092 | mac[4] = 0x00; | |
1093 | mac[5] = 0x00; | |
08c64f93 | 1094 | /* use default mac address */ |
06a05884 LF |
1095 | memcpy(mac_addr, mac, ETH_ALEN); |
1096 | DBG_88E("MAC Address from efuse error, assign default one !!!\n"); | |
1097 | } | |
1098 | ||
1099 | DBG_88E("rtw_macaddr_cfg MAC Address = %pM\n", (mac_addr)); | |
1100 | } | |
1101 | ||
1102 | void dump_ies(u8 *buf, u32 buf_len) | |
1103 | { | |
1104 | u8 *pos = (u8 *)buf; | |
1105 | u8 id, len; | |
1106 | ||
1107 | while (pos-buf <= buf_len) { | |
1108 | id = *pos; | |
1109 | len = *(pos+1); | |
1110 | ||
1111 | DBG_88E("%s ID:%u, LEN:%u\n", __func__, id, len); | |
06a05884 LF |
1112 | dump_wps_ie(pos, len); |
1113 | ||
1114 | pos += (2 + len); | |
1115 | } | |
1116 | } | |
1117 | ||
1118 | void dump_wps_ie(u8 *ie, u32 ie_len) | |
1119 | { | |
1120 | u8 *pos = (u8 *)ie; | |
1121 | u16 id; | |
1122 | u16 len; | |
1123 | u8 *wps_ie; | |
1124 | uint wps_ielen; | |
1125 | ||
1126 | wps_ie = rtw_get_wps_ie(ie, ie_len, NULL, &wps_ielen); | |
1127 | if (wps_ie != ie || wps_ielen == 0) | |
1128 | return; | |
1129 | ||
1130 | pos += 6; | |
1131 | while (pos-ie < ie_len) { | |
27c8aac7 | 1132 | id = get_unaligned_be16(pos); |
1133 | len = get_unaligned_be16(pos + 2); | |
06a05884 LF |
1134 | DBG_88E("%s ID:0x%04x, LEN:%u\n", __func__, id, len); |
1135 | pos += (4+len); | |
1136 | } | |
1137 | } | |
1138 | ||
06a05884 LF |
1139 | /* Baron adds to avoid FreeBSD warning */ |
1140 | int ieee80211_is_empty_essid(const char *essid, int essid_len) | |
1141 | { | |
1142 | /* Single white space is for Linksys APs */ | |
1143 | if (essid_len == 1 && essid[0] == ' ') | |
1144 | return 1; | |
1145 | ||
1146 | /* Otherwise, if the entire essid is 0, we assume it is hidden */ | |
1147 | while (essid_len) { | |
1148 | essid_len--; | |
1149 | if (essid[essid_len] != '\0') | |
1150 | return 0; | |
1151 | } | |
1152 | ||
1153 | return 1; | |
1154 | } | |
1155 | ||
1156 | int ieee80211_get_hdrlen(u16 fc) | |
1157 | { | |
1158 | int hdrlen = 24; | |
1159 | ||
1160 | switch (WLAN_FC_GET_TYPE(fc)) { | |
1161 | case RTW_IEEE80211_FTYPE_DATA: | |
1162 | if (fc & RTW_IEEE80211_STYPE_QOS_DATA) | |
1163 | hdrlen += 2; | |
1164 | if ((fc & RTW_IEEE80211_FCTL_FROMDS) && (fc & RTW_IEEE80211_FCTL_TODS)) | |
1165 | hdrlen += 6; /* Addr4 */ | |
1166 | break; | |
1167 | case RTW_IEEE80211_FTYPE_CTL: | |
1168 | switch (WLAN_FC_GET_STYPE(fc)) { | |
1169 | case RTW_IEEE80211_STYPE_CTS: | |
1170 | case RTW_IEEE80211_STYPE_ACK: | |
1171 | hdrlen = 10; | |
1172 | break; | |
1173 | default: | |
1174 | hdrlen = 16; | |
1175 | break; | |
1176 | } | |
1177 | break; | |
1178 | } | |
1179 | ||
1180 | return hdrlen; | |
1181 | } | |
1182 | ||
1183 | static int rtw_get_cipher_info(struct wlan_network *pnetwork) | |
1184 | { | |
1185 | u32 wpa_ielen; | |
1186 | unsigned char *pbuf; | |
1187 | int group_cipher = 0, pairwise_cipher = 0, is8021x = 0; | |
1188 | int ret = _FAIL; | |
1189 | pbuf = rtw_get_wpa_ie(&pnetwork->network.IEs[12], &wpa_ielen, pnetwork->network.IELength-12); | |
1190 | ||
1191 | if (pbuf && (wpa_ielen > 0)) { | |
1192 | RT_TRACE(_module_rtl871x_mlme_c_, _drv_info_, ("rtw_get_cipher_info: wpa_ielen: %d", wpa_ielen)); | |
1193 | if (_SUCCESS == rtw_parse_wpa_ie(pbuf, wpa_ielen+2, &group_cipher, &pairwise_cipher, &is8021x)) { | |
1194 | pnetwork->BcnInfo.pairwise_cipher = pairwise_cipher; | |
1195 | pnetwork->BcnInfo.group_cipher = group_cipher; | |
1196 | pnetwork->BcnInfo.is_8021x = is8021x; | |
1197 | RT_TRACE(_module_rtl871x_mlme_c_, _drv_info_, ("%s: pnetwork->pairwise_cipher: %d, is_8021x is %d", | |
1198 | __func__, pnetwork->BcnInfo.pairwise_cipher, pnetwork->BcnInfo.is_8021x)); | |
1199 | ret = _SUCCESS; | |
1200 | } | |
1201 | } else { | |
1202 | pbuf = rtw_get_wpa2_ie(&pnetwork->network.IEs[12], &wpa_ielen, pnetwork->network.IELength-12); | |
1203 | ||
1204 | if (pbuf && (wpa_ielen > 0)) { | |
1205 | RT_TRACE(_module_rtl871x_mlme_c_, _drv_info_, ("get RSN IE\n")); | |
1206 | if (_SUCCESS == rtw_parse_wpa2_ie(pbuf, wpa_ielen+2, &group_cipher, &pairwise_cipher, &is8021x)) { | |
1207 | RT_TRACE(_module_rtl871x_mlme_c_, _drv_info_, ("get RSN IE OK!!!\n")); | |
1208 | pnetwork->BcnInfo.pairwise_cipher = pairwise_cipher; | |
1209 | pnetwork->BcnInfo.group_cipher = group_cipher; | |
1210 | pnetwork->BcnInfo.is_8021x = is8021x; | |
1211 | RT_TRACE(_module_rtl871x_mlme_c_, _drv_info_, ("%s: pnetwork->pairwise_cipher: %d," | |
1212 | "pnetwork->group_cipher is %d, is_8021x is %d", __func__, pnetwork->BcnInfo.pairwise_cipher, | |
1213 | pnetwork->BcnInfo.group_cipher, pnetwork->BcnInfo.is_8021x)); | |
1214 | ret = _SUCCESS; | |
1215 | } | |
1216 | } | |
1217 | } | |
1218 | ||
1219 | return ret; | |
1220 | } | |
1221 | ||
1222 | void rtw_get_bcn_info(struct wlan_network *pnetwork) | |
1223 | { | |
1224 | unsigned short cap = 0; | |
1225 | u8 bencrypt = 0; | |
1226 | __le16 le_tmp; | |
1227 | u16 wpa_len = 0, rsn_len = 0; | |
1228 | struct HT_info_element *pht_info = NULL; | |
1229 | struct rtw_ieee80211_ht_cap *pht_cap = NULL; | |
1230 | unsigned int len; | |
1231 | unsigned char *p; | |
1232 | ||
1233 | memcpy(&le_tmp, rtw_get_capability_from_ie(pnetwork->network.IEs), 2); | |
1234 | cap = le16_to_cpu(le_tmp); | |
1235 | if (cap & WLAN_CAPABILITY_PRIVACY) { | |
1236 | bencrypt = 1; | |
1237 | pnetwork->network.Privacy = 1; | |
1238 | } else { | |
1239 | pnetwork->BcnInfo.encryp_protocol = ENCRYP_PROTOCOL_OPENSYS; | |
1240 | } | |
4c0dbe0a | 1241 | rtw_get_sec_ie(pnetwork->network.IEs, pnetwork->network.IELength, NULL, &rsn_len, NULL, &wpa_len); |
06a05884 LF |
1242 | RT_TRACE(_module_rtl871x_mlme_c_, _drv_info_, ("rtw_get_bcn_info: ssid =%s\n", pnetwork->network.Ssid.Ssid)); |
1243 | RT_TRACE(_module_rtl871x_mlme_c_, _drv_info_, ("rtw_get_bcn_info: wpa_len =%d rsn_len =%d\n", wpa_len, rsn_len)); | |
1244 | RT_TRACE(_module_rtl871x_mlme_c_, _drv_info_, ("rtw_get_bcn_info: ssid =%s\n", pnetwork->network.Ssid.Ssid)); | |
1245 | RT_TRACE(_module_rtl871x_mlme_c_, _drv_info_, ("rtw_get_bcn_info: wpa_len =%d rsn_len =%d\n", wpa_len, rsn_len)); | |
1246 | ||
1247 | if (rsn_len > 0) { | |
1248 | pnetwork->BcnInfo.encryp_protocol = ENCRYP_PROTOCOL_WPA2; | |
1249 | } else if (wpa_len > 0) { | |
1250 | pnetwork->BcnInfo.encryp_protocol = ENCRYP_PROTOCOL_WPA; | |
1251 | } else { | |
1252 | if (bencrypt) | |
1253 | pnetwork->BcnInfo.encryp_protocol = ENCRYP_PROTOCOL_WEP; | |
1254 | } | |
1255 | RT_TRACE(_module_rtl871x_mlme_c_, _drv_info_, ("rtw_get_bcn_info: pnetwork->encryp_protocol is %x\n", | |
1256 | pnetwork->BcnInfo.encryp_protocol)); | |
1257 | RT_TRACE(_module_rtl871x_mlme_c_, _drv_info_, ("rtw_get_bcn_info: pnetwork->encryp_protocol is %x\n", | |
1258 | pnetwork->BcnInfo.encryp_protocol)); | |
1259 | rtw_get_cipher_info(pnetwork); | |
1260 | ||
1261 | /* get bwmode and ch_offset */ | |
1262 | /* parsing HT_CAP_IE */ | |
1263 | p = rtw_get_ie(pnetwork->network.IEs + _FIXED_IE_LENGTH_, _HT_CAPABILITY_IE_, &len, pnetwork->network.IELength - _FIXED_IE_LENGTH_); | |
1264 | if (p && len > 0) { | |
1265 | pht_cap = (struct rtw_ieee80211_ht_cap *)(p + 2); | |
1266 | pnetwork->BcnInfo.ht_cap_info = pht_cap->cap_info; | |
1267 | } else { | |
1268 | pnetwork->BcnInfo.ht_cap_info = 0; | |
1269 | } | |
1270 | /* parsing HT_INFO_IE */ | |
1271 | p = rtw_get_ie(pnetwork->network.IEs + _FIXED_IE_LENGTH_, _HT_ADD_INFO_IE_, &len, pnetwork->network.IELength - _FIXED_IE_LENGTH_); | |
1272 | if (p && len > 0) { | |
1273 | pht_info = (struct HT_info_element *)(p + 2); | |
1274 | pnetwork->BcnInfo.ht_info_infos_0 = pht_info->infos[0]; | |
1275 | } else { | |
1276 | pnetwork->BcnInfo.ht_info_infos_0 = 0; | |
1277 | } | |
1278 | } | |
1279 | ||
1280 | /* show MCS rate, unit: 100Kbps */ | |
1281 | u16 rtw_mcs_rate(u8 rf_type, u8 bw_40MHz, u8 short_GI_20, u8 short_GI_40, unsigned char *MCS_rate) | |
1282 | { | |
1283 | u16 max_rate = 0; | |
1284 | ||
1285 | if (rf_type == RF_1T1R) { | |
1286 | if (MCS_rate[0] & BIT(7)) | |
1287 | max_rate = (bw_40MHz) ? ((short_GI_40) ? 1500 : 1350) : ((short_GI_20) ? 722 : 650); | |
1288 | else if (MCS_rate[0] & BIT(6)) | |
1289 | max_rate = (bw_40MHz) ? ((short_GI_40) ? 1350 : 1215) : ((short_GI_20) ? 650 : 585); | |
1290 | else if (MCS_rate[0] & BIT(5)) | |
1291 | max_rate = (bw_40MHz) ? ((short_GI_40) ? 1200 : 1080) : ((short_GI_20) ? 578 : 520); | |
1292 | else if (MCS_rate[0] & BIT(4)) | |
1293 | max_rate = (bw_40MHz) ? ((short_GI_40) ? 900 : 810) : ((short_GI_20) ? 433 : 390); | |
1294 | else if (MCS_rate[0] & BIT(3)) | |
1295 | max_rate = (bw_40MHz) ? ((short_GI_40) ? 600 : 540) : ((short_GI_20) ? 289 : 260); | |
1296 | else if (MCS_rate[0] & BIT(2)) | |
1297 | max_rate = (bw_40MHz) ? ((short_GI_40) ? 450 : 405) : ((short_GI_20) ? 217 : 195); | |
1298 | else if (MCS_rate[0] & BIT(1)) | |
1299 | max_rate = (bw_40MHz) ? ((short_GI_40) ? 300 : 270) : ((short_GI_20) ? 144 : 130); | |
1300 | else if (MCS_rate[0] & BIT(0)) | |
1301 | max_rate = (bw_40MHz) ? ((short_GI_40) ? 150 : 135) : ((short_GI_20) ? 72 : 65); | |
1302 | } else { | |
1303 | if (MCS_rate[1]) { | |
1304 | if (MCS_rate[1] & BIT(7)) | |
1305 | max_rate = (bw_40MHz) ? ((short_GI_40) ? 3000 : 2700) : ((short_GI_20) ? 1444 : 1300); | |
1306 | else if (MCS_rate[1] & BIT(6)) | |
1307 | max_rate = (bw_40MHz) ? ((short_GI_40) ? 2700 : 2430) : ((short_GI_20) ? 1300 : 1170); | |
1308 | else if (MCS_rate[1] & BIT(5)) | |
1309 | max_rate = (bw_40MHz) ? ((short_GI_40) ? 2400 : 2160) : ((short_GI_20) ? 1156 : 1040); | |
1310 | else if (MCS_rate[1] & BIT(4)) | |
1311 | max_rate = (bw_40MHz) ? ((short_GI_40) ? 1800 : 1620) : ((short_GI_20) ? 867 : 780); | |
1312 | else if (MCS_rate[1] & BIT(3)) | |
1313 | max_rate = (bw_40MHz) ? ((short_GI_40) ? 1200 : 1080) : ((short_GI_20) ? 578 : 520); | |
1314 | else if (MCS_rate[1] & BIT(2)) | |
1315 | max_rate = (bw_40MHz) ? ((short_GI_40) ? 900 : 810) : ((short_GI_20) ? 433 : 390); | |
1316 | else if (MCS_rate[1] & BIT(1)) | |
1317 | max_rate = (bw_40MHz) ? ((short_GI_40) ? 600 : 540) : ((short_GI_20) ? 289 : 260); | |
1318 | else if (MCS_rate[1] & BIT(0)) | |
1319 | max_rate = (bw_40MHz) ? ((short_GI_40) ? 300 : 270) : ((short_GI_20) ? 144 : 130); | |
1320 | } else { | |
1321 | if (MCS_rate[0] & BIT(7)) | |
1322 | max_rate = (bw_40MHz) ? ((short_GI_40) ? 1500 : 1350) : ((short_GI_20) ? 722 : 650); | |
1323 | else if (MCS_rate[0] & BIT(6)) | |
1324 | max_rate = (bw_40MHz) ? ((short_GI_40) ? 1350 : 1215) : ((short_GI_20) ? 650 : 585); | |
1325 | else if (MCS_rate[0] & BIT(5)) | |
1326 | max_rate = (bw_40MHz) ? ((short_GI_40) ? 1200 : 1080) : ((short_GI_20) ? 578 : 520); | |
1327 | else if (MCS_rate[0] & BIT(4)) | |
1328 | max_rate = (bw_40MHz) ? ((short_GI_40) ? 900 : 810) : ((short_GI_20) ? 433 : 390); | |
1329 | else if (MCS_rate[0] & BIT(3)) | |
1330 | max_rate = (bw_40MHz) ? ((short_GI_40) ? 600 : 540) : ((short_GI_20) ? 289 : 260); | |
1331 | else if (MCS_rate[0] & BIT(2)) | |
1332 | max_rate = (bw_40MHz) ? ((short_GI_40) ? 450 : 405) : ((short_GI_20) ? 217 : 195); | |
1333 | else if (MCS_rate[0] & BIT(1)) | |
1334 | max_rate = (bw_40MHz) ? ((short_GI_40) ? 300 : 270) : ((short_GI_20) ? 144 : 130); | |
1335 | else if (MCS_rate[0] & BIT(0)) | |
1336 | max_rate = (bw_40MHz) ? ((short_GI_40) ? 150 : 135) : ((short_GI_20) ? 72 : 65); | |
1337 | } | |
1338 | } | |
1339 | return max_rate; | |
1340 | } | |
1341 | ||
1342 | int rtw_action_frame_parse(const u8 *frame, u32 frame_len, u8 *category, u8 *action) | |
1343 | { | |
1344 | const u8 *frame_body = frame + sizeof(struct rtw_ieee80211_hdr_3addr); | |
1345 | u16 fc; | |
1346 | u8 c, a = 0; | |
1347 | ||
1348 | fc = le16_to_cpu(((struct rtw_ieee80211_hdr_3addr *)frame)->frame_ctl); | |
1349 | ||
1350 | if ((fc & (RTW_IEEE80211_FCTL_FTYPE|RTW_IEEE80211_FCTL_STYPE)) != | |
1351 | (RTW_IEEE80211_FTYPE_MGMT|RTW_IEEE80211_STYPE_ACTION)) | |
1352 | return false; | |
1353 | ||
1354 | c = frame_body[0]; | |
1355 | ||
1356 | switch (c) { | |
1357 | case RTW_WLAN_CATEGORY_P2P: /* vendor-specific */ | |
1358 | break; | |
1359 | default: | |
1360 | a = frame_body[1]; | |
1361 | } | |
1362 | ||
1363 | if (category) | |
1364 | *category = c; | |
1365 | if (action) | |
1366 | *action = a; | |
1367 | ||
1368 | return true; | |
1369 | } | |
1370 | ||
1371 | static const char *_action_public_str[] = { | |
1372 | "ACT_PUB_BSSCOEXIST", | |
1373 | "ACT_PUB_DSE_ENABLE", | |
1374 | "ACT_PUB_DSE_DEENABLE", | |
1375 | "ACT_PUB_DSE_REG_LOCATION", | |
1376 | "ACT_PUB_EXT_CHL_SWITCH", | |
1377 | "ACT_PUB_DSE_MSR_REQ", | |
1378 | "ACT_PUB_DSE_MSR_RPRT", | |
1379 | "ACT_PUB_MP", | |
1380 | "ACT_PUB_DSE_PWR_CONSTRAINT", | |
1381 | "ACT_PUB_VENDOR", | |
1382 | "ACT_PUB_GAS_INITIAL_REQ", | |
1383 | "ACT_PUB_GAS_INITIAL_RSP", | |
1384 | "ACT_PUB_GAS_COMEBACK_REQ", | |
1385 | "ACT_PUB_GAS_COMEBACK_RSP", | |
1386 | "ACT_PUB_TDLS_DISCOVERY_RSP", | |
1387 | "ACT_PUB_LOCATION_TRACK", | |
1388 | "ACT_PUB_RSVD", | |
1389 | }; | |
1390 | ||
1391 | const char *action_public_str(u8 action) | |
1392 | { | |
1393 | action = (action >= ACT_PUBLIC_MAX) ? ACT_PUBLIC_MAX : action; | |
1394 | return _action_public_str[action]; | |
1395 | } |