Commit | Line | Data |
---|---|---|
00b3ed16 GKH |
1 | /* src/p80211/p80211conv.c |
2 | * | |
3 | * Ether/802.11 conversions and packet buffer routines | |
4 | * | |
5 | * Copyright (C) 1999 AbsoluteValue Systems, Inc. All Rights Reserved. | |
6 | * -------------------------------------------------------------------- | |
7 | * | |
8 | * linux-wlan | |
9 | * | |
10 | * The contents of this file are subject to the Mozilla Public | |
11 | * License Version 1.1 (the "License"); you may not use this file | |
12 | * except in compliance with the License. You may obtain a copy of | |
13 | * the License at http://www.mozilla.org/MPL/ | |
14 | * | |
15 | * Software distributed under the License is distributed on an "AS | |
16 | * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or | |
17 | * implied. See the License for the specific language governing | |
18 | * rights and limitations under the License. | |
19 | * | |
20 | * Alternatively, the contents of this file may be used under the | |
21 | * terms of the GNU Public License version 2 (the "GPL"), in which | |
22 | * case the provisions of the GPL are applicable instead of the | |
23 | * above. If you wish to allow the use of your version of this file | |
24 | * only under the terms of the GPL and not to allow others to use | |
25 | * your version of this file under the MPL, indicate your decision | |
26 | * by deleting the provisions above and replace them with the notice | |
27 | * and other provisions required by the GPL. If you do not delete | |
28 | * the provisions above, a recipient may use your version of this | |
29 | * file under either the MPL or the GPL. | |
30 | * | |
31 | * -------------------------------------------------------------------- | |
32 | * | |
33 | * Inquiries regarding the linux-wlan Open Source project can be | |
34 | * made directly to: | |
35 | * | |
36 | * AbsoluteValue Systems Inc. | |
37 | * info@linux-wlan.com | |
38 | * http://www.linux-wlan.com | |
39 | * | |
40 | * -------------------------------------------------------------------- | |
41 | * | |
42 | * Portions of the development of this software were funded by | |
43 | * Intersil Corporation as part of PRISM(R) chipset product development. | |
44 | * | |
45 | * -------------------------------------------------------------------- | |
46 | * | |
47 | * This file defines the functions that perform Ethernet to/from | |
48 | * 802.11 frame conversions. | |
49 | * | |
50 | * -------------------------------------------------------------------- | |
82eaca7d MM |
51 | * |
52 | *================================================================ */ | |
00b3ed16 GKH |
53 | |
54 | #include <linux/module.h> | |
55 | #include <linux/kernel.h> | |
56 | #include <linux/sched.h> | |
57 | #include <linux/types.h> | |
58 | #include <linux/skbuff.h> | |
59 | #include <linux/slab.h> | |
60 | #include <linux/wireless.h> | |
61 | #include <linux/netdevice.h> | |
62 | #include <linux/etherdevice.h> | |
63 | #include <linux/if_ether.h> | |
ae26230b | 64 | #include <linux/byteorder/generic.h> |
00b3ed16 GKH |
65 | |
66 | #include <asm/byteorder.h> | |
67 | ||
00b3ed16 GKH |
68 | #include "p80211types.h" |
69 | #include "p80211hdr.h" | |
70 | #include "p80211conv.h" | |
71 | #include "p80211mgmt.h" | |
72 | #include "p80211msg.h" | |
73 | #include "p80211netdev.h" | |
74 | #include "p80211ioctl.h" | |
75 | #include "p80211req.h" | |
76 | ||
82eaca7d MM |
77 | static u8 oui_rfc1042[] = { 0x00, 0x00, 0x00 }; |
78 | static u8 oui_8021h[] = { 0x00, 0x00, 0xf8 }; | |
00b3ed16 GKH |
79 | |
80 | /*---------------------------------------------------------------- | |
81 | * p80211pb_ether_to_80211 | |
82 | * | |
83 | * Uses the contents of the ether frame and the etherconv setting | |
84 | * to build the elements of the 802.11 frame. | |
85 | * | |
86 | * We don't actually set | |
87 | * up the frame header here. That's the MAC's job. We're only handling | |
88 | * conversion of DIXII or 802.3+LLC frames to something that works | |
89 | * with 802.11. | |
90 | * | |
91 | * Note -- 802.11 header is NOT part of the skb. Likewise, the 802.11 | |
92 | * FCS is also not present and will need to be added elsewhere. | |
93 | * | |
94 | * Arguments: | |
95 | * ethconv Conversion type to perform | |
96 | * skb skbuff containing the ether frame | |
97 | * p80211_hdr 802.11 header | |
98 | * | |
99 | * Returns: | |
100 | * 0 on success, non-zero otherwise | |
101 | * | |
102 | * Call context: | |
103 | * May be called in interrupt or non-interrupt context | |
104 | ----------------------------------------------------------------*/ | |
297f06ce | 105 | int skb_ether_to_p80211(wlandevice_t *wlandev, u32 ethconv, |
93df38e5 | 106 | struct sk_buff *skb, union p80211_hdr *p80211_hdr, |
51e4896a | 107 | struct p80211_metawep *p80211_wep) |
00b3ed16 GKH |
108 | { |
109 | ||
82eaca7d MM |
110 | u16 fc; |
111 | u16 proto; | |
51e4896a EH |
112 | struct wlan_ethhdr e_hdr; |
113 | struct wlan_llc *e_llc; | |
114 | struct wlan_snap *e_snap; | |
00b3ed16 GKH |
115 | int foo; |
116 | ||
00b3ed16 GKH |
117 | memcpy(&e_hdr, skb->data, sizeof(e_hdr)); |
118 | ||
119 | if (skb->len <= 0) { | |
a7cf7bae | 120 | pr_debug("zero-length skb!\n"); |
00b3ed16 GKH |
121 | return 1; |
122 | } | |
123 | ||
82eaca7d MM |
124 | if (ethconv == WLAN_ETHCONV_ENCAP) { /* simplest case */ |
125 | pr_debug("ENCAP len: %d\n", skb->len); | |
00b3ed16 GKH |
126 | /* here, we don't care what kind of ether frm. Just stick it */ |
127 | /* in the 80211 payload */ | |
128 | /* which is to say, leave the skb alone. */ | |
129 | } else { | |
130 | /* step 1: classify ether frame, DIX or 802.3? */ | |
131 | proto = ntohs(e_hdr.type); | |
82eaca7d MM |
132 | if (proto <= 1500) { |
133 | pr_debug("802.3 len: %d\n", skb->len); | |
134 | /* codes <= 1500 reserved for 802.3 lengths */ | |
00b3ed16 GKH |
135 | /* it's 802.3, pass ether payload unchanged, */ |
136 | ||
137 | /* trim off ethernet header */ | |
138 | skb_pull(skb, WLAN_ETHHDR_LEN); | |
139 | ||
140 | /* leave off any PAD octets. */ | |
141 | skb_trim(skb, proto); | |
142 | } else { | |
82eaca7d | 143 | pr_debug("DIXII len: %d\n", skb->len); |
00b3ed16 GKH |
144 | /* it's DIXII, time for some conversion */ |
145 | ||
146 | /* trim off ethernet header */ | |
147 | skb_pull(skb, WLAN_ETHHDR_LEN); | |
148 | ||
149 | /* tack on SNAP */ | |
82eaca7d | 150 | e_snap = |
4eb28f71 JM |
151 | (struct wlan_snap *) skb_push(skb, |
152 | sizeof(struct wlan_snap)); | |
00b3ed16 | 153 | e_snap->type = htons(proto); |
82eaca7d MM |
154 | if (ethconv == WLAN_ETHCONV_8021h |
155 | && p80211_stt_findproto(proto)) { | |
156 | memcpy(e_snap->oui, oui_8021h, | |
157 | WLAN_IEEE_OUI_LEN); | |
00b3ed16 | 158 | } else { |
82eaca7d MM |
159 | memcpy(e_snap->oui, oui_rfc1042, |
160 | WLAN_IEEE_OUI_LEN); | |
00b3ed16 GKH |
161 | } |
162 | ||
163 | /* tack on llc */ | |
82eaca7d | 164 | e_llc = |
4eb28f71 JM |
165 | (struct wlan_llc *) skb_push(skb, |
166 | sizeof(struct wlan_llc)); | |
00b3ed16 GKH |
167 | e_llc->dsap = 0xAA; /* SNAP, see IEEE 802 */ |
168 | e_llc->ssap = 0xAA; | |
169 | e_llc->ctl = 0x03; | |
170 | ||
171 | } | |
172 | } | |
173 | ||
174 | /* Set up the 802.11 header */ | |
175 | /* It's a data frame */ | |
82eaca7d MM |
176 | fc = cpu_to_le16(WLAN_SET_FC_FTYPE(WLAN_FTYPE_DATA) | |
177 | WLAN_SET_FC_FSTYPE(WLAN_FSTYPE_DATAONLY)); | |
00b3ed16 | 178 | |
82eaca7d | 179 | switch (wlandev->macmode) { |
00b3ed16 | 180 | case WLAN_MACMODE_IBSS_STA: |
28b17a4b MM |
181 | memcpy(p80211_hdr->a3.a1, &e_hdr.daddr, ETH_ALEN); |
182 | memcpy(p80211_hdr->a3.a2, wlandev->netdev->dev_addr, ETH_ALEN); | |
183 | memcpy(p80211_hdr->a3.a3, wlandev->bssid, ETH_ALEN); | |
00b3ed16 GKH |
184 | break; |
185 | case WLAN_MACMODE_ESS_STA: | |
ae26230b | 186 | fc |= cpu_to_le16(WLAN_SET_FC_TODS(1)); |
28b17a4b MM |
187 | memcpy(p80211_hdr->a3.a1, wlandev->bssid, ETH_ALEN); |
188 | memcpy(p80211_hdr->a3.a2, wlandev->netdev->dev_addr, ETH_ALEN); | |
189 | memcpy(p80211_hdr->a3.a3, &e_hdr.daddr, ETH_ALEN); | |
00b3ed16 GKH |
190 | break; |
191 | case WLAN_MACMODE_ESS_AP: | |
ae26230b | 192 | fc |= cpu_to_le16(WLAN_SET_FC_FROMDS(1)); |
28b17a4b MM |
193 | memcpy(p80211_hdr->a3.a1, &e_hdr.daddr, ETH_ALEN); |
194 | memcpy(p80211_hdr->a3.a2, wlandev->bssid, ETH_ALEN); | |
195 | memcpy(p80211_hdr->a3.a3, &e_hdr.saddr, ETH_ALEN); | |
00b3ed16 GKH |
196 | break; |
197 | default: | |
82eaca7d MM |
198 | printk(KERN_ERR |
199 | "Error: Converting eth to wlan in unknown mode.\n"); | |
00b3ed16 GKH |
200 | return 1; |
201 | break; | |
202 | } | |
203 | ||
204 | p80211_wep->data = NULL; | |
205 | ||
82eaca7d MM |
206 | if ((wlandev->hostwep & HOSTWEP_PRIVACYINVOKED) |
207 | && (wlandev->hostwep & HOSTWEP_ENCRYPT)) { | |
208 | /* XXXX need to pick keynum other than default? */ | |
00b3ed16 | 209 | |
00b3ed16 | 210 | p80211_wep->data = kmalloc(skb->len, GFP_ATOMIC); |
5dd8acc8 | 211 | foo = wep_encrypt(wlandev, skb->data, p80211_wep->data, |
b02957d5 | 212 | skb->len, |
5813b624 | 213 | (wlandev->hostwep & HOSTWEP_DEFAULTKEY_MASK), |
3f4b4e77 | 214 | p80211_wep->iv, p80211_wep->icv); |
5dd8acc8 | 215 | if (foo) { |
82eaca7d MM |
216 | printk(KERN_WARNING |
217 | "Host en-WEP failed, dropping frame (%d).\n", | |
218 | foo); | |
00b3ed16 GKH |
219 | return 2; |
220 | } | |
ae26230b | 221 | fc |= cpu_to_le16(WLAN_SET_FC_ISWEP(1)); |
00b3ed16 GKH |
222 | } |
223 | ||
82eaca7d | 224 | /* skb->nh.raw = skb->data; */ |
00b3ed16 GKH |
225 | |
226 | p80211_hdr->a3.fc = fc; | |
227 | p80211_hdr->a3.dur = 0; | |
228 | p80211_hdr->a3.seq = 0; | |
229 | ||
00b3ed16 GKH |
230 | return 0; |
231 | } | |
232 | ||
233 | /* jkriegl: from orinoco, modified */ | |
297f06ce | 234 | static void orinoco_spy_gather(wlandevice_t *wlandev, char *mac, |
51e4896a | 235 | struct p80211_rxmeta *rxmeta) |
00b3ed16 | 236 | { |
82eaca7d | 237 | int i; |
00b3ed16 | 238 | |
82eaca7d MM |
239 | /* Gather wireless spy statistics: for each packet, compare the |
240 | * source address with out list, and if match, get the stats... */ | |
00b3ed16 | 241 | |
82eaca7d | 242 | for (i = 0; i < wlandev->spy_number; i++) { |
00b3ed16 | 243 | |
82eaca7d | 244 | if (!memcmp(wlandev->spy_address[i], mac, ETH_ALEN)) { |
00b3ed16 | 245 | memcpy(wlandev->spy_address[i], mac, ETH_ALEN); |
82eaca7d MM |
246 | wlandev->spy_stat[i].level = rxmeta->signal; |
247 | wlandev->spy_stat[i].noise = rxmeta->noise; | |
248 | wlandev->spy_stat[i].qual = | |
249 | (rxmeta->signal > | |
250 | rxmeta->noise) ? (rxmeta->signal - | |
251 | rxmeta->noise) : 0; | |
252 | wlandev->spy_stat[i].updated = 0x7; | |
253 | } | |
254 | } | |
00b3ed16 GKH |
255 | } |
256 | ||
257 | /*---------------------------------------------------------------- | |
258 | * p80211pb_80211_to_ether | |
259 | * | |
260 | * Uses the contents of a received 802.11 frame and the etherconv | |
261 | * setting to build an ether frame. | |
262 | * | |
263 | * This function extracts the src and dest address from the 802.11 | |
264 | * frame to use in the construction of the eth frame. | |
265 | * | |
266 | * Arguments: | |
267 | * ethconv Conversion type to perform | |
268 | * skb Packet buffer containing the 802.11 frame | |
269 | * | |
270 | * Returns: | |
271 | * 0 on success, non-zero otherwise | |
272 | * | |
273 | * Call context: | |
274 | * May be called in interrupt or non-interrupt context | |
275 | ----------------------------------------------------------------*/ | |
297f06ce | 276 | int skb_p80211_to_ether(wlandevice_t *wlandev, u32 ethconv, |
82eaca7d | 277 | struct sk_buff *skb) |
00b3ed16 | 278 | { |
82eaca7d MM |
279 | netdevice_t *netdev = wlandev->netdev; |
280 | u16 fc; | |
281 | unsigned int payload_length; | |
282 | unsigned int payload_offset; | |
283 | u8 daddr[WLAN_ETHADDR_LEN]; | |
284 | u8 saddr[WLAN_ETHADDR_LEN]; | |
93df38e5 | 285 | union p80211_hdr *w_hdr; |
51e4896a EH |
286 | struct wlan_ethhdr *e_hdr; |
287 | struct wlan_llc *e_llc; | |
288 | struct wlan_snap *e_snap; | |
00b3ed16 GKH |
289 | |
290 | int foo; | |
291 | ||
00b3ed16 GKH |
292 | payload_length = skb->len - WLAN_HDR_A3_LEN - WLAN_CRC_LEN; |
293 | payload_offset = WLAN_HDR_A3_LEN; | |
294 | ||
93df38e5 | 295 | w_hdr = (union p80211_hdr *) skb->data; |
00b3ed16 | 296 | |
82eaca7d | 297 | /* setup some vars for convenience */ |
ae26230b | 298 | fc = le16_to_cpu(w_hdr->a3.fc); |
82eaca7d | 299 | if ((WLAN_GET_FC_TODS(fc) == 0) && (WLAN_GET_FC_FROMDS(fc) == 0)) { |
00b3ed16 GKH |
300 | memcpy(daddr, w_hdr->a3.a1, WLAN_ETHADDR_LEN); |
301 | memcpy(saddr, w_hdr->a3.a2, WLAN_ETHADDR_LEN); | |
4eb28f71 JM |
302 | } else if ((WLAN_GET_FC_TODS(fc) == 0) |
303 | && (WLAN_GET_FC_FROMDS(fc) == 1)) { | |
00b3ed16 GKH |
304 | memcpy(daddr, w_hdr->a3.a1, WLAN_ETHADDR_LEN); |
305 | memcpy(saddr, w_hdr->a3.a3, WLAN_ETHADDR_LEN); | |
4eb28f71 JM |
306 | } else if ((WLAN_GET_FC_TODS(fc) == 1) |
307 | && (WLAN_GET_FC_FROMDS(fc) == 0)) { | |
00b3ed16 GKH |
308 | memcpy(daddr, w_hdr->a3.a3, WLAN_ETHADDR_LEN); |
309 | memcpy(saddr, w_hdr->a3.a2, WLAN_ETHADDR_LEN); | |
310 | } else { | |
311 | payload_offset = WLAN_HDR_A4_LEN; | |
1f9e9ce1 | 312 | if (payload_length < WLAN_HDR_A4_LEN - WLAN_HDR_A3_LEN) { |
edbd606c | 313 | printk(KERN_ERR "A4 frame too short!\n"); |
00b3ed16 GKH |
314 | return 1; |
315 | } | |
1f9e9ce1 | 316 | payload_length -= (WLAN_HDR_A4_LEN - WLAN_HDR_A3_LEN); |
00b3ed16 GKH |
317 | memcpy(daddr, w_hdr->a4.a3, WLAN_ETHADDR_LEN); |
318 | memcpy(saddr, w_hdr->a4.a4, WLAN_ETHADDR_LEN); | |
319 | } | |
320 | ||
321 | /* perform de-wep if necessary.. */ | |
82eaca7d MM |
322 | if ((wlandev->hostwep & HOSTWEP_PRIVACYINVOKED) && WLAN_GET_FC_ISWEP(fc) |
323 | && (wlandev->hostwep & HOSTWEP_DECRYPT)) { | |
00b3ed16 | 324 | if (payload_length <= 8) { |
edbd606c | 325 | printk(KERN_ERR "WEP frame too short (%u).\n", |
82eaca7d | 326 | skb->len); |
00b3ed16 GKH |
327 | return 1; |
328 | } | |
5dd8acc8 | 329 | foo = wep_decrypt(wlandev, skb->data + payload_offset + 4, |
00b3ed16 GKH |
330 | payload_length - 8, -1, |
331 | skb->data + payload_offset, | |
82eaca7d | 332 | skb->data + payload_offset + |
3f4b4e77 | 333 | payload_length - 4); |
5dd8acc8 | 334 | if (foo) { |
00b3ed16 | 335 | /* de-wep failed, drop skb. */ |
75f49e07 MT |
336 | pr_debug("Host de-WEP failed, dropping frame (%d).\n", |
337 | foo); | |
00b3ed16 GKH |
338 | wlandev->rx.decrypt_err++; |
339 | return 2; | |
340 | } | |
341 | ||
342 | /* subtract the IV+ICV length off the payload */ | |
343 | payload_length -= 8; | |
344 | /* chop off the IV */ | |
345 | skb_pull(skb, 4); | |
346 | /* chop off the ICV. */ | |
347 | skb_trim(skb, skb->len - 4); | |
348 | ||
349 | wlandev->rx.decrypt++; | |
350 | } | |
351 | ||
51e4896a | 352 | e_hdr = (struct wlan_ethhdr *) (skb->data + payload_offset); |
00b3ed16 | 353 | |
51e4896a | 354 | e_llc = (struct wlan_llc *) (skb->data + payload_offset); |
82eaca7d | 355 | e_snap = |
4eb28f71 JM |
356 | (struct wlan_snap *) (skb->data + payload_offset + |
357 | sizeof(struct wlan_llc)); | |
00b3ed16 GKH |
358 | |
359 | /* Test for the various encodings */ | |
51e4896a | 360 | if ((payload_length >= sizeof(struct wlan_ethhdr)) && |
82eaca7d MM |
361 | (e_llc->dsap != 0xaa || e_llc->ssap != 0xaa) && |
362 | ((memcmp(daddr, e_hdr->daddr, WLAN_ETHADDR_LEN) == 0) || | |
00b3ed16 | 363 | (memcmp(saddr, e_hdr->saddr, WLAN_ETHADDR_LEN) == 0))) { |
a7cf7bae | 364 | pr_debug("802.3 ENCAP len: %d\n", payload_length); |
00b3ed16 | 365 | /* 802.3 Encapsulated */ |
33ce0ca6 | 366 | /* Test for an overlength frame */ |
82eaca7d | 367 | if (payload_length > (netdev->mtu + WLAN_ETHHDR_LEN)) { |
33ce0ca6 RK |
368 | /* A bogus length ethfrm has been encap'd. */ |
369 | /* Is someone trying an oflow attack? */ | |
edbd606c | 370 | printk(KERN_ERR "ENCAP frame too large (%d > %d)\n", |
82eaca7d | 371 | payload_length, netdev->mtu + WLAN_ETHHDR_LEN); |
33ce0ca6 RK |
372 | return 1; |
373 | } | |
00b3ed16 GKH |
374 | |
375 | /* Chop off the 802.11 header. it's already sane. */ | |
376 | skb_pull(skb, payload_offset); | |
377 | /* chop off the 802.11 CRC */ | |
378 | skb_trim(skb, skb->len - WLAN_CRC_LEN); | |
379 | ||
4eb28f71 JM |
380 | } else if ((payload_length >= sizeof(struct wlan_llc) + |
381 | sizeof(struct wlan_snap)) | |
e80528b7 | 382 | && (e_llc->dsap == 0xaa) |
4eb28f71 JM |
383 | && (e_llc->ssap == 0xaa) |
384 | && (e_llc->ctl == 0x03) | |
82eaca7d MM |
385 | && |
386 | (((memcmp(e_snap->oui, oui_rfc1042, WLAN_IEEE_OUI_LEN) == 0) | |
387 | && (ethconv == WLAN_ETHCONV_8021h) | |
388 | && (p80211_stt_findproto(le16_to_cpu(e_snap->type)))) | |
389 | || (memcmp(e_snap->oui, oui_rfc1042, WLAN_IEEE_OUI_LEN) != | |
390 | 0))) { | |
a7cf7bae | 391 | pr_debug("SNAP+RFC1042 len: %d\n", payload_length); |
00b3ed16 GKH |
392 | /* it's a SNAP + RFC1042 frame && protocol is in STT */ |
393 | /* build 802.3 + RFC1042 */ | |
394 | ||
33ce0ca6 | 395 | /* Test for an overlength frame */ |
82eaca7d | 396 | if (payload_length > netdev->mtu) { |
33ce0ca6 RK |
397 | /* A bogus length ethfrm has been sent. */ |
398 | /* Is someone trying an oflow attack? */ | |
edbd606c | 399 | printk(KERN_ERR "SNAP frame too large (%d > %d)\n", |
82eaca7d | 400 | payload_length, netdev->mtu); |
33ce0ca6 RK |
401 | return 1; |
402 | } | |
403 | ||
00b3ed16 GKH |
404 | /* chop 802.11 header from skb. */ |
405 | skb_pull(skb, payload_offset); | |
406 | ||
407 | /* create 802.3 header at beginning of skb. */ | |
51e4896a | 408 | e_hdr = (struct wlan_ethhdr *) skb_push(skb, WLAN_ETHHDR_LEN); |
00b3ed16 GKH |
409 | memcpy(e_hdr->daddr, daddr, WLAN_ETHADDR_LEN); |
410 | memcpy(e_hdr->saddr, saddr, WLAN_ETHADDR_LEN); | |
411 | e_hdr->type = htons(payload_length); | |
412 | ||
413 | /* chop off the 802.11 CRC */ | |
414 | skb_trim(skb, skb->len - WLAN_CRC_LEN); | |
415 | ||
4eb28f71 JM |
416 | } else if ((payload_length >= sizeof(struct wlan_llc) + |
417 | sizeof(struct wlan_snap)) | |
e80528b7 | 418 | && (e_llc->dsap == 0xaa) |
4eb28f71 JM |
419 | && (e_llc->ssap == 0xaa) |
420 | && (e_llc->ctl == 0x03)) { | |
a7cf7bae | 421 | pr_debug("802.1h/RFC1042 len: %d\n", payload_length); |
4eb28f71 JM |
422 | /* it's an 802.1h frame || (an RFC1042 && protocol not in STT) |
423 | build a DIXII + RFC894 */ | |
00b3ed16 | 424 | |
33ce0ca6 | 425 | /* Test for an overlength frame */ |
4eb28f71 JM |
426 | if ((payload_length - sizeof(struct wlan_llc) - |
427 | sizeof(struct wlan_snap)) | |
428 | > netdev->mtu) { | |
33ce0ca6 RK |
429 | /* A bogus length ethfrm has been sent. */ |
430 | /* Is someone trying an oflow attack? */ | |
edbd606c | 431 | printk(KERN_ERR "DIXII frame too large (%ld > %d)\n", |
4eb28f71 JM |
432 | (long int)(payload_length - |
433 | sizeof(struct wlan_llc) - | |
434 | sizeof(struct wlan_snap)), netdev->mtu); | |
33ce0ca6 RK |
435 | return 1; |
436 | } | |
437 | ||
00b3ed16 GKH |
438 | /* chop 802.11 header from skb. */ |
439 | skb_pull(skb, payload_offset); | |
440 | ||
441 | /* chop llc header from skb. */ | |
51e4896a | 442 | skb_pull(skb, sizeof(struct wlan_llc)); |
00b3ed16 GKH |
443 | |
444 | /* chop snap header from skb. */ | |
51e4896a | 445 | skb_pull(skb, sizeof(struct wlan_snap)); |
00b3ed16 GKH |
446 | |
447 | /* create 802.3 header at beginning of skb. */ | |
51e4896a | 448 | e_hdr = (struct wlan_ethhdr *) skb_push(skb, WLAN_ETHHDR_LEN); |
00b3ed16 GKH |
449 | e_hdr->type = e_snap->type; |
450 | memcpy(e_hdr->daddr, daddr, WLAN_ETHADDR_LEN); | |
451 | memcpy(e_hdr->saddr, saddr, WLAN_ETHADDR_LEN); | |
452 | ||
453 | /* chop off the 802.11 CRC */ | |
454 | skb_trim(skb, skb->len - WLAN_CRC_LEN); | |
455 | } else { | |
a7cf7bae | 456 | pr_debug("NON-ENCAP len: %d\n", payload_length); |
00b3ed16 GKH |
457 | /* any NON-ENCAP */ |
458 | /* it's a generic 80211+LLC or IPX 'Raw 802.3' */ | |
459 | /* build an 802.3 frame */ | |
460 | /* allocate space and setup hostbuf */ | |
461 | ||
33ce0ca6 | 462 | /* Test for an overlength frame */ |
82eaca7d | 463 | if (payload_length > netdev->mtu) { |
33ce0ca6 RK |
464 | /* A bogus length ethfrm has been sent. */ |
465 | /* Is someone trying an oflow attack? */ | |
edbd606c | 466 | printk(KERN_ERR "OTHER frame too large (%d > %d)\n", |
82eaca7d | 467 | payload_length, netdev->mtu); |
33ce0ca6 RK |
468 | return 1; |
469 | } | |
470 | ||
00b3ed16 GKH |
471 | /* Chop off the 802.11 header. */ |
472 | skb_pull(skb, payload_offset); | |
473 | ||
474 | /* create 802.3 header at beginning of skb. */ | |
51e4896a | 475 | e_hdr = (struct wlan_ethhdr *) skb_push(skb, WLAN_ETHHDR_LEN); |
00b3ed16 GKH |
476 | memcpy(e_hdr->daddr, daddr, WLAN_ETHADDR_LEN); |
477 | memcpy(e_hdr->saddr, saddr, WLAN_ETHADDR_LEN); | |
478 | e_hdr->type = htons(payload_length); | |
479 | ||
480 | /* chop off the 802.11 CRC */ | |
481 | skb_trim(skb, skb->len - WLAN_CRC_LEN); | |
482 | ||
483 | } | |
484 | ||
82eaca7d MM |
485 | /* |
486 | * Note that eth_type_trans() expects an skb w/ skb->data pointing | |
487 | * at the MAC header, it then sets the following skb members: | |
488 | * skb->mac_header, | |
489 | * skb->data, and | |
490 | * skb->pkt_type. | |
491 | * It then _returns_ the value that _we're_ supposed to stuff in | |
492 | * skb->protocol. This is nuts. | |
493 | */ | |
00b3ed16 | 494 | skb->protocol = eth_type_trans(skb, netdev); |
00b3ed16 | 495 | |
82eaca7d | 496 | /* jkriegl: process signal and noise as set in hfa384x_int_rx() */ |
00b3ed16 | 497 | /* jkriegl: only process signal/noise if requested by iwspy */ |
82eaca7d MM |
498 | if (wlandev->spy_number) |
499 | orinoco_spy_gather(wlandev, eth_hdr(skb)->h_source, | |
500 | P80211SKB_RXMETA(skb)); | |
00b3ed16 GKH |
501 | |
502 | /* Free the metadata */ | |
503 | p80211skb_rxmeta_detach(skb); | |
504 | ||
00b3ed16 GKH |
505 | return 0; |
506 | } | |
507 | ||
508 | /*---------------------------------------------------------------- | |
509 | * p80211_stt_findproto | |
510 | * | |
511 | * Searches the 802.1h Selective Translation Table for a given | |
512 | * protocol. | |
513 | * | |
514 | * Arguments: | |
515 | * proto protocl number (in host order) to search for. | |
516 | * | |
517 | * Returns: | |
518 | * 1 - if the table is empty or a match is found. | |
519 | * 0 - if the table is non-empty and a match is not found. | |
520 | * | |
521 | * Call context: | |
522 | * May be called in interrupt or non-interrupt context | |
523 | ----------------------------------------------------------------*/ | |
aaad4303 | 524 | int p80211_stt_findproto(u16 proto) |
00b3ed16 GKH |
525 | { |
526 | /* Always return found for now. This is the behavior used by the */ | |
527 | /* Zoom Win95 driver when 802.1h mode is selected */ | |
528 | /* TODO: If necessary, add an actual search we'll probably | |
82eaca7d MM |
529 | need this to match the CMAC's way of doing things. |
530 | Need to do some testing to confirm. | |
531 | */ | |
00b3ed16 | 532 | |
82eaca7d | 533 | if (proto == 0x80f3) /* APPLETALK */ |
00b3ed16 GKH |
534 | return 1; |
535 | ||
536 | return 0; | |
537 | } | |
538 | ||
539 | /*---------------------------------------------------------------- | |
540 | * p80211skb_rxmeta_detach | |
541 | * | |
542 | * Disconnects the frmmeta and rxmeta from an skb. | |
543 | * | |
544 | * Arguments: | |
545 | * wlandev The wlandev this skb belongs to. | |
546 | * skb The skb we're attaching to. | |
547 | * | |
548 | * Returns: | |
549 | * 0 on success, non-zero otherwise | |
550 | * | |
551 | * Call context: | |
552 | * May be called in interrupt or non-interrupt context | |
553 | ----------------------------------------------------------------*/ | |
82eaca7d | 554 | void p80211skb_rxmeta_detach(struct sk_buff *skb) |
00b3ed16 | 555 | { |
51e4896a EH |
556 | struct p80211_rxmeta *rxmeta; |
557 | struct p80211_frmmeta *frmmeta; | |
00b3ed16 | 558 | |
00b3ed16 | 559 | /* Sanity checks */ |
82eaca7d | 560 | if (skb == NULL) { /* bad skb */ |
a7cf7bae | 561 | pr_debug("Called w/ null skb.\n"); |
00b3ed16 GKH |
562 | goto exit; |
563 | } | |
564 | frmmeta = P80211SKB_FRMMETA(skb); | |
82eaca7d | 565 | if (frmmeta == NULL) { /* no magic */ |
a7cf7bae | 566 | pr_debug("Called w/ bad frmmeta magic.\n"); |
00b3ed16 GKH |
567 | goto exit; |
568 | } | |
569 | rxmeta = frmmeta->rx; | |
82eaca7d | 570 | if (rxmeta == NULL) { /* bad meta ptr */ |
a7cf7bae | 571 | pr_debug("Called w/ bad rxmeta ptr.\n"); |
00b3ed16 GKH |
572 | goto exit; |
573 | } | |
574 | ||
575 | /* Free rxmeta */ | |
576 | kfree(rxmeta); | |
577 | ||
578 | /* Clear skb->cb */ | |
579 | memset(skb->cb, 0, sizeof(skb->cb)); | |
580 | exit: | |
00b3ed16 GKH |
581 | return; |
582 | } | |
583 | ||
584 | /*---------------------------------------------------------------- | |
585 | * p80211skb_rxmeta_attach | |
586 | * | |
587 | * Allocates a p80211rxmeta structure, initializes it, and attaches | |
588 | * it to an skb. | |
589 | * | |
590 | * Arguments: | |
591 | * wlandev The wlandev this skb belongs to. | |
592 | * skb The skb we're attaching to. | |
593 | * | |
594 | * Returns: | |
595 | * 0 on success, non-zero otherwise | |
596 | * | |
597 | * Call context: | |
598 | * May be called in interrupt or non-interrupt context | |
599 | ----------------------------------------------------------------*/ | |
82eaca7d | 600 | int p80211skb_rxmeta_attach(struct wlandevice *wlandev, struct sk_buff *skb) |
00b3ed16 | 601 | { |
82eaca7d | 602 | int result = 0; |
51e4896a EH |
603 | struct p80211_rxmeta *rxmeta; |
604 | struct p80211_frmmeta *frmmeta; | |
00b3ed16 | 605 | |
00b3ed16 GKH |
606 | /* If these already have metadata, we error out! */ |
607 | if (P80211SKB_RXMETA(skb) != NULL) { | |
edbd606c | 608 | printk(KERN_ERR "%s: RXmeta already attached!\n", |
82eaca7d | 609 | wlandev->name); |
00b3ed16 GKH |
610 | result = 0; |
611 | goto exit; | |
612 | } | |
613 | ||
614 | /* Allocate the rxmeta */ | |
51e4896a | 615 | rxmeta = kzalloc(sizeof(struct p80211_rxmeta), GFP_ATOMIC); |
00b3ed16 | 616 | |
82eaca7d | 617 | if (rxmeta == NULL) { |
edbd606c | 618 | printk(KERN_ERR "%s: Failed to allocate rxmeta.\n", |
82eaca7d | 619 | wlandev->name); |
00b3ed16 GKH |
620 | result = 1; |
621 | goto exit; | |
622 | } | |
623 | ||
624 | /* Initialize the rxmeta */ | |
00b3ed16 GKH |
625 | rxmeta->wlandev = wlandev; |
626 | rxmeta->hosttime = jiffies; | |
627 | ||
628 | /* Overlay a frmmeta_t onto skb->cb */ | |
51e4896a EH |
629 | memset(skb->cb, 0, sizeof(struct p80211_frmmeta)); |
630 | frmmeta = (struct p80211_frmmeta *) (skb->cb); | |
00b3ed16 GKH |
631 | frmmeta->magic = P80211_FRMMETA_MAGIC; |
632 | frmmeta->rx = rxmeta; | |
633 | exit: | |
00b3ed16 GKH |
634 | return result; |
635 | } | |
636 | ||
637 | /*---------------------------------------------------------------- | |
638 | * p80211skb_free | |
639 | * | |
640 | * Frees an entire p80211skb by checking and freeing the meta struct | |
641 | * and then freeing the skb. | |
642 | * | |
643 | * Arguments: | |
644 | * wlandev The wlandev this skb belongs to. | |
645 | * skb The skb we're attaching to. | |
646 | * | |
647 | * Returns: | |
648 | * 0 on success, non-zero otherwise | |
649 | * | |
650 | * Call context: | |
651 | * May be called in interrupt or non-interrupt context | |
652 | ----------------------------------------------------------------*/ | |
82eaca7d | 653 | void p80211skb_free(struct wlandevice *wlandev, struct sk_buff *skb) |
00b3ed16 | 654 | { |
51e4896a | 655 | struct p80211_frmmeta *meta; |
8a251b55 | 656 | |
00b3ed16 | 657 | meta = P80211SKB_FRMMETA(skb); |
82eaca7d | 658 | if (meta && meta->rx) |
00b3ed16 | 659 | p80211skb_rxmeta_detach(skb); |
82eaca7d | 660 | else |
edbd606c | 661 | printk(KERN_ERR "Freeing an skb (%p) w/ no frmmeta.\n", skb); |
00b3ed16 | 662 | dev_kfree_skb(skb); |
00b3ed16 GKH |
663 | return; |
664 | } |