[PATCH] mac80211: pass frames to monitor interfaces early
This makes mac80211 pass all frames to monitor interfaces early before all receive processing with the benefit that only a single copy needs to be made, all monitors can receive clones of the skb and if the frame will be discarded we don't even need to make a single copy. Signed-off-by: Johannes Berg <johannes@sipsolutions.net> Signed-off-by: John W. Linville <linville@tuxdriver.com>
This commit is contained in:
parent
5b2812e925
commit
b2e7771e55
1 changed files with 226 additions and 124 deletions
|
@ -25,6 +25,207 @@
|
|||
#include "tkip.h"
|
||||
#include "wme.h"
|
||||
|
||||
/*
|
||||
* monitor mode reception
|
||||
*
|
||||
* 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,
|
||||
int rtap_len)
|
||||
{
|
||||
skb_pull(skb, rtap_len);
|
||||
|
||||
if (local->hw.flags & IEEE80211_HW_RX_INCLUDES_FCS) {
|
||||
if (likely(skb->len > FCS_LEN))
|
||||
skb_trim(skb, skb->len - FCS_LEN);
|
||||
else {
|
||||
/* driver bug */
|
||||
WARN_ON(1);
|
||||
dev_kfree_skb(skb);
|
||||
skb = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
return skb;
|
||||
}
|
||||
|
||||
static inline int should_drop_frame(struct ieee80211_rx_status *status,
|
||||
struct sk_buff *skb,
|
||||
int present_fcs_len,
|
||||
int radiotap_len)
|
||||
{
|
||||
struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data;
|
||||
|
||||
if (status->flag & (RX_FLAG_FAILED_FCS_CRC | RX_FLAG_FAILED_PLCP_CRC))
|
||||
return 1;
|
||||
if (unlikely(skb->len < 16 + present_fcs_len + radiotap_len))
|
||||
return 1;
|
||||
if ((hdr->frame_control & cpu_to_le16(IEEE80211_FCTL_FTYPE)) ==
|
||||
cpu_to_le16(IEEE80211_FTYPE_CTL))
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* 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_rx_status *status)
|
||||
{
|
||||
struct ieee80211_sub_if_data *sdata;
|
||||
struct ieee80211_rate *rate;
|
||||
int needed_headroom = 0;
|
||||
struct ieee80211_rtap_hdr {
|
||||
struct ieee80211_radiotap_header hdr;
|
||||
u8 flags;
|
||||
u8 rate;
|
||||
__le16 chan_freq;
|
||||
__le16 chan_flags;
|
||||
u8 antsignal;
|
||||
u8 padding_for_rxflags;
|
||||
__le16 rx_flags;
|
||||
} __attribute__ ((packed)) *rthdr;
|
||||
struct sk_buff *skb, *skb2;
|
||||
struct net_device *prev_dev = NULL;
|
||||
int present_fcs_len = 0;
|
||||
int rtap_len = 0;
|
||||
|
||||
/*
|
||||
* 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 (status->flag & RX_FLAG_RADIOTAP)
|
||||
rtap_len = ieee80211_get_radiotap_len(origskb->data);
|
||||
else
|
||||
needed_headroom = sizeof(*rthdr);
|
||||
|
||||
if (local->hw.flags & IEEE80211_HW_RX_INCLUDES_FCS)
|
||||
present_fcs_len = FCS_LEN;
|
||||
|
||||
if (!local->monitors) {
|
||||
if (should_drop_frame(status, origskb, present_fcs_len,
|
||||
rtap_len)) {
|
||||
dev_kfree_skb(origskb);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return remove_monitor_info(local, origskb, rtap_len);
|
||||
}
|
||||
|
||||
if (should_drop_frame(status, origskb, present_fcs_len, rtap_len)) {
|
||||
/* 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, sizeof(*rthdr), 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_len);
|
||||
|
||||
if (!skb)
|
||||
return origskb;
|
||||
}
|
||||
|
||||
/* if necessary, prepend radiotap information */
|
||||
if (!(status->flag & RX_FLAG_RADIOTAP)) {
|
||||
rthdr = (void *) skb_push(skb, sizeof(*rthdr));
|
||||
memset(rthdr, 0, sizeof(*rthdr));
|
||||
rthdr->hdr.it_len = cpu_to_le16(sizeof(*rthdr));
|
||||
rthdr->hdr.it_present =
|
||||
cpu_to_le32((1 << IEEE80211_RADIOTAP_FLAGS) |
|
||||
(1 << IEEE80211_RADIOTAP_RATE) |
|
||||
(1 << IEEE80211_RADIOTAP_CHANNEL) |
|
||||
(1 << IEEE80211_RADIOTAP_DB_ANTSIGNAL) |
|
||||
(1 << IEEE80211_RADIOTAP_RX_FLAGS));
|
||||
rthdr->flags = local->hw.flags & IEEE80211_HW_RX_INCLUDES_FCS ?
|
||||
IEEE80211_RADIOTAP_F_FCS : 0;
|
||||
|
||||
/* FIXME: when radiotap gets a 'bad PLCP' flag use it here */
|
||||
rthdr->rx_flags = 0;
|
||||
if (status->flag &
|
||||
(RX_FLAG_FAILED_FCS_CRC | RX_FLAG_FAILED_PLCP_CRC))
|
||||
rthdr->rx_flags |=
|
||||
cpu_to_le16(IEEE80211_RADIOTAP_F_RX_BADFCS);
|
||||
|
||||
rate = ieee80211_get_rate(local, status->phymode,
|
||||
status->rate);
|
||||
if (rate)
|
||||
rthdr->rate = rate->rate / 5;
|
||||
|
||||
rthdr->chan_freq = cpu_to_le16(status->freq);
|
||||
|
||||
if (status->phymode == MODE_IEEE80211A)
|
||||
rthdr->chan_flags =
|
||||
cpu_to_le16(IEEE80211_CHAN_OFDM |
|
||||
IEEE80211_CHAN_5GHZ);
|
||||
else
|
||||
rthdr->chan_flags =
|
||||
cpu_to_le16(IEEE80211_CHAN_DYN |
|
||||
IEEE80211_CHAN_2GHZ);
|
||||
|
||||
rthdr->antsignal = status->ssi;
|
||||
}
|
||||
|
||||
skb_set_mac_header(skb, 0);
|
||||
skb->ip_summed = CHECKSUM_UNNECESSARY;
|
||||
skb->pkt_type = PACKET_OTHERHOST;
|
||||
skb->protocol = htons(ETH_P_802_2);
|
||||
|
||||
list_for_each_entry_rcu(sdata, &local->interfaces, list) {
|
||||
if (!netif_running(sdata->dev))
|
||||
continue;
|
||||
|
||||
if (sdata->type != IEEE80211_IF_TYPE_MNTR)
|
||||
continue;
|
||||
|
||||
if (prev_dev) {
|
||||
skb2 = skb_clone(skb, GFP_ATOMIC);
|
||||
if (skb2) {
|
||||
skb2->dev = prev_dev;
|
||||
netif_rx(skb2);
|
||||
}
|
||||
}
|
||||
|
||||
prev_dev = sdata->dev;
|
||||
sdata->dev->stats.rx_packets++;
|
||||
sdata->dev->stats.rx_bytes += skb->len;
|
||||
}
|
||||
|
||||
if (prev_dev) {
|
||||
skb->dev = prev_dev;
|
||||
netif_rx(skb);
|
||||
} else
|
||||
dev_kfree_skb(skb);
|
||||
|
||||
return origskb;
|
||||
}
|
||||
|
||||
|
||||
/* pre-rx handlers
|
||||
*
|
||||
* these don't have dev/sdata fields in the rx data
|
||||
|
@ -132,100 +333,6 @@ ieee80211_rx_h_if_stats(struct ieee80211_txrx_data *rx)
|
|||
return TXRX_CONTINUE;
|
||||
}
|
||||
|
||||
static void
|
||||
ieee80211_rx_monitor(struct net_device *dev, struct sk_buff *skb,
|
||||
struct ieee80211_rx_status *status)
|
||||
{
|
||||
struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
|
||||
struct ieee80211_rate *rate;
|
||||
struct ieee80211_rtap_hdr {
|
||||
struct ieee80211_radiotap_header hdr;
|
||||
u8 flags;
|
||||
u8 rate;
|
||||
__le16 chan_freq;
|
||||
__le16 chan_flags;
|
||||
u8 antsignal;
|
||||
u8 padding_for_rxflags;
|
||||
__le16 rx_flags;
|
||||
} __attribute__ ((packed)) *rthdr;
|
||||
|
||||
skb->dev = dev;
|
||||
|
||||
if (status->flag & RX_FLAG_RADIOTAP)
|
||||
goto out;
|
||||
|
||||
if (skb_headroom(skb) < sizeof(*rthdr)) {
|
||||
I802_DEBUG_INC(local->rx_expand_skb_head);
|
||||
if (pskb_expand_head(skb, sizeof(*rthdr), 0, GFP_ATOMIC)) {
|
||||
dev_kfree_skb(skb);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
rthdr = (struct ieee80211_rtap_hdr *) skb_push(skb, sizeof(*rthdr));
|
||||
memset(rthdr, 0, sizeof(*rthdr));
|
||||
rthdr->hdr.it_len = cpu_to_le16(sizeof(*rthdr));
|
||||
rthdr->hdr.it_present =
|
||||
cpu_to_le32((1 << IEEE80211_RADIOTAP_FLAGS) |
|
||||
(1 << IEEE80211_RADIOTAP_RATE) |
|
||||
(1 << IEEE80211_RADIOTAP_CHANNEL) |
|
||||
(1 << IEEE80211_RADIOTAP_DB_ANTSIGNAL) |
|
||||
(1 << IEEE80211_RADIOTAP_RX_FLAGS));
|
||||
rthdr->flags = local->hw.flags & IEEE80211_HW_RX_INCLUDES_FCS ?
|
||||
IEEE80211_RADIOTAP_F_FCS : 0;
|
||||
|
||||
/* FIXME: when radiotap gets a 'bad PLCP' flag use it here */
|
||||
rthdr->rx_flags = 0;
|
||||
if (status->flag &
|
||||
(RX_FLAG_FAILED_FCS_CRC | RX_FLAG_FAILED_PLCP_CRC))
|
||||
rthdr->rx_flags |= cpu_to_le16(IEEE80211_RADIOTAP_F_RX_BADFCS);
|
||||
|
||||
rate = ieee80211_get_rate(local, status->phymode, status->rate);
|
||||
if (rate)
|
||||
rthdr->rate = rate->rate / 5;
|
||||
|
||||
rthdr->chan_freq = cpu_to_le16(status->freq);
|
||||
rthdr->chan_flags =
|
||||
status->phymode == MODE_IEEE80211A ?
|
||||
cpu_to_le16(IEEE80211_CHAN_OFDM | IEEE80211_CHAN_5GHZ) :
|
||||
cpu_to_le16(IEEE80211_CHAN_DYN | IEEE80211_CHAN_2GHZ);
|
||||
rthdr->antsignal = status->ssi;
|
||||
|
||||
out:
|
||||
dev->stats.rx_packets++;
|
||||
dev->stats.rx_bytes += skb->len;
|
||||
|
||||
skb_set_mac_header(skb, 0);
|
||||
skb->ip_summed = CHECKSUM_UNNECESSARY;
|
||||
skb->pkt_type = PACKET_OTHERHOST;
|
||||
skb->protocol = htons(ETH_P_802_2);
|
||||
memset(skb->cb, 0, sizeof(skb->cb));
|
||||
netif_rx(skb);
|
||||
}
|
||||
|
||||
static ieee80211_txrx_result
|
||||
ieee80211_rx_h_monitor(struct ieee80211_txrx_data *rx)
|
||||
{
|
||||
if (rx->sdata->type == IEEE80211_IF_TYPE_MNTR) {
|
||||
ieee80211_rx_monitor(rx->dev, rx->skb, rx->u.rx.status);
|
||||
return TXRX_QUEUED;
|
||||
}
|
||||
|
||||
/*
|
||||
* Drop frames with failed FCS/PLCP checksums here, they are only
|
||||
* relevant for monitor mode, the rest of the stack should never
|
||||
* see them.
|
||||
*/
|
||||
if (rx->u.rx.status->flag &
|
||||
(RX_FLAG_FAILED_FCS_CRC | RX_FLAG_FAILED_PLCP_CRC))
|
||||
return TXRX_DROP;
|
||||
|
||||
if (rx->u.rx.status->flag & RX_FLAG_RADIOTAP)
|
||||
skb_pull(rx->skb, ieee80211_get_radiotap_len(rx->skb->data));
|
||||
|
||||
return TXRX_CONTINUE;
|
||||
}
|
||||
|
||||
static ieee80211_txrx_result
|
||||
ieee80211_rx_h_passive_scan(struct ieee80211_txrx_data *rx)
|
||||
{
|
||||
|
@ -266,10 +373,6 @@ ieee80211_rx_h_check(struct ieee80211_txrx_data *rx)
|
|||
rx->sta->last_seq_ctrl[rx->u.rx.queue] = hdr->seq_ctrl;
|
||||
}
|
||||
|
||||
if ((rx->local->hw.flags & IEEE80211_HW_RX_INCLUDES_FCS) &&
|
||||
rx->skb->len > FCS_LEN)
|
||||
skb_trim(rx->skb, rx->skb->len - FCS_LEN);
|
||||
|
||||
if (unlikely(rx->skb->len < 16)) {
|
||||
I802_DEBUG_INC(rx->local->rx_handlers_drop_short);
|
||||
return TXRX_DROP;
|
||||
|
@ -1264,7 +1367,6 @@ static void ieee80211_rx_michael_mic_report(struct net_device *dev,
|
|||
ieee80211_rx_handler ieee80211_rx_handlers[] =
|
||||
{
|
||||
ieee80211_rx_h_if_stats,
|
||||
ieee80211_rx_h_monitor,
|
||||
ieee80211_rx_h_passive_scan,
|
||||
ieee80211_rx_h_check,
|
||||
ieee80211_rx_h_load_key,
|
||||
|
@ -1371,16 +1473,10 @@ void __ieee80211_rx(struct ieee80211_hw *hw, struct sk_buff *skb,
|
|||
struct ieee80211_hdr *hdr;
|
||||
struct ieee80211_txrx_data rx;
|
||||
u16 type;
|
||||
int radiotap_len = 0, prepres;
|
||||
int prepres;
|
||||
struct ieee80211_sub_if_data *prev = NULL;
|
||||
struct sk_buff *skb_new;
|
||||
u8 *bssid;
|
||||
int bogon;
|
||||
|
||||
if (status->flag & RX_FLAG_RADIOTAP) {
|
||||
radiotap_len = ieee80211_get_radiotap_len(skb->data);
|
||||
skb_pull(skb, radiotap_len);
|
||||
}
|
||||
|
||||
/*
|
||||
* key references and virtual interfaces are protected using RCU
|
||||
|
@ -1389,30 +1485,35 @@ void __ieee80211_rx(struct ieee80211_hw *hw, struct sk_buff *skb,
|
|||
*/
|
||||
rcu_read_lock();
|
||||
|
||||
/*
|
||||
* Frames with failed FCS/PLCP checksum are not returned,
|
||||
* all other frames are returned without radiotap header
|
||||
* if it was previously present.
|
||||
* Also, frames with less than 16 bytes are dropped.
|
||||
*/
|
||||
skb = ieee80211_rx_monitor(local, skb, status);
|
||||
if (!skb) {
|
||||
rcu_read_unlock();
|
||||
return;
|
||||
}
|
||||
|
||||
hdr = (struct ieee80211_hdr *) skb->data;
|
||||
memset(&rx, 0, sizeof(rx));
|
||||
rx.skb = skb;
|
||||
rx.local = local;
|
||||
|
||||
rx.u.rx.status = status;
|
||||
rx.fc = skb->len >= 2 ? le16_to_cpu(hdr->frame_control) : 0;
|
||||
rx.fc = le16_to_cpu(hdr->frame_control);
|
||||
type = rx.fc & IEEE80211_FCTL_FTYPE;
|
||||
|
||||
bogon = status->flag & (RX_FLAG_FAILED_FCS_CRC |
|
||||
RX_FLAG_FAILED_PLCP_CRC);
|
||||
|
||||
if (!bogon && (type == IEEE80211_FTYPE_DATA ||
|
||||
type == IEEE80211_FTYPE_MGMT))
|
||||
if (type == IEEE80211_FTYPE_DATA || type == IEEE80211_FTYPE_MGMT)
|
||||
local->dot11ReceivedFragmentCount++;
|
||||
|
||||
if (!bogon && skb->len >= 16) {
|
||||
sta = rx.sta = sta_info_get(local, hdr->addr2);
|
||||
if (sta) {
|
||||
rx.dev = rx.sta->dev;
|
||||
rx.sdata = IEEE80211_DEV_TO_SUB_IF(rx.dev);
|
||||
}
|
||||
} else
|
||||
sta = rx.sta = NULL;
|
||||
sta = rx.sta = sta_info_get(local, hdr->addr2);
|
||||
if (sta) {
|
||||
rx.dev = rx.sta->dev;
|
||||
rx.sdata = IEEE80211_DEV_TO_SUB_IF(rx.dev);
|
||||
}
|
||||
|
||||
if ((status->flag & RX_FLAG_MMIC_ERROR)) {
|
||||
ieee80211_rx_michael_mic_report(local->mdev, hdr, sta, &rx);
|
||||
|
@ -1427,7 +1528,6 @@ void __ieee80211_rx(struct ieee80211_hw *hw, struct sk_buff *skb,
|
|||
goto end;
|
||||
skb = rx.skb;
|
||||
|
||||
skb_push(skb, radiotap_len);
|
||||
if (sta && !(sta->flags & (WLAN_STA_WDS | WLAN_STA_ASSOC_AP)) &&
|
||||
!local->iff_promiscs && !is_multicast_ether_addr(hdr->addr1)) {
|
||||
rx.flags |= IEEE80211_TXRXD_RXRA_MATCH;
|
||||
|
@ -1438,14 +1538,16 @@ void __ieee80211_rx(struct ieee80211_hw *hw, struct sk_buff *skb,
|
|||
return;
|
||||
}
|
||||
|
||||
bssid = ieee80211_get_bssid(hdr, skb->len - radiotap_len);
|
||||
bssid = ieee80211_get_bssid(hdr, skb->len);
|
||||
|
||||
list_for_each_entry_rcu(sdata, &local->interfaces, list) {
|
||||
rx.flags |= IEEE80211_TXRXD_RXRA_MATCH;
|
||||
|
||||
if (!netif_running(sdata->dev))
|
||||
continue;
|
||||
|
||||
if (sdata->type == IEEE80211_IF_TYPE_MNTR)
|
||||
continue;
|
||||
|
||||
rx.flags |= IEEE80211_TXRXD_RXRA_MATCH;
|
||||
prepres = prepare_for_handlers(sdata, bssid, &rx, hdr);
|
||||
/* prepare_for_handlers can change sta */
|
||||
sta = rx.sta;
|
||||
|
|
Loading…
Reference in a new issue