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