1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * Original code based Host AP (software wireless LAN access point) driver |
4 | * for Intersil Prism2/2.5/3 - hostap.o module, common routines |
5 | * |
6 | * Copyright (c) 2001-2002, SSH Communications Security Corp and Jouni Malinen |
7 | * <j@w1.fi> |
8 | * Copyright (c) 2002-2003, Jouni Malinen <j@w1.fi> |
9 | * Copyright (c) 2004-2005, Intel Corporation |
10 | */ |
11 | |
12 | #include <linux/compiler.h> |
13 | #include <linux/errno.h> |
14 | #include <linux/if_arp.h> |
15 | #include <linux/in6.h> |
16 | #include <linux/gfp.h> |
17 | #include <linux/in.h> |
18 | #include <linux/ip.h> |
19 | #include <linux/kernel.h> |
20 | #include <linux/module.h> |
21 | #include <linux/netdevice.h> |
22 | #include <linux/proc_fs.h> |
23 | #include <linux/skbuff.h> |
24 | #include <linux/tcp.h> |
25 | #include <linux/types.h> |
26 | #include <linux/wireless.h> |
27 | #include <linux/etherdevice.h> |
28 | #include <linux/uaccess.h> |
29 | #include <linux/ctype.h> |
30 | |
31 | #include <net/lib80211.h> |
32 | |
33 | #include "libipw.h" |
34 | |
35 | static void libipw_monitor_rx(struct libipw_device *ieee, |
36 | struct sk_buff *skb, |
37 | struct libipw_rx_stats *rx_stats) |
38 | { |
39 | struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; |
40 | u16 fc = le16_to_cpu(hdr->frame_control); |
41 | |
42 | skb->dev = ieee->dev; |
43 | skb_reset_mac_header(skb); |
44 | skb_pull(skb, len: libipw_get_hdrlen(fc)); |
45 | skb->pkt_type = PACKET_OTHERHOST; |
46 | skb->protocol = htons(ETH_P_80211_RAW); |
47 | memset(skb->cb, 0, sizeof(skb->cb)); |
48 | netif_rx(skb); |
49 | } |
50 | |
51 | /* Called only as a tasklet (software IRQ) */ |
52 | static struct libipw_frag_entry *libipw_frag_cache_find(struct |
53 | libipw_device |
54 | *ieee, |
55 | unsigned int seq, |
56 | unsigned int frag, |
57 | u8 * src, |
58 | u8 * dst) |
59 | { |
60 | struct libipw_frag_entry *entry; |
61 | int i; |
62 | |
63 | for (i = 0; i < LIBIPW_FRAG_CACHE_LEN; i++) { |
64 | entry = &ieee->frag_cache[i]; |
65 | if (entry->skb != NULL && |
66 | time_after(jiffies, entry->first_frag_time + 2 * HZ)) { |
67 | LIBIPW_DEBUG_FRAG("expiring fragment cache entry " |
68 | "seq=%u last_frag=%u\n" , |
69 | entry->seq, entry->last_frag); |
70 | dev_kfree_skb_any(skb: entry->skb); |
71 | entry->skb = NULL; |
72 | } |
73 | |
74 | if (entry->skb != NULL && entry->seq == seq && |
75 | (entry->last_frag + 1 == frag || frag == -1) && |
76 | ether_addr_equal(addr1: entry->src_addr, addr2: src) && |
77 | ether_addr_equal(addr1: entry->dst_addr, addr2: dst)) |
78 | return entry; |
79 | } |
80 | |
81 | return NULL; |
82 | } |
83 | |
84 | /* Called only as a tasklet (software IRQ) */ |
85 | static struct sk_buff *libipw_frag_cache_get(struct libipw_device *ieee, |
86 | struct libipw_hdr_4addr *hdr) |
87 | { |
88 | struct sk_buff *skb = NULL; |
89 | u16 sc; |
90 | unsigned int frag, seq; |
91 | struct libipw_frag_entry *entry; |
92 | |
93 | sc = le16_to_cpu(hdr->seq_ctl); |
94 | frag = WLAN_GET_SEQ_FRAG(sc); |
95 | seq = WLAN_GET_SEQ_SEQ(sc); |
96 | |
97 | if (frag == 0) { |
98 | /* Reserve enough space to fit maximum frame length */ |
99 | skb = dev_alloc_skb(length: ieee->dev->mtu + |
100 | sizeof(struct libipw_hdr_4addr) + |
101 | 8 /* LLC */ + |
102 | 2 /* alignment */ + |
103 | 8 /* WEP */ + ETH_ALEN /* WDS */ ); |
104 | if (skb == NULL) |
105 | return NULL; |
106 | |
107 | entry = &ieee->frag_cache[ieee->frag_next_idx]; |
108 | ieee->frag_next_idx++; |
109 | if (ieee->frag_next_idx >= LIBIPW_FRAG_CACHE_LEN) |
110 | ieee->frag_next_idx = 0; |
111 | |
112 | if (entry->skb != NULL) |
113 | dev_kfree_skb_any(skb: entry->skb); |
114 | |
115 | entry->first_frag_time = jiffies; |
116 | entry->seq = seq; |
117 | entry->last_frag = frag; |
118 | entry->skb = skb; |
119 | memcpy(entry->src_addr, hdr->addr2, ETH_ALEN); |
120 | memcpy(entry->dst_addr, hdr->addr1, ETH_ALEN); |
121 | } else { |
122 | /* received a fragment of a frame for which the head fragment |
123 | * should have already been received */ |
124 | entry = libipw_frag_cache_find(ieee, seq, frag, src: hdr->addr2, |
125 | dst: hdr->addr1); |
126 | if (entry != NULL) { |
127 | entry->last_frag = frag; |
128 | skb = entry->skb; |
129 | } |
130 | } |
131 | |
132 | return skb; |
133 | } |
134 | |
135 | /* Called only as a tasklet (software IRQ) */ |
136 | static int libipw_frag_cache_invalidate(struct libipw_device *ieee, |
137 | struct libipw_hdr_4addr *hdr) |
138 | { |
139 | u16 sc; |
140 | unsigned int seq; |
141 | struct libipw_frag_entry *entry; |
142 | |
143 | sc = le16_to_cpu(hdr->seq_ctl); |
144 | seq = WLAN_GET_SEQ_SEQ(sc); |
145 | |
146 | entry = libipw_frag_cache_find(ieee, seq, frag: -1, src: hdr->addr2, |
147 | dst: hdr->addr1); |
148 | |
149 | if (entry == NULL) { |
150 | LIBIPW_DEBUG_FRAG("could not invalidate fragment cache " |
151 | "entry (seq=%u)\n" , seq); |
152 | return -1; |
153 | } |
154 | |
155 | entry->skb = NULL; |
156 | return 0; |
157 | } |
158 | |
159 | #ifdef NOT_YET |
160 | /* libipw_rx_frame_mgtmt |
161 | * |
162 | * Responsible for handling management control frames |
163 | * |
164 | * Called by libipw_rx */ |
165 | static int |
166 | libipw_rx_frame_mgmt(struct libipw_device *ieee, struct sk_buff *skb, |
167 | struct libipw_rx_stats *rx_stats, u16 type, |
168 | u16 stype) |
169 | { |
170 | if (ieee->iw_mode == IW_MODE_MASTER) { |
171 | printk(KERN_DEBUG "%s: Master mode not yet supported.\n" , |
172 | ieee->dev->name); |
173 | return 0; |
174 | /* |
175 | hostap_update_sta_ps(ieee, (struct hostap_libipw_hdr_4addr *) |
176 | skb->data);*/ |
177 | } |
178 | |
179 | if (ieee->hostapd && type == WLAN_FC_TYPE_MGMT) { |
180 | if (stype == WLAN_FC_STYPE_BEACON && |
181 | ieee->iw_mode == IW_MODE_MASTER) { |
182 | struct sk_buff *skb2; |
183 | /* Process beacon frames also in kernel driver to |
184 | * update STA(AP) table statistics */ |
185 | skb2 = skb_clone(skb, GFP_ATOMIC); |
186 | if (skb2) |
187 | hostap_rx(skb2->dev, skb2, rx_stats); |
188 | } |
189 | |
190 | /* send management frames to the user space daemon for |
191 | * processing */ |
192 | ieee->apdevstats.rx_packets++; |
193 | ieee->apdevstats.rx_bytes += skb->len; |
194 | prism2_rx_80211(ieee->apdev, skb, rx_stats, PRISM2_RX_MGMT); |
195 | return 0; |
196 | } |
197 | |
198 | if (ieee->iw_mode == IW_MODE_MASTER) { |
199 | if (type != WLAN_FC_TYPE_MGMT && type != WLAN_FC_TYPE_CTRL) { |
200 | printk(KERN_DEBUG "%s: unknown management frame " |
201 | "(type=0x%02x, stype=0x%02x) dropped\n" , |
202 | skb->dev->name, type, stype); |
203 | return -1; |
204 | } |
205 | |
206 | hostap_rx(skb->dev, skb, rx_stats); |
207 | return 0; |
208 | } |
209 | |
210 | printk(KERN_DEBUG "%s: hostap_rx_frame_mgmt: management frame " |
211 | "received in non-Host AP mode\n" , skb->dev->name); |
212 | return -1; |
213 | } |
214 | #endif |
215 | |
216 | /* See IEEE 802.1H for LLC/SNAP encapsulation/decapsulation */ |
217 | /* Ethernet-II snap header (RFC1042 for most EtherTypes) */ |
218 | static unsigned char [] = |
219 | { 0xaa, 0xaa, 0x03, 0x00, 0x00, 0x00 }; |
220 | |
221 | /* Bridge-Tunnel header (for EtherTypes ETH_P_AARP and ETH_P_IPX) */ |
222 | static unsigned char [] = |
223 | { 0xaa, 0xaa, 0x03, 0x00, 0x00, 0xf8 }; |
224 | /* No encapsulation header if EtherType < 0x600 (=length) */ |
225 | |
226 | /* Called by libipw_rx_frame_decrypt */ |
227 | static int libipw_is_eapol_frame(struct libipw_device *ieee, |
228 | struct sk_buff *skb) |
229 | { |
230 | struct net_device *dev = ieee->dev; |
231 | u16 fc, ethertype; |
232 | struct libipw_hdr_3addr *hdr; |
233 | u8 *pos; |
234 | |
235 | if (skb->len < 24) |
236 | return 0; |
237 | |
238 | hdr = (struct libipw_hdr_3addr *)skb->data; |
239 | fc = le16_to_cpu(hdr->frame_ctl); |
240 | |
241 | /* check that the frame is unicast frame to us */ |
242 | if ((fc & (IEEE80211_FCTL_TODS | IEEE80211_FCTL_FROMDS)) == |
243 | IEEE80211_FCTL_TODS && |
244 | ether_addr_equal(addr1: hdr->addr1, addr2: dev->dev_addr) && |
245 | ether_addr_equal(addr1: hdr->addr3, addr2: dev->dev_addr)) { |
246 | /* ToDS frame with own addr BSSID and DA */ |
247 | } else if ((fc & (IEEE80211_FCTL_TODS | IEEE80211_FCTL_FROMDS)) == |
248 | IEEE80211_FCTL_FROMDS && |
249 | ether_addr_equal(addr1: hdr->addr1, addr2: dev->dev_addr)) { |
250 | /* FromDS frame with own addr as DA */ |
251 | } else |
252 | return 0; |
253 | |
254 | if (skb->len < 24 + 8) |
255 | return 0; |
256 | |
257 | /* check for port access entity Ethernet type */ |
258 | pos = skb->data + 24; |
259 | ethertype = (pos[6] << 8) | pos[7]; |
260 | if (ethertype == ETH_P_PAE) |
261 | return 1; |
262 | |
263 | return 0; |
264 | } |
265 | |
266 | /* Called only as a tasklet (software IRQ), by libipw_rx */ |
267 | static int |
268 | libipw_rx_frame_decrypt(struct libipw_device *ieee, struct sk_buff *skb, |
269 | struct lib80211_crypt_data *crypt) |
270 | { |
271 | struct libipw_hdr_3addr *hdr; |
272 | int res, hdrlen; |
273 | |
274 | if (crypt == NULL || crypt->ops->decrypt_mpdu == NULL) |
275 | return 0; |
276 | |
277 | hdr = (struct libipw_hdr_3addr *)skb->data; |
278 | hdrlen = libipw_get_hdrlen(le16_to_cpu(hdr->frame_ctl)); |
279 | |
280 | atomic_inc(v: &crypt->refcnt); |
281 | res = crypt->ops->decrypt_mpdu(skb, hdrlen, crypt->priv); |
282 | atomic_dec(v: &crypt->refcnt); |
283 | if (res < 0) { |
284 | LIBIPW_DEBUG_DROP("decryption failed (SA=%pM) res=%d\n" , |
285 | hdr->addr2, res); |
286 | if (res == -2) |
287 | LIBIPW_DEBUG_DROP("Decryption failed ICV " |
288 | "mismatch (key %d)\n" , |
289 | skb->data[hdrlen + 3] >> 6); |
290 | ieee->ieee_stats.rx_discards_undecryptable++; |
291 | return -1; |
292 | } |
293 | |
294 | return res; |
295 | } |
296 | |
297 | /* Called only as a tasklet (software IRQ), by libipw_rx */ |
298 | static int |
299 | libipw_rx_frame_decrypt_msdu(struct libipw_device *ieee, |
300 | struct sk_buff *skb, int keyidx, |
301 | struct lib80211_crypt_data *crypt) |
302 | { |
303 | struct libipw_hdr_3addr *hdr; |
304 | int res, hdrlen; |
305 | |
306 | if (crypt == NULL || crypt->ops->decrypt_msdu == NULL) |
307 | return 0; |
308 | |
309 | hdr = (struct libipw_hdr_3addr *)skb->data; |
310 | hdrlen = libipw_get_hdrlen(le16_to_cpu(hdr->frame_ctl)); |
311 | |
312 | atomic_inc(v: &crypt->refcnt); |
313 | res = crypt->ops->decrypt_msdu(skb, keyidx, hdrlen, crypt->priv); |
314 | atomic_dec(v: &crypt->refcnt); |
315 | if (res < 0) { |
316 | printk(KERN_DEBUG "%s: MSDU decryption/MIC verification failed" |
317 | " (SA=%pM keyidx=%d)\n" , ieee->dev->name, hdr->addr2, |
318 | keyidx); |
319 | return -1; |
320 | } |
321 | |
322 | return 0; |
323 | } |
324 | |
325 | /* All received frames are sent to this function. @skb contains the frame in |
326 | * IEEE 802.11 format, i.e., in the format it was sent over air. |
327 | * This function is called only as a tasklet (software IRQ). */ |
328 | int libipw_rx(struct libipw_device *ieee, struct sk_buff *skb, |
329 | struct libipw_rx_stats *rx_stats) |
330 | { |
331 | struct net_device *dev = ieee->dev; |
332 | struct libipw_hdr_4addr *hdr; |
333 | size_t hdrlen; |
334 | u16 fc, type, stype, sc; |
335 | unsigned int frag; |
336 | u8 *payload; |
337 | u16 ethertype; |
338 | #ifdef NOT_YET |
339 | struct net_device *wds = NULL; |
340 | struct sk_buff *skb2 = NULL; |
341 | struct net_device *wds = NULL; |
342 | int frame_authorized = 0; |
343 | int from_assoc_ap = 0; |
344 | void *sta = NULL; |
345 | #endif |
346 | u8 dst[ETH_ALEN]; |
347 | u8 src[ETH_ALEN]; |
348 | struct lib80211_crypt_data *crypt = NULL; |
349 | int keyidx = 0; |
350 | int can_be_decrypted = 0; |
351 | |
352 | hdr = (struct libipw_hdr_4addr *)skb->data; |
353 | if (skb->len < 10) { |
354 | printk(KERN_INFO "%s: SKB length < 10\n" , dev->name); |
355 | goto rx_dropped; |
356 | } |
357 | |
358 | fc = le16_to_cpu(hdr->frame_ctl); |
359 | type = WLAN_FC_GET_TYPE(fc); |
360 | stype = WLAN_FC_GET_STYPE(fc); |
361 | sc = le16_to_cpu(hdr->seq_ctl); |
362 | frag = WLAN_GET_SEQ_FRAG(sc); |
363 | hdrlen = libipw_get_hdrlen(fc); |
364 | |
365 | if (skb->len < hdrlen) { |
366 | printk(KERN_INFO "%s: invalid SKB length %d\n" , |
367 | dev->name, skb->len); |
368 | goto rx_dropped; |
369 | } |
370 | |
371 | /* Put this code here so that we avoid duplicating it in all |
372 | * Rx paths. - Jean II */ |
373 | #ifdef CONFIG_WIRELESS_EXT |
374 | #ifdef IW_WIRELESS_SPY /* defined in iw_handler.h */ |
375 | /* If spy monitoring on */ |
376 | if (ieee->spy_data.spy_number > 0) { |
377 | struct iw_quality wstats; |
378 | |
379 | wstats.updated = 0; |
380 | if (rx_stats->mask & LIBIPW_STATMASK_RSSI) { |
381 | wstats.level = rx_stats->signal; |
382 | wstats.updated |= IW_QUAL_LEVEL_UPDATED; |
383 | } else |
384 | wstats.updated |= IW_QUAL_LEVEL_INVALID; |
385 | |
386 | if (rx_stats->mask & LIBIPW_STATMASK_NOISE) { |
387 | wstats.noise = rx_stats->noise; |
388 | wstats.updated |= IW_QUAL_NOISE_UPDATED; |
389 | } else |
390 | wstats.updated |= IW_QUAL_NOISE_INVALID; |
391 | |
392 | if (rx_stats->mask & LIBIPW_STATMASK_SIGNAL) { |
393 | wstats.qual = rx_stats->signal; |
394 | wstats.updated |= IW_QUAL_QUAL_UPDATED; |
395 | } else |
396 | wstats.updated |= IW_QUAL_QUAL_INVALID; |
397 | |
398 | /* Update spy records */ |
399 | wireless_spy_update(dev: ieee->dev, address: hdr->addr2, wstats: &wstats); |
400 | } |
401 | #endif /* IW_WIRELESS_SPY */ |
402 | #endif /* CONFIG_WIRELESS_EXT */ |
403 | |
404 | #ifdef NOT_YET |
405 | hostap_update_rx_stats(local->ap, hdr, rx_stats); |
406 | #endif |
407 | |
408 | if (ieee->iw_mode == IW_MODE_MONITOR) { |
409 | dev->stats.rx_packets++; |
410 | dev->stats.rx_bytes += skb->len; |
411 | libipw_monitor_rx(ieee, skb, rx_stats); |
412 | return 1; |
413 | } |
414 | |
415 | can_be_decrypted = (is_multicast_ether_addr(addr: hdr->addr1) || |
416 | is_broadcast_ether_addr(addr: hdr->addr2)) ? |
417 | ieee->host_mc_decrypt : ieee->host_decrypt; |
418 | |
419 | if (can_be_decrypted) { |
420 | if (skb->len >= hdrlen + 3) { |
421 | /* Top two-bits of byte 3 are the key index */ |
422 | keyidx = skb->data[hdrlen + 3] >> 6; |
423 | } |
424 | |
425 | /* ieee->crypt[] is WEP_KEY (4) in length. Given that keyidx |
426 | * is only allowed 2-bits of storage, no value of keyidx can |
427 | * be provided via above code that would result in keyidx |
428 | * being out of range */ |
429 | crypt = ieee->crypt_info.crypt[keyidx]; |
430 | |
431 | #ifdef NOT_YET |
432 | sta = NULL; |
433 | |
434 | /* Use station specific key to override default keys if the |
435 | * receiver address is a unicast address ("individual RA"). If |
436 | * bcrx_sta_key parameter is set, station specific key is used |
437 | * even with broad/multicast targets (this is against IEEE |
438 | * 802.11, but makes it easier to use different keys with |
439 | * stations that do not support WEP key mapping). */ |
440 | |
441 | if (is_unicast_ether_addr(hdr->addr1) || local->bcrx_sta_key) |
442 | (void)hostap_handle_sta_crypto(local, hdr, &crypt, |
443 | &sta); |
444 | #endif |
445 | |
446 | /* allow NULL decrypt to indicate an station specific override |
447 | * for default encryption */ |
448 | if (crypt && (crypt->ops == NULL || |
449 | crypt->ops->decrypt_mpdu == NULL)) |
450 | crypt = NULL; |
451 | |
452 | if (!crypt && (fc & IEEE80211_FCTL_PROTECTED)) { |
453 | /* This seems to be triggered by some (multicast?) |
454 | * frames from other than current BSS, so just drop the |
455 | * frames silently instead of filling system log with |
456 | * these reports. */ |
457 | LIBIPW_DEBUG_DROP("Decryption failed (not set)" |
458 | " (SA=%pM)\n" , hdr->addr2); |
459 | ieee->ieee_stats.rx_discards_undecryptable++; |
460 | goto rx_dropped; |
461 | } |
462 | } |
463 | #ifdef NOT_YET |
464 | if (type != WLAN_FC_TYPE_DATA) { |
465 | if (type == WLAN_FC_TYPE_MGMT && stype == WLAN_FC_STYPE_AUTH && |
466 | fc & IEEE80211_FCTL_PROTECTED && ieee->host_decrypt && |
467 | (keyidx = hostap_rx_frame_decrypt(ieee, skb, crypt)) < 0) { |
468 | printk(KERN_DEBUG "%s: failed to decrypt mgmt::auth " |
469 | "from %pM\n" , dev->name, hdr->addr2); |
470 | /* TODO: could inform hostapd about this so that it |
471 | * could send auth failure report */ |
472 | goto rx_dropped; |
473 | } |
474 | |
475 | if (libipw_rx_frame_mgmt(ieee, skb, rx_stats, type, stype)) |
476 | goto rx_dropped; |
477 | else |
478 | goto rx_exit; |
479 | } |
480 | #endif |
481 | /* drop duplicate 802.11 retransmissions (IEEE 802.11 Chap. 9.29) */ |
482 | if (sc == ieee->prev_seq_ctl) |
483 | goto rx_dropped; |
484 | else |
485 | ieee->prev_seq_ctl = sc; |
486 | |
487 | /* Data frame - extract src/dst addresses */ |
488 | if (skb->len < LIBIPW_3ADDR_LEN) |
489 | goto rx_dropped; |
490 | |
491 | switch (fc & (IEEE80211_FCTL_FROMDS | IEEE80211_FCTL_TODS)) { |
492 | case IEEE80211_FCTL_FROMDS: |
493 | memcpy(dst, hdr->addr1, ETH_ALEN); |
494 | memcpy(src, hdr->addr3, ETH_ALEN); |
495 | break; |
496 | case IEEE80211_FCTL_TODS: |
497 | memcpy(dst, hdr->addr3, ETH_ALEN); |
498 | memcpy(src, hdr->addr2, ETH_ALEN); |
499 | break; |
500 | case IEEE80211_FCTL_FROMDS | IEEE80211_FCTL_TODS: |
501 | if (skb->len < LIBIPW_4ADDR_LEN) |
502 | goto rx_dropped; |
503 | memcpy(dst, hdr->addr3, ETH_ALEN); |
504 | memcpy(src, hdr->addr4, ETH_ALEN); |
505 | break; |
506 | default: |
507 | memcpy(dst, hdr->addr1, ETH_ALEN); |
508 | memcpy(src, hdr->addr2, ETH_ALEN); |
509 | break; |
510 | } |
511 | |
512 | #ifdef NOT_YET |
513 | if (hostap_rx_frame_wds(ieee, hdr, fc, &wds)) |
514 | goto rx_dropped; |
515 | if (wds) { |
516 | skb->dev = dev = wds; |
517 | stats = hostap_get_stats(dev); |
518 | } |
519 | |
520 | if (ieee->iw_mode == IW_MODE_MASTER && !wds && |
521 | (fc & (IEEE80211_FCTL_TODS | IEEE80211_FCTL_FROMDS)) == |
522 | IEEE80211_FCTL_FROMDS && ieee->stadev && |
523 | ether_addr_equal(hdr->addr2, ieee->assoc_ap_addr)) { |
524 | /* Frame from BSSID of the AP for which we are a client */ |
525 | skb->dev = dev = ieee->stadev; |
526 | stats = hostap_get_stats(dev); |
527 | from_assoc_ap = 1; |
528 | } |
529 | #endif |
530 | |
531 | #ifdef NOT_YET |
532 | if ((ieee->iw_mode == IW_MODE_MASTER || |
533 | ieee->iw_mode == IW_MODE_REPEAT) && !from_assoc_ap) { |
534 | switch (hostap_handle_sta_rx(ieee, dev, skb, rx_stats, |
535 | wds != NULL)) { |
536 | case AP_RX_CONTINUE_NOT_AUTHORIZED: |
537 | frame_authorized = 0; |
538 | break; |
539 | case AP_RX_CONTINUE: |
540 | frame_authorized = 1; |
541 | break; |
542 | case AP_RX_DROP: |
543 | goto rx_dropped; |
544 | case AP_RX_EXIT: |
545 | goto rx_exit; |
546 | } |
547 | } |
548 | #endif |
549 | |
550 | /* Nullfunc frames may have PS-bit set, so they must be passed to |
551 | * hostap_handle_sta_rx() before being dropped here. */ |
552 | |
553 | stype &= ~IEEE80211_STYPE_QOS_DATA; |
554 | |
555 | if (stype != IEEE80211_STYPE_DATA && |
556 | stype != IEEE80211_STYPE_DATA_CFACK && |
557 | stype != IEEE80211_STYPE_DATA_CFPOLL && |
558 | stype != IEEE80211_STYPE_DATA_CFACKPOLL) { |
559 | if (stype != IEEE80211_STYPE_NULLFUNC) |
560 | LIBIPW_DEBUG_DROP("RX: dropped data frame " |
561 | "with no data (type=0x%02x, " |
562 | "subtype=0x%02x, len=%d)\n" , |
563 | type, stype, skb->len); |
564 | goto rx_dropped; |
565 | } |
566 | |
567 | /* skb: hdr + (possibly fragmented, possibly encrypted) payload */ |
568 | |
569 | if ((fc & IEEE80211_FCTL_PROTECTED) && can_be_decrypted && |
570 | (keyidx = libipw_rx_frame_decrypt(ieee, skb, crypt)) < 0) |
571 | goto rx_dropped; |
572 | |
573 | hdr = (struct libipw_hdr_4addr *)skb->data; |
574 | |
575 | /* skb: hdr + (possibly fragmented) plaintext payload */ |
576 | // PR: FIXME: hostap has additional conditions in the "if" below: |
577 | // ieee->host_decrypt && (fc & IEEE80211_FCTL_PROTECTED) && |
578 | if ((frag != 0) || (fc & IEEE80211_FCTL_MOREFRAGS)) { |
579 | int flen; |
580 | struct sk_buff *frag_skb = libipw_frag_cache_get(ieee, hdr); |
581 | LIBIPW_DEBUG_FRAG("Rx Fragment received (%u)\n" , frag); |
582 | |
583 | if (!frag_skb) { |
584 | LIBIPW_DEBUG(LIBIPW_DL_RX | LIBIPW_DL_FRAG, |
585 | "Rx cannot get skb from fragment " |
586 | "cache (morefrag=%d seq=%u frag=%u)\n" , |
587 | (fc & IEEE80211_FCTL_MOREFRAGS) != 0, |
588 | WLAN_GET_SEQ_SEQ(sc), frag); |
589 | goto rx_dropped; |
590 | } |
591 | |
592 | flen = skb->len; |
593 | if (frag != 0) |
594 | flen -= hdrlen; |
595 | |
596 | if (frag_skb->tail + flen > frag_skb->end) { |
597 | printk(KERN_WARNING "%s: host decrypted and " |
598 | "reassembled frame did not fit skb\n" , |
599 | dev->name); |
600 | libipw_frag_cache_invalidate(ieee, hdr); |
601 | goto rx_dropped; |
602 | } |
603 | |
604 | if (frag == 0) { |
605 | /* copy first fragment (including full headers) into |
606 | * beginning of the fragment cache skb */ |
607 | skb_copy_from_linear_data(skb, to: skb_put(skb: frag_skb, len: flen), len: flen); |
608 | } else { |
609 | /* append frame payload to the end of the fragment |
610 | * cache skb */ |
611 | skb_copy_from_linear_data_offset(skb, offset: hdrlen, |
612 | to: skb_put(skb: frag_skb, len: flen), len: flen); |
613 | } |
614 | dev_kfree_skb_any(skb); |
615 | skb = NULL; |
616 | |
617 | if (fc & IEEE80211_FCTL_MOREFRAGS) { |
618 | /* more fragments expected - leave the skb in fragment |
619 | * cache for now; it will be delivered to upper layers |
620 | * after all fragments have been received */ |
621 | goto rx_exit; |
622 | } |
623 | |
624 | /* this was the last fragment and the frame will be |
625 | * delivered, so remove skb from fragment cache */ |
626 | skb = frag_skb; |
627 | hdr = (struct libipw_hdr_4addr *)skb->data; |
628 | libipw_frag_cache_invalidate(ieee, hdr); |
629 | } |
630 | |
631 | /* skb: hdr + (possible reassembled) full MSDU payload; possibly still |
632 | * encrypted/authenticated */ |
633 | if ((fc & IEEE80211_FCTL_PROTECTED) && can_be_decrypted && |
634 | libipw_rx_frame_decrypt_msdu(ieee, skb, keyidx, crypt)) |
635 | goto rx_dropped; |
636 | |
637 | hdr = (struct libipw_hdr_4addr *)skb->data; |
638 | if (crypt && !(fc & IEEE80211_FCTL_PROTECTED) && !ieee->open_wep) { |
639 | if ( /*ieee->ieee802_1x && */ |
640 | libipw_is_eapol_frame(ieee, skb)) { |
641 | /* pass unencrypted EAPOL frames even if encryption is |
642 | * configured */ |
643 | } else { |
644 | LIBIPW_DEBUG_DROP("encryption configured, but RX " |
645 | "frame not encrypted (SA=%pM)\n" , |
646 | hdr->addr2); |
647 | goto rx_dropped; |
648 | } |
649 | } |
650 | |
651 | if (crypt && !(fc & IEEE80211_FCTL_PROTECTED) && !ieee->open_wep && |
652 | !libipw_is_eapol_frame(ieee, skb)) { |
653 | LIBIPW_DEBUG_DROP("dropped unencrypted RX data " |
654 | "frame from %pM (drop_unencrypted=1)\n" , |
655 | hdr->addr2); |
656 | goto rx_dropped; |
657 | } |
658 | |
659 | /* If the frame was decrypted in hardware, we may need to strip off |
660 | * any security data (IV, ICV, etc) that was left behind */ |
661 | if (!can_be_decrypted && (fc & IEEE80211_FCTL_PROTECTED) && |
662 | ieee->host_strip_iv_icv) { |
663 | int trimlen = 0; |
664 | |
665 | /* Top two-bits of byte 3 are the key index */ |
666 | if (skb->len >= hdrlen + 3) |
667 | keyidx = skb->data[hdrlen + 3] >> 6; |
668 | |
669 | /* To strip off any security data which appears before the |
670 | * payload, we simply increase hdrlen (as the header gets |
671 | * chopped off immediately below). For the security data which |
672 | * appears after the payload, we use skb_trim. */ |
673 | |
674 | switch (ieee->sec.encode_alg[keyidx]) { |
675 | case SEC_ALG_WEP: |
676 | /* 4 byte IV */ |
677 | hdrlen += 4; |
678 | /* 4 byte ICV */ |
679 | trimlen = 4; |
680 | break; |
681 | case SEC_ALG_TKIP: |
682 | /* 4 byte IV, 4 byte ExtIV */ |
683 | hdrlen += 8; |
684 | /* 8 byte MIC, 4 byte ICV */ |
685 | trimlen = 12; |
686 | break; |
687 | case SEC_ALG_CCMP: |
688 | /* 8 byte CCMP header */ |
689 | hdrlen += 8; |
690 | /* 8 byte MIC */ |
691 | trimlen = 8; |
692 | break; |
693 | } |
694 | |
695 | if (skb->len < trimlen) |
696 | goto rx_dropped; |
697 | |
698 | __skb_trim(skb, len: skb->len - trimlen); |
699 | |
700 | if (skb->len < hdrlen) |
701 | goto rx_dropped; |
702 | } |
703 | |
704 | /* skb: hdr + (possible reassembled) full plaintext payload */ |
705 | |
706 | payload = skb->data + hdrlen; |
707 | ethertype = (payload[6] << 8) | payload[7]; |
708 | |
709 | #ifdef NOT_YET |
710 | /* If IEEE 802.1X is used, check whether the port is authorized to send |
711 | * the received frame. */ |
712 | if (ieee->ieee802_1x && ieee->iw_mode == IW_MODE_MASTER) { |
713 | if (ethertype == ETH_P_PAE) { |
714 | printk(KERN_DEBUG "%s: RX: IEEE 802.1X frame\n" , |
715 | dev->name); |
716 | if (ieee->hostapd && ieee->apdev) { |
717 | /* Send IEEE 802.1X frames to the user |
718 | * space daemon for processing */ |
719 | prism2_rx_80211(ieee->apdev, skb, rx_stats, |
720 | PRISM2_RX_MGMT); |
721 | ieee->apdevstats.rx_packets++; |
722 | ieee->apdevstats.rx_bytes += skb->len; |
723 | goto rx_exit; |
724 | } |
725 | } else if (!frame_authorized) { |
726 | printk(KERN_DEBUG "%s: dropped frame from " |
727 | "unauthorized port (IEEE 802.1X): " |
728 | "ethertype=0x%04x\n" , dev->name, ethertype); |
729 | goto rx_dropped; |
730 | } |
731 | } |
732 | #endif |
733 | |
734 | /* convert hdr + possible LLC headers into Ethernet header */ |
735 | if (skb->len - hdrlen >= 8 && |
736 | ((memcmp(p: payload, q: libipw_rfc1042_header, SNAP_SIZE) == 0 && |
737 | ethertype != ETH_P_AARP && ethertype != ETH_P_IPX) || |
738 | memcmp(p: payload, q: libipw_bridge_tunnel_header, SNAP_SIZE) == 0)) { |
739 | /* remove RFC1042 or Bridge-Tunnel encapsulation and |
740 | * replace EtherType */ |
741 | skb_pull(skb, len: hdrlen + SNAP_SIZE); |
742 | memcpy(skb_push(skb, ETH_ALEN), src, ETH_ALEN); |
743 | memcpy(skb_push(skb, ETH_ALEN), dst, ETH_ALEN); |
744 | } else { |
745 | __be16 len; |
746 | /* Leave Ethernet header part of hdr and full payload */ |
747 | skb_pull(skb, len: hdrlen); |
748 | len = htons(skb->len); |
749 | memcpy(skb_push(skb, 2), &len, 2); |
750 | memcpy(skb_push(skb, ETH_ALEN), src, ETH_ALEN); |
751 | memcpy(skb_push(skb, ETH_ALEN), dst, ETH_ALEN); |
752 | } |
753 | |
754 | #ifdef NOT_YET |
755 | if (wds && ((fc & (IEEE80211_FCTL_TODS | IEEE80211_FCTL_FROMDS)) == |
756 | IEEE80211_FCTL_TODS) && skb->len >= ETH_HLEN + ETH_ALEN) { |
757 | /* Non-standard frame: get addr4 from its bogus location after |
758 | * the payload */ |
759 | skb_copy_to_linear_data_offset(skb, ETH_ALEN, |
760 | skb->data + skb->len - ETH_ALEN, |
761 | ETH_ALEN); |
762 | skb_trim(skb, skb->len - ETH_ALEN); |
763 | } |
764 | #endif |
765 | |
766 | dev->stats.rx_packets++; |
767 | dev->stats.rx_bytes += skb->len; |
768 | |
769 | #ifdef NOT_YET |
770 | if (ieee->iw_mode == IW_MODE_MASTER && !wds && ieee->ap->bridge_packets) { |
771 | if (is_multicast_ether_addr(dst)) { |
772 | /* copy multicast frame both to the higher layers and |
773 | * to the wireless media */ |
774 | ieee->ap->bridged_multicast++; |
775 | skb2 = skb_clone(skb, GFP_ATOMIC); |
776 | if (skb2 == NULL) |
777 | printk(KERN_DEBUG "%s: skb_clone failed for " |
778 | "multicast frame\n" , dev->name); |
779 | } else if (hostap_is_sta_assoc(ieee->ap, dst)) { |
780 | /* send frame directly to the associated STA using |
781 | * wireless media and not passing to higher layers */ |
782 | ieee->ap->bridged_unicast++; |
783 | skb2 = skb; |
784 | skb = NULL; |
785 | } |
786 | } |
787 | |
788 | if (skb2 != NULL) { |
789 | /* send to wireless media */ |
790 | skb2->dev = dev; |
791 | skb2->protocol = htons(ETH_P_802_3); |
792 | skb_reset_mac_header(skb2); |
793 | skb_reset_network_header(skb2); |
794 | /* skb2->network_header += ETH_HLEN; */ |
795 | dev_queue_xmit(skb2); |
796 | } |
797 | #endif |
798 | |
799 | if (skb) { |
800 | skb->protocol = eth_type_trans(skb, dev); |
801 | memset(skb->cb, 0, sizeof(skb->cb)); |
802 | skb->ip_summed = CHECKSUM_NONE; /* 802.11 crc not sufficient */ |
803 | if (netif_rx(skb) == NET_RX_DROP) { |
804 | /* netif_rx always succeeds, but it might drop |
805 | * the packet. If it drops the packet, we log that |
806 | * in our stats. */ |
807 | LIBIPW_DEBUG_DROP |
808 | ("RX: netif_rx dropped the packet\n" ); |
809 | dev->stats.rx_dropped++; |
810 | } |
811 | } |
812 | |
813 | rx_exit: |
814 | #ifdef NOT_YET |
815 | if (sta) |
816 | hostap_handle_sta_release(sta); |
817 | #endif |
818 | return 1; |
819 | |
820 | rx_dropped: |
821 | dev->stats.rx_dropped++; |
822 | |
823 | /* Returning 0 indicates to caller that we have not handled the SKB-- |
824 | * so it is still allocated and can be used again by underlying |
825 | * hardware as a DMA target */ |
826 | return 0; |
827 | } |
828 | |
829 | /* Filter out unrelated packets, call libipw_rx[_mgt] |
830 | * This function takes over the skb, it should not be used again after calling |
831 | * this function. */ |
832 | void libipw_rx_any(struct libipw_device *ieee, |
833 | struct sk_buff *skb, struct libipw_rx_stats *stats) |
834 | { |
835 | struct libipw_hdr_4addr *hdr; |
836 | int is_packet_for_us; |
837 | u16 fc; |
838 | |
839 | if (ieee->iw_mode == IW_MODE_MONITOR) { |
840 | if (!libipw_rx(ieee, skb, rx_stats: stats)) |
841 | dev_kfree_skb_irq(skb); |
842 | return; |
843 | } |
844 | |
845 | if (skb->len < sizeof(struct ieee80211_hdr)) |
846 | goto drop_free; |
847 | |
848 | hdr = (struct libipw_hdr_4addr *)skb->data; |
849 | fc = le16_to_cpu(hdr->frame_ctl); |
850 | |
851 | if ((fc & IEEE80211_FCTL_VERS) != 0) |
852 | goto drop_free; |
853 | |
854 | switch (fc & IEEE80211_FCTL_FTYPE) { |
855 | case IEEE80211_FTYPE_MGMT: |
856 | if (skb->len < sizeof(struct libipw_hdr_3addr)) |
857 | goto drop_free; |
858 | libipw_rx_mgt(ieee, header: hdr, stats); |
859 | dev_kfree_skb_irq(skb); |
860 | return; |
861 | case IEEE80211_FTYPE_DATA: |
862 | break; |
863 | case IEEE80211_FTYPE_CTL: |
864 | return; |
865 | default: |
866 | return; |
867 | } |
868 | |
869 | is_packet_for_us = 0; |
870 | switch (ieee->iw_mode) { |
871 | case IW_MODE_ADHOC: |
872 | /* our BSS and not from/to DS */ |
873 | if (ether_addr_equal(addr1: hdr->addr3, addr2: ieee->bssid)) |
874 | if ((fc & (IEEE80211_FCTL_TODS+IEEE80211_FCTL_FROMDS)) == 0) { |
875 | /* promisc: get all */ |
876 | if (ieee->dev->flags & IFF_PROMISC) |
877 | is_packet_for_us = 1; |
878 | /* to us */ |
879 | else if (ether_addr_equal(addr1: hdr->addr1, addr2: ieee->dev->dev_addr)) |
880 | is_packet_for_us = 1; |
881 | /* mcast */ |
882 | else if (is_multicast_ether_addr(addr: hdr->addr1)) |
883 | is_packet_for_us = 1; |
884 | } |
885 | break; |
886 | case IW_MODE_INFRA: |
887 | /* our BSS (== from our AP) and from DS */ |
888 | if (ether_addr_equal(addr1: hdr->addr2, addr2: ieee->bssid)) |
889 | if ((fc & (IEEE80211_FCTL_TODS+IEEE80211_FCTL_FROMDS)) == IEEE80211_FCTL_FROMDS) { |
890 | /* promisc: get all */ |
891 | if (ieee->dev->flags & IFF_PROMISC) |
892 | is_packet_for_us = 1; |
893 | /* to us */ |
894 | else if (ether_addr_equal(addr1: hdr->addr1, addr2: ieee->dev->dev_addr)) |
895 | is_packet_for_us = 1; |
896 | /* mcast */ |
897 | else if (is_multicast_ether_addr(addr: hdr->addr1)) { |
898 | /* not our own packet bcasted from AP */ |
899 | if (!ether_addr_equal(addr1: hdr->addr3, addr2: ieee->dev->dev_addr)) |
900 | is_packet_for_us = 1; |
901 | } |
902 | } |
903 | break; |
904 | default: |
905 | /* ? */ |
906 | break; |
907 | } |
908 | |
909 | if (is_packet_for_us) |
910 | if (!libipw_rx(ieee, skb, rx_stats: stats)) |
911 | dev_kfree_skb_irq(skb); |
912 | return; |
913 | |
914 | drop_free: |
915 | dev_kfree_skb_irq(skb); |
916 | ieee->dev->stats.rx_dropped++; |
917 | } |
918 | |
919 | #define MGMT_FRAME_FIXED_PART_LENGTH 0x24 |
920 | |
921 | static u8 qos_oui[QOS_OUI_LEN] = { 0x00, 0x50, 0xF2 }; |
922 | |
923 | /* |
924 | * Make the structure we read from the beacon packet to have |
925 | * the right values |
926 | */ |
927 | static int libipw_verify_qos_info(struct libipw_qos_information_element |
928 | *info_element, int sub_type) |
929 | { |
930 | if (info_element->elementID != QOS_ELEMENT_ID) |
931 | return -1; |
932 | if (info_element->qui_subtype != sub_type) |
933 | return -1; |
934 | if (memcmp(p: info_element->qui, q: qos_oui, QOS_OUI_LEN)) |
935 | return -1; |
936 | if (info_element->qui_type != QOS_OUI_TYPE) |
937 | return -1; |
938 | if (info_element->version != QOS_VERSION_1) |
939 | return -1; |
940 | |
941 | return 0; |
942 | } |
943 | |
944 | /* |
945 | * Parse a QoS parameter element |
946 | */ |
947 | static int libipw_read_qos_param_element( |
948 | struct libipw_qos_parameter_info *element_param, |
949 | struct libipw_info_element *info_element) |
950 | { |
951 | size_t size = sizeof(*element_param); |
952 | |
953 | if (!element_param || !info_element || info_element->len != size - 2) |
954 | return -1; |
955 | |
956 | memcpy(element_param, info_element, size); |
957 | return libipw_verify_qos_info(info_element: &element_param->info_element, |
958 | QOS_OUI_PARAM_SUB_TYPE); |
959 | } |
960 | |
961 | /* |
962 | * Parse a QoS information element |
963 | */ |
964 | static int libipw_read_qos_info_element( |
965 | struct libipw_qos_information_element *element_info, |
966 | struct libipw_info_element *info_element) |
967 | { |
968 | size_t size = sizeof(struct libipw_qos_information_element) - 2; |
969 | |
970 | if (!element_info || !info_element || info_element->len != size - 2) |
971 | return -1; |
972 | |
973 | memcpy(element_info, info_element, size); |
974 | return libipw_verify_qos_info(info_element: element_info, QOS_OUI_INFO_SUB_TYPE); |
975 | } |
976 | |
977 | /* |
978 | * Write QoS parameters from the ac parameters. |
979 | */ |
980 | static void libipw_qos_convert_ac_to_parameters(struct |
981 | libipw_qos_parameter_info |
982 | *param_elm, struct |
983 | libipw_qos_parameters |
984 | *qos_param) |
985 | { |
986 | int i; |
987 | struct libipw_qos_ac_parameter *ac_params; |
988 | u32 txop; |
989 | u8 cw_min; |
990 | u8 cw_max; |
991 | |
992 | for (i = 0; i < QOS_QUEUE_NUM; i++) { |
993 | ac_params = &(param_elm->ac_params_record[i]); |
994 | |
995 | qos_param->aifs[i] = (ac_params->aci_aifsn) & 0x0F; |
996 | qos_param->aifs[i] -= (qos_param->aifs[i] < 2) ? 0 : 2; |
997 | |
998 | cw_min = ac_params->ecw_min_max & 0x0F; |
999 | qos_param->cw_min[i] = cpu_to_le16((1 << cw_min) - 1); |
1000 | |
1001 | cw_max = (ac_params->ecw_min_max & 0xF0) >> 4; |
1002 | qos_param->cw_max[i] = cpu_to_le16((1 << cw_max) - 1); |
1003 | |
1004 | qos_param->flag[i] = |
1005 | (ac_params->aci_aifsn & 0x10) ? 0x01 : 0x00; |
1006 | |
1007 | txop = le16_to_cpu(ac_params->tx_op_limit) * 32; |
1008 | qos_param->tx_op_limit[i] = cpu_to_le16(txop); |
1009 | } |
1010 | } |
1011 | |
1012 | /* |
1013 | * we have a generic data element which it may contain QoS information or |
1014 | * parameters element. check the information element length to decide |
1015 | * which type to read |
1016 | */ |
1017 | static int libipw_parse_qos_info_param_IE(struct libipw_info_element |
1018 | *info_element, |
1019 | struct libipw_network *network) |
1020 | { |
1021 | int rc = 0; |
1022 | struct libipw_qos_parameters *qos_param = NULL; |
1023 | struct libipw_qos_information_element qos_info_element; |
1024 | |
1025 | rc = libipw_read_qos_info_element(element_info: &qos_info_element, info_element); |
1026 | |
1027 | if (rc == 0) { |
1028 | network->qos_data.param_count = qos_info_element.ac_info & 0x0F; |
1029 | network->flags |= NETWORK_HAS_QOS_INFORMATION; |
1030 | } else { |
1031 | struct libipw_qos_parameter_info param_element; |
1032 | |
1033 | rc = libipw_read_qos_param_element(element_param: ¶m_element, |
1034 | info_element); |
1035 | if (rc == 0) { |
1036 | qos_param = &(network->qos_data.parameters); |
1037 | libipw_qos_convert_ac_to_parameters(param_elm: ¶m_element, |
1038 | qos_param); |
1039 | network->flags |= NETWORK_HAS_QOS_PARAMETERS; |
1040 | network->qos_data.param_count = |
1041 | param_element.info_element.ac_info & 0x0F; |
1042 | } |
1043 | } |
1044 | |
1045 | if (rc == 0) { |
1046 | LIBIPW_DEBUG_QOS("QoS is supported\n" ); |
1047 | network->qos_data.supported = 1; |
1048 | } |
1049 | return rc; |
1050 | } |
1051 | |
1052 | #ifdef CONFIG_LIBIPW_DEBUG |
1053 | #define MFIE_STRING(x) case WLAN_EID_ ##x: return #x |
1054 | |
1055 | static const char *get_info_element_string(u16 id) |
1056 | { |
1057 | switch (id) { |
1058 | MFIE_STRING(SSID); |
1059 | MFIE_STRING(SUPP_RATES); |
1060 | MFIE_STRING(FH_PARAMS); |
1061 | MFIE_STRING(DS_PARAMS); |
1062 | MFIE_STRING(CF_PARAMS); |
1063 | MFIE_STRING(TIM); |
1064 | MFIE_STRING(IBSS_PARAMS); |
1065 | MFIE_STRING(COUNTRY); |
1066 | MFIE_STRING(REQUEST); |
1067 | MFIE_STRING(CHALLENGE); |
1068 | MFIE_STRING(PWR_CONSTRAINT); |
1069 | MFIE_STRING(PWR_CAPABILITY); |
1070 | MFIE_STRING(TPC_REQUEST); |
1071 | MFIE_STRING(TPC_REPORT); |
1072 | MFIE_STRING(SUPPORTED_CHANNELS); |
1073 | MFIE_STRING(CHANNEL_SWITCH); |
1074 | MFIE_STRING(MEASURE_REQUEST); |
1075 | MFIE_STRING(MEASURE_REPORT); |
1076 | MFIE_STRING(QUIET); |
1077 | MFIE_STRING(IBSS_DFS); |
1078 | MFIE_STRING(ERP_INFO); |
1079 | MFIE_STRING(RSN); |
1080 | MFIE_STRING(EXT_SUPP_RATES); |
1081 | MFIE_STRING(VENDOR_SPECIFIC); |
1082 | MFIE_STRING(QOS_PARAMETER); |
1083 | default: |
1084 | return "UNKNOWN" ; |
1085 | } |
1086 | } |
1087 | #endif |
1088 | |
1089 | static int libipw_parse_info_param(struct libipw_info_element |
1090 | *info_element, u16 length, |
1091 | struct libipw_network *network) |
1092 | { |
1093 | u8 i; |
1094 | #ifdef CONFIG_LIBIPW_DEBUG |
1095 | char rates_str[64]; |
1096 | char *p; |
1097 | #endif |
1098 | |
1099 | while (length >= sizeof(*info_element)) { |
1100 | if (sizeof(*info_element) + info_element->len > length) { |
1101 | LIBIPW_DEBUG_MGMT("Info elem: parse failed: " |
1102 | "info_element->len + 2 > left : " |
1103 | "info_element->len+2=%zd left=%d, id=%d.\n" , |
1104 | info_element->len + |
1105 | sizeof(*info_element), |
1106 | length, info_element->id); |
1107 | /* We stop processing but don't return an error here |
1108 | * because some misbehaviour APs break this rule. ie. |
1109 | * Orinoco AP1000. */ |
1110 | break; |
1111 | } |
1112 | |
1113 | switch (info_element->id) { |
1114 | case WLAN_EID_SSID: |
1115 | network->ssid_len = min(info_element->len, |
1116 | (u8) IW_ESSID_MAX_SIZE); |
1117 | memcpy(network->ssid, info_element->data, |
1118 | network->ssid_len); |
1119 | if (network->ssid_len < IW_ESSID_MAX_SIZE) |
1120 | memset(network->ssid + network->ssid_len, 0, |
1121 | IW_ESSID_MAX_SIZE - network->ssid_len); |
1122 | |
1123 | LIBIPW_DEBUG_MGMT("WLAN_EID_SSID: '%*pE' len=%d.\n" , |
1124 | network->ssid_len, network->ssid, |
1125 | network->ssid_len); |
1126 | break; |
1127 | |
1128 | case WLAN_EID_SUPP_RATES: |
1129 | #ifdef CONFIG_LIBIPW_DEBUG |
1130 | p = rates_str; |
1131 | #endif |
1132 | network->rates_len = min(info_element->len, |
1133 | MAX_RATES_LENGTH); |
1134 | for (i = 0; i < network->rates_len; i++) { |
1135 | network->rates[i] = info_element->data[i]; |
1136 | #ifdef CONFIG_LIBIPW_DEBUG |
1137 | p += scnprintf(buf: p, size: sizeof(rates_str) - |
1138 | (p - rates_str), fmt: "%02X " , |
1139 | network->rates[i]); |
1140 | #endif |
1141 | if (libipw_is_ofdm_rate |
1142 | (rate: info_element->data[i])) { |
1143 | network->flags |= NETWORK_HAS_OFDM; |
1144 | if (info_element->data[i] & |
1145 | LIBIPW_BASIC_RATE_MASK) |
1146 | network->flags &= |
1147 | ~NETWORK_HAS_CCK; |
1148 | } |
1149 | } |
1150 | |
1151 | LIBIPW_DEBUG_MGMT("WLAN_EID_SUPP_RATES: '%s' (%d)\n" , |
1152 | rates_str, network->rates_len); |
1153 | break; |
1154 | |
1155 | case WLAN_EID_EXT_SUPP_RATES: |
1156 | #ifdef CONFIG_LIBIPW_DEBUG |
1157 | p = rates_str; |
1158 | #endif |
1159 | network->rates_ex_len = min(info_element->len, |
1160 | MAX_RATES_EX_LENGTH); |
1161 | for (i = 0; i < network->rates_ex_len; i++) { |
1162 | network->rates_ex[i] = info_element->data[i]; |
1163 | #ifdef CONFIG_LIBIPW_DEBUG |
1164 | p += scnprintf(buf: p, size: sizeof(rates_str) - |
1165 | (p - rates_str), fmt: "%02X " , |
1166 | network->rates_ex[i]); |
1167 | #endif |
1168 | if (libipw_is_ofdm_rate |
1169 | (rate: info_element->data[i])) { |
1170 | network->flags |= NETWORK_HAS_OFDM; |
1171 | if (info_element->data[i] & |
1172 | LIBIPW_BASIC_RATE_MASK) |
1173 | network->flags &= |
1174 | ~NETWORK_HAS_CCK; |
1175 | } |
1176 | } |
1177 | |
1178 | LIBIPW_DEBUG_MGMT("WLAN_EID_EXT_SUPP_RATES: '%s' (%d)\n" , |
1179 | rates_str, network->rates_ex_len); |
1180 | break; |
1181 | |
1182 | case WLAN_EID_DS_PARAMS: |
1183 | LIBIPW_DEBUG_MGMT("WLAN_EID_DS_PARAMS: %d\n" , |
1184 | info_element->data[0]); |
1185 | network->channel = info_element->data[0]; |
1186 | break; |
1187 | |
1188 | case WLAN_EID_FH_PARAMS: |
1189 | LIBIPW_DEBUG_MGMT("WLAN_EID_FH_PARAMS: ignored\n" ); |
1190 | break; |
1191 | |
1192 | case WLAN_EID_CF_PARAMS: |
1193 | LIBIPW_DEBUG_MGMT("WLAN_EID_CF_PARAMS: ignored\n" ); |
1194 | break; |
1195 | |
1196 | case WLAN_EID_TIM: |
1197 | network->tim.tim_count = info_element->data[0]; |
1198 | network->tim.tim_period = info_element->data[1]; |
1199 | LIBIPW_DEBUG_MGMT("WLAN_EID_TIM: partially ignored\n" ); |
1200 | break; |
1201 | |
1202 | case WLAN_EID_ERP_INFO: |
1203 | network->erp_value = info_element->data[0]; |
1204 | network->flags |= NETWORK_HAS_ERP_VALUE; |
1205 | LIBIPW_DEBUG_MGMT("MFIE_TYPE_ERP_SET: %d\n" , |
1206 | network->erp_value); |
1207 | break; |
1208 | |
1209 | case WLAN_EID_IBSS_PARAMS: |
1210 | network->atim_window = info_element->data[0]; |
1211 | LIBIPW_DEBUG_MGMT("WLAN_EID_IBSS_PARAMS: %d\n" , |
1212 | network->atim_window); |
1213 | break; |
1214 | |
1215 | case WLAN_EID_CHALLENGE: |
1216 | LIBIPW_DEBUG_MGMT("WLAN_EID_CHALLENGE: ignored\n" ); |
1217 | break; |
1218 | |
1219 | case WLAN_EID_VENDOR_SPECIFIC: |
1220 | LIBIPW_DEBUG_MGMT("WLAN_EID_VENDOR_SPECIFIC: %d bytes\n" , |
1221 | info_element->len); |
1222 | if (!libipw_parse_qos_info_param_IE(info_element, |
1223 | network)) |
1224 | break; |
1225 | |
1226 | if (info_element->len >= 4 && |
1227 | info_element->data[0] == 0x00 && |
1228 | info_element->data[1] == 0x50 && |
1229 | info_element->data[2] == 0xf2 && |
1230 | info_element->data[3] == 0x01) { |
1231 | network->wpa_ie_len = min(info_element->len + 2, |
1232 | MAX_WPA_IE_LEN); |
1233 | memcpy(network->wpa_ie, info_element, |
1234 | network->wpa_ie_len); |
1235 | } |
1236 | break; |
1237 | |
1238 | case WLAN_EID_RSN: |
1239 | LIBIPW_DEBUG_MGMT("WLAN_EID_RSN: %d bytes\n" , |
1240 | info_element->len); |
1241 | network->rsn_ie_len = min(info_element->len + 2, |
1242 | MAX_WPA_IE_LEN); |
1243 | memcpy(network->rsn_ie, info_element, |
1244 | network->rsn_ie_len); |
1245 | break; |
1246 | |
1247 | case WLAN_EID_QOS_PARAMETER: |
1248 | printk(KERN_ERR |
1249 | "QoS Error need to parse QOS_PARAMETER IE\n" ); |
1250 | break; |
1251 | /* 802.11h */ |
1252 | case WLAN_EID_PWR_CONSTRAINT: |
1253 | network->power_constraint = info_element->data[0]; |
1254 | network->flags |= NETWORK_HAS_POWER_CONSTRAINT; |
1255 | break; |
1256 | |
1257 | case WLAN_EID_CHANNEL_SWITCH: |
1258 | network->power_constraint = info_element->data[0]; |
1259 | network->flags |= NETWORK_HAS_CSA; |
1260 | break; |
1261 | |
1262 | case WLAN_EID_QUIET: |
1263 | network->quiet.count = info_element->data[0]; |
1264 | network->quiet.period = info_element->data[1]; |
1265 | network->quiet.duration = info_element->data[2]; |
1266 | network->quiet.offset = info_element->data[3]; |
1267 | network->flags |= NETWORK_HAS_QUIET; |
1268 | break; |
1269 | |
1270 | case WLAN_EID_IBSS_DFS: |
1271 | network->flags |= NETWORK_HAS_IBSS_DFS; |
1272 | break; |
1273 | |
1274 | case WLAN_EID_TPC_REPORT: |
1275 | network->tpc_report.transmit_power = |
1276 | info_element->data[0]; |
1277 | network->tpc_report.link_margin = info_element->data[1]; |
1278 | network->flags |= NETWORK_HAS_TPC_REPORT; |
1279 | break; |
1280 | |
1281 | default: |
1282 | LIBIPW_DEBUG_MGMT |
1283 | ("Unsupported info element: %s (%d)\n" , |
1284 | get_info_element_string(info_element->id), |
1285 | info_element->id); |
1286 | break; |
1287 | } |
1288 | |
1289 | length -= sizeof(*info_element) + info_element->len; |
1290 | info_element = |
1291 | (struct libipw_info_element *)&info_element-> |
1292 | data[info_element->len]; |
1293 | } |
1294 | |
1295 | return 0; |
1296 | } |
1297 | |
1298 | static int libipw_handle_assoc_resp(struct libipw_device *ieee, struct libipw_assoc_response |
1299 | *frame, struct libipw_rx_stats *stats) |
1300 | { |
1301 | struct libipw_network network_resp = { }; |
1302 | struct libipw_network *network = &network_resp; |
1303 | struct net_device *dev = ieee->dev; |
1304 | |
1305 | network->flags = 0; |
1306 | network->qos_data.active = 0; |
1307 | network->qos_data.supported = 0; |
1308 | network->qos_data.param_count = 0; |
1309 | network->qos_data.old_param_count = 0; |
1310 | |
1311 | //network->atim_window = le16_to_cpu(frame->aid) & (0x3FFF); |
1312 | network->atim_window = le16_to_cpu(frame->aid); |
1313 | network->listen_interval = le16_to_cpu(frame->status); |
1314 | memcpy(network->bssid, frame->header.addr3, ETH_ALEN); |
1315 | network->capability = le16_to_cpu(frame->capability); |
1316 | network->last_scanned = jiffies; |
1317 | network->rates_len = network->rates_ex_len = 0; |
1318 | network->last_associate = 0; |
1319 | network->ssid_len = 0; |
1320 | network->erp_value = |
1321 | (network->capability & WLAN_CAPABILITY_IBSS) ? 0x3 : 0x0; |
1322 | |
1323 | if (stats->freq == LIBIPW_52GHZ_BAND) { |
1324 | /* for A band (No DS info) */ |
1325 | network->channel = stats->received_channel; |
1326 | } else |
1327 | network->flags |= NETWORK_HAS_CCK; |
1328 | |
1329 | network->wpa_ie_len = 0; |
1330 | network->rsn_ie_len = 0; |
1331 | |
1332 | if (libipw_parse_info_param(info_element: (void *)frame->variable, |
1333 | length: stats->len - sizeof(*frame), network)) |
1334 | return 1; |
1335 | |
1336 | network->mode = 0; |
1337 | if (stats->freq == LIBIPW_52GHZ_BAND) |
1338 | network->mode = IEEE_A; |
1339 | else { |
1340 | if (network->flags & NETWORK_HAS_OFDM) |
1341 | network->mode |= IEEE_G; |
1342 | if (network->flags & NETWORK_HAS_CCK) |
1343 | network->mode |= IEEE_B; |
1344 | } |
1345 | |
1346 | memcpy(&network->stats, stats, sizeof(network->stats)); |
1347 | |
1348 | if (ieee->handle_assoc_response != NULL) |
1349 | ieee->handle_assoc_response(dev, frame, network); |
1350 | |
1351 | return 0; |
1352 | } |
1353 | |
1354 | /***************************************************/ |
1355 | |
1356 | static int libipw_network_init(struct libipw_device *ieee, struct libipw_probe_response |
1357 | *beacon, |
1358 | struct libipw_network *network, |
1359 | struct libipw_rx_stats *stats) |
1360 | { |
1361 | network->qos_data.active = 0; |
1362 | network->qos_data.supported = 0; |
1363 | network->qos_data.param_count = 0; |
1364 | network->qos_data.old_param_count = 0; |
1365 | |
1366 | /* Pull out fixed field data */ |
1367 | memcpy(network->bssid, beacon->header.addr3, ETH_ALEN); |
1368 | network->capability = le16_to_cpu(beacon->capability); |
1369 | network->last_scanned = jiffies; |
1370 | network->time_stamp[0] = le32_to_cpu(beacon->time_stamp[0]); |
1371 | network->time_stamp[1] = le32_to_cpu(beacon->time_stamp[1]); |
1372 | network->beacon_interval = le16_to_cpu(beacon->beacon_interval); |
1373 | /* Where to pull this? beacon->listen_interval; */ |
1374 | network->listen_interval = 0x0A; |
1375 | network->rates_len = network->rates_ex_len = 0; |
1376 | network->last_associate = 0; |
1377 | network->ssid_len = 0; |
1378 | network->flags = 0; |
1379 | network->atim_window = 0; |
1380 | network->erp_value = (network->capability & WLAN_CAPABILITY_IBSS) ? |
1381 | 0x3 : 0x0; |
1382 | |
1383 | if (stats->freq == LIBIPW_52GHZ_BAND) { |
1384 | /* for A band (No DS info) */ |
1385 | network->channel = stats->received_channel; |
1386 | } else |
1387 | network->flags |= NETWORK_HAS_CCK; |
1388 | |
1389 | network->wpa_ie_len = 0; |
1390 | network->rsn_ie_len = 0; |
1391 | |
1392 | if (libipw_parse_info_param(info_element: (void *)beacon->variable, |
1393 | length: stats->len - sizeof(*beacon), network)) |
1394 | return 1; |
1395 | |
1396 | network->mode = 0; |
1397 | if (stats->freq == LIBIPW_52GHZ_BAND) |
1398 | network->mode = IEEE_A; |
1399 | else { |
1400 | if (network->flags & NETWORK_HAS_OFDM) |
1401 | network->mode |= IEEE_G; |
1402 | if (network->flags & NETWORK_HAS_CCK) |
1403 | network->mode |= IEEE_B; |
1404 | } |
1405 | |
1406 | if (network->mode == 0) { |
1407 | LIBIPW_DEBUG_SCAN("Filtered out '%*pE (%pM)' network.\n" , |
1408 | network->ssid_len, network->ssid, |
1409 | network->bssid); |
1410 | return 1; |
1411 | } |
1412 | |
1413 | memcpy(&network->stats, stats, sizeof(network->stats)); |
1414 | |
1415 | return 0; |
1416 | } |
1417 | |
1418 | static inline int is_same_network(struct libipw_network *src, |
1419 | struct libipw_network *dst) |
1420 | { |
1421 | /* A network is only a duplicate if the channel, BSSID, and ESSID |
1422 | * all match. We treat all <hidden> with the same BSSID and channel |
1423 | * as one network */ |
1424 | return ((src->ssid_len == dst->ssid_len) && |
1425 | (src->channel == dst->channel) && |
1426 | ether_addr_equal_64bits(addr1: src->bssid, addr2: dst->bssid) && |
1427 | !memcmp(p: src->ssid, q: dst->ssid, size: src->ssid_len)); |
1428 | } |
1429 | |
1430 | static void update_network(struct libipw_network *dst, |
1431 | struct libipw_network *src) |
1432 | { |
1433 | int qos_active; |
1434 | u8 old_param; |
1435 | |
1436 | /* We only update the statistics if they were created by receiving |
1437 | * the network information on the actual channel the network is on. |
1438 | * |
1439 | * This keeps beacons received on neighbor channels from bringing |
1440 | * down the signal level of an AP. */ |
1441 | if (dst->channel == src->stats.received_channel) |
1442 | memcpy(&dst->stats, &src->stats, |
1443 | sizeof(struct libipw_rx_stats)); |
1444 | else |
1445 | LIBIPW_DEBUG_SCAN("Network %pM info received " |
1446 | "off channel (%d vs. %d)\n" , src->bssid, |
1447 | dst->channel, src->stats.received_channel); |
1448 | |
1449 | dst->capability = src->capability; |
1450 | memcpy(dst->rates, src->rates, src->rates_len); |
1451 | dst->rates_len = src->rates_len; |
1452 | memcpy(dst->rates_ex, src->rates_ex, src->rates_ex_len); |
1453 | dst->rates_ex_len = src->rates_ex_len; |
1454 | |
1455 | dst->mode = src->mode; |
1456 | dst->flags = src->flags; |
1457 | dst->time_stamp[0] = src->time_stamp[0]; |
1458 | dst->time_stamp[1] = src->time_stamp[1]; |
1459 | |
1460 | dst->beacon_interval = src->beacon_interval; |
1461 | dst->listen_interval = src->listen_interval; |
1462 | dst->atim_window = src->atim_window; |
1463 | dst->erp_value = src->erp_value; |
1464 | dst->tim = src->tim; |
1465 | |
1466 | memcpy(dst->wpa_ie, src->wpa_ie, src->wpa_ie_len); |
1467 | dst->wpa_ie_len = src->wpa_ie_len; |
1468 | memcpy(dst->rsn_ie, src->rsn_ie, src->rsn_ie_len); |
1469 | dst->rsn_ie_len = src->rsn_ie_len; |
1470 | |
1471 | dst->last_scanned = jiffies; |
1472 | qos_active = src->qos_data.active; |
1473 | old_param = dst->qos_data.old_param_count; |
1474 | if (dst->flags & NETWORK_HAS_QOS_MASK) |
1475 | memcpy(&dst->qos_data, &src->qos_data, |
1476 | sizeof(struct libipw_qos_data)); |
1477 | else { |
1478 | dst->qos_data.supported = src->qos_data.supported; |
1479 | dst->qos_data.param_count = src->qos_data.param_count; |
1480 | } |
1481 | |
1482 | if (dst->qos_data.supported == 1) { |
1483 | if (dst->ssid_len) |
1484 | LIBIPW_DEBUG_QOS |
1485 | ("QoS the network %s is QoS supported\n" , |
1486 | dst->ssid); |
1487 | else |
1488 | LIBIPW_DEBUG_QOS |
1489 | ("QoS the network is QoS supported\n" ); |
1490 | } |
1491 | dst->qos_data.active = qos_active; |
1492 | dst->qos_data.old_param_count = old_param; |
1493 | |
1494 | /* dst->last_associate is not overwritten */ |
1495 | } |
1496 | |
1497 | static inline int is_beacon(__le16 fc) |
1498 | { |
1499 | return (WLAN_FC_GET_STYPE(le16_to_cpu(fc)) == IEEE80211_STYPE_BEACON); |
1500 | } |
1501 | |
1502 | static void libipw_process_probe_response(struct libipw_device |
1503 | *ieee, struct |
1504 | libipw_probe_response |
1505 | *beacon, struct libipw_rx_stats |
1506 | *stats) |
1507 | { |
1508 | struct net_device *dev = ieee->dev; |
1509 | struct libipw_network network = { }; |
1510 | struct libipw_network *target; |
1511 | struct libipw_network *oldest = NULL; |
1512 | #ifdef CONFIG_LIBIPW_DEBUG |
1513 | struct libipw_info_element *info_element = (void *)beacon->variable; |
1514 | #endif |
1515 | unsigned long flags; |
1516 | |
1517 | LIBIPW_DEBUG_SCAN("'%*pE' (%pM): %c%c%c%c %c%c%c%c-%c%c%c%c %c%c%c%c\n" , |
1518 | info_element->len, info_element->data, |
1519 | beacon->header.addr3, |
1520 | (beacon->capability & cpu_to_le16(1 << 0xf)) ? '1' : '0', |
1521 | (beacon->capability & cpu_to_le16(1 << 0xe)) ? '1' : '0', |
1522 | (beacon->capability & cpu_to_le16(1 << 0xd)) ? '1' : '0', |
1523 | (beacon->capability & cpu_to_le16(1 << 0xc)) ? '1' : '0', |
1524 | (beacon->capability & cpu_to_le16(1 << 0xb)) ? '1' : '0', |
1525 | (beacon->capability & cpu_to_le16(1 << 0xa)) ? '1' : '0', |
1526 | (beacon->capability & cpu_to_le16(1 << 0x9)) ? '1' : '0', |
1527 | (beacon->capability & cpu_to_le16(1 << 0x8)) ? '1' : '0', |
1528 | (beacon->capability & cpu_to_le16(1 << 0x7)) ? '1' : '0', |
1529 | (beacon->capability & cpu_to_le16(1 << 0x6)) ? '1' : '0', |
1530 | (beacon->capability & cpu_to_le16(1 << 0x5)) ? '1' : '0', |
1531 | (beacon->capability & cpu_to_le16(1 << 0x4)) ? '1' : '0', |
1532 | (beacon->capability & cpu_to_le16(1 << 0x3)) ? '1' : '0', |
1533 | (beacon->capability & cpu_to_le16(1 << 0x2)) ? '1' : '0', |
1534 | (beacon->capability & cpu_to_le16(1 << 0x1)) ? '1' : '0', |
1535 | (beacon->capability & cpu_to_le16(1 << 0x0)) ? '1' : '0'); |
1536 | |
1537 | if (libipw_network_init(ieee, beacon, network: &network, stats)) { |
1538 | LIBIPW_DEBUG_SCAN("Dropped '%*pE' (%pM) via %s.\n" , |
1539 | info_element->len, info_element->data, |
1540 | beacon->header.addr3, |
1541 | is_beacon(beacon->header.frame_ctl) ? |
1542 | "BEACON" : "PROBE RESPONSE" ); |
1543 | return; |
1544 | } |
1545 | |
1546 | /* The network parsed correctly -- so now we scan our known networks |
1547 | * to see if we can find it in our list. |
1548 | * |
1549 | * NOTE: This search is definitely not optimized. Once its doing |
1550 | * the "right thing" we'll optimize it for efficiency if |
1551 | * necessary */ |
1552 | |
1553 | /* Search for this entry in the list and update it if it is |
1554 | * already there. */ |
1555 | |
1556 | spin_lock_irqsave(&ieee->lock, flags); |
1557 | |
1558 | list_for_each_entry(target, &ieee->network_list, list) { |
1559 | if (is_same_network(src: target, dst: &network)) |
1560 | break; |
1561 | |
1562 | if ((oldest == NULL) || |
1563 | time_before(target->last_scanned, oldest->last_scanned)) |
1564 | oldest = target; |
1565 | } |
1566 | |
1567 | /* If we didn't find a match, then get a new network slot to initialize |
1568 | * with this beacon's information */ |
1569 | if (&target->list == &ieee->network_list) { |
1570 | if (list_empty(head: &ieee->network_free_list)) { |
1571 | /* If there are no more slots, expire the oldest */ |
1572 | list_del(entry: &oldest->list); |
1573 | target = oldest; |
1574 | LIBIPW_DEBUG_SCAN("Expired '%*pE' (%pM) from network list.\n" , |
1575 | target->ssid_len, target->ssid, |
1576 | target->bssid); |
1577 | } else { |
1578 | /* Otherwise just pull from the free list */ |
1579 | target = list_entry(ieee->network_free_list.next, |
1580 | struct libipw_network, list); |
1581 | list_del(entry: ieee->network_free_list.next); |
1582 | } |
1583 | |
1584 | #ifdef CONFIG_LIBIPW_DEBUG |
1585 | LIBIPW_DEBUG_SCAN("Adding '%*pE' (%pM) via %s.\n" , |
1586 | network.ssid_len, network.ssid, |
1587 | network.bssid, |
1588 | is_beacon(beacon->header.frame_ctl) ? |
1589 | "BEACON" : "PROBE RESPONSE" ); |
1590 | #endif |
1591 | memcpy(target, &network, sizeof(*target)); |
1592 | list_add_tail(new: &target->list, head: &ieee->network_list); |
1593 | } else { |
1594 | LIBIPW_DEBUG_SCAN("Updating '%*pE' (%pM) via %s.\n" , |
1595 | target->ssid_len, target->ssid, |
1596 | target->bssid, |
1597 | is_beacon(beacon->header.frame_ctl) ? |
1598 | "BEACON" : "PROBE RESPONSE" ); |
1599 | update_network(dst: target, src: &network); |
1600 | } |
1601 | |
1602 | spin_unlock_irqrestore(lock: &ieee->lock, flags); |
1603 | |
1604 | if (is_beacon(fc: beacon->header.frame_ctl)) { |
1605 | if (ieee->handle_beacon != NULL) |
1606 | ieee->handle_beacon(dev, beacon, target); |
1607 | } else { |
1608 | if (ieee->handle_probe_response != NULL) |
1609 | ieee->handle_probe_response(dev, beacon, target); |
1610 | } |
1611 | } |
1612 | |
1613 | void libipw_rx_mgt(struct libipw_device *ieee, |
1614 | struct libipw_hdr_4addr *, |
1615 | struct libipw_rx_stats *stats) |
1616 | { |
1617 | switch (WLAN_FC_GET_STYPE(le16_to_cpu(header->frame_ctl))) { |
1618 | case IEEE80211_STYPE_ASSOC_RESP: |
1619 | LIBIPW_DEBUG_MGMT("received ASSOCIATION RESPONSE (%d)\n" , |
1620 | WLAN_FC_GET_STYPE(le16_to_cpu |
1621 | (header->frame_ctl))); |
1622 | libipw_handle_assoc_resp(ieee, |
1623 | frame: (struct libipw_assoc_response *) |
1624 | header, stats); |
1625 | break; |
1626 | |
1627 | case IEEE80211_STYPE_REASSOC_RESP: |
1628 | LIBIPW_DEBUG_MGMT("received REASSOCIATION RESPONSE (%d)\n" , |
1629 | WLAN_FC_GET_STYPE(le16_to_cpu |
1630 | (header->frame_ctl))); |
1631 | break; |
1632 | |
1633 | case IEEE80211_STYPE_PROBE_REQ: |
1634 | LIBIPW_DEBUG_MGMT("received auth (%d)\n" , |
1635 | WLAN_FC_GET_STYPE(le16_to_cpu |
1636 | (header->frame_ctl))); |
1637 | |
1638 | if (ieee->handle_probe_request != NULL) |
1639 | ieee->handle_probe_request(ieee->dev, |
1640 | (struct |
1641 | libipw_probe_request *) |
1642 | header, stats); |
1643 | break; |
1644 | |
1645 | case IEEE80211_STYPE_PROBE_RESP: |
1646 | LIBIPW_DEBUG_MGMT("received PROBE RESPONSE (%d)\n" , |
1647 | WLAN_FC_GET_STYPE(le16_to_cpu |
1648 | (header->frame_ctl))); |
1649 | LIBIPW_DEBUG_SCAN("Probe response\n" ); |
1650 | libipw_process_probe_response(ieee, |
1651 | beacon: (struct |
1652 | libipw_probe_response *) |
1653 | header, stats); |
1654 | break; |
1655 | |
1656 | case IEEE80211_STYPE_BEACON: |
1657 | LIBIPW_DEBUG_MGMT("received BEACON (%d)\n" , |
1658 | WLAN_FC_GET_STYPE(le16_to_cpu |
1659 | (header->frame_ctl))); |
1660 | LIBIPW_DEBUG_SCAN("Beacon\n" ); |
1661 | libipw_process_probe_response(ieee, |
1662 | beacon: (struct |
1663 | libipw_probe_response *) |
1664 | header, stats); |
1665 | break; |
1666 | case IEEE80211_STYPE_AUTH: |
1667 | |
1668 | LIBIPW_DEBUG_MGMT("received auth (%d)\n" , |
1669 | WLAN_FC_GET_STYPE(le16_to_cpu |
1670 | (header->frame_ctl))); |
1671 | |
1672 | if (ieee->handle_auth != NULL) |
1673 | ieee->handle_auth(ieee->dev, |
1674 | (struct libipw_auth *)header); |
1675 | break; |
1676 | |
1677 | case IEEE80211_STYPE_DISASSOC: |
1678 | if (ieee->handle_disassoc != NULL) |
1679 | ieee->handle_disassoc(ieee->dev, |
1680 | (struct libipw_disassoc *) |
1681 | header); |
1682 | break; |
1683 | |
1684 | case IEEE80211_STYPE_ACTION: |
1685 | LIBIPW_DEBUG_MGMT("ACTION\n" ); |
1686 | if (ieee->handle_action) |
1687 | ieee->handle_action(ieee->dev, |
1688 | (struct libipw_action *) |
1689 | header, stats); |
1690 | break; |
1691 | |
1692 | case IEEE80211_STYPE_REASSOC_REQ: |
1693 | LIBIPW_DEBUG_MGMT("received reassoc (%d)\n" , |
1694 | WLAN_FC_GET_STYPE(le16_to_cpu |
1695 | (header->frame_ctl))); |
1696 | |
1697 | LIBIPW_DEBUG_MGMT("%s: LIBIPW_REASSOC_REQ received\n" , |
1698 | ieee->dev->name); |
1699 | if (ieee->handle_reassoc_request != NULL) |
1700 | ieee->handle_reassoc_request(ieee->dev, |
1701 | (struct libipw_reassoc_request *) |
1702 | header); |
1703 | break; |
1704 | |
1705 | case IEEE80211_STYPE_ASSOC_REQ: |
1706 | LIBIPW_DEBUG_MGMT("received assoc (%d)\n" , |
1707 | WLAN_FC_GET_STYPE(le16_to_cpu |
1708 | (header->frame_ctl))); |
1709 | |
1710 | LIBIPW_DEBUG_MGMT("%s: LIBIPW_ASSOC_REQ received\n" , |
1711 | ieee->dev->name); |
1712 | if (ieee->handle_assoc_request != NULL) |
1713 | ieee->handle_assoc_request(ieee->dev); |
1714 | break; |
1715 | |
1716 | case IEEE80211_STYPE_DEAUTH: |
1717 | LIBIPW_DEBUG_MGMT("DEAUTH\n" ); |
1718 | if (ieee->handle_deauth != NULL) |
1719 | ieee->handle_deauth(ieee->dev, |
1720 | (struct libipw_deauth *) |
1721 | header); |
1722 | break; |
1723 | default: |
1724 | LIBIPW_DEBUG_MGMT("received UNKNOWN (%d)\n" , |
1725 | WLAN_FC_GET_STYPE(le16_to_cpu |
1726 | (header->frame_ctl))); |
1727 | LIBIPW_DEBUG_MGMT("%s: Unknown management packet: %d\n" , |
1728 | ieee->dev->name, |
1729 | WLAN_FC_GET_STYPE(le16_to_cpu |
1730 | (header->frame_ctl))); |
1731 | break; |
1732 | } |
1733 | } |
1734 | |
1735 | EXPORT_SYMBOL_GPL(libipw_rx_any); |
1736 | EXPORT_SYMBOL(libipw_rx_mgt); |
1737 | EXPORT_SYMBOL(libipw_rx); |
1738 | |