]>
Commit | Line | Data |
---|---|---|
2865d42c LF |
1 | /****************************************************************************** |
2 | * ieee80211.c | |
3 | * | |
4 | * Copyright(c) 2007 - 2010 Realtek Corporation. All rights reserved. | |
5 | * Linux device driver for RTL8192SU | |
6 | * | |
7 | * This program is free software; you can redistribute it and/or modify it | |
8 | * under the terms of version 2 of the GNU General Public License as | |
9 | * published by the Free Software Foundation. | |
10 | * | |
11 | * This program is distributed in the hope that it will be useful, but WITHOUT | |
12 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | |
13 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | |
14 | * more details. | |
15 | * | |
16 | * You should have received a copy of the GNU General Public License along with | |
17 | * this program; if not, write to the Free Software Foundation, Inc., | |
18 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA | |
19 | * | |
20 | * Modifications for inclusion into the Linux staging tree are | |
21 | * Copyright(c) 2010 Larry Finger. All rights reserved. | |
22 | * | |
23 | * Contact information: | |
24 | * WLAN FAE <wlanfae@realtek.com>. | |
25 | * Larry Finger <Larry.Finger@lwfinger.net> | |
26 | * | |
27 | ******************************************************************************/ | |
28 | ||
29 | #define _IEEE80211_C | |
30 | ||
31 | #include "drv_types.h" | |
32 | #include "ieee80211.h" | |
33 | #include "wifi.h" | |
34 | #include "osdep_service.h" | |
35 | #include "wlan_bssdef.h" | |
36 | ||
37 | static const u8 WPA_OUI_TYPE[] = {0x00, 0x50, 0xf2, 1}; | |
38 | static const u8 WPA_CIPHER_SUITE_NONE[] = {0x00, 0x50, 0xf2, 0}; | |
39 | static const u8 WPA_CIPHER_SUITE_WEP40[] = {0x00, 0x50, 0xf2, 1}; | |
40 | static const u8 WPA_CIPHER_SUITE_TKIP[] = {0x00, 0x50, 0xf2, 2}; | |
41 | static const u8 WPA_CIPHER_SUITE_CCMP[] = {0x00, 0x50, 0xf2, 4}; | |
42 | static const u8 WPA_CIPHER_SUITE_WEP104[] = {0x00, 0x50, 0xf2, 5}; | |
43 | ||
44 | static const u8 RSN_CIPHER_SUITE_NONE[] = {0x00, 0x0f, 0xac, 0}; | |
45 | static const u8 RSN_CIPHER_SUITE_WEP40[] = {0x00, 0x0f, 0xac, 1}; | |
46 | static const u8 RSN_CIPHER_SUITE_TKIP[] = {0x00, 0x0f, 0xac, 2}; | |
47 | static const u8 RSN_CIPHER_SUITE_CCMP[] = {0x00, 0x0f, 0xac, 4}; | |
48 | static const u8 RSN_CIPHER_SUITE_WEP104[] = {0x00, 0x0f, 0xac, 5}; | |
49 | ||
50 | /*----------------------------------------------------------- | |
51 | * for adhoc-master to generate ie and provide supported-rate to fw | |
52 | *----------------------------------------------------------- | |
53 | */ | |
54 | ||
55 | static u8 WIFI_CCKRATES[] = { | |
56 | (IEEE80211_CCK_RATE_1MB | IEEE80211_BASIC_RATE_MASK), | |
57 | (IEEE80211_CCK_RATE_2MB | IEEE80211_BASIC_RATE_MASK), | |
58 | (IEEE80211_CCK_RATE_5MB | IEEE80211_BASIC_RATE_MASK), | |
59 | (IEEE80211_CCK_RATE_11MB | IEEE80211_BASIC_RATE_MASK) | |
60 | }; | |
61 | ||
62 | static u8 WIFI_OFDMRATES[] = { | |
63 | (IEEE80211_OFDM_RATE_6MB), | |
64 | (IEEE80211_OFDM_RATE_9MB), | |
65 | (IEEE80211_OFDM_RATE_12MB), | |
66 | (IEEE80211_OFDM_RATE_18MB), | |
67 | (IEEE80211_OFDM_RATE_24MB), | |
68 | (IEEE80211_OFDM_RATE_36MB), | |
69 | (IEEE80211_OFDM_RATE_48MB), | |
70 | (IEEE80211_OFDM_RATE_54MB) | |
71 | }; | |
72 | ||
73 | uint r8712_is_cckrates_included(u8 *rate) | |
74 | { | |
75 | u32 i = 0; | |
76 | ||
77 | while (rate[i] != 0) { | |
78 | if ((((rate[i]) & 0x7f) == 2) || (((rate[i]) & 0x7f) == 4) || | |
79 | (((rate[i]) & 0x7f) == 11) || (((rate[i]) & 0x7f) == 22)) | |
80 | return true; | |
879cb0c9 DC |
81 | i++; |
82 | } | |
83 | return false; | |
2865d42c LF |
84 | } |
85 | ||
86 | uint r8712_is_cckratesonly_included(u8 *rate) | |
87 | { | |
88 | u32 i = 0; | |
89 | ||
90 | while (rate[i] != 0) { | |
91 | if ((((rate[i]) & 0x7f) != 2) && (((rate[i]) & 0x7f) != 4) && | |
92 | (((rate[i]) & 0x7f) != 11) && (((rate[i]) & 0x7f) != 22)) | |
93 | return false; | |
94 | i++; | |
95 | } | |
96 | return true; | |
97 | } | |
98 | ||
99 | /* r8712_set_ie will update frame length */ | |
100 | u8 *r8712_set_ie(u8 *pbuf, sint index, uint len, u8 *source, uint *frlen) | |
101 | { | |
102 | *pbuf = (u8)index; | |
103 | *(pbuf + 1) = (u8)len; | |
104 | if (len > 0) | |
105 | memcpy((void *)(pbuf + 2), (void *)source, len); | |
106 | *frlen = *frlen + (len + 2); | |
107 | return pbuf + len + 2; | |
108 | } | |
109 | ||
110 | /*---------------------------------------------------------------------------- | |
111 | index: the information element id index, limit is the limit for search | |
112 | -----------------------------------------------------------------------------*/ | |
113 | u8 *r8712_get_ie(u8 *pbuf, sint index, sint *len, sint limit) | |
114 | { | |
115 | sint tmp, i; | |
116 | u8 *p; | |
117 | ||
118 | if (limit < 1) | |
119 | return NULL; | |
120 | p = pbuf; | |
121 | i = 0; | |
122 | *len = 0; | |
123 | while (1) { | |
124 | if (*p == index) { | |
125 | *len = *(p + 1); | |
126 | return p; | |
2865d42c | 127 | } |
e92c3511 NK |
128 | tmp = *(p + 1); |
129 | p += (tmp + 2); | |
130 | i += (tmp + 2); | |
2865d42c LF |
131 | if (i >= limit) |
132 | break; | |
133 | } | |
134 | return NULL; | |
135 | } | |
136 | ||
137 | static void set_supported_rate(u8 *SupportedRates, uint mode) | |
138 | { | |
139 | memset(SupportedRates, 0, NDIS_802_11_LENGTH_RATES_EX); | |
140 | switch (mode) { | |
141 | case WIRELESS_11B: | |
142 | memcpy(SupportedRates, WIFI_CCKRATES, | |
143 | IEEE80211_CCK_RATE_LEN); | |
144 | break; | |
145 | case WIRELESS_11G: | |
146 | case WIRELESS_11A: | |
147 | memcpy(SupportedRates, WIFI_OFDMRATES, | |
148 | IEEE80211_NUM_OFDM_RATESLEN); | |
149 | break; | |
150 | case WIRELESS_11BG: | |
151 | memcpy(SupportedRates, WIFI_CCKRATES, IEEE80211_CCK_RATE_LEN); | |
152 | memcpy(SupportedRates + IEEE80211_CCK_RATE_LEN, WIFI_OFDMRATES, | |
153 | IEEE80211_NUM_OFDM_RATESLEN); | |
154 | break; | |
155 | } | |
156 | } | |
157 | ||
158 | static uint r8712_get_rateset_len(u8 *rateset) | |
159 | { | |
160 | uint i = 0; | |
161 | ||
162 | while (1) { | |
163 | if ((rateset[i]) == 0) | |
164 | break; | |
165 | if (i > 12) | |
166 | break; | |
167 | i++; | |
168 | } | |
169 | return i; | |
170 | } | |
171 | ||
ee5b1aad | 172 | int r8712_generate_ie(struct registry_priv *pregistrypriv) |
2865d42c LF |
173 | { |
174 | int sz = 0, rateLen; | |
175 | struct wlan_bssid_ex *pdev_network = &pregistrypriv->dev_network; | |
176 | u8 *ie = pdev_network->IEs; | |
2865d42c LF |
177 | |
178 | /*timestamp will be inserted by hardware*/ | |
179 | sz += 8; | |
180 | ie += sz; | |
181 | /*beacon interval : 2bytes*/ | |
182 | *(u16 *)ie = cpu_to_le16((u16)pdev_network->Configuration.BeaconPeriod); | |
183 | sz += 2; | |
184 | ie += 2; | |
185 | /*capability info*/ | |
186 | *(u16 *)ie = 0; | |
187 | *(u16 *)ie |= cpu_to_le16(cap_IBSS); | |
188 | if (pregistrypriv->preamble == PREAMBLE_SHORT) | |
189 | *(u16 *)ie |= cpu_to_le16(cap_ShortPremble); | |
190 | if (pdev_network->Privacy) | |
191 | *(u16 *)ie |= cpu_to_le16(cap_Privacy); | |
192 | sz += 2; | |
193 | ie += 2; | |
194 | /*SSID*/ | |
195 | ie = r8712_set_ie(ie, _SSID_IE_, pdev_network->Ssid.SsidLength, | |
196 | pdev_network->Ssid.Ssid, &sz); | |
197 | /*supported rates*/ | |
198 | set_supported_rate(pdev_network->SupportedRates, | |
199 | pregistrypriv->wireless_mode); | |
200 | rateLen = r8712_get_rateset_len(pdev_network->SupportedRates); | |
201 | if (rateLen > 8) { | |
202 | ie = r8712_set_ie(ie, _SUPPORTEDRATES_IE_, 8, | |
203 | pdev_network->SupportedRates, &sz); | |
204 | ie = r8712_set_ie(ie, _EXT_SUPPORTEDRATES_IE_, (rateLen - 8), | |
205 | (pdev_network->SupportedRates + 8), &sz); | |
206 | } else | |
207 | ie = r8712_set_ie(ie, _SUPPORTEDRATES_IE_, | |
208 | rateLen, pdev_network->SupportedRates, &sz); | |
209 | /*DS parameter set*/ | |
210 | ie = r8712_set_ie(ie, _DSSET_IE_, 1, | |
211 | (u8 *)&(pdev_network->Configuration.DSConfig), &sz); | |
212 | /*IBSS Parameter Set*/ | |
213 | ie = r8712_set_ie(ie, _IBSS_PARA_IE_, 2, | |
214 | (u8 *)&(pdev_network->Configuration.ATIMWindow), &sz); | |
2865d42c LF |
215 | return sz; |
216 | } | |
217 | ||
218 | unsigned char *r8712_get_wpa_ie(unsigned char *pie, int *wpa_ie_len, int limit) | |
219 | { | |
220 | int len; | |
221 | u16 val16; | |
222 | unsigned char wpa_oui_type[] = {0x00, 0x50, 0xf2, 0x01}; | |
223 | u8 *pbuf = pie; | |
224 | ||
225 | while (1) { | |
226 | pbuf = r8712_get_ie(pbuf, _WPA_IE_ID_, &len, limit); | |
227 | if (pbuf) { | |
228 | /*check if oui matches...*/ | |
229 | if (memcmp((pbuf + 2), wpa_oui_type, | |
230 | sizeof(wpa_oui_type))) | |
231 | goto check_next_ie; | |
232 | /*check version...*/ | |
233 | memcpy((u8 *)&val16, (pbuf + 6), sizeof(val16)); | |
234 | val16 = le16_to_cpu(val16); | |
235 | if (val16 != 0x0001) | |
236 | goto check_next_ie; | |
237 | *wpa_ie_len = *(pbuf + 1); | |
238 | return pbuf; | |
2865d42c | 239 | } |
e92c3511 NK |
240 | *wpa_ie_len = 0; |
241 | return NULL; | |
2865d42c LF |
242 | check_next_ie: |
243 | limit = limit - (pbuf - pie) - 2 - len; | |
244 | if (limit <= 0) | |
245 | break; | |
246 | pbuf += (2 + len); | |
247 | } | |
248 | *wpa_ie_len = 0; | |
249 | return NULL; | |
250 | } | |
251 | ||
252 | unsigned char *r8712_get_wpa2_ie(unsigned char *pie, int *rsn_ie_len, int limit) | |
253 | { | |
254 | return r8712_get_ie(pie, _WPA2_IE_ID_, rsn_ie_len, limit); | |
255 | } | |
256 | ||
257 | static int r8712_get_wpa_cipher_suite(u8 *s) | |
258 | { | |
259 | if (!memcmp(s, (void *)WPA_CIPHER_SUITE_NONE, WPA_SELECTOR_LEN)) | |
260 | return WPA_CIPHER_NONE; | |
261 | if (!memcmp(s, (void *)WPA_CIPHER_SUITE_WEP40, WPA_SELECTOR_LEN)) | |
262 | return WPA_CIPHER_WEP40; | |
263 | if (!memcmp(s, (void *)WPA_CIPHER_SUITE_TKIP, WPA_SELECTOR_LEN)) | |
264 | return WPA_CIPHER_TKIP; | |
265 | if (!memcmp(s, (void *)WPA_CIPHER_SUITE_CCMP, WPA_SELECTOR_LEN)) | |
266 | return WPA_CIPHER_CCMP; | |
267 | if (!memcmp(s, (void *)WPA_CIPHER_SUITE_WEP104, WPA_SELECTOR_LEN)) | |
268 | return WPA_CIPHER_WEP104; | |
269 | return 0; | |
270 | } | |
271 | ||
272 | static int r8712_get_wpa2_cipher_suite(u8 *s) | |
273 | { | |
274 | if (!memcmp(s, (void *)RSN_CIPHER_SUITE_NONE, RSN_SELECTOR_LEN)) | |
275 | return WPA_CIPHER_NONE; | |
276 | if (!memcmp(s, (void *)RSN_CIPHER_SUITE_WEP40, RSN_SELECTOR_LEN)) | |
277 | return WPA_CIPHER_WEP40; | |
278 | if (!memcmp(s, (void *)RSN_CIPHER_SUITE_TKIP, RSN_SELECTOR_LEN)) | |
279 | return WPA_CIPHER_TKIP; | |
280 | if (!memcmp(s, (void *)RSN_CIPHER_SUITE_CCMP, RSN_SELECTOR_LEN)) | |
281 | return WPA_CIPHER_CCMP; | |
282 | if (!memcmp(s, (void *)RSN_CIPHER_SUITE_WEP104, RSN_SELECTOR_LEN)) | |
283 | return WPA_CIPHER_WEP104; | |
284 | return 0; | |
285 | } | |
286 | ||
287 | int r8712_parse_wpa_ie(u8 *wpa_ie, int wpa_ie_len, int *group_cipher, | |
288 | int *pairwise_cipher) | |
289 | { | |
8ffca9ea | 290 | int i; |
2865d42c LF |
291 | int left, count; |
292 | u8 *pos; | |
293 | ||
294 | if (wpa_ie_len <= 0) { | |
295 | /* No WPA IE - fail silently */ | |
296 | return _FAIL; | |
297 | } | |
298 | if ((*wpa_ie != _WPA_IE_ID_) || (*(wpa_ie + 1) != (u8)(wpa_ie_len - 2)) | |
299 | || (memcmp(wpa_ie + 2, (void *)WPA_OUI_TYPE, WPA_SELECTOR_LEN))) | |
300 | return _FAIL; | |
301 | pos = wpa_ie; | |
302 | pos += 8; | |
303 | left = wpa_ie_len - 8; | |
304 | /*group_cipher*/ | |
305 | if (left >= WPA_SELECTOR_LEN) { | |
306 | *group_cipher = r8712_get_wpa_cipher_suite(pos); | |
307 | pos += WPA_SELECTOR_LEN; | |
308 | left -= WPA_SELECTOR_LEN; | |
309 | } else if (left > 0) | |
310 | return _FAIL; | |
311 | /*pairwise_cipher*/ | |
312 | if (left >= 2) { | |
313 | count = le16_to_cpu(*(u16 *)pos); | |
314 | pos += 2; | |
315 | left -= 2; | |
316 | if (count == 0 || left < count * WPA_SELECTOR_LEN) | |
317 | return _FAIL; | |
318 | for (i = 0; i < count; i++) { | |
319 | *pairwise_cipher |= r8712_get_wpa_cipher_suite(pos); | |
320 | pos += WPA_SELECTOR_LEN; | |
321 | left -= WPA_SELECTOR_LEN; | |
322 | } | |
323 | } else if (left == 1) | |
324 | return _FAIL; | |
8ffca9ea | 325 | return _SUCCESS; |
2865d42c LF |
326 | } |
327 | ||
328 | int r8712_parse_wpa2_ie(u8 *rsn_ie, int rsn_ie_len, int *group_cipher, | |
329 | int *pairwise_cipher) | |
330 | { | |
8ffca9ea | 331 | int i; |
2865d42c LF |
332 | int left, count; |
333 | u8 *pos; | |
334 | ||
335 | if (rsn_ie_len <= 0) { | |
336 | /* No RSN IE - fail silently */ | |
337 | return _FAIL; | |
338 | } | |
339 | if ((*rsn_ie != _WPA2_IE_ID_) || (*(rsn_ie+1) != (u8)(rsn_ie_len - 2))) | |
340 | return _FAIL; | |
341 | pos = rsn_ie; | |
342 | pos += 4; | |
343 | left = rsn_ie_len - 4; | |
344 | /*group_cipher*/ | |
345 | if (left >= RSN_SELECTOR_LEN) { | |
346 | *group_cipher = r8712_get_wpa2_cipher_suite(pos); | |
347 | pos += RSN_SELECTOR_LEN; | |
348 | left -= RSN_SELECTOR_LEN; | |
349 | } else if (left > 0) | |
350 | return _FAIL; | |
351 | /*pairwise_cipher*/ | |
352 | if (left >= 2) { | |
353 | count = le16_to_cpu(*(u16 *)pos); | |
354 | pos += 2; | |
355 | left -= 2; | |
356 | if (count == 0 || left < count * RSN_SELECTOR_LEN) | |
357 | return _FAIL; | |
358 | for (i = 0; i < count; i++) { | |
359 | *pairwise_cipher |= r8712_get_wpa2_cipher_suite(pos); | |
360 | pos += RSN_SELECTOR_LEN; | |
361 | left -= RSN_SELECTOR_LEN; | |
362 | } | |
363 | } else if (left == 1) | |
364 | return _FAIL; | |
8ffca9ea | 365 | return _SUCCESS; |
2865d42c LF |
366 | } |
367 | ||
368 | int r8712_get_sec_ie(u8 *in_ie, uint in_len, u8 *rsn_ie, u16 *rsn_len, | |
369 | u8 *wpa_ie, u16 *wpa_len) | |
370 | { | |
e29d3ebc | 371 | u8 authmode; |
2865d42c LF |
372 | u8 wpa_oui[4] = {0x0, 0x50, 0xf2, 0x01}; |
373 | uint cnt; | |
374 | ||
375 | /*Search required WPA or WPA2 IE and copy to sec_ie[ ]*/ | |
376 | cnt = (_TIMESTAMP_ + _BEACON_ITERVAL_ + _CAPABILITY_); | |
2865d42c LF |
377 | while (cnt < in_len) { |
378 | authmode = in_ie[cnt]; | |
379 | if ((authmode == _WPA_IE_ID_) && | |
380 | (!memcmp(&in_ie[cnt + 2], &wpa_oui[0], 4))) { | |
381 | memcpy(wpa_ie, &in_ie[cnt], in_ie[cnt + 1] + 2); | |
382 | *wpa_len = in_ie[cnt+1]+2; | |
383 | cnt += in_ie[cnt + 1] + 2; /*get next */ | |
384 | } else { | |
385 | if (authmode == _WPA2_IE_ID_) { | |
386 | memcpy(rsn_ie, &in_ie[cnt], | |
387 | in_ie[cnt + 1] + 2); | |
388 | *rsn_len = in_ie[cnt+1] + 2; | |
389 | cnt += in_ie[cnt+1] + 2; /*get next*/ | |
390 | } else | |
391 | cnt += in_ie[cnt+1] + 2; /*get next*/ | |
392 | } | |
393 | } | |
394 | return *rsn_len + *wpa_len; | |
395 | } | |
396 | ||
397 | int r8712_get_wps_ie(u8 *in_ie, uint in_len, u8 *wps_ie, uint *wps_ielen) | |
398 | { | |
399 | int match; | |
400 | uint cnt; | |
401 | u8 eid, wps_oui[4] = {0x0, 0x50, 0xf2, 0x04}; | |
402 | ||
403 | cnt = 12; | |
404 | match = false; | |
405 | while (cnt < in_len) { | |
406 | eid = in_ie[cnt]; | |
407 | if ((eid == _WPA_IE_ID_) && | |
408 | (!memcmp(&in_ie[cnt+2], wps_oui, 4))) { | |
409 | memcpy(wps_ie, &in_ie[cnt], in_ie[cnt+1]+2); | |
410 | *wps_ielen = in_ie[cnt+1]+2; | |
411 | cnt += in_ie[cnt+1]+2; | |
412 | match = true; | |
413 | break; | |
e92c3511 | 414 | } |
2865d42c LF |
415 | cnt += in_ie[cnt+1]+2; /* goto next */ |
416 | } | |
417 | return match; | |
418 | } |