|
@@ -156,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) {
|
|
@@ -164,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;
|
|
|
}
|
|
@@ -329,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,
|
|
@@ -345,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));
|
|
|
}
|
|
@@ -356,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;
|
|
@@ -402,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;
|
|
@@ -449,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 */
|
|
@@ -533,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
|
|
@@ -544,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;
|
|
@@ -583,9 +640,11 @@ 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;
|
|
|
}
|
|
@@ -597,67 +656,46 @@ ieee80211_rx_monitor(struct ieee80211_local *local, struct sk_buff *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);
|
|
|
- remove_monitor_info(origskb, present_fcs_len,
|
|
|
- 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) {
|
|
|
- if (prev_dev) {
|
|
|
- skb2 = skb_clone(skb, GFP_ATOMIC);
|
|
|
- if (skb2) {
|
|
|
- skb2->dev = prev_dev;
|
|
|
- netif_receive_skb(skb2);
|
|
|
+ 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);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- 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;
|
|
|
}
|
|
|
|
|
@@ -3303,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);
|
|
@@ -3541,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:
|
|
@@ -3565,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;
|
|
@@ -3585,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;
|
|
@@ -4248,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:
|
|
@@ -4266,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];
|