1f7056d33SGreg Kroah-Hartman // SPDX-License-Identifier: (GPL-2.0 OR MPL-1.1)
2*8ffd91d9SAldas Taraškevičius /*
300b3ed16SGreg Kroah-Hartman  *
400b3ed16SGreg Kroah-Hartman  * Ether/802.11 conversions and packet buffer routines
500b3ed16SGreg Kroah-Hartman  *
600b3ed16SGreg Kroah-Hartman  * Copyright (C) 1999 AbsoluteValue Systems, Inc.  All Rights Reserved.
700b3ed16SGreg Kroah-Hartman  * --------------------------------------------------------------------
800b3ed16SGreg Kroah-Hartman  *
900b3ed16SGreg Kroah-Hartman  * linux-wlan
1000b3ed16SGreg Kroah-Hartman  *
1100b3ed16SGreg Kroah-Hartman  * --------------------------------------------------------------------
1200b3ed16SGreg Kroah-Hartman  *
1300b3ed16SGreg Kroah-Hartman  * Inquiries regarding the linux-wlan Open Source project can be
1400b3ed16SGreg Kroah-Hartman  * made directly to:
1500b3ed16SGreg Kroah-Hartman  *
1600b3ed16SGreg Kroah-Hartman  * AbsoluteValue Systems Inc.
1700b3ed16SGreg Kroah-Hartman  * info@linux-wlan.com
1800b3ed16SGreg Kroah-Hartman  * http://www.linux-wlan.com
1900b3ed16SGreg Kroah-Hartman  *
2000b3ed16SGreg Kroah-Hartman  * --------------------------------------------------------------------
2100b3ed16SGreg Kroah-Hartman  *
2200b3ed16SGreg Kroah-Hartman  * Portions of the development of this software were funded by
2300b3ed16SGreg Kroah-Hartman  * Intersil Corporation as part of PRISM(R) chipset product development.
2400b3ed16SGreg Kroah-Hartman  *
2500b3ed16SGreg Kroah-Hartman  * --------------------------------------------------------------------
2600b3ed16SGreg Kroah-Hartman  *
2700b3ed16SGreg Kroah-Hartman  * This file defines the functions that perform Ethernet to/from
2800b3ed16SGreg Kroah-Hartman  * 802.11 frame conversions.
2900b3ed16SGreg Kroah-Hartman  *
3000b3ed16SGreg Kroah-Hartman  * --------------------------------------------------------------------
3182eaca7dSMoritz Muehlenhoff  *
324a55218eSPranjal Bhor  *================================================================
334a55218eSPranjal Bhor  */
3400b3ed16SGreg Kroah-Hartman 
3500b3ed16SGreg Kroah-Hartman #include <linux/module.h>
3600b3ed16SGreg Kroah-Hartman #include <linux/kernel.h>
3700b3ed16SGreg Kroah-Hartman #include <linux/sched.h>
3800b3ed16SGreg Kroah-Hartman #include <linux/types.h>
3900b3ed16SGreg Kroah-Hartman #include <linux/skbuff.h>
4000b3ed16SGreg Kroah-Hartman #include <linux/slab.h>
4100b3ed16SGreg Kroah-Hartman #include <linux/wireless.h>
4200b3ed16SGreg Kroah-Hartman #include <linux/netdevice.h>
4300b3ed16SGreg Kroah-Hartman #include <linux/etherdevice.h>
4400b3ed16SGreg Kroah-Hartman #include <linux/if_ether.h>
45ae26230bSMoritz Muehlenhoff #include <linux/byteorder/generic.h>
4600b3ed16SGreg Kroah-Hartman 
4700b3ed16SGreg Kroah-Hartman #include <asm/byteorder.h>
4800b3ed16SGreg Kroah-Hartman 
4900b3ed16SGreg Kroah-Hartman #include "p80211types.h"
5000b3ed16SGreg Kroah-Hartman #include "p80211hdr.h"
5100b3ed16SGreg Kroah-Hartman #include "p80211conv.h"
5200b3ed16SGreg Kroah-Hartman #include "p80211mgmt.h"
5300b3ed16SGreg Kroah-Hartman #include "p80211msg.h"
5400b3ed16SGreg Kroah-Hartman #include "p80211netdev.h"
5500b3ed16SGreg Kroah-Hartman #include "p80211ioctl.h"
5600b3ed16SGreg Kroah-Hartman #include "p80211req.h"
5700b3ed16SGreg Kroah-Hartman 
5878822dc3SClaudiu Beznea static const u8 oui_rfc1042[] = { 0x00, 0x00, 0x00 };
5978822dc3SClaudiu Beznea static const u8 oui_8021h[] = { 0x00, 0x00, 0xf8 };
6000b3ed16SGreg Kroah-Hartman 
6100b3ed16SGreg Kroah-Hartman /*----------------------------------------------------------------
6200b3ed16SGreg Kroah-Hartman  * p80211pb_ether_to_80211
6300b3ed16SGreg Kroah-Hartman  *
6400b3ed16SGreg Kroah-Hartman  * Uses the contents of the ether frame and the etherconv setting
6500b3ed16SGreg Kroah-Hartman  * to build the elements of the 802.11 frame.
6600b3ed16SGreg Kroah-Hartman  *
6700b3ed16SGreg Kroah-Hartman  * We don't actually set
6800b3ed16SGreg Kroah-Hartman  * up the frame header here.  That's the MAC's job.  We're only handling
6900b3ed16SGreg Kroah-Hartman  * conversion of DIXII or 802.3+LLC frames to something that works
7000b3ed16SGreg Kroah-Hartman  * with 802.11.
7100b3ed16SGreg Kroah-Hartman  *
7200b3ed16SGreg Kroah-Hartman  * Note -- 802.11 header is NOT part of the skb.  Likewise, the 802.11
7300b3ed16SGreg Kroah-Hartman  *         FCS is also not present and will need to be added elsewhere.
7400b3ed16SGreg Kroah-Hartman  *
7500b3ed16SGreg Kroah-Hartman  * Arguments:
7600b3ed16SGreg Kroah-Hartman  *	ethconv		Conversion type to perform
7700b3ed16SGreg Kroah-Hartman  *	skb		skbuff containing the ether frame
7800b3ed16SGreg Kroah-Hartman  *       p80211_hdr      802.11 header
7900b3ed16SGreg Kroah-Hartman  *
8000b3ed16SGreg Kroah-Hartman  * Returns:
8100b3ed16SGreg Kroah-Hartman  *	0 on success, non-zero otherwise
8200b3ed16SGreg Kroah-Hartman  *
8300b3ed16SGreg Kroah-Hartman  * Call context:
8400b3ed16SGreg Kroah-Hartman  *	May be called in interrupt or non-interrupt context
854a55218eSPranjal Bhor  *----------------------------------------------------------------
864a55218eSPranjal Bhor  */
skb_ether_to_p80211(struct wlandevice * wlandev,u32 ethconv,struct sk_buff * skb,struct p80211_hdr * p80211_hdr,struct p80211_metawep * p80211_wep)87c9573a8dSsayli karnik int skb_ether_to_p80211(struct wlandevice *wlandev, u32 ethconv,
886277fbfdSKees Cook 			struct sk_buff *skb, struct p80211_hdr *p80211_hdr,
8951e4896aSEdgardo Hames 			struct p80211_metawep *p80211_wep)
9000b3ed16SGreg Kroah-Hartman {
91f474f5e5SEbru Akagunduz 	__le16 fc;
92aaad4303SSolomon Peachy 	u16 proto;
9351e4896aSEdgardo Hames 	struct wlan_ethhdr e_hdr;
9451e4896aSEdgardo Hames 	struct wlan_llc *e_llc;
9551e4896aSEdgardo Hames 	struct wlan_snap *e_snap;
9600b3ed16SGreg Kroah-Hartman 	int foo;
9700b3ed16SGreg Kroah-Hartman 
9800b3ed16SGreg Kroah-Hartman 	memcpy(&e_hdr, skb->data, sizeof(e_hdr));
9900b3ed16SGreg Kroah-Hartman 
10000b3ed16SGreg Kroah-Hartman 	if (skb->len <= 0) {
101a7cf7baeSMoritz Muehlenhoff 		pr_debug("zero-length skb!\n");
10200b3ed16SGreg Kroah-Hartman 		return 1;
10300b3ed16SGreg Kroah-Hartman 	}
10400b3ed16SGreg Kroah-Hartman 
10500b3ed16SGreg Kroah-Hartman 	if (ethconv == WLAN_ETHCONV_ENCAP) {	/* simplest case */
106a7cf7baeSMoritz Muehlenhoff 		pr_debug("ENCAP len: %d\n", skb->len);
10700b3ed16SGreg Kroah-Hartman 		/* here, we don't care what kind of ether frm. Just stick it */
10800b3ed16SGreg Kroah-Hartman 		/*  in the 80211 payload */
10900b3ed16SGreg Kroah-Hartman 		/* which is to say, leave the skb alone. */
11000b3ed16SGreg Kroah-Hartman 	} else {
11100b3ed16SGreg Kroah-Hartman 		/* step 1: classify ether frame, DIX or 802.3? */
11200b3ed16SGreg Kroah-Hartman 		proto = ntohs(e_hdr.type);
1134c6b0ec2SHari Prasath Gujulan Elango 		if (proto <= ETH_DATA_LEN) {
114a7cf7baeSMoritz Muehlenhoff 			pr_debug("802.3 len: %d\n", skb->len);
11500b3ed16SGreg Kroah-Hartman 			/* codes <= 1500 reserved for 802.3 lengths */
11600b3ed16SGreg Kroah-Hartman 			/* it's 802.3, pass ether payload unchanged,  */
11700b3ed16SGreg Kroah-Hartman 
11800b3ed16SGreg Kroah-Hartman 			/* trim off ethernet header */
119242850f4SAnish Bhatt 			skb_pull(skb, ETH_HLEN);
12000b3ed16SGreg Kroah-Hartman 
12100b3ed16SGreg Kroah-Hartman 			/*   leave off any PAD octets.  */
12200b3ed16SGreg Kroah-Hartman 			skb_trim(skb, proto);
12300b3ed16SGreg Kroah-Hartman 		} else {
124a7cf7baeSMoritz Muehlenhoff 			pr_debug("DIXII len: %d\n", skb->len);
12500b3ed16SGreg Kroah-Hartman 			/* it's DIXII, time for some conversion */
12600b3ed16SGreg Kroah-Hartman 
12700b3ed16SGreg Kroah-Hartman 			/* trim off ethernet header */
128242850f4SAnish Bhatt 			skb_pull(skb, ETH_HLEN);
12900b3ed16SGreg Kroah-Hartman 
13000b3ed16SGreg Kroah-Hartman 			/* tack on SNAP */
131d58ff351SJohannes Berg 			e_snap = skb_push(skb, sizeof(struct wlan_snap));
13200b3ed16SGreg Kroah-Hartman 			e_snap->type = htons(proto);
13325845388SPranjal Bhor 			if (ethconv == WLAN_ETHCONV_8021h &&
13425845388SPranjal Bhor 			    p80211_stt_findproto(proto)) {
13582eaca7dSMoritz Muehlenhoff 				memcpy(e_snap->oui, oui_8021h,
13682eaca7dSMoritz Muehlenhoff 				       WLAN_IEEE_OUI_LEN);
13700b3ed16SGreg Kroah-Hartman 			} else {
13882eaca7dSMoritz Muehlenhoff 				memcpy(e_snap->oui, oui_rfc1042,
13982eaca7dSMoritz Muehlenhoff 				       WLAN_IEEE_OUI_LEN);
14000b3ed16SGreg Kroah-Hartman 			}
14100b3ed16SGreg Kroah-Hartman 
14200b3ed16SGreg Kroah-Hartman 			/* tack on llc */
143d58ff351SJohannes Berg 			e_llc = skb_push(skb, sizeof(struct wlan_llc));
14400b3ed16SGreg Kroah-Hartman 			e_llc->dsap = 0xAA;	/* SNAP, see IEEE 802 */
14500b3ed16SGreg Kroah-Hartman 			e_llc->ssap = 0xAA;
14600b3ed16SGreg Kroah-Hartman 			e_llc->ctl = 0x03;
14700b3ed16SGreg Kroah-Hartman 		}
14800b3ed16SGreg Kroah-Hartman 	}
14900b3ed16SGreg Kroah-Hartman 
15000b3ed16SGreg Kroah-Hartman 	/* Set up the 802.11 header */
15100b3ed16SGreg Kroah-Hartman 	/* It's a data frame */
152ae26230bSMoritz Muehlenhoff 	fc = cpu_to_le16(WLAN_SET_FC_FTYPE(WLAN_FTYPE_DATA) |
15300b3ed16SGreg Kroah-Hartman 			 WLAN_SET_FC_FSTYPE(WLAN_FSTYPE_DATAONLY));
15400b3ed16SGreg Kroah-Hartman 
15500b3ed16SGreg Kroah-Hartman 	switch (wlandev->macmode) {
15600b3ed16SGreg Kroah-Hartman 	case WLAN_MACMODE_IBSS_STA:
15786a0727bSKees Cook 		memcpy(p80211_hdr->address1, &e_hdr.daddr, ETH_ALEN);
15886a0727bSKees Cook 		memcpy(p80211_hdr->address2, wlandev->netdev->dev_addr, ETH_ALEN);
15986a0727bSKees Cook 		memcpy(p80211_hdr->address3, wlandev->bssid, ETH_ALEN);
16000b3ed16SGreg Kroah-Hartman 		break;
16100b3ed16SGreg Kroah-Hartman 	case WLAN_MACMODE_ESS_STA:
162ae26230bSMoritz Muehlenhoff 		fc |= cpu_to_le16(WLAN_SET_FC_TODS(1));
16386a0727bSKees Cook 		memcpy(p80211_hdr->address1, wlandev->bssid, ETH_ALEN);
16486a0727bSKees Cook 		memcpy(p80211_hdr->address2, wlandev->netdev->dev_addr, ETH_ALEN);
16586a0727bSKees Cook 		memcpy(p80211_hdr->address3, &e_hdr.daddr, ETH_ALEN);
16600b3ed16SGreg Kroah-Hartman 		break;
16700b3ed16SGreg Kroah-Hartman 	case WLAN_MACMODE_ESS_AP:
168ae26230bSMoritz Muehlenhoff 		fc |= cpu_to_le16(WLAN_SET_FC_FROMDS(1));
16986a0727bSKees Cook 		memcpy(p80211_hdr->address1, &e_hdr.daddr, ETH_ALEN);
17086a0727bSKees Cook 		memcpy(p80211_hdr->address2, wlandev->bssid, ETH_ALEN);
17186a0727bSKees Cook 		memcpy(p80211_hdr->address3, &e_hdr.saddr, ETH_ALEN);
17200b3ed16SGreg Kroah-Hartman 		break;
17300b3ed16SGreg Kroah-Hartman 	default:
17402d9b1ebSVitaly Osipov 		netdev_err(wlandev->netdev,
17582eaca7dSMoritz Muehlenhoff 			   "Error: Converting eth to wlan in unknown mode.\n");
17600b3ed16SGreg Kroah-Hartman 		return 1;
17700b3ed16SGreg Kroah-Hartman 	}
17800b3ed16SGreg Kroah-Hartman 
17900b3ed16SGreg Kroah-Hartman 	p80211_wep->data = NULL;
18000b3ed16SGreg Kroah-Hartman 
18125845388SPranjal Bhor 	if ((wlandev->hostwep & HOSTWEP_PRIVACYINVOKED) &&
18225845388SPranjal Bhor 	    (wlandev->hostwep & HOSTWEP_ENCRYPT)) {
18382eaca7dSMoritz Muehlenhoff 		/* XXXX need to pick keynum other than default? */
18400b3ed16SGreg Kroah-Hartman 
18500b3ed16SGreg Kroah-Hartman 		p80211_wep->data = kmalloc(skb->len, GFP_ATOMIC);
1864bff39dfSGujulan Elango, Hari Prasath (H.) 		if (!p80211_wep->data)
1874bff39dfSGujulan Elango, Hari Prasath (H.) 			return -ENOMEM;
1885dd8acc8SSvenne Krap 		foo = wep_encrypt(wlandev, skb->data, p80211_wep->data,
18900b3ed16SGreg Kroah-Hartman 				  skb->len,
1908954ef90SGargi Sharma 				  wlandev->hostwep & HOSTWEP_DEFAULTKEY_MASK,
1915dd8acc8SSvenne Krap 				  p80211_wep->iv, p80211_wep->icv);
1925dd8acc8SSvenne Krap 		if (foo) {
19302d9b1ebSVitaly Osipov 			netdev_warn(wlandev->netdev,
19482eaca7dSMoritz Muehlenhoff 				    "Host en-WEP failed, dropping frame (%d).\n",
19582eaca7dSMoritz Muehlenhoff 				    foo);
196b5956dd2SLynn Lei 			kfree(p80211_wep->data);
19700b3ed16SGreg Kroah-Hartman 			return 2;
19800b3ed16SGreg Kroah-Hartman 		}
199ae26230bSMoritz Muehlenhoff 		fc |= cpu_to_le16(WLAN_SET_FC_ISWEP(1));
20000b3ed16SGreg Kroah-Hartman 	}
20100b3ed16SGreg Kroah-Hartman 
20282eaca7dSMoritz Muehlenhoff 	/*      skb->nh.raw = skb->data; */
20300b3ed16SGreg Kroah-Hartman 
20486a0727bSKees Cook 	p80211_hdr->frame_control = fc;
20586a0727bSKees Cook 	p80211_hdr->duration_id = 0;
20686a0727bSKees Cook 	p80211_hdr->sequence_control = 0;
20700b3ed16SGreg Kroah-Hartman 
20800b3ed16SGreg Kroah-Hartman 	return 0;
20900b3ed16SGreg Kroah-Hartman }
21000b3ed16SGreg Kroah-Hartman 
21100b3ed16SGreg Kroah-Hartman /* jkriegl: from orinoco, modified */
orinoco_spy_gather(struct wlandevice * wlandev,char * mac,struct p80211_rxmeta * rxmeta)212c9573a8dSsayli karnik static void orinoco_spy_gather(struct wlandevice *wlandev, char *mac,
21351e4896aSEdgardo Hames 			       struct p80211_rxmeta *rxmeta)
21400b3ed16SGreg Kroah-Hartman {
21500b3ed16SGreg Kroah-Hartman 	int i;
21600b3ed16SGreg Kroah-Hartman 
21700b3ed16SGreg Kroah-Hartman 	/* Gather wireless spy statistics: for each packet, compare the
2184a55218eSPranjal Bhor 	 * source address with out list, and if match, get the stats...
2194a55218eSPranjal Bhor 	 */
22000b3ed16SGreg Kroah-Hartman 
22100b3ed16SGreg Kroah-Hartman 	for (i = 0; i < wlandev->spy_number; i++) {
22200b3ed16SGreg Kroah-Hartman 		if (!memcmp(wlandev->spy_address[i], mac, ETH_ALEN)) {
22300b3ed16SGreg Kroah-Hartman 			wlandev->spy_stat[i].level = rxmeta->signal;
22400b3ed16SGreg Kroah-Hartman 			wlandev->spy_stat[i].noise = rxmeta->noise;
22582eaca7dSMoritz Muehlenhoff 			wlandev->spy_stat[i].qual =
22682eaca7dSMoritz Muehlenhoff 			    (rxmeta->signal >
22782eaca7dSMoritz Muehlenhoff 			     rxmeta->noise) ? (rxmeta->signal -
22882eaca7dSMoritz Muehlenhoff 					       rxmeta->noise) : 0;
22900b3ed16SGreg Kroah-Hartman 			wlandev->spy_stat[i].updated = 0x7;
23000b3ed16SGreg Kroah-Hartman 		}
23100b3ed16SGreg Kroah-Hartman 	}
23200b3ed16SGreg Kroah-Hartman }
23300b3ed16SGreg Kroah-Hartman 
23400b3ed16SGreg Kroah-Hartman /*----------------------------------------------------------------
23500b3ed16SGreg Kroah-Hartman  * p80211pb_80211_to_ether
23600b3ed16SGreg Kroah-Hartman  *
23700b3ed16SGreg Kroah-Hartman  * Uses the contents of a received 802.11 frame and the etherconv
23800b3ed16SGreg Kroah-Hartman  * setting to build an ether frame.
23900b3ed16SGreg Kroah-Hartman  *
24000b3ed16SGreg Kroah-Hartman  * This function extracts the src and dest address from the 802.11
24100b3ed16SGreg Kroah-Hartman  * frame to use in the construction of the eth frame.
24200b3ed16SGreg Kroah-Hartman  *
24300b3ed16SGreg Kroah-Hartman  * Arguments:
24400b3ed16SGreg Kroah-Hartman  *	ethconv		Conversion type to perform
24500b3ed16SGreg Kroah-Hartman  *	skb		Packet buffer containing the 802.11 frame
24600b3ed16SGreg Kroah-Hartman  *
24700b3ed16SGreg Kroah-Hartman  * Returns:
24800b3ed16SGreg Kroah-Hartman  *	0 on success, non-zero otherwise
24900b3ed16SGreg Kroah-Hartman  *
25000b3ed16SGreg Kroah-Hartman  * Call context:
25100b3ed16SGreg Kroah-Hartman  *	May be called in interrupt or non-interrupt context
2524a55218eSPranjal Bhor  *----------------------------------------------------------------
2534a55218eSPranjal Bhor  */
skb_p80211_to_ether(struct wlandevice * wlandev,u32 ethconv,struct sk_buff * skb)254c9573a8dSsayli karnik int skb_p80211_to_ether(struct wlandevice *wlandev, u32 ethconv,
25582eaca7dSMoritz Muehlenhoff 			struct sk_buff *skb)
25600b3ed16SGreg Kroah-Hartman {
25719066982SSergio Paracuellos 	struct net_device *netdev = wlandev->netdev;
258aaad4303SSolomon Peachy 	u16 fc;
259aaad4303SSolomon Peachy 	unsigned int payload_length;
260aaad4303SSolomon Peachy 	unsigned int payload_offset;
261242850f4SAnish Bhatt 	u8 daddr[ETH_ALEN];
262242850f4SAnish Bhatt 	u8 saddr[ETH_ALEN];
2636277fbfdSKees Cook 	struct p80211_hdr *w_hdr;
26451e4896aSEdgardo Hames 	struct wlan_ethhdr *e_hdr;
26551e4896aSEdgardo Hames 	struct wlan_llc *e_llc;
26651e4896aSEdgardo Hames 	struct wlan_snap *e_snap;
26700b3ed16SGreg Kroah-Hartman 
26800b3ed16SGreg Kroah-Hartman 	int foo;
26900b3ed16SGreg Kroah-Hartman 
27000b3ed16SGreg Kroah-Hartman 	payload_length = skb->len - WLAN_HDR_A3_LEN - WLAN_CRC_LEN;
27100b3ed16SGreg Kroah-Hartman 	payload_offset = WLAN_HDR_A3_LEN;
27200b3ed16SGreg Kroah-Hartman 
2736277fbfdSKees Cook 	w_hdr = (struct p80211_hdr *)skb->data;
27400b3ed16SGreg Kroah-Hartman 
27500b3ed16SGreg Kroah-Hartman 	/* setup some vars for convenience */
27686a0727bSKees Cook 	fc = le16_to_cpu(w_hdr->frame_control);
27700b3ed16SGreg Kroah-Hartman 	if ((WLAN_GET_FC_TODS(fc) == 0) && (WLAN_GET_FC_FROMDS(fc) == 0)) {
27886a0727bSKees Cook 		ether_addr_copy(daddr, w_hdr->address1);
27986a0727bSKees Cook 		ether_addr_copy(saddr, w_hdr->address2);
28025845388SPranjal Bhor 	} else if ((WLAN_GET_FC_TODS(fc) == 0) &&
28125845388SPranjal Bhor 		   (WLAN_GET_FC_FROMDS(fc) == 1)) {
28286a0727bSKees Cook 		ether_addr_copy(daddr, w_hdr->address1);
28386a0727bSKees Cook 		ether_addr_copy(saddr, w_hdr->address3);
28425845388SPranjal Bhor 	} else if ((WLAN_GET_FC_TODS(fc) == 1) &&
28525845388SPranjal Bhor 		   (WLAN_GET_FC_FROMDS(fc) == 0)) {
28686a0727bSKees Cook 		ether_addr_copy(daddr, w_hdr->address3);
28786a0727bSKees Cook 		ether_addr_copy(saddr, w_hdr->address2);
28800b3ed16SGreg Kroah-Hartman 	} else {
28900b3ed16SGreg Kroah-Hartman 		payload_offset = WLAN_HDR_A4_LEN;
2901f9e9ce1SRoel Kluin 		if (payload_length < WLAN_HDR_A4_LEN - WLAN_HDR_A3_LEN) {
29102d9b1ebSVitaly Osipov 			netdev_err(netdev, "A4 frame too short!\n");
29200b3ed16SGreg Kroah-Hartman 			return 1;
29300b3ed16SGreg Kroah-Hartman 		}
2941f9e9ce1SRoel Kluin 		payload_length -= (WLAN_HDR_A4_LEN - WLAN_HDR_A3_LEN);
29586a0727bSKees Cook 		ether_addr_copy(daddr, w_hdr->address3);
29686a0727bSKees Cook 		ether_addr_copy(saddr, w_hdr->address4);
29700b3ed16SGreg Kroah-Hartman 	}
29800b3ed16SGreg Kroah-Hartman 
29900b3ed16SGreg Kroah-Hartman 	/* perform de-wep if necessary.. */
30025845388SPranjal Bhor 	if ((wlandev->hostwep & HOSTWEP_PRIVACYINVOKED) &&
30125845388SPranjal Bhor 	    WLAN_GET_FC_ISWEP(fc) &&
30225845388SPranjal Bhor 	    (wlandev->hostwep & HOSTWEP_DECRYPT)) {
30300b3ed16SGreg Kroah-Hartman 		if (payload_length <= 8) {
30402d9b1ebSVitaly Osipov 			netdev_err(netdev,
30502d9b1ebSVitaly Osipov 				   "WEP frame too short (%u).\n", skb->len);
30600b3ed16SGreg Kroah-Hartman 			return 1;
30700b3ed16SGreg Kroah-Hartman 		}
3085dd8acc8SSvenne Krap 		foo = wep_decrypt(wlandev, skb->data + payload_offset + 4,
30900b3ed16SGreg Kroah-Hartman 				  payload_length - 8, -1,
31000b3ed16SGreg Kroah-Hartman 				  skb->data + payload_offset,
31182eaca7dSMoritz Muehlenhoff 				  skb->data + payload_offset +
3125dd8acc8SSvenne Krap 				  payload_length - 4);
3135dd8acc8SSvenne Krap 		if (foo) {
31400b3ed16SGreg Kroah-Hartman 			/* de-wep failed, drop skb. */
31575f49e07SMithlesh Thukral 			pr_debug("Host de-WEP failed, dropping frame (%d).\n",
31682eaca7dSMoritz Muehlenhoff 				 foo);
31700b3ed16SGreg Kroah-Hartman 			wlandev->rx.decrypt_err++;
31800b3ed16SGreg Kroah-Hartman 			return 2;
31900b3ed16SGreg Kroah-Hartman 		}
32000b3ed16SGreg Kroah-Hartman 
32100b3ed16SGreg Kroah-Hartman 		/* subtract the IV+ICV length off the payload */
32200b3ed16SGreg Kroah-Hartman 		payload_length -= 8;
32300b3ed16SGreg Kroah-Hartman 		/* chop off the IV */
32400b3ed16SGreg Kroah-Hartman 		skb_pull(skb, 4);
32500b3ed16SGreg Kroah-Hartman 		/* chop off the ICV. */
32600b3ed16SGreg Kroah-Hartman 		skb_trim(skb, skb->len - 4);
32700b3ed16SGreg Kroah-Hartman 
32800b3ed16SGreg Kroah-Hartman 		wlandev->rx.decrypt++;
32900b3ed16SGreg Kroah-Hartman 	}
33000b3ed16SGreg Kroah-Hartman 
33151e4896aSEdgardo Hames 	e_hdr = (struct wlan_ethhdr *)(skb->data + payload_offset);
33200b3ed16SGreg Kroah-Hartman 
33351e4896aSEdgardo Hames 	e_llc = (struct wlan_llc *)(skb->data + payload_offset);
33482eaca7dSMoritz Muehlenhoff 	e_snap =
3354eb28f71SJohan Meiring 	    (struct wlan_snap *)(skb->data + payload_offset +
3364eb28f71SJohan Meiring 		sizeof(struct wlan_llc));
33700b3ed16SGreg Kroah-Hartman 
33800b3ed16SGreg Kroah-Hartman 	/* Test for the various encodings */
33951e4896aSEdgardo Hames 	if ((payload_length >= sizeof(struct wlan_ethhdr)) &&
34000b3ed16SGreg Kroah-Hartman 	    (e_llc->dsap != 0xaa || e_llc->ssap != 0xaa) &&
341242850f4SAnish Bhatt 	    ((!ether_addr_equal_unaligned(daddr, e_hdr->daddr)) ||
342242850f4SAnish Bhatt 	     (!ether_addr_equal_unaligned(saddr, e_hdr->saddr)))) {
343a7cf7baeSMoritz Muehlenhoff 		pr_debug("802.3 ENCAP len: %d\n", payload_length);
34400b3ed16SGreg Kroah-Hartman 		/* 802.3 Encapsulated */
34533ce0ca6SRichard Kennedy 		/* Test for an overlength frame */
346242850f4SAnish Bhatt 		if (payload_length > (netdev->mtu + ETH_HLEN)) {
34733ce0ca6SRichard Kennedy 			/* A bogus length ethfrm has been encap'd. */
34833ce0ca6SRichard Kennedy 			/* Is someone trying an oflow attack? */
34902d9b1ebSVitaly Osipov 			netdev_err(netdev, "ENCAP frame too large (%d > %d)\n",
350242850f4SAnish Bhatt 				   payload_length, netdev->mtu + ETH_HLEN);
35133ce0ca6SRichard Kennedy 			return 1;
35233ce0ca6SRichard Kennedy 		}
35300b3ed16SGreg Kroah-Hartman 
35400b3ed16SGreg Kroah-Hartman 		/* Chop off the 802.11 header.  it's already sane. */
35500b3ed16SGreg Kroah-Hartman 		skb_pull(skb, payload_offset);
35600b3ed16SGreg Kroah-Hartman 		/* chop off the 802.11 CRC */
35700b3ed16SGreg Kroah-Hartman 		skb_trim(skb, skb->len - WLAN_CRC_LEN);
35800b3ed16SGreg Kroah-Hartman 
3594eb28f71SJohan Meiring 	} else if ((payload_length >= sizeof(struct wlan_llc) +
36025845388SPranjal Bhor 		sizeof(struct wlan_snap)) &&
36125845388SPranjal Bhor 		(e_llc->dsap == 0xaa) &&
36225845388SPranjal Bhor 		(e_llc->ssap == 0xaa) &&
36325845388SPranjal Bhor 		(e_llc->ctl == 0x03) &&
36425845388SPranjal Bhor 		   (((memcmp(e_snap->oui, oui_rfc1042,
36525845388SPranjal Bhor 		   WLAN_IEEE_OUI_LEN) == 0) &&
36625845388SPranjal Bhor 		   (ethconv == WLAN_ETHCONV_8021h) &&
367606ea2bdSAlexander Alemayhu 		   (p80211_stt_findproto(be16_to_cpu(e_snap->type)))) ||
36825845388SPranjal Bhor 		   (memcmp(e_snap->oui, oui_rfc1042, WLAN_IEEE_OUI_LEN) !=
36982eaca7dSMoritz Muehlenhoff 			0))) {
370a7cf7baeSMoritz Muehlenhoff 		pr_debug("SNAP+RFC1042 len: %d\n", payload_length);
37100b3ed16SGreg Kroah-Hartman 		/* it's a SNAP + RFC1042 frame && protocol is in STT */
37200b3ed16SGreg Kroah-Hartman 		/* build 802.3 + RFC1042 */
37300b3ed16SGreg Kroah-Hartman 
37433ce0ca6SRichard Kennedy 		/* Test for an overlength frame */
37533ce0ca6SRichard Kennedy 		if (payload_length > netdev->mtu) {
37633ce0ca6SRichard Kennedy 			/* A bogus length ethfrm has been sent. */
37733ce0ca6SRichard Kennedy 			/* Is someone trying an oflow attack? */
37802d9b1ebSVitaly Osipov 			netdev_err(netdev, "SNAP frame too large (%d > %d)\n",
37933ce0ca6SRichard Kennedy 				   payload_length, netdev->mtu);
38033ce0ca6SRichard Kennedy 			return 1;
38133ce0ca6SRichard Kennedy 		}
38233ce0ca6SRichard Kennedy 
38300b3ed16SGreg Kroah-Hartman 		/* chop 802.11 header from skb. */
38400b3ed16SGreg Kroah-Hartman 		skb_pull(skb, payload_offset);
38500b3ed16SGreg Kroah-Hartman 
38600b3ed16SGreg Kroah-Hartman 		/* create 802.3 header at beginning of skb. */
387d58ff351SJohannes Berg 		e_hdr = skb_push(skb, ETH_HLEN);
388242850f4SAnish Bhatt 		ether_addr_copy(e_hdr->daddr, daddr);
389242850f4SAnish Bhatt 		ether_addr_copy(e_hdr->saddr, saddr);
39000b3ed16SGreg Kroah-Hartman 		e_hdr->type = htons(payload_length);
39100b3ed16SGreg Kroah-Hartman 
39200b3ed16SGreg Kroah-Hartman 		/* chop off the 802.11 CRC */
39300b3ed16SGreg Kroah-Hartman 		skb_trim(skb, skb->len - WLAN_CRC_LEN);
39400b3ed16SGreg Kroah-Hartman 
3954eb28f71SJohan Meiring 	} else if ((payload_length >= sizeof(struct wlan_llc) +
39625845388SPranjal Bhor 		sizeof(struct wlan_snap)) &&
39725845388SPranjal Bhor 		(e_llc->dsap == 0xaa) &&
39825845388SPranjal Bhor 		(e_llc->ssap == 0xaa) &&
39925845388SPranjal Bhor 		(e_llc->ctl == 0x03)) {
400a7cf7baeSMoritz Muehlenhoff 		pr_debug("802.1h/RFC1042 len: %d\n", payload_length);
4014eb28f71SJohan Meiring 		/* it's an 802.1h frame || (an RFC1042 && protocol not in STT)
4024a55218eSPranjal Bhor 		 * build a DIXII + RFC894
4034a55218eSPranjal Bhor 		 */
40400b3ed16SGreg Kroah-Hartman 
40533ce0ca6SRichard Kennedy 		/* Test for an overlength frame */
4064eb28f71SJohan Meiring 		if ((payload_length - sizeof(struct wlan_llc) -
4074eb28f71SJohan Meiring 			sizeof(struct wlan_snap))
40833ce0ca6SRichard Kennedy 			> netdev->mtu) {
40933ce0ca6SRichard Kennedy 			/* A bogus length ethfrm has been sent. */
41033ce0ca6SRichard Kennedy 			/* Is someone trying an oflow attack? */
41102d9b1ebSVitaly Osipov 			netdev_err(netdev, "DIXII frame too large (%ld > %d)\n",
4122bc51572SMaya Nakamura 				   (long)(payload_length -
4134eb28f71SJohan Meiring 				   sizeof(struct wlan_llc) -
41451e4896aSEdgardo Hames 				   sizeof(struct wlan_snap)), netdev->mtu);
41533ce0ca6SRichard Kennedy 			return 1;
41633ce0ca6SRichard Kennedy 		}
41733ce0ca6SRichard Kennedy 
41800b3ed16SGreg Kroah-Hartman 		/* chop 802.11 header from skb. */
41900b3ed16SGreg Kroah-Hartman 		skb_pull(skb, payload_offset);
42000b3ed16SGreg Kroah-Hartman 
42100b3ed16SGreg Kroah-Hartman 		/* chop llc header from skb. */
42251e4896aSEdgardo Hames 		skb_pull(skb, sizeof(struct wlan_llc));
42300b3ed16SGreg Kroah-Hartman 
42400b3ed16SGreg Kroah-Hartman 		/* chop snap header from skb. */
42551e4896aSEdgardo Hames 		skb_pull(skb, sizeof(struct wlan_snap));
42600b3ed16SGreg Kroah-Hartman 
42700b3ed16SGreg Kroah-Hartman 		/* create 802.3 header at beginning of skb. */
428d58ff351SJohannes Berg 		e_hdr = skb_push(skb, ETH_HLEN);
42900b3ed16SGreg Kroah-Hartman 		e_hdr->type = e_snap->type;
430242850f4SAnish Bhatt 		ether_addr_copy(e_hdr->daddr, daddr);
431242850f4SAnish Bhatt 		ether_addr_copy(e_hdr->saddr, saddr);
43200b3ed16SGreg Kroah-Hartman 
43300b3ed16SGreg Kroah-Hartman 		/* chop off the 802.11 CRC */
43400b3ed16SGreg Kroah-Hartman 		skb_trim(skb, skb->len - WLAN_CRC_LEN);
43500b3ed16SGreg Kroah-Hartman 	} else {
436a7cf7baeSMoritz Muehlenhoff 		pr_debug("NON-ENCAP len: %d\n", payload_length);
43700b3ed16SGreg Kroah-Hartman 		/* any NON-ENCAP */
43800b3ed16SGreg Kroah-Hartman 		/* it's a generic 80211+LLC or IPX 'Raw 802.3' */
43900b3ed16SGreg Kroah-Hartman 		/*  build an 802.3 frame */
44000b3ed16SGreg Kroah-Hartman 		/* allocate space and setup hostbuf */
44100b3ed16SGreg Kroah-Hartman 
44233ce0ca6SRichard Kennedy 		/* Test for an overlength frame */
44333ce0ca6SRichard Kennedy 		if (payload_length > netdev->mtu) {
44433ce0ca6SRichard Kennedy 			/* A bogus length ethfrm has been sent. */
44533ce0ca6SRichard Kennedy 			/* Is someone trying an oflow attack? */
44602d9b1ebSVitaly Osipov 			netdev_err(netdev, "OTHER frame too large (%d > %d)\n",
44782eaca7dSMoritz Muehlenhoff 				   payload_length, netdev->mtu);
44833ce0ca6SRichard Kennedy 			return 1;
44933ce0ca6SRichard Kennedy 		}
45033ce0ca6SRichard Kennedy 
45100b3ed16SGreg Kroah-Hartman 		/* Chop off the 802.11 header. */
45200b3ed16SGreg Kroah-Hartman 		skb_pull(skb, payload_offset);
45300b3ed16SGreg Kroah-Hartman 
45400b3ed16SGreg Kroah-Hartman 		/* create 802.3 header at beginning of skb. */
455d58ff351SJohannes Berg 		e_hdr = skb_push(skb, ETH_HLEN);
456242850f4SAnish Bhatt 		ether_addr_copy(e_hdr->daddr, daddr);
457242850f4SAnish Bhatt 		ether_addr_copy(e_hdr->saddr, saddr);
45800b3ed16SGreg Kroah-Hartman 		e_hdr->type = htons(payload_length);
45900b3ed16SGreg Kroah-Hartman 
46000b3ed16SGreg Kroah-Hartman 		/* chop off the 802.11 CRC */
46100b3ed16SGreg Kroah-Hartman 		skb_trim(skb, skb->len - WLAN_CRC_LEN);
46200b3ed16SGreg Kroah-Hartman 	}
46300b3ed16SGreg Kroah-Hartman 
46433ce0ca6SRichard Kennedy 	/*
46533ce0ca6SRichard Kennedy 	 * Note that eth_type_trans() expects an skb w/ skb->data pointing
46633ce0ca6SRichard Kennedy 	 * at the MAC header, it then sets the following skb members:
46733ce0ca6SRichard Kennedy 	 * skb->mac_header,
46833ce0ca6SRichard Kennedy 	 * skb->data, and
46933ce0ca6SRichard Kennedy 	 * skb->pkt_type.
47033ce0ca6SRichard Kennedy 	 * It then _returns_ the value that _we're_ supposed to stuff in
47133ce0ca6SRichard Kennedy 	 * skb->protocol.  This is nuts.
47233ce0ca6SRichard Kennedy 	 */
47300b3ed16SGreg Kroah-Hartman 	skb->protocol = eth_type_trans(skb, netdev);
47400b3ed16SGreg Kroah-Hartman 
47500b3ed16SGreg Kroah-Hartman 	/* jkriegl: process signal and noise as set in hfa384x_int_rx() */
47600b3ed16SGreg Kroah-Hartman 	/* jkriegl: only process signal/noise if requested by iwspy */
47700b3ed16SGreg Kroah-Hartman 	if (wlandev->spy_number)
47882eaca7dSMoritz Muehlenhoff 		orinoco_spy_gather(wlandev, eth_hdr(skb)->h_source,
479173ffd09STim Collier 				   p80211skb_rxmeta(skb));
48000b3ed16SGreg Kroah-Hartman 
48100b3ed16SGreg Kroah-Hartman 	/* Free the metadata */
48200b3ed16SGreg Kroah-Hartman 	p80211skb_rxmeta_detach(skb);
48300b3ed16SGreg Kroah-Hartman 
48400b3ed16SGreg Kroah-Hartman 	return 0;
48500b3ed16SGreg Kroah-Hartman }
48600b3ed16SGreg Kroah-Hartman 
48700b3ed16SGreg Kroah-Hartman /*----------------------------------------------------------------
48800b3ed16SGreg Kroah-Hartman  * p80211_stt_findproto
48900b3ed16SGreg Kroah-Hartman  *
49000b3ed16SGreg Kroah-Hartman  * Searches the 802.1h Selective Translation Table for a given
49100b3ed16SGreg Kroah-Hartman  * protocol.
49200b3ed16SGreg Kroah-Hartman  *
49300b3ed16SGreg Kroah-Hartman  * Arguments:
4941a6dfce7SMasanari Iida  *	proto	protocol number (in host order) to search for.
49500b3ed16SGreg Kroah-Hartman  *
49600b3ed16SGreg Kroah-Hartman  * Returns:
49700b3ed16SGreg Kroah-Hartman  *	1 - if the table is empty or a match is found.
49800b3ed16SGreg Kroah-Hartman  *	0 - if the table is non-empty and a match is not found.
49900b3ed16SGreg Kroah-Hartman  *
50000b3ed16SGreg Kroah-Hartman  * Call context:
50100b3ed16SGreg Kroah-Hartman  *	May be called in interrupt or non-interrupt context
5024a55218eSPranjal Bhor  *----------------------------------------------------------------
5034a55218eSPranjal Bhor  */
p80211_stt_findproto(u16 proto)504aaad4303SSolomon Peachy int p80211_stt_findproto(u16 proto)
50500b3ed16SGreg Kroah-Hartman {
50600b3ed16SGreg Kroah-Hartman 	/* Always return found for now.  This is the behavior used by the */
50700b3ed16SGreg Kroah-Hartman 	/* Zoom Win95 driver when 802.1h mode is selected */
50800b3ed16SGreg Kroah-Hartman 	/* TODO: If necessary, add an actual search we'll probably
5094a55218eSPranjal Bhor 	 * need this to match the CMAC's way of doing things.
5104a55218eSPranjal Bhor 	 * Need to do some testing to confirm.
51100b3ed16SGreg Kroah-Hartman 	 */
51200b3ed16SGreg Kroah-Hartman 
5134c6b0ec2SHari Prasath Gujulan Elango 	if (proto == ETH_P_AARP)	/* APPLETALK */
51400b3ed16SGreg Kroah-Hartman 		return 1;
51500b3ed16SGreg Kroah-Hartman 
51600b3ed16SGreg Kroah-Hartman 	return 0;
51700b3ed16SGreg Kroah-Hartman }
51800b3ed16SGreg Kroah-Hartman 
51900b3ed16SGreg Kroah-Hartman /*----------------------------------------------------------------
52000b3ed16SGreg Kroah-Hartman  * p80211skb_rxmeta_detach
52100b3ed16SGreg Kroah-Hartman  *
52200b3ed16SGreg Kroah-Hartman  * Disconnects the frmmeta and rxmeta from an skb.
52300b3ed16SGreg Kroah-Hartman  *
52400b3ed16SGreg Kroah-Hartman  * Arguments:
52500b3ed16SGreg Kroah-Hartman  *	wlandev		The wlandev this skb belongs to.
52600b3ed16SGreg Kroah-Hartman  *	skb		The skb we're attaching to.
52700b3ed16SGreg Kroah-Hartman  *
52800b3ed16SGreg Kroah-Hartman  * Returns:
52900b3ed16SGreg Kroah-Hartman  *	0 on success, non-zero otherwise
53000b3ed16SGreg Kroah-Hartman  *
53100b3ed16SGreg Kroah-Hartman  * Call context:
53200b3ed16SGreg Kroah-Hartman  *	May be called in interrupt or non-interrupt context
5334a55218eSPranjal Bhor  *----------------------------------------------------------------
5344a55218eSPranjal Bhor  */
p80211skb_rxmeta_detach(struct sk_buff * skb)53582eaca7dSMoritz Muehlenhoff void p80211skb_rxmeta_detach(struct sk_buff *skb)
53600b3ed16SGreg Kroah-Hartman {
53751e4896aSEdgardo Hames 	struct p80211_rxmeta *rxmeta;
53851e4896aSEdgardo Hames 	struct p80211_frmmeta *frmmeta;
53900b3ed16SGreg Kroah-Hartman 
54000b3ed16SGreg Kroah-Hartman 	/* Sanity checks */
541d2305435SPranjal Bhor 	if (!skb) {	/* bad skb */
542a7cf7baeSMoritz Muehlenhoff 		pr_debug("Called w/ null skb.\n");
543311e24fbSDevendra Naga 		return;
54400b3ed16SGreg Kroah-Hartman 	}
545df6835d1STim Collier 	frmmeta = p80211skb_frmmeta(skb);
546d2305435SPranjal Bhor 	if (!frmmeta) {	/* no magic */
547a7cf7baeSMoritz Muehlenhoff 		pr_debug("Called w/ bad frmmeta magic.\n");
548311e24fbSDevendra Naga 		return;
54900b3ed16SGreg Kroah-Hartman 	}
55000b3ed16SGreg Kroah-Hartman 	rxmeta = frmmeta->rx;
551d2305435SPranjal Bhor 	if (!rxmeta) {	/* bad meta ptr */
552a7cf7baeSMoritz Muehlenhoff 		pr_debug("Called w/ bad rxmeta ptr.\n");
553311e24fbSDevendra Naga 		return;
55400b3ed16SGreg Kroah-Hartman 	}
55500b3ed16SGreg Kroah-Hartman 
55600b3ed16SGreg Kroah-Hartman 	/* Free rxmeta */
55700b3ed16SGreg Kroah-Hartman 	kfree(rxmeta);
55800b3ed16SGreg Kroah-Hartman 
55900b3ed16SGreg Kroah-Hartman 	/* Clear skb->cb */
56000b3ed16SGreg Kroah-Hartman 	memset(skb->cb, 0, sizeof(skb->cb));
56100b3ed16SGreg Kroah-Hartman }
56200b3ed16SGreg Kroah-Hartman 
56300b3ed16SGreg Kroah-Hartman /*----------------------------------------------------------------
56400b3ed16SGreg Kroah-Hartman  * p80211skb_rxmeta_attach
56500b3ed16SGreg Kroah-Hartman  *
56600b3ed16SGreg Kroah-Hartman  * Allocates a p80211rxmeta structure, initializes it, and attaches
56700b3ed16SGreg Kroah-Hartman  * it to an skb.
56800b3ed16SGreg Kroah-Hartman  *
56900b3ed16SGreg Kroah-Hartman  * Arguments:
57000b3ed16SGreg Kroah-Hartman  *	wlandev		The wlandev this skb belongs to.
57100b3ed16SGreg Kroah-Hartman  *	skb		The skb we're attaching to.
57200b3ed16SGreg Kroah-Hartman  *
57300b3ed16SGreg Kroah-Hartman  * Returns:
57400b3ed16SGreg Kroah-Hartman  *	0 on success, non-zero otherwise
57500b3ed16SGreg Kroah-Hartman  *
57600b3ed16SGreg Kroah-Hartman  * Call context:
57700b3ed16SGreg Kroah-Hartman  *	May be called in interrupt or non-interrupt context
5784a55218eSPranjal Bhor  *----------------------------------------------------------------
5794a55218eSPranjal Bhor  */
p80211skb_rxmeta_attach(struct wlandevice * wlandev,struct sk_buff * skb)58082eaca7dSMoritz Muehlenhoff int p80211skb_rxmeta_attach(struct wlandevice *wlandev, struct sk_buff *skb)
58100b3ed16SGreg Kroah-Hartman {
58200b3ed16SGreg Kroah-Hartman 	int result = 0;
58351e4896aSEdgardo Hames 	struct p80211_rxmeta *rxmeta;
58451e4896aSEdgardo Hames 	struct p80211_frmmeta *frmmeta;
58500b3ed16SGreg Kroah-Hartman 
58600b3ed16SGreg Kroah-Hartman 	/* If these already have metadata, we error out! */
587173ffd09STim Collier 	if (p80211skb_rxmeta(skb)) {
58802d9b1ebSVitaly Osipov 		netdev_err(wlandev->netdev,
58902d9b1ebSVitaly Osipov 			   "%s: RXmeta already attached!\n", wlandev->name);
59000b3ed16SGreg Kroah-Hartman 		result = 0;
59100b3ed16SGreg Kroah-Hartman 		goto exit;
59200b3ed16SGreg Kroah-Hartman 	}
59300b3ed16SGreg Kroah-Hartman 
59400b3ed16SGreg Kroah-Hartman 	/* Allocate the rxmeta */
595229dbdc5SSergio Paracuellos 	rxmeta = kzalloc(sizeof(*rxmeta), GFP_ATOMIC);
59600b3ed16SGreg Kroah-Hartman 
597d2305435SPranjal Bhor 	if (!rxmeta) {
59800b3ed16SGreg Kroah-Hartman 		result = 1;
59900b3ed16SGreg Kroah-Hartman 		goto exit;
60000b3ed16SGreg Kroah-Hartman 	}
60100b3ed16SGreg Kroah-Hartman 
60200b3ed16SGreg Kroah-Hartman 	/* Initialize the rxmeta */
60300b3ed16SGreg Kroah-Hartman 	rxmeta->wlandev = wlandev;
60400b3ed16SGreg Kroah-Hartman 	rxmeta->hosttime = jiffies;
60500b3ed16SGreg Kroah-Hartman 
60600b3ed16SGreg Kroah-Hartman 	/* Overlay a frmmeta_t onto skb->cb */
60751e4896aSEdgardo Hames 	memset(skb->cb, 0, sizeof(struct p80211_frmmeta));
60851e4896aSEdgardo Hames 	frmmeta = (struct p80211_frmmeta *)(skb->cb);
60900b3ed16SGreg Kroah-Hartman 	frmmeta->magic = P80211_FRMMETA_MAGIC;
61000b3ed16SGreg Kroah-Hartman 	frmmeta->rx = rxmeta;
61100b3ed16SGreg Kroah-Hartman exit:
61200b3ed16SGreg Kroah-Hartman 	return result;
61300b3ed16SGreg Kroah-Hartman }
61400b3ed16SGreg Kroah-Hartman 
61500b3ed16SGreg Kroah-Hartman /*----------------------------------------------------------------
61600b3ed16SGreg Kroah-Hartman  * p80211skb_free
61700b3ed16SGreg Kroah-Hartman  *
61800b3ed16SGreg Kroah-Hartman  * Frees an entire p80211skb by checking and freeing the meta struct
61900b3ed16SGreg Kroah-Hartman  * and then freeing the skb.
62000b3ed16SGreg Kroah-Hartman  *
62100b3ed16SGreg Kroah-Hartman  * Arguments:
62200b3ed16SGreg Kroah-Hartman  *	wlandev		The wlandev this skb belongs to.
62300b3ed16SGreg Kroah-Hartman  *	skb		The skb we're attaching to.
62400b3ed16SGreg Kroah-Hartman  *
62500b3ed16SGreg Kroah-Hartman  * Returns:
62600b3ed16SGreg Kroah-Hartman  *	0 on success, non-zero otherwise
62700b3ed16SGreg Kroah-Hartman  *
62800b3ed16SGreg Kroah-Hartman  * Call context:
62900b3ed16SGreg Kroah-Hartman  *	May be called in interrupt or non-interrupt context
6304a55218eSPranjal Bhor  *----------------------------------------------------------------
6314a55218eSPranjal Bhor  */
p80211skb_free(struct wlandevice * wlandev,struct sk_buff * skb)63282eaca7dSMoritz Muehlenhoff void p80211skb_free(struct wlandevice *wlandev, struct sk_buff *skb)
63300b3ed16SGreg Kroah-Hartman {
63451e4896aSEdgardo Hames 	struct p80211_frmmeta *meta;
6358a251b55SMoritz Muehlenhoff 
636df6835d1STim Collier 	meta = p80211skb_frmmeta(skb);
63782eaca7dSMoritz Muehlenhoff 	if (meta && meta->rx)
63800b3ed16SGreg Kroah-Hartman 		p80211skb_rxmeta_detach(skb);
63982eaca7dSMoritz Muehlenhoff 	else
64002d9b1ebSVitaly Osipov 		netdev_err(wlandev->netdev,
64102d9b1ebSVitaly Osipov 			   "Freeing an skb (%p) w/ no frmmeta.\n", skb);
64200b3ed16SGreg Kroah-Hartman 	dev_kfree_skb(skb);
64300b3ed16SGreg Kroah-Hartman }
644