summaryrefslogtreecommitdiff
path: root/net/mac80211/rx.c
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2017-05-02 16:40:27 -0700
committerLinus Torvalds <torvalds@linux-foundation.org>2017-05-02 16:40:27 -0700
commit8d65b08debc7e62b2c6032d7fe7389d895b92cbc (patch)
tree0c3141b60c3a03cc32742b5750c5e763b9dae489 /net/mac80211/rx.c
parent5a0387a8a8efb90ae7fea1e2e5c62de3efa74691 (diff)
parent5d15af6778b8e4ed1fd41b040283af278e7a9a72 (diff)
Merge git://git.kernel.org/pub/scm/linux/kernel/git/davem/net-next
Pull networking updates from David Millar: "Here are some highlights from the 2065 networking commits that happened this development cycle: 1) XDP support for IXGBE (John Fastabend) and thunderx (Sunil Kowuri) 2) Add a generic XDP driver, so that anyone can test XDP even if they lack a networking device whose driver has explicit XDP support (me). 3) Sparc64 now has an eBPF JIT too (me) 4) Add a BPF program testing framework via BPF_PROG_TEST_RUN (Alexei Starovoitov) 5) Make netfitler network namespace teardown less expensive (Florian Westphal) 6) Add symmetric hashing support to nft_hash (Laura Garcia Liebana) 7) Implement NAPI and GRO in netvsc driver (Stephen Hemminger) 8) Support TC flower offload statistics in mlxsw (Arkadi Sharshevsky) 9) Multiqueue support in stmmac driver (Joao Pinto) 10) Remove TCP timewait recycling, it never really could possibly work well in the real world and timestamp randomization really zaps any hint of usability this feature had (Soheil Hassas Yeganeh) 11) Support level3 vs level4 ECMP route hashing in ipv4 (Nikolay Aleksandrov) 12) Add socket busy poll support to epoll (Sridhar Samudrala) 13) Netlink extended ACK support (Johannes Berg, Pablo Neira Ayuso, and several others) 14) IPSEC hw offload infrastructure (Steffen Klassert)" * git://git.kernel.org/pub/scm/linux/kernel/git/davem/net-next: (2065 commits) tipc: refactor function tipc_sk_recv_stream() tipc: refactor function tipc_sk_recvmsg() net: thunderx: Optimize page recycling for XDP net: thunderx: Support for XDP header adjustment net: thunderx: Add support for XDP_TX net: thunderx: Add support for XDP_DROP net: thunderx: Add basic XDP support net: thunderx: Cleanup receive buffer allocation net: thunderx: Optimize CQE_TX handling net: thunderx: Optimize RBDR descriptor handling net: thunderx: Support for page recycling ipx: call ipxitf_put() in ioctl error path net: sched: add helpers to handle extended actions qed*: Fix issues in the ptp filter config implementation. qede: Fix concurrency issue in PTP Tx path processing. stmmac: Add support for SIMATIC IOT2000 platform net: hns: fix ethtool_get_strings overflow in hns driver tcp: fix wraparound issue in tcp_lp bpf, arm64: fix jit branch offset related to ldimm64 bpf, arm64: implement jiting of BPF_XADD ...
Diffstat (limited to 'net/mac80211/rx.c')
-rw-r--r--net/mac80211/rx.c272
1 files changed, 152 insertions, 120 deletions
diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c
index 4d7543d1a62c..35f4c7d7a500 100644
--- a/net/mac80211/rx.c
+++ b/net/mac80211/rx.c
@@ -95,24 +95,13 @@ static u8 *ieee80211_get_bssid(struct ieee80211_hdr *hdr, size_t len,
* This function cleans up the SKB, i.e. it removes all the stuff
* only useful for monitoring.
*/
-static struct sk_buff *remove_monitor_info(struct ieee80211_local *local,
- struct sk_buff *skb,
- unsigned int rtap_vendor_space)
+static void remove_monitor_info(struct sk_buff *skb,
+ unsigned int present_fcs_len,
+ unsigned int rtap_vendor_space)
{
- if (ieee80211_hw_check(&local->hw, RX_INCLUDES_FCS)) {
- if (likely(skb->len > FCS_LEN))
- __pskb_trim(skb, skb->len - FCS_LEN);
- else {
- /* driver bug */
- WARN_ON(1);
- dev_kfree_skb(skb);
- return NULL;
- }
- }
-
+ if (present_fcs_len)
+ __pskb_trim(skb, skb->len - present_fcs_len);
__pskb_pull(skb, rtap_vendor_space);
-
- return skb;
}
static inline bool should_drop_frame(struct sk_buff *skb, int present_fcs_len,
@@ -167,7 +156,7 @@ ieee80211_rx_radiotap_hdrlen(struct ieee80211_local *local,
/* padding for RX_FLAGS if necessary */
len = ALIGN(len, 2);
- if (status->flag & RX_FLAG_HT) /* HT info */
+ if (status->encoding == RX_ENC_HT) /* HT info */
len += 3;
if (status->flag & RX_FLAG_AMPDU_DETAILS) {
@@ -175,7 +164,7 @@ ieee80211_rx_radiotap_hdrlen(struct ieee80211_local *local,
len += 8;
}
- if (status->flag & RX_FLAG_VHT) {
+ if (status->encoding == RX_ENC_VHT) {
len = ALIGN(len, 2);
len += 12;
}
@@ -340,12 +329,12 @@ ieee80211_add_rx_radiotap_header(struct ieee80211_local *local,
*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->flag & RX_FLAG_SHORTPRE)
+ if (status->enc_flags & RX_ENC_FLAG_SHORTPRE)
*pos |= IEEE80211_RADIOTAP_F_SHORTPRE;
pos++;
/* IEEE80211_RADIOTAP_RATE */
- if (!rate || status->flag & (RX_FLAG_HT | RX_FLAG_VHT)) {
+ 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,
@@ -356,9 +345,9 @@ ieee80211_add_rx_radiotap_header(struct ieee80211_local *local,
} else {
int shift = 0;
rthdr->it_present |= cpu_to_le32(1 << IEEE80211_RADIOTAP_RATE);
- if (status->flag & RX_FLAG_10MHZ)
+ if (status->bw == RATE_INFO_BW_10)
shift = 1;
- else if (status->flag & RX_FLAG_5MHZ)
+ else if (status->bw == RATE_INFO_BW_5)
shift = 2;
*pos = DIV_ROUND_UP(rate->bitrate, 5 * (1 << shift));
}
@@ -367,14 +356,14 @@ ieee80211_add_rx_radiotap_header(struct ieee80211_local *local,
/* IEEE80211_RADIOTAP_CHANNEL */
put_unaligned_le16(status->freq, pos);
pos += 2;
- if (status->flag & RX_FLAG_10MHZ)
+ if (status->bw == RATE_INFO_BW_10)
channel_flags |= IEEE80211_CHAN_HALF;
- else if (status->flag & RX_FLAG_5MHZ)
+ 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->flag & (RX_FLAG_HT | RX_FLAG_VHT))
+ 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;
@@ -413,21 +402,21 @@ ieee80211_add_rx_radiotap_header(struct ieee80211_local *local,
put_unaligned_le16(rx_flags, pos);
pos += 2;
- if (status->flag & RX_FLAG_HT) {
+ 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->flag & RX_FLAG_SHORT_GI)
+ if (status->enc_flags & RX_ENC_FLAG_SHORT_GI)
*pos |= IEEE80211_RADIOTAP_MCS_SGI;
- if (status->flag & RX_FLAG_40MHZ)
+ if (status->bw == RATE_INFO_BW_40)
*pos |= IEEE80211_RADIOTAP_MCS_BW_40;
- if (status->flag & RX_FLAG_HT_GF)
+ if (status->enc_flags & RX_ENC_FLAG_HT_GF)
*pos |= IEEE80211_RADIOTAP_MCS_FMT_GF;
- if (status->flag & RX_FLAG_LDPC)
+ if (status->enc_flags & RX_ENC_FLAG_LDPC)
*pos |= IEEE80211_RADIOTAP_MCS_FEC_LDPC;
- stbc = (status->flag & RX_FLAG_STBC_MASK) >> RX_FLAG_STBC_SHIFT;
+ 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;
@@ -460,35 +449,40 @@ ieee80211_add_rx_radiotap_header(struct ieee80211_local *local,
*pos++ = 0;
}
- if (status->flag & RX_FLAG_VHT) {
+ 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->flag & RX_FLAG_SHORT_GI)
+ if (status->enc_flags & RX_ENC_FLAG_SHORT_GI)
*pos |= IEEE80211_RADIOTAP_VHT_FLAG_SGI;
/* in VHT, STBC is binary */
- if (status->flag & RX_FLAG_STBC_MASK)
+ if (status->enc_flags & RX_ENC_FLAG_STBC_MASK)
*pos |= IEEE80211_RADIOTAP_VHT_FLAG_STBC;
- if (status->vht_flag & RX_VHT_FLAG_BF)
+ if (status->enc_flags & RX_ENC_FLAG_BF)
*pos |= IEEE80211_RADIOTAP_VHT_FLAG_BEAMFORMED;
pos++;
/* bandwidth */
- if (status->vht_flag & RX_VHT_FLAG_80MHZ)
+ switch (status->bw) {
+ case RATE_INFO_BW_80:
*pos++ = 4;
- else if (status->vht_flag & RX_VHT_FLAG_160MHZ)
+ break;
+ case RATE_INFO_BW_160:
*pos++ = 11;
- else if (status->flag & RX_FLAG_40MHZ)
+ break;
+ case RATE_INFO_BW_40:
*pos++ = 1;
- else /* 20 MHz */
+ break;
+ default:
*pos++ = 0;
+ }
/* MCS/NSS */
- *pos = (status->rate_idx << 4) | status->vht_nss;
+ *pos = (status->rate_idx << 4) | status->nss;
pos += 4;
/* coding field */
- if (status->flag & RX_FLAG_LDPC)
+ if (status->enc_flags & RX_ENC_FLAG_LDPC)
*pos |= IEEE80211_RADIOTAP_CODING_LDPC_USER0;
pos++;
/* group ID */
@@ -544,6 +538,59 @@ ieee80211_add_rx_radiotap_header(struct ieee80211_local *local,
}
}
+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;
+}
+
/*
* This function copies a received frame to all monitor interfaces and
* returns a cleaned-up SKB that no longer includes the FCS nor the
@@ -555,13 +602,12 @@ ieee80211_rx_monitor(struct ieee80211_local *local, struct sk_buff *origskb,
{
struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(origskb);
struct ieee80211_sub_if_data *sdata;
- int rt_hdrlen, needed_headroom;
- struct sk_buff *skb, *skb2;
- struct net_device *prev_dev = NULL;
+ 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;
@@ -578,8 +624,15 @@ ieee80211_rx_monitor(struct ieee80211_local *local, struct sk_buff *origskb,
* the SKB because it has a bad FCS/PLCP checksum.
*/
- if (ieee80211_hw_check(&local->hw, RX_INCLUDES_FCS))
+ 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)) {
@@ -587,89 +640,62 @@ ieee80211_rx_monitor(struct ieee80211_local *local, struct sk_buff *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 (should_drop_frame(origskb, present_fcs_len,
- rtap_vendor_space)) {
+ if (only_monitor) {
dev_kfree_skb(origskb);
return NULL;
}
- return remove_monitor_info(local, origskb, rtap_vendor_space);
+ remove_monitor_info(origskb, present_fcs_len,
+ rtap_vendor_space);
+ return origskb;
}
ieee80211_handle_mu_mimo_mon(monitor_sdata, origskb, rtap_vendor_space);
- /* 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 (should_drop_frame(origskb, present_fcs_len, rtap_vendor_space)) {
- /* 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);
-
- origskb = remove_monitor_info(local, origskb,
- rtap_vendor_space);
-
- if (!skb)
- return origskb;
- }
-
- /* 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);
+ 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);
- list_for_each_entry_rcu(sdata, &local->interfaces, list) {
- if (sdata->vif.type != NL80211_IFTYPE_MONITOR)
- continue;
+ if (!monskb)
+ monskb = ieee80211_make_monitor_skb(local, &origskb,
+ rate,
+ rtap_vendor_space,
+ only_monitor &&
+ last_monitor);
- if (sdata->u.mntr.flags & MONITOR_FLAG_COOK_FRAMES)
- continue;
+ if (monskb) {
+ struct sk_buff *skb;
- if (!ieee80211_sdata_running(sdata))
- continue;
+ if (last_monitor) {
+ skb = monskb;
+ monskb = NULL;
+ } else {
+ skb = skb_clone(monskb, GFP_ATOMIC);
+ }
- if (prev_dev) {
- skb2 = skb_clone(skb, GFP_ATOMIC);
- if (skb2) {
- skb2->dev = prev_dev;
- netif_receive_skb(skb2);
+ if (skb) {
+ skb->dev = sdata->dev;
+ ieee80211_rx_stats(skb->dev, skb->len);
+ netif_receive_skb(skb);
}
}
- prev_dev = sdata->dev;
- ieee80211_rx_stats(sdata->dev, skb->len);
+ if (last_monitor)
+ break;
}
- if (prev_dev) {
- skb->dev = prev_dev;
- netif_receive_skb(skb);
- } else
- dev_kfree_skb(skb);
+ /* 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;
}
@@ -3315,8 +3341,8 @@ static void ieee80211_rx_handlers_result(struct ieee80211_rx_data *rx,
status = IEEE80211_SKB_RXCB((rx->skb));
sband = rx->local->hw.wiphy->bands[status->band];
- if (!(status->flag & RX_FLAG_HT) &&
- !(status->flag & RX_FLAG_VHT))
+ if (!(status->encoding == RX_ENC_HT) &&
+ !(status->encoding == RX_ENC_VHT))
rate = &sband->bitrates[status->rate_idx];
ieee80211_rx_cooked_monitor(rx, rate);
@@ -3553,7 +3579,7 @@ static bool ieee80211_accept_frame(struct ieee80211_rx_data *rx)
struct ieee80211_hdr *hdr = (void *)skb->data;
struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb);
u8 *bssid = ieee80211_get_bssid(hdr, skb->len, sdata->vif.type);
- int multicast = is_multicast_ether_addr(hdr->addr1);
+ bool multicast = is_multicast_ether_addr(hdr->addr1);
switch (sdata->vif.type) {
case NL80211_IFTYPE_STATION:
@@ -3577,7 +3603,7 @@ static bool ieee80211_accept_frame(struct ieee80211_rx_data *rx)
return false;
if (!rx->sta) {
int rate_idx;
- if (status->flag & (RX_FLAG_HT | RX_FLAG_VHT))
+ if (status->encoding != RX_ENC_LEGACY)
rate_idx = 0; /* TODO: HT/VHT rates */
else
rate_idx = status->rate_idx;
@@ -3597,7 +3623,7 @@ static bool ieee80211_accept_frame(struct ieee80211_rx_data *rx)
return false;
if (!rx->sta) {
int rate_idx;
- if (status->flag & RX_FLAG_HT)
+ if (status->encoding != RX_ENC_LEGACY)
rate_idx = 0; /* TODO: HT rates */
else
rate_idx = status->rate_idx;
@@ -4260,7 +4286,8 @@ void ieee80211_rx_napi(struct ieee80211_hw *hw, struct ieee80211_sta *pubsta,
* we probably can't have a valid rate here anyway.
*/
- if (status->flag & RX_FLAG_HT) {
+ switch (status->encoding) {
+ case RX_ENC_HT:
/*
* rate_idx is MCS index, which can be [0-76]
* as documented on:
@@ -4278,14 +4305,19 @@ void ieee80211_rx_napi(struct ieee80211_hw *hw, struct ieee80211_sta *pubsta,
status->rate_idx,
status->rate_idx))
goto drop;
- } else if (status->flag & RX_FLAG_VHT) {
+ break;
+ case RX_ENC_VHT:
if (WARN_ONCE(status->rate_idx > 9 ||
- !status->vht_nss ||
- status->vht_nss > 8,
+ !status->nss ||
+ status->nss > 8,
"Rate marked as a VHT rate but data is invalid: MCS: %d, NSS: %d\n",
- status->rate_idx, status->vht_nss))
+ status->rate_idx, status->nss))
goto drop;
- } else {
+ break;
+ default:
+ WARN_ON_ONCE(1);
+ /* fall through */
+ case RX_ENC_LEGACY:
if (WARN_ON(status->rate_idx >= sband->n_bitrates))
goto drop;
rate = &sband->bitrates[status->rate_idx];