|
@@ -39,7 +39,8 @@
|
|
|
* only useful for monitoring.
|
|
|
*/
|
|
|
static struct sk_buff *remove_monitor_info(struct ieee80211_local *local,
|
|
|
- struct sk_buff *skb)
|
|
|
+ struct sk_buff *skb,
|
|
|
+ unsigned int rtap_vendor_space)
|
|
|
{
|
|
|
if (local->hw.flags & IEEE80211_HW_RX_INCLUDES_FCS) {
|
|
|
if (likely(skb->len > FCS_LEN))
|
|
@@ -52,20 +53,25 @@ static struct sk_buff *remove_monitor_info(struct ieee80211_local *local,
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+ __pskb_pull(skb, rtap_vendor_space);
|
|
|
+
|
|
|
return skb;
|
|
|
}
|
|
|
|
|
|
-static inline bool should_drop_frame(struct sk_buff *skb, int present_fcs_len)
|
|
|
+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 = (void *)skb->data;
|
|
|
+ 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_AMPDU_IS_ZEROLEN))
|
|
|
return true;
|
|
|
|
|
|
- if (unlikely(skb->len < 16 + present_fcs_len))
|
|
|
+ if (unlikely(skb->len < 16 + present_fcs_len + rtap_vendor_space))
|
|
|
return true;
|
|
|
|
|
|
if (ieee80211_is_ctl(hdr->frame_control) &&
|
|
@@ -77,8 +83,9 @@ static inline bool should_drop_frame(struct sk_buff *skb, int present_fcs_len)
|
|
|
}
|
|
|
|
|
|
static int
|
|
|
-ieee80211_rx_radiotap_space(struct ieee80211_local *local,
|
|
|
- struct ieee80211_rx_status *status)
|
|
|
+ieee80211_rx_radiotap_hdrlen(struct ieee80211_local *local,
|
|
|
+ struct ieee80211_rx_status *status,
|
|
|
+ struct sk_buff *skb)
|
|
|
{
|
|
|
int len;
|
|
|
|
|
@@ -121,6 +128,21 @@ ieee80211_rx_radiotap_space(struct ieee80211_local *local,
|
|
|
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;
|
|
|
}
|
|
|
|
|
@@ -144,13 +166,20 @@ ieee80211_add_rx_radiotap_header(struct ieee80211_local *local,
|
|
|
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 && (local->hw.flags & IEEE80211_HW_RX_INCLUDES_FCS)))
|
|
|
mpdulen += FCS_LEN;
|
|
|
|
|
|
rthdr = (struct ieee80211_radiotap_header *)skb_push(skb, rtap_len);
|
|
|
- memset(rthdr, 0, rtap_len);
|
|
|
+ memset(rthdr, 0, rtap_len - rtap.len - rtap.pad);
|
|
|
it_present = &rthdr->it_present;
|
|
|
|
|
|
/* radiotap header, set always present flags */
|
|
@@ -172,6 +201,14 @@ ieee80211_add_rx_radiotap_header(struct ieee80211_local *local,
|
|
|
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);
|
|
@@ -366,6 +403,22 @@ ieee80211_add_rx_radiotap_header(struct ieee80211_local *local,
|
|
|
*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 */
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
/*
|
|
@@ -379,10 +432,17 @@ 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 needed_headroom;
|
|
|
+ int rt_hdrlen, needed_headroom;
|
|
|
struct sk_buff *skb, *skb2;
|
|
|
struct net_device *prev_dev = NULL;
|
|
|
int present_fcs_len = 0;
|
|
|
+ unsigned int rtap_vendor_space = 0;
|
|
|
+
|
|
|
+ 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
|
|
@@ -396,25 +456,27 @@ ieee80211_rx_monitor(struct ieee80211_local *local, struct sk_buff *origskb,
|
|
|
if (local->hw.flags & IEEE80211_HW_RX_INCLUDES_FCS)
|
|
|
present_fcs_len = FCS_LEN;
|
|
|
|
|
|
- /* ensure hdr->frame_control is in skb head */
|
|
|
- if (!pskb_may_pull(origskb, 2)) {
|
|
|
+ /* 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;
|
|
|
}
|
|
|
|
|
|
if (!local->monitors) {
|
|
|
- if (should_drop_frame(origskb, present_fcs_len)) {
|
|
|
+ if (should_drop_frame(origskb, present_fcs_len,
|
|
|
+ rtap_vendor_space)) {
|
|
|
dev_kfree_skb(origskb);
|
|
|
return NULL;
|
|
|
}
|
|
|
|
|
|
- return remove_monitor_info(local, origskb);
|
|
|
+ return remove_monitor_info(local, origskb, rtap_vendor_space);
|
|
|
}
|
|
|
|
|
|
/* room for the radiotap header based on driver features */
|
|
|
- needed_headroom = ieee80211_rx_radiotap_space(local, status);
|
|
|
+ rt_hdrlen = ieee80211_rx_radiotap_hdrlen(local, status, origskb);
|
|
|
+ needed_headroom = rt_hdrlen - rtap_vendor_space;
|
|
|
|
|
|
- if (should_drop_frame(origskb, present_fcs_len)) {
|
|
|
+ if (should_drop_frame(origskb, present_fcs_len, rtap_vendor_space)) {
|
|
|
/* only need to expand headroom if necessary */
|
|
|
skb = origskb;
|
|
|
origskb = NULL;
|
|
@@ -438,15 +500,15 @@ ieee80211_rx_monitor(struct ieee80211_local *local, struct sk_buff *origskb,
|
|
|
*/
|
|
|
skb = skb_copy_expand(origskb, needed_headroom, 0, GFP_ATOMIC);
|
|
|
|
|
|
- origskb = remove_monitor_info(local, origskb);
|
|
|
+ origskb = remove_monitor_info(local, origskb,
|
|
|
+ rtap_vendor_space);
|
|
|
|
|
|
if (!skb)
|
|
|
return origskb;
|
|
|
}
|
|
|
|
|
|
/* prepend radiotap information */
|
|
|
- ieee80211_add_rx_radiotap_header(local, skb, rate, needed_headroom,
|
|
|
- true);
|
|
|
+ ieee80211_add_rx_radiotap_header(local, skb, rate, rt_hdrlen, true);
|
|
|
|
|
|
skb_reset_mac_header(skb);
|
|
|
skb->ip_summed = CHECKSUM_UNNECESSARY;
|
|
@@ -2892,8 +2954,10 @@ static void ieee80211_rx_cooked_monitor(struct ieee80211_rx_data *rx,
|
|
|
if (!local->cooked_mntrs)
|
|
|
goto out_free_skb;
|
|
|
|
|
|
+ /* vendor data is long removed here */
|
|
|
+ status->flag &= ~RX_FLAG_RADIOTAP_VENDOR_DATA;
|
|
|
/* room for the radiotap header based on driver features */
|
|
|
- needed_headroom = ieee80211_rx_radiotap_space(local, status);
|
|
|
+ needed_headroom = ieee80211_rx_radiotap_hdrlen(local, status, skb);
|
|
|
|
|
|
if (skb_headroom(skb) < needed_headroom &&
|
|
|
pskb_expand_head(skb, needed_headroom, 0, GFP_ATOMIC))
|