Commit 127f60bf authored by Johannes Berg's avatar Johannes Berg

mac80211: rewrite monitor mode delivery logic

The monitor mode delivery logic makes it hard to add any
kind of filtering in an efficient way, because the monitor
SKB is created first and then passed to all interfaces.

Rewrite the logic to create the monitor SKB the first time
it's actually needed, and then keep delivering it.
Signed-off-by: default avatarJohannes Berg <johannes.berg@intel.com>
parent cd50ac0f
...@@ -533,6 +533,59 @@ ieee80211_add_rx_radiotap_header(struct ieee80211_local *local, ...@@ -533,6 +533,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 * This function copies a received frame to all monitor interfaces and
* returns a cleaned-up SKB that no longer includes the FCS nor the * returns a cleaned-up SKB that no longer includes the FCS nor the
...@@ -544,13 +597,12 @@ ieee80211_rx_monitor(struct ieee80211_local *local, struct sk_buff *origskb, ...@@ -544,13 +597,12 @@ ieee80211_rx_monitor(struct ieee80211_local *local, struct sk_buff *origskb,
{ {
struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(origskb); struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(origskb);
struct ieee80211_sub_if_data *sdata; struct ieee80211_sub_if_data *sdata;
int rt_hdrlen, needed_headroom; struct sk_buff *monskb = NULL;
struct sk_buff *skb, *skb2;
struct net_device *prev_dev = NULL;
int present_fcs_len = 0; int present_fcs_len = 0;
unsigned int rtap_vendor_space = 0; unsigned int rtap_vendor_space = 0;
struct ieee80211_sub_if_data *monitor_sdata = struct ieee80211_sub_if_data *monitor_sdata =
rcu_dereference(local->monitor_sdata); rcu_dereference(local->monitor_sdata);
bool only_monitor = false;
if (unlikely(status->flag & RX_FLAG_RADIOTAP_VENDOR_DATA)) { if (unlikely(status->flag & RX_FLAG_RADIOTAP_VENDOR_DATA)) {
struct ieee80211_vendor_radiotap *rtap = (void *)origskb->data; struct ieee80211_vendor_radiotap *rtap = (void *)origskb->data;
...@@ -583,9 +635,11 @@ ieee80211_rx_monitor(struct ieee80211_local *local, struct sk_buff *origskb, ...@@ -583,9 +635,11 @@ ieee80211_rx_monitor(struct ieee80211_local *local, struct sk_buff *origskb,
return NULL; return NULL;
} }
only_monitor = should_drop_frame(origskb, present_fcs_len,
rtap_vendor_space);
if (!local->monitors || (status->flag & RX_FLAG_SKIP_MONITOR)) { if (!local->monitors || (status->flag & RX_FLAG_SKIP_MONITOR)) {
if (should_drop_frame(origskb, present_fcs_len, if (only_monitor) {
rtap_vendor_space)) {
dev_kfree_skb(origskb); dev_kfree_skb(origskb);
return NULL; return NULL;
} }
...@@ -597,67 +651,46 @@ ieee80211_rx_monitor(struct ieee80211_local *local, struct sk_buff *origskb, ...@@ -597,67 +651,46 @@ ieee80211_rx_monitor(struct ieee80211_local *local, struct sk_buff *origskb,
ieee80211_handle_mu_mimo_mon(monitor_sdata, origskb, rtap_vendor_space); 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) { list_for_each_entry_rcu(sdata, &local->mon_list, u.mntr.list) {
if (prev_dev) { bool last_monitor = list_is_last(&sdata->u.mntr.list,
skb2 = skb_clone(skb, GFP_ATOMIC); &local->mon_list);
if (skb2) {
skb2->dev = prev_dev; if (!monskb)
netif_receive_skb(skb2); 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; if (last_monitor)
ieee80211_rx_stats(sdata->dev, skb->len); break;
} }
if (prev_dev) { /* this happens if last_monitor was erroneously false */
skb->dev = prev_dev; dev_kfree_skb(monskb);
netif_receive_skb(skb);
} else /* ditto */
dev_kfree_skb(skb); if (!origskb)
return NULL;
remove_monitor_info(origskb, present_fcs_len, rtap_vendor_space);
return origskb; return origskb;
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment