Release 4.14 net/mac80211/rx.c
/*
* Copyright 2002-2005, Instant802 Networks, Inc.
* Copyright 2005-2006, Devicescape Software, Inc.
* Copyright 2006-2007 Jiri Benc <jbenc@suse.cz>
* Copyright 2007-2010 Johannes Berg <johannes@sipsolutions.net>
* Copyright 2013-2014 Intel Mobile Communications GmbH
* Copyright(c) 2015 - 2017 Intel Deutschland GmbH
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <linux/jiffies.h>
#include <linux/slab.h>
#include <linux/kernel.h>
#include <linux/skbuff.h>
#include <linux/netdevice.h>
#include <linux/etherdevice.h>
#include <linux/rcupdate.h>
#include <linux/export.h>
#include <linux/bitops.h>
#include <net/mac80211.h>
#include <net/ieee80211_radiotap.h>
#include <asm/unaligned.h>
#include "ieee80211_i.h"
#include "driver-ops.h"
#include "led.h"
#include "mesh.h"
#include "wep.h"
#include "wpa.h"
#include "tkip.h"
#include "wme.h"
#include "rate.h"
static inline void ieee80211_rx_stats(struct net_device *dev, u32 len)
{
struct pcpu_sw_netstats *tstats = this_cpu_ptr(dev->tstats);
u64_stats_update_begin(&tstats->syncp);
tstats->rx_packets++;
tstats->rx_bytes += len;
u64_stats_update_end(&tstats->syncp);
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Johannes Berg | 54 | 100.00% | 1 | 100.00% |
Total | 54 | 100.00% | 1 | 100.00% |
static u8 *ieee80211_get_bssid(struct ieee80211_hdr *hdr, size_t len,
enum nl80211_iftype type)
{
__le16 fc = hdr->frame_control;
if (ieee80211_is_data(fc)) {
if (len < 24) /* drop incorrect hdr len (data) */
return NULL;
if (ieee80211_has_a4(fc))
return NULL;
if (ieee80211_has_tods(fc))
return hdr->addr1;
if (ieee80211_has_fromds(fc))
return hdr->addr2;
return hdr->addr3;
}
if (ieee80211_is_mgmt(fc)) {
if (len < 24) /* drop incorrect hdr len (mgmt) */
return NULL;
return hdr->addr3;
}
if (ieee80211_is_ctl(fc)) {
if (ieee80211_is_pspoll(fc))
return hdr->addr1;
if (ieee80211_is_back_req(fc)) {
switch (type) {
case NL80211_IFTYPE_STATION:
return hdr->addr2;
case NL80211_IFTYPE_AP:
case NL80211_IFTYPE_AP_VLAN:
return hdr->addr1;
default:
break; /* fall through to the return */
}
}
}
return NULL;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Johannes Berg | 169 | 100.00% | 1 | 100.00% |
Total | 169 | 100.00% | 1 | 100.00% |
/*
* monitor mode reception
*
* This function cleans up the SKB, i.e. it removes all the stuff
* only useful for monitoring.
*/
static void remove_monitor_info(struct sk_buff *skb,
unsigned int present_fcs_len,
unsigned int rtap_vendor_space)
{
if (present_fcs_len)
__pskb_trim(skb, skb->len - present_fcs_len);
__pskb_pull(skb, rtap_vendor_space);
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Johannes Berg | 40 | 97.56% | 5 | 83.33% |
Yi Zhu | 1 | 2.44% | 1 | 16.67% |
Total | 41 | 100.00% | 6 | 100.00% |
static inline bool should_drop_frame(struct sk_buff *skb, int present_fcs_len,
unsigned int rtap_vendor_space)
{
struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb);
struct ieee80211_hdr *hdr;
hdr = (void *)(skb->data + rtap_vendor_space);
if (status->flag & (RX_FLAG_FAILED_FCS_CRC |
RX_FLAG_FAILED_PLCP_CRC |
RX_FLAG_ONLY_MONITOR))
return true;
if (unlikely(skb->len < 16 + present_fcs_len + rtap_vendor_space))
return true;
if (ieee80211_is_ctl(hdr->frame_control) &&
!ieee80211_is_pspoll(hdr->frame_control) &&
!ieee80211_is_back_req(hdr->frame_control))
return true;
return false;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Johannes Berg | 89 | 78.07% | 6 | 54.55% |
Ron Rindjunsky | 10 | 8.77% | 2 | 18.18% |
Harvey Harrison | 8 | 7.02% | 1 | 9.09% |
Zhao, Gang | 5 | 4.39% | 1 | 9.09% |
Grzegorz Bajorski | 2 | 1.75% | 1 | 9.09% |
Total | 114 | 100.00% | 11 | 100.00% |
static int
ieee80211_rx_radiotap_hdrlen(struct ieee80211_local *local,
struct ieee80211_rx_status *status,
struct sk_buff *skb)
{
int len;
/* always present fields */
len = sizeof(struct ieee80211_radiotap_header) + 8;
/* allocate extra bitmaps */
if (status->chains)
len += 4 * hweight8(status->chains);
if (ieee80211_have_rx_timestamp(status)) {
len = ALIGN(len, 8);
len += 8;
}
if (ieee80211_hw_check(&local->hw, SIGNAL_DBM))
len += 1;
/* antenna field, if we don't have per-chain info */
if (!status->chains)
len += 1;
/* padding for RX_FLAGS if necessary */
len = ALIGN(len, 2);
if (status->encoding == RX_ENC_HT) /* HT info */
len += 3;
if (status->flag & RX_FLAG_AMPDU_DETAILS) {
len = ALIGN(len, 4);
len += 8;
}
if (status->encoding == RX_ENC_VHT) {
len = ALIGN(len, 2);
len += 12;
}
if (local->hw.radiotap_timestamp.units_pos >= 0) {
len = ALIGN(len, 8);
len += 12;
}
if (status->chains) {
/* antenna and antenna signal fields */
len += 2 * hweight8(status->chains);
}
if (status->flag & RX_FLAG_RADIOTAP_VENDOR_DATA) {
struct ieee80211_vendor_radiotap *rtap = (void *)skb->data;
/* vendor presence bitmap */
len += 4;
/* alignment for fixed 6-byte vendor data header */
len = ALIGN(len, 2);
/* vendor data header */
len += 6;
if (WARN_ON(rtap->align == 0))
rtap->align = 1;
len = ALIGN(len, rtap->align);
len += rtap->len + rtap->pad;
}
return len;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Johannes Berg | 281 | 92.74% | 13 | 86.67% |
Bruno Randolf | 19 | 6.27% | 1 | 6.67% |
Thomas Pedersen | 3 | 0.99% | 1 | 6.67% |
Total | 303 | 100.00% | 15 | 100.00% |
static void ieee80211_handle_mu_mimo_mon(struct ieee80211_sub_if_data *sdata,
struct sk_buff *skb,
int rtap_vendor_space)
{
struct {
struct ieee80211_hdr_3addr hdr;
u8 category;
u8 action_code;
} __packed action;
if (!sdata)
return;
BUILD_BUG_ON(sizeof(action) != IEEE80211_MIN_ACTION_SIZE + 1);
if (skb->len < rtap_vendor_space + sizeof(action) +
VHT_MUMIMO_GROUPS_DATA_LEN)
return;
if (!is_valid_ether_addr(sdata->u.mntr.mu_follow_addr))
return;
skb_copy_bits(skb, rtap_vendor_space, &action, sizeof(action));
if (!ieee80211_is_action(action.hdr.frame_control))
return;
if (action.category != WLAN_CATEGORY_VHT)
return;
if (action.action_code != WLAN_VHT_ACTION_GROUPID_MGMT)
return;
if (!ether_addr_equal(action.hdr.addr1, sdata->u.mntr.mu_follow_addr))
return;
skb = skb_copy(skb, GFP_ATOMIC);
if (!skb)
return;
skb_queue_tail(&sdata->skb_queue, skb);
ieee80211_queue_work(&sdata->local->hw, &sdata->work);
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Johannes Berg | 191 | 100.00% | 1 | 100.00% |
Total | 191 | 100.00% | 1 | 100.00% |
/*
* ieee80211_add_rx_radiotap_header - add radiotap header
*
* add a radiotap header containing all the fields which the hardware provided.
*/
static void
ieee80211_add_rx_radiotap_header(struct ieee80211_local *local,
struct sk_buff *skb,
struct ieee80211_rate *rate,
int rtap_len, bool has_fcs)
{
struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb);
struct ieee80211_radiotap_header *rthdr;
unsigned char *pos;
__le32 *it_present;
u32 it_present_val;
u16 rx_flags = 0;
u16 channel_flags = 0;
int mpdulen, chain;
unsigned long chains = status->chains;
struct ieee80211_vendor_radiotap rtap = {};
if (status->flag & RX_FLAG_RADIOTAP_VENDOR_DATA) {
rtap = *(struct ieee80211_vendor_radiotap *)skb->data;
/* rtap.len and rtap.pad are undone immediately */
skb_pull(skb, sizeof(rtap) + rtap.len + rtap.pad);
}
mpdulen = skb->len;
if (!(has_fcs && ieee80211_hw_check(&local->hw, RX_INCLUDES_FCS)))
mpdulen += FCS_LEN;
rthdr = skb_push(skb, rtap_len);
memset(rthdr, 0, rtap_len - rtap.len - rtap.pad);
it_present = &rthdr->it_present;
/* radiotap header, set always present flags */
rthdr->it_len = cpu_to_le16(rtap_len);
it_present_val = BIT(IEEE80211_RADIOTAP_FLAGS) |
BIT(IEEE80211_RADIOTAP_CHANNEL) |
BIT(IEEE80211_RADIOTAP_RX_FLAGS);
if (!status->chains)
it_present_val |= BIT(IEEE80211_RADIOTAP_ANTENNA);
for_each_set_bit(chain, &chains, IEEE80211_MAX_CHAINS) {
it_present_val |=
BIT(IEEE80211_RADIOTAP_EXT) |
BIT(IEEE80211_RADIOTAP_RADIOTAP_NAMESPACE);
put_unaligned_le32(it_present_val, it_present);
it_present++;
it_present_val = BIT(IEEE80211_RADIOTAP_ANTENNA) |
BIT(IEEE80211_RADIOTAP_DBM_ANTSIGNAL);
}
if (status->flag & RX_FLAG_RADIOTAP_VENDOR_DATA) {
it_present_val |= BIT(IEEE80211_RADIOTAP_VENDOR_NAMESPACE) |
BIT(IEEE80211_RADIOTAP_EXT);
put_unaligned_le32(it_present_val, it_present);
it_present++;
it_present_val = rtap.present;
}
put_unaligned_le32(it_present_val, it_present);
pos = (void *)(it_present + 1);
/* the order of the following fields is important */
/* IEEE80211_RADIOTAP_TSFT */
if (ieee80211_have_rx_timestamp(status)) {
/* padding */
while ((pos - (u8 *)rthdr) & 7)
*pos++ = 0;
put_unaligned_le64(
ieee80211_calculate_rx_timestamp(local, status,
mpdulen, 0),
pos);
rthdr->it_present |= cpu_to_le32(1 << IEEE80211_RADIOTAP_TSFT);
pos += 8;
}
/* IEEE80211_RADIOTAP_FLAGS */
if (has_fcs && ieee80211_hw_check(&local->hw, RX_INCLUDES_FCS))
*pos |= IEEE80211_RADIOTAP_F_FCS;
if (status->flag & (RX_FLAG_FAILED_FCS_CRC | RX_FLAG_FAILED_PLCP_CRC))
*pos |= IEEE80211_RADIOTAP_F_BADFCS;
if (status->enc_flags & RX_ENC_FLAG_SHORTPRE)
*pos |= IEEE80211_RADIOTAP_F_SHORTPRE;
pos++;
/* IEEE80211_RADIOTAP_RATE */
if (!rate || status->encoding != RX_ENC_LEGACY) {
/*
* Without rate information don't add it. If we have,
* MCS information is a separate field in radiotap,
* added below. The byte here is needed as padding
* for the channel though, so initialise it to 0.
*/
*pos = 0;
} else {
int shift = 0;
rthdr->it_present |= cpu_to_le32(1 << IEEE80211_RADIOTAP_RATE);
if (status->bw == RATE_INFO_BW_10)
shift = 1;
else if (status->bw == RATE_INFO_BW_5)
shift = 2;
*pos = DIV_ROUND_UP(rate->bitrate, 5 * (1 << shift));
}
pos++;
/* IEEE80211_RADIOTAP_CHANNEL */
put_unaligned_le16(status->freq, pos);
pos += 2;
if (status->bw == RATE_INFO_BW_10)
channel_flags |= IEEE80211_CHAN_HALF;
else if (status->bw == RATE_INFO_BW_5)
channel_flags |= IEEE80211_CHAN_QUARTER;
if (status->band == NL80211_BAND_5GHZ)
channel_flags |= IEEE80211_CHAN_OFDM | IEEE80211_CHAN_5GHZ;
else if (status->encoding != RX_ENC_LEGACY)
channel_flags |= IEEE80211_CHAN_DYN | IEEE80211_CHAN_2GHZ;
else if (rate && rate->flags & IEEE80211_RATE_ERP_G)
channel_flags |= IEEE80211_CHAN_OFDM | IEEE80211_CHAN_2GHZ;
else if (rate)
channel_flags |= IEEE80211_CHAN_CCK | IEEE80211_CHAN_2GHZ;
else
channel_flags |= IEEE80211_CHAN_2GHZ;
put_unaligned_le16(channel_flags, pos);
pos += 2;
/* IEEE80211_RADIOTAP_DBM_ANTSIGNAL */
if (ieee80211_hw_check(&local->hw, SIGNAL_DBM) &&
!(status->flag & RX_FLAG_NO_SIGNAL_VAL)) {
*pos = status->signal;
rthdr->it_present |=
cpu_to_le32(1 << IEEE80211_RADIOTAP_DBM_ANTSIGNAL);
pos++;
}
/* IEEE80211_RADIOTAP_LOCK_QUALITY is missing */
if (!status->chains) {
/* IEEE80211_RADIOTAP_ANTENNA */
*pos = status->antenna;
pos++;
}
/* IEEE80211_RADIOTAP_DB_ANTNOISE is not used */
/* IEEE80211_RADIOTAP_RX_FLAGS */
/* ensure 2 byte alignment for the 2 byte field as required */
if ((pos - (u8 *)rthdr) & 1)
*pos++ = 0;
if (status->flag & RX_FLAG_FAILED_PLCP_CRC)
rx_flags |= IEEE80211_RADIOTAP_F_RX_BADPLCP;
put_unaligned_le16(rx_flags, pos);
pos += 2;
if (status->encoding == RX_ENC_HT) {
unsigned int stbc;
rthdr->it_present |= cpu_to_le32(1 << IEEE80211_RADIOTAP_MCS);
*pos++ = local->hw.radiotap_mcs_details;
*pos = 0;
if (status->enc_flags & RX_ENC_FLAG_SHORT_GI)
*pos |= IEEE80211_RADIOTAP_MCS_SGI;
if (status->bw == RATE_INFO_BW_40)
*pos |= IEEE80211_RADIOTAP_MCS_BW_40;
if (status->enc_flags & RX_ENC_FLAG_HT_GF)
*pos |= IEEE80211_RADIOTAP_MCS_FMT_GF;
if (status->enc_flags & RX_ENC_FLAG_LDPC)
*pos |= IEEE80211_RADIOTAP_MCS_FEC_LDPC;
stbc = (status->enc_flags & RX_ENC_FLAG_STBC_MASK) >> RX_ENC_FLAG_STBC_SHIFT;
*pos |= stbc << IEEE80211_RADIOTAP_MCS_STBC_SHIFT;
pos++;
*pos++ = status->rate_idx;
}
if (status->flag & RX_FLAG_AMPDU_DETAILS) {
u16 flags = 0;
/* ensure 4 byte alignment */
while ((pos - (u8 *)rthdr) & 3)
pos++;
rthdr->it_present |=
cpu_to_le32(1 << IEEE80211_RADIOTAP_AMPDU_STATUS);
put_unaligned_le32(status->ampdu_reference, pos);
pos += 4;
if (status->flag & RX_FLAG_AMPDU_LAST_KNOWN)
flags |= IEEE80211_RADIOTAP_AMPDU_LAST_KNOWN;
if (status->flag & RX_FLAG_AMPDU_IS_LAST)
flags |= IEEE80211_RADIOTAP_AMPDU_IS_LAST;
if (status->flag & RX_FLAG_AMPDU_DELIM_CRC_ERROR)
flags |= IEEE80211_RADIOTAP_AMPDU_DELIM_CRC_ERR;
if (status->flag & RX_FLAG_AMPDU_DELIM_CRC_KNOWN)
flags |= IEEE80211_RADIOTAP_AMPDU_DELIM_CRC_KNOWN;
put_unaligned_le16(flags, pos);
pos += 2;
if (status->flag & RX_FLAG_AMPDU_DELIM_CRC_KNOWN)
*pos++ = status->ampdu_delimiter_crc;
else
*pos++ = 0;
*pos++ = 0;
}
if (status->encoding == RX_ENC_VHT) {
u16 known = local->hw.radiotap_vht_details;
rthdr->it_present |= cpu_to_le32(1 << IEEE80211_RADIOTAP_VHT);
put_unaligned_le16(known, pos);
pos += 2;
/* flags */
if (status->enc_flags & RX_ENC_FLAG_SHORT_GI)
*pos |= IEEE80211_RADIOTAP_VHT_FLAG_SGI;
/* in VHT, STBC is binary */
if (status->enc_flags & RX_ENC_FLAG_STBC_MASK)
*pos |= IEEE80211_RADIOTAP_VHT_FLAG_STBC;
if (status->enc_flags & RX_ENC_FLAG_BF)
*pos |= IEEE80211_RADIOTAP_VHT_FLAG_BEAMFORMED;
pos++;
/* bandwidth */
switch (status->bw) {
case RATE_INFO_BW_80:
*pos++ = 4;
break;
case RATE_INFO_BW_160:
*pos++ = 11;
break;
case RATE_INFO_BW_40:
*pos++ = 1;
break;
default:
*pos++ = 0;
}
/* MCS/NSS */
*pos = (status->rate_idx << 4) | status->nss;
pos += 4;
/* coding field */
if (status->enc_flags & RX_ENC_FLAG_LDPC)
*pos |= IEEE80211_RADIOTAP_CODING_LDPC_USER0;
pos++;
/* group ID */
pos++;
/* partial_aid */
pos += 2;
}
if (local->hw.radiotap_timestamp.units_pos >= 0) {
u16 accuracy = 0;
u8 flags = IEEE80211_RADIOTAP_TIMESTAMP_FLAG_32BIT;
rthdr->it_present |=
cpu_to_le32(1 << IEEE80211_RADIOTAP_TIMESTAMP);
/* ensure 8 byte alignment */
while ((pos - (u8 *)rthdr) & 7)
pos++;
put_unaligned_le64(status->device_timestamp, pos);
pos += sizeof(u64);
if (local->hw.radiotap_timestamp.accuracy >= 0) {
accuracy = local->hw.radiotap_timestamp.accuracy;
flags |= IEEE80211_RADIOTAP_TIMESTAMP_FLAG_ACCURACY;
}
put_unaligned_le16(accuracy, pos);
pos += sizeof(u16);
*pos++ = local->hw.radiotap_timestamp.units_pos;
*pos++ = flags;
}
for_each_set_bit(chain, &chains, IEEE80211_MAX_CHAINS) {
*pos++ = status->chain_signal[chain];
*pos++ = chain;
}
if (status->flag & RX_FLAG_RADIOTAP_VENDOR_DATA) {
/* ensure 2 byte alignment for the vendor field as required */
if ((pos - (u8 *)rthdr) & 1)
*pos++ = 0;
*pos++ = rtap.oui[0];
*pos++ = rtap.oui[1];
*pos++ = rtap.oui[2];
*pos++ = rtap.subns;
put_unaligned_le16(rtap.len, pos);
pos += 2;
/* align the actual payload as requested */
while ((pos - (u8 *)rthdr) & (rtap.align - 1))
*pos++ = 0;
/* data (and possible padding) already follows */
}
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Johannes Berg | 1087 | 74.25% | 23 | 58.97% |
Bruno Randolf | 164 | 11.20% | 4 | 10.26% |
Simon Wunderlich | 71 | 4.85% | 2 | 5.13% |
Emmanuel Grumbach | 45 | 3.07% | 2 | 5.13% |
Thomas Pedersen | 36 | 2.46% | 1 | 2.56% |
Jouni Malinen | 26 | 1.78% | 3 | 7.69% |
Oleksij Rempel | 20 | 1.37% | 1 | 2.56% |
Felix Fietkau | 14 | 0.96% | 2 | 5.13% |
Mathy Vanhoef | 1 | 0.07% | 1 | 2.56% |
Total | 1464 | 100.00% | 39 | 100.00% |
static struct sk_buff *
ieee80211_make_monitor_skb(struct ieee80211_local *local,
struct sk_buff **origskb,
struct ieee80211_rate *rate,
int rtap_vendor_space, bool use_origskb)
{
struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(*origskb);
int rt_hdrlen, needed_headroom;
struct sk_buff *skb;
/* room for the radiotap header based on driver features */
rt_hdrlen = ieee80211_rx_radiotap_hdrlen(local, status, *origskb);
needed_headroom = rt_hdrlen - rtap_vendor_space;
if (use_origskb) {
/* only need to expand headroom if necessary */
skb = *origskb;
*origskb = NULL;
/*
* This shouldn't trigger often because most devices have an
* RX header they pull before we get here, and that should
* be big enough for our radiotap information. We should
* probably export the length to drivers so that we can have
* them allocate enough headroom to start with.
*/
if (skb_headroom(skb) < needed_headroom &&
pskb_expand_head(skb, needed_headroom, 0, GFP_ATOMIC)) {
dev_kfree_skb(skb);
return NULL;
}
} else {
/*
* Need to make a copy and possibly remove radiotap header
* and FCS from the original.
*/
skb = skb_copy_expand(*origskb, needed_headroom, 0, GFP_ATOMIC);
if (!skb)
return NULL;
}
/* prepend radiotap information */
ieee80211_add_rx_radiotap_header(local, skb, rate, rt_hdrlen, true);
skb_reset_mac_header(skb);
skb->ip_summed = CHECKSUM_UNNECESSARY;
skb->pkt_type = PACKET_OTHERHOST;
skb->protocol = htons(ETH_P_802_2);
return skb;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Johannes Berg | 187 | 100.00% | 1 | 100.00% |
Total | 187 | 100.00% | 1 | 100.00% |
/*
* This function copies a received frame to all monitor interfaces and
* returns a cleaned-up SKB that no longer includes the FCS nor the
* radiotap header the driver might have added.
*/
static struct sk_buff *
ieee80211_rx_monitor(struct ieee80211_local *local, struct sk_buff *origskb,
struct ieee80211_rate *rate)
{
struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(origskb);
struct ieee80211_sub_if_data *sdata;
struct sk_buff *monskb = NULL;
int present_fcs_len = 0;
unsigned int rtap_vendor_space = 0;
struct ieee80211_sub_if_data *monitor_sdata =
rcu_dereference(local->monitor_sdata);
bool only_monitor = false;
if (unlikely(status->flag & RX_FLAG_RADIOTAP_VENDOR_DATA)) {
struct ieee80211_vendor_radiotap *rtap = (void *)origskb->data;
rtap_vendor_space = sizeof(*rtap) + rtap->len + rtap->pad;
}
/*
* First, we may need to make a copy of the skb because
* (1) we need to modify it for radiotap (if not present), and
* (2) the other RX handlers will modify the skb we got.
*
* We don't need to, of course, if we aren't going to return
* the SKB because it has a bad FCS/PLCP checksum.
*/
if (ieee80211_hw_check(&local->hw, RX_INCLUDES_FCS)) {
if (unlikely(origskb->len <= FCS_LEN)) {
/* driver bug */
WARN_ON(1);
dev_kfree_skb(origskb);
return NULL;
}
present_fcs_len = FCS_LEN;
}
/* ensure hdr->frame_control and vendor radiotap data are in skb head */
if (!pskb_may_pull(origskb, 2 + rtap_vendor_space)) {
dev_kfree_skb(origskb);
return NULL;
}
only_monitor = should_drop_frame(origskb, present_fcs_len,
rtap_vendor_space);
if (!local->monitors || (status->flag & RX_FLAG_SKIP_MONITOR)) {
if (only_monitor) {
dev_kfree_skb(origskb);
return NULL;
}
remove_monitor_info(origskb, present_fcs_len,
rtap_vendor_space);
return origskb;
}
ieee80211_handle_mu_mimo_mon(monitor_sdata, origskb, rtap_vendor_space);
list_for_each_entry_rcu(sdata, &local->mon_list, u.mntr.list) {
bool last_monitor = list_is_last(&sdata->u.mntr.list,
&local->mon_list);
if (!monskb)
monskb = ieee80211_make_monitor_skb(local, &origskb,
rate,
rtap_vendor_space,
only_monitor &&
last_monitor);
if (monskb) {
struct sk_buff *skb;
if (last_monitor) {
skb = monskb;
monskb = NULL;
} else {
skb = skb_clone(monskb, GFP_ATOMIC);
}
if (skb) {
skb->dev = sdata->dev;
ieee80211_rx_stats(skb->dev, skb->len);
netif_receive_skb(skb);
}
}
if (last_monitor)
break;
}
/* this happens if last_monitor was erroneously false */
dev_kfree_skb(monskb);
/* ditto */
if (!origskb)
return NULL;
remove_monitor_info(origskb, present_fcs_len, rtap_vendor_space);
return origskb;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Johannes Berg | 292 | 73.18% | 11 | 64.71% |
Bruno Randolf | 63 | 15.79% | 1 | 5.88% |
Yi Zhu | 20 | 5.01% | 1 | 5.88% |
Aviya Erenfeld | 12 | 3.01% | 1 | 5.88% |
Grzegorz Bajorski | 8 | 2.01% | 1 | 5.88% |
Helmut Schaa | 3 | 0.75% | 1 | 5.88% |
John W. Linville | 1 | 0.25% | 1 | 5.88% |
Total | 399 | 100.00% | 17 | 100.00% |
static void ieee80211_parse_qos(struct ieee80211_rx_data *rx)
{
struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)rx->skb->data;
struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(rx->skb);