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 MT |
105 | int skb_ether_to_p80211(wlandevice_t *wlandev, u32 ethconv, |
106 | struct sk_buff *skb, p80211_hdr_t *p80211_hdr, | |
107 | p80211_metawep_t *p80211_wep) | |
00b3ed16 GKH |
108 | { |
109 | ||
82eaca7d MM |
110 | u16 fc; |
111 | u16 proto; | |
112 | wlan_ethhdr_t e_hdr; | |
113 | wlan_llc_t *e_llc; | |
114 | wlan_snap_t *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 MM |
150 | e_snap = |
151 | (wlan_snap_t *) skb_push(skb, sizeof(wlan_snap_t)); | |
00b3ed16 | 152 | e_snap->type = htons(proto); |
82eaca7d MM |
153 | if (ethconv == WLAN_ETHCONV_8021h |
154 | && p80211_stt_findproto(proto)) { | |
155 | memcpy(e_snap->oui, oui_8021h, | |
156 | WLAN_IEEE_OUI_LEN); | |
00b3ed16 | 157 | } else { |
82eaca7d MM |
158 | memcpy(e_snap->oui, oui_rfc1042, |
159 | WLAN_IEEE_OUI_LEN); | |
00b3ed16 GKH |
160 | } |
161 | ||
162 | /* tack on llc */ | |
82eaca7d MM |
163 | e_llc = |
164 | (wlan_llc_t *) skb_push(skb, sizeof(wlan_llc_t)); | |
00b3ed16 GKH |
165 | e_llc->dsap = 0xAA; /* SNAP, see IEEE 802 */ |
166 | e_llc->ssap = 0xAA; | |
167 | e_llc->ctl = 0x03; | |
168 | ||
169 | } | |
170 | } | |
171 | ||
172 | /* Set up the 802.11 header */ | |
173 | /* It's a data frame */ | |
82eaca7d MM |
174 | fc = cpu_to_le16(WLAN_SET_FC_FTYPE(WLAN_FTYPE_DATA) | |
175 | WLAN_SET_FC_FSTYPE(WLAN_FSTYPE_DATAONLY)); | |
00b3ed16 | 176 | |
82eaca7d | 177 | switch (wlandev->macmode) { |
00b3ed16 | 178 | case WLAN_MACMODE_IBSS_STA: |
28b17a4b MM |
179 | memcpy(p80211_hdr->a3.a1, &e_hdr.daddr, ETH_ALEN); |
180 | memcpy(p80211_hdr->a3.a2, wlandev->netdev->dev_addr, ETH_ALEN); | |
181 | memcpy(p80211_hdr->a3.a3, wlandev->bssid, ETH_ALEN); | |
00b3ed16 GKH |
182 | break; |
183 | case WLAN_MACMODE_ESS_STA: | |
ae26230b | 184 | fc |= cpu_to_le16(WLAN_SET_FC_TODS(1)); |
28b17a4b MM |
185 | memcpy(p80211_hdr->a3.a1, wlandev->bssid, ETH_ALEN); |
186 | memcpy(p80211_hdr->a3.a2, wlandev->netdev->dev_addr, ETH_ALEN); | |
187 | memcpy(p80211_hdr->a3.a3, &e_hdr.daddr, ETH_ALEN); | |
00b3ed16 GKH |
188 | break; |
189 | case WLAN_MACMODE_ESS_AP: | |
ae26230b | 190 | fc |= cpu_to_le16(WLAN_SET_FC_FROMDS(1)); |
28b17a4b MM |
191 | memcpy(p80211_hdr->a3.a1, &e_hdr.daddr, ETH_ALEN); |
192 | memcpy(p80211_hdr->a3.a2, wlandev->bssid, ETH_ALEN); | |
193 | memcpy(p80211_hdr->a3.a3, &e_hdr.saddr, ETH_ALEN); | |
00b3ed16 GKH |
194 | break; |
195 | default: | |
82eaca7d MM |
196 | printk(KERN_ERR |
197 | "Error: Converting eth to wlan in unknown mode.\n"); | |
00b3ed16 GKH |
198 | return 1; |
199 | break; | |
200 | } | |
201 | ||
202 | p80211_wep->data = NULL; | |
203 | ||
82eaca7d MM |
204 | if ((wlandev->hostwep & HOSTWEP_PRIVACYINVOKED) |
205 | && (wlandev->hostwep & HOSTWEP_ENCRYPT)) { | |
206 | /* XXXX need to pick keynum other than default? */ | |
00b3ed16 | 207 | |
00b3ed16 | 208 | p80211_wep->data = kmalloc(skb->len, GFP_ATOMIC); |
5dd8acc8 | 209 | foo = wep_encrypt(wlandev, skb->data, p80211_wep->data, |
b02957d5 | 210 | skb->len, |
5813b624 | 211 | (wlandev->hostwep & HOSTWEP_DEFAULTKEY_MASK), |
3f4b4e77 | 212 | p80211_wep->iv, p80211_wep->icv); |
5dd8acc8 | 213 | if (foo) { |
82eaca7d MM |
214 | printk(KERN_WARNING |
215 | "Host en-WEP failed, dropping frame (%d).\n", | |
216 | foo); | |
00b3ed16 GKH |
217 | return 2; |
218 | } | |
ae26230b | 219 | fc |= cpu_to_le16(WLAN_SET_FC_ISWEP(1)); |
00b3ed16 GKH |
220 | } |
221 | ||
82eaca7d | 222 | /* skb->nh.raw = skb->data; */ |
00b3ed16 GKH |
223 | |
224 | p80211_hdr->a3.fc = fc; | |
225 | p80211_hdr->a3.dur = 0; | |
226 | p80211_hdr->a3.seq = 0; | |
227 | ||
00b3ed16 GKH |
228 | return 0; |
229 | } | |
230 | ||
231 | /* jkriegl: from orinoco, modified */ | |
297f06ce MT |
232 | static void orinoco_spy_gather(wlandevice_t *wlandev, char *mac, |
233 | p80211_rxmeta_t *rxmeta) | |
00b3ed16 | 234 | { |
82eaca7d | 235 | int i; |
00b3ed16 | 236 | |
82eaca7d MM |
237 | /* Gather wireless spy statistics: for each packet, compare the |
238 | * source address with out list, and if match, get the stats... */ | |
00b3ed16 | 239 | |
82eaca7d | 240 | for (i = 0; i < wlandev->spy_number; i++) { |
00b3ed16 | 241 | |
82eaca7d | 242 | if (!memcmp(wlandev->spy_address[i], mac, ETH_ALEN)) { |
00b3ed16 | 243 | memcpy(wlandev->spy_address[i], mac, ETH_ALEN); |
82eaca7d MM |
244 | wlandev->spy_stat[i].level = rxmeta->signal; |
245 | wlandev->spy_stat[i].noise = rxmeta->noise; | |
246 | wlandev->spy_stat[i].qual = | |
247 | (rxmeta->signal > | |
248 | rxmeta->noise) ? (rxmeta->signal - | |
249 | rxmeta->noise) : 0; | |
250 | wlandev->spy_stat[i].updated = 0x7; | |
251 | } | |
252 | } | |
00b3ed16 GKH |
253 | } |
254 | ||
255 | /*---------------------------------------------------------------- | |
256 | * p80211pb_80211_to_ether | |
257 | * | |
258 | * Uses the contents of a received 802.11 frame and the etherconv | |
259 | * setting to build an ether frame. | |
260 | * | |
261 | * This function extracts the src and dest address from the 802.11 | |
262 | * frame to use in the construction of the eth frame. | |
263 | * | |
264 | * Arguments: | |
265 | * ethconv Conversion type to perform | |
266 | * skb Packet buffer containing the 802.11 frame | |
267 | * | |
268 | * Returns: | |
269 | * 0 on success, non-zero otherwise | |
270 | * | |
271 | * Call context: | |
272 | * May be called in interrupt or non-interrupt context | |
273 | ----------------------------------------------------------------*/ | |
297f06ce | 274 | int skb_p80211_to_ether(wlandevice_t *wlandev, u32 ethconv, |
82eaca7d | 275 | struct sk_buff *skb) |
00b3ed16 | 276 | { |
82eaca7d MM |
277 | netdevice_t *netdev = wlandev->netdev; |
278 | u16 fc; | |
279 | unsigned int payload_length; | |
280 | unsigned int payload_offset; | |
281 | u8 daddr[WLAN_ETHADDR_LEN]; | |
282 | u8 saddr[WLAN_ETHADDR_LEN]; | |
283 | p80211_hdr_t *w_hdr; | |
284 | wlan_ethhdr_t *e_hdr; | |
285 | wlan_llc_t *e_llc; | |
286 | wlan_snap_t *e_snap; | |
00b3ed16 GKH |
287 | |
288 | int foo; | |
289 | ||
00b3ed16 GKH |
290 | payload_length = skb->len - WLAN_HDR_A3_LEN - WLAN_CRC_LEN; |
291 | payload_offset = WLAN_HDR_A3_LEN; | |
292 | ||
293 | w_hdr = (p80211_hdr_t *) skb->data; | |
294 | ||
82eaca7d | 295 | /* setup some vars for convenience */ |
ae26230b | 296 | fc = le16_to_cpu(w_hdr->a3.fc); |
82eaca7d | 297 | if ((WLAN_GET_FC_TODS(fc) == 0) && (WLAN_GET_FC_FROMDS(fc) == 0)) { |
00b3ed16 GKH |
298 | memcpy(daddr, w_hdr->a3.a1, WLAN_ETHADDR_LEN); |
299 | memcpy(saddr, w_hdr->a3.a2, WLAN_ETHADDR_LEN); | |
82eaca7d | 300 | } else if ((WLAN_GET_FC_TODS(fc) == 0) && (WLAN_GET_FC_FROMDS(fc) == 1)) { |
00b3ed16 GKH |
301 | memcpy(daddr, w_hdr->a3.a1, WLAN_ETHADDR_LEN); |
302 | memcpy(saddr, w_hdr->a3.a3, WLAN_ETHADDR_LEN); | |
82eaca7d | 303 | } else if ((WLAN_GET_FC_TODS(fc) == 1) && (WLAN_GET_FC_FROMDS(fc) == 0)) { |
00b3ed16 GKH |
304 | memcpy(daddr, w_hdr->a3.a3, WLAN_ETHADDR_LEN); |
305 | memcpy(saddr, w_hdr->a3.a2, WLAN_ETHADDR_LEN); | |
306 | } else { | |
307 | payload_offset = WLAN_HDR_A4_LEN; | |
1f9e9ce1 | 308 | if (payload_length < WLAN_HDR_A4_LEN - WLAN_HDR_A3_LEN) { |
edbd606c | 309 | printk(KERN_ERR "A4 frame too short!\n"); |
00b3ed16 GKH |
310 | return 1; |
311 | } | |
1f9e9ce1 | 312 | payload_length -= (WLAN_HDR_A4_LEN - WLAN_HDR_A3_LEN); |
00b3ed16 GKH |
313 | memcpy(daddr, w_hdr->a4.a3, WLAN_ETHADDR_LEN); |
314 | memcpy(saddr, w_hdr->a4.a4, WLAN_ETHADDR_LEN); | |
315 | } | |
316 | ||
317 | /* perform de-wep if necessary.. */ | |
82eaca7d MM |
318 | if ((wlandev->hostwep & HOSTWEP_PRIVACYINVOKED) && WLAN_GET_FC_ISWEP(fc) |
319 | && (wlandev->hostwep & HOSTWEP_DECRYPT)) { | |
00b3ed16 | 320 | if (payload_length <= 8) { |
edbd606c | 321 | printk(KERN_ERR "WEP frame too short (%u).\n", |
82eaca7d | 322 | skb->len); |
00b3ed16 GKH |
323 | return 1; |
324 | } | |
5dd8acc8 | 325 | foo = wep_decrypt(wlandev, skb->data + payload_offset + 4, |
00b3ed16 GKH |
326 | payload_length - 8, -1, |
327 | skb->data + payload_offset, | |
82eaca7d | 328 | skb->data + payload_offset + |
3f4b4e77 | 329 | payload_length - 4); |
5dd8acc8 | 330 | if (foo) { |
00b3ed16 | 331 | /* de-wep failed, drop skb. */ |
75f49e07 MT |
332 | pr_debug("Host de-WEP failed, dropping frame (%d).\n", |
333 | foo); | |
00b3ed16 GKH |
334 | wlandev->rx.decrypt_err++; |
335 | return 2; | |
336 | } | |
337 | ||
338 | /* subtract the IV+ICV length off the payload */ | |
339 | payload_length -= 8; | |
340 | /* chop off the IV */ | |
341 | skb_pull(skb, 4); | |
342 | /* chop off the ICV. */ | |
343 | skb_trim(skb, skb->len - 4); | |
344 | ||
345 | wlandev->rx.decrypt++; | |
346 | } | |
347 | ||
348 | e_hdr = (wlan_ethhdr_t *) (skb->data + payload_offset); | |
349 | ||
350 | e_llc = (wlan_llc_t *) (skb->data + payload_offset); | |
82eaca7d MM |
351 | e_snap = |
352 | (wlan_snap_t *) (skb->data + payload_offset + sizeof(wlan_llc_t)); | |
00b3ed16 GKH |
353 | |
354 | /* Test for the various encodings */ | |
82eaca7d MM |
355 | if ((payload_length >= sizeof(wlan_ethhdr_t)) && |
356 | (e_llc->dsap != 0xaa || e_llc->ssap != 0xaa) && | |
357 | ((memcmp(daddr, e_hdr->daddr, WLAN_ETHADDR_LEN) == 0) || | |
00b3ed16 | 358 | (memcmp(saddr, e_hdr->saddr, WLAN_ETHADDR_LEN) == 0))) { |
a7cf7bae | 359 | pr_debug("802.3 ENCAP len: %d\n", payload_length); |
00b3ed16 | 360 | /* 802.3 Encapsulated */ |
33ce0ca6 | 361 | /* Test for an overlength frame */ |
82eaca7d | 362 | if (payload_length > (netdev->mtu + WLAN_ETHHDR_LEN)) { |
33ce0ca6 RK |
363 | /* A bogus length ethfrm has been encap'd. */ |
364 | /* Is someone trying an oflow attack? */ | |
edbd606c | 365 | printk(KERN_ERR "ENCAP frame too large (%d > %d)\n", |
82eaca7d | 366 | payload_length, netdev->mtu + WLAN_ETHHDR_LEN); |
33ce0ca6 RK |
367 | return 1; |
368 | } | |
00b3ed16 GKH |
369 | |
370 | /* Chop off the 802.11 header. it's already sane. */ | |
371 | skb_pull(skb, payload_offset); | |
372 | /* chop off the 802.11 CRC */ | |
373 | skb_trim(skb, skb->len - WLAN_CRC_LEN); | |
374 | ||
82eaca7d MM |
375 | } else if ((payload_length >= sizeof(wlan_llc_t) + sizeof(wlan_snap_t)) |
376 | && (e_llc->dsap == 0xaa) && (e_llc->ssap == 0xaa) | |
377 | && (e_llc->ctl == 0x03) | |
378 | && | |
379 | (((memcmp(e_snap->oui, oui_rfc1042, WLAN_IEEE_OUI_LEN) == 0) | |
380 | && (ethconv == WLAN_ETHCONV_8021h) | |
381 | && (p80211_stt_findproto(le16_to_cpu(e_snap->type)))) | |
382 | || (memcmp(e_snap->oui, oui_rfc1042, WLAN_IEEE_OUI_LEN) != | |
383 | 0))) { | |
a7cf7bae | 384 | pr_debug("SNAP+RFC1042 len: %d\n", payload_length); |
00b3ed16 GKH |
385 | /* it's a SNAP + RFC1042 frame && protocol is in STT */ |
386 | /* build 802.3 + RFC1042 */ | |
387 | ||
33ce0ca6 | 388 | /* Test for an overlength frame */ |
82eaca7d | 389 | if (payload_length > netdev->mtu) { |
33ce0ca6 RK |
390 | /* A bogus length ethfrm has been sent. */ |
391 | /* Is someone trying an oflow attack? */ | |
edbd606c | 392 | printk(KERN_ERR "SNAP frame too large (%d > %d)\n", |
82eaca7d | 393 | payload_length, netdev->mtu); |
33ce0ca6 RK |
394 | return 1; |
395 | } | |
396 | ||
00b3ed16 GKH |
397 | /* chop 802.11 header from skb. */ |
398 | skb_pull(skb, payload_offset); | |
399 | ||
400 | /* create 802.3 header at beginning of skb. */ | |
401 | e_hdr = (wlan_ethhdr_t *) skb_push(skb, WLAN_ETHHDR_LEN); | |
402 | memcpy(e_hdr->daddr, daddr, WLAN_ETHADDR_LEN); | |
403 | memcpy(e_hdr->saddr, saddr, WLAN_ETHADDR_LEN); | |
404 | e_hdr->type = htons(payload_length); | |
405 | ||
406 | /* chop off the 802.11 CRC */ | |
407 | skb_trim(skb, skb->len - WLAN_CRC_LEN); | |
408 | ||
82eaca7d MM |
409 | } else if ((payload_length >= sizeof(wlan_llc_t) + sizeof(wlan_snap_t)) |
410 | && (e_llc->dsap == 0xaa) && (e_llc->ssap == 0xaa) | |
411 | && (e_llc->ctl == 0x03)) { | |
a7cf7bae | 412 | pr_debug("802.1h/RFC1042 len: %d\n", payload_length); |
00b3ed16 GKH |
413 | /* it's an 802.1h frame || (an RFC1042 && protocol is not in STT) */ |
414 | /* build a DIXII + RFC894 */ | |
415 | ||
33ce0ca6 RK |
416 | /* Test for an overlength frame */ |
417 | if ((payload_length - sizeof(wlan_llc_t) - sizeof(wlan_snap_t)) | |
418 | > netdev->mtu) { | |
419 | /* A bogus length ethfrm has been sent. */ | |
420 | /* Is someone trying an oflow attack? */ | |
edbd606c | 421 | printk(KERN_ERR "DIXII frame too large (%ld > %d)\n", |
82eaca7d MM |
422 | (long int)(payload_length - sizeof(wlan_llc_t) - |
423 | sizeof(wlan_snap_t)), netdev->mtu); | |
33ce0ca6 RK |
424 | return 1; |
425 | } | |
426 | ||
00b3ed16 GKH |
427 | /* chop 802.11 header from skb. */ |
428 | skb_pull(skb, payload_offset); | |
429 | ||
430 | /* chop llc header from skb. */ | |
431 | skb_pull(skb, sizeof(wlan_llc_t)); | |
432 | ||
433 | /* chop snap header from skb. */ | |
434 | skb_pull(skb, sizeof(wlan_snap_t)); | |
435 | ||
436 | /* create 802.3 header at beginning of skb. */ | |
437 | e_hdr = (wlan_ethhdr_t *) skb_push(skb, WLAN_ETHHDR_LEN); | |
438 | e_hdr->type = e_snap->type; | |
439 | memcpy(e_hdr->daddr, daddr, WLAN_ETHADDR_LEN); | |
440 | memcpy(e_hdr->saddr, saddr, WLAN_ETHADDR_LEN); | |
441 | ||
442 | /* chop off the 802.11 CRC */ | |
443 | skb_trim(skb, skb->len - WLAN_CRC_LEN); | |
444 | } else { | |
a7cf7bae | 445 | pr_debug("NON-ENCAP len: %d\n", payload_length); |
00b3ed16 GKH |
446 | /* any NON-ENCAP */ |
447 | /* it's a generic 80211+LLC or IPX 'Raw 802.3' */ | |
448 | /* build an 802.3 frame */ | |
449 | /* allocate space and setup hostbuf */ | |
450 | ||
33ce0ca6 | 451 | /* Test for an overlength frame */ |
82eaca7d | 452 | if (payload_length > netdev->mtu) { |
33ce0ca6 RK |
453 | /* A bogus length ethfrm has been sent. */ |
454 | /* Is someone trying an oflow attack? */ | |
edbd606c | 455 | printk(KERN_ERR "OTHER frame too large (%d > %d)\n", |
82eaca7d | 456 | payload_length, netdev->mtu); |
33ce0ca6 RK |
457 | return 1; |
458 | } | |
459 | ||
00b3ed16 GKH |
460 | /* Chop off the 802.11 header. */ |
461 | skb_pull(skb, payload_offset); | |
462 | ||
463 | /* create 802.3 header at beginning of skb. */ | |
464 | e_hdr = (wlan_ethhdr_t *) skb_push(skb, WLAN_ETHHDR_LEN); | |
465 | memcpy(e_hdr->daddr, daddr, WLAN_ETHADDR_LEN); | |
466 | memcpy(e_hdr->saddr, saddr, WLAN_ETHADDR_LEN); | |
467 | e_hdr->type = htons(payload_length); | |
468 | ||
469 | /* chop off the 802.11 CRC */ | |
470 | skb_trim(skb, skb->len - WLAN_CRC_LEN); | |
471 | ||
472 | } | |
473 | ||
82eaca7d MM |
474 | /* |
475 | * Note that eth_type_trans() expects an skb w/ skb->data pointing | |
476 | * at the MAC header, it then sets the following skb members: | |
477 | * skb->mac_header, | |
478 | * skb->data, and | |
479 | * skb->pkt_type. | |
480 | * It then _returns_ the value that _we're_ supposed to stuff in | |
481 | * skb->protocol. This is nuts. | |
482 | */ | |
00b3ed16 | 483 | skb->protocol = eth_type_trans(skb, netdev); |
00b3ed16 | 484 | |
82eaca7d | 485 | /* jkriegl: process signal and noise as set in hfa384x_int_rx() */ |
00b3ed16 | 486 | /* jkriegl: only process signal/noise if requested by iwspy */ |
82eaca7d MM |
487 | if (wlandev->spy_number) |
488 | orinoco_spy_gather(wlandev, eth_hdr(skb)->h_source, | |
489 | P80211SKB_RXMETA(skb)); | |
00b3ed16 GKH |
490 | |
491 | /* Free the metadata */ | |
492 | p80211skb_rxmeta_detach(skb); | |
493 | ||
00b3ed16 GKH |
494 | return 0; |
495 | } | |
496 | ||
497 | /*---------------------------------------------------------------- | |
498 | * p80211_stt_findproto | |
499 | * | |
500 | * Searches the 802.1h Selective Translation Table for a given | |
501 | * protocol. | |
502 | * | |
503 | * Arguments: | |
504 | * proto protocl number (in host order) to search for. | |
505 | * | |
506 | * Returns: | |
507 | * 1 - if the table is empty or a match is found. | |
508 | * 0 - if the table is non-empty and a match is not found. | |
509 | * | |
510 | * Call context: | |
511 | * May be called in interrupt or non-interrupt context | |
512 | ----------------------------------------------------------------*/ | |
aaad4303 | 513 | int p80211_stt_findproto(u16 proto) |
00b3ed16 GKH |
514 | { |
515 | /* Always return found for now. This is the behavior used by the */ | |
516 | /* Zoom Win95 driver when 802.1h mode is selected */ | |
517 | /* TODO: If necessary, add an actual search we'll probably | |
82eaca7d MM |
518 | need this to match the CMAC's way of doing things. |
519 | Need to do some testing to confirm. | |
520 | */ | |
00b3ed16 | 521 | |
82eaca7d | 522 | if (proto == 0x80f3) /* APPLETALK */ |
00b3ed16 GKH |
523 | return 1; |
524 | ||
525 | return 0; | |
526 | } | |
527 | ||
528 | /*---------------------------------------------------------------- | |
529 | * p80211skb_rxmeta_detach | |
530 | * | |
531 | * Disconnects the frmmeta and rxmeta from an skb. | |
532 | * | |
533 | * Arguments: | |
534 | * wlandev The wlandev this skb belongs to. | |
535 | * skb The skb we're attaching to. | |
536 | * | |
537 | * Returns: | |
538 | * 0 on success, non-zero otherwise | |
539 | * | |
540 | * Call context: | |
541 | * May be called in interrupt or non-interrupt context | |
542 | ----------------------------------------------------------------*/ | |
82eaca7d | 543 | void p80211skb_rxmeta_detach(struct sk_buff *skb) |
00b3ed16 | 544 | { |
82eaca7d MM |
545 | p80211_rxmeta_t *rxmeta; |
546 | p80211_frmmeta_t *frmmeta; | |
00b3ed16 | 547 | |
00b3ed16 | 548 | /* Sanity checks */ |
82eaca7d | 549 | if (skb == NULL) { /* bad skb */ |
a7cf7bae | 550 | pr_debug("Called w/ null skb.\n"); |
00b3ed16 GKH |
551 | goto exit; |
552 | } | |
553 | frmmeta = P80211SKB_FRMMETA(skb); | |
82eaca7d | 554 | if (frmmeta == NULL) { /* no magic */ |
a7cf7bae | 555 | pr_debug("Called w/ bad frmmeta magic.\n"); |
00b3ed16 GKH |
556 | goto exit; |
557 | } | |
558 | rxmeta = frmmeta->rx; | |
82eaca7d | 559 | if (rxmeta == NULL) { /* bad meta ptr */ |
a7cf7bae | 560 | pr_debug("Called w/ bad rxmeta ptr.\n"); |
00b3ed16 GKH |
561 | goto exit; |
562 | } | |
563 | ||
564 | /* Free rxmeta */ | |
565 | kfree(rxmeta); | |
566 | ||
567 | /* Clear skb->cb */ | |
568 | memset(skb->cb, 0, sizeof(skb->cb)); | |
569 | exit: | |
00b3ed16 GKH |
570 | return; |
571 | } | |
572 | ||
573 | /*---------------------------------------------------------------- | |
574 | * p80211skb_rxmeta_attach | |
575 | * | |
576 | * Allocates a p80211rxmeta structure, initializes it, and attaches | |
577 | * it to an skb. | |
578 | * | |
579 | * Arguments: | |
580 | * wlandev The wlandev this skb belongs to. | |
581 | * skb The skb we're attaching to. | |
582 | * | |
583 | * Returns: | |
584 | * 0 on success, non-zero otherwise | |
585 | * | |
586 | * Call context: | |
587 | * May be called in interrupt or non-interrupt context | |
588 | ----------------------------------------------------------------*/ | |
82eaca7d | 589 | int p80211skb_rxmeta_attach(struct wlandevice *wlandev, struct sk_buff *skb) |
00b3ed16 | 590 | { |
82eaca7d MM |
591 | int result = 0; |
592 | p80211_rxmeta_t *rxmeta; | |
593 | p80211_frmmeta_t *frmmeta; | |
00b3ed16 | 594 | |
00b3ed16 GKH |
595 | /* If these already have metadata, we error out! */ |
596 | if (P80211SKB_RXMETA(skb) != NULL) { | |
edbd606c | 597 | printk(KERN_ERR "%s: RXmeta already attached!\n", |
82eaca7d | 598 | wlandev->name); |
00b3ed16 GKH |
599 | result = 0; |
600 | goto exit; | |
601 | } | |
602 | ||
603 | /* Allocate the rxmeta */ | |
7a6cb0d5 | 604 | rxmeta = kzalloc(sizeof(p80211_rxmeta_t), GFP_ATOMIC); |
00b3ed16 | 605 | |
82eaca7d | 606 | if (rxmeta == NULL) { |
edbd606c | 607 | printk(KERN_ERR "%s: Failed to allocate rxmeta.\n", |
82eaca7d | 608 | wlandev->name); |
00b3ed16 GKH |
609 | result = 1; |
610 | goto exit; | |
611 | } | |
612 | ||
613 | /* Initialize the rxmeta */ | |
00b3ed16 GKH |
614 | rxmeta->wlandev = wlandev; |
615 | rxmeta->hosttime = jiffies; | |
616 | ||
617 | /* Overlay a frmmeta_t onto skb->cb */ | |
618 | memset(skb->cb, 0, sizeof(p80211_frmmeta_t)); | |
82eaca7d | 619 | frmmeta = (p80211_frmmeta_t *) (skb->cb); |
00b3ed16 GKH |
620 | frmmeta->magic = P80211_FRMMETA_MAGIC; |
621 | frmmeta->rx = rxmeta; | |
622 | exit: | |
00b3ed16 GKH |
623 | return result; |
624 | } | |
625 | ||
626 | /*---------------------------------------------------------------- | |
627 | * p80211skb_free | |
628 | * | |
629 | * Frees an entire p80211skb by checking and freeing the meta struct | |
630 | * and then freeing the skb. | |
631 | * | |
632 | * Arguments: | |
633 | * wlandev The wlandev this skb belongs to. | |
634 | * skb The skb we're attaching to. | |
635 | * | |
636 | * Returns: | |
637 | * 0 on success, non-zero otherwise | |
638 | * | |
639 | * Call context: | |
640 | * May be called in interrupt or non-interrupt context | |
641 | ----------------------------------------------------------------*/ | |
82eaca7d | 642 | void p80211skb_free(struct wlandevice *wlandev, struct sk_buff *skb) |
00b3ed16 | 643 | { |
82eaca7d | 644 | p80211_frmmeta_t *meta; |
8a251b55 | 645 | |
00b3ed16 | 646 | meta = P80211SKB_FRMMETA(skb); |
82eaca7d | 647 | if (meta && meta->rx) |
00b3ed16 | 648 | p80211skb_rxmeta_detach(skb); |
82eaca7d | 649 | else |
edbd606c | 650 | printk(KERN_ERR "Freeing an skb (%p) w/ no frmmeta.\n", skb); |
00b3ed16 | 651 | dev_kfree_skb(skb); |
00b3ed16 GKH |
652 | return; |
653 | } |