[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:
Johannes Berg 2007-09-26 15:19:39 +02:00 committed by David S. Miller
parent 5b2812e925
commit b2e7771e55

View file

@ -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;