cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

p80211conv.c (19697B)


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