diff options
author | Michael Wu <flamingice@sourmilk.net> | 2008-01-31 19:48:27 +0100 |
---|---|---|
committer | John W. Linville <linville@tuxdriver.com> | 2008-02-29 15:37:03 -0500 |
commit | 3d30d949cf3f9763393f3457721bca3ac2426e42 (patch) | |
tree | d80d1490f8a5263d74b4ed105835a7ef21eb6b80 /net/mac80211/rx.c | |
parent | 8944b79fe9b1fe249c599e7e51f1bfad539aab6d (diff) |
mac80211: Add cooked monitor mode support
This adds "cooked" monitor mode to mac80211. A monitor interface
in "cooked" mode will see all frames that mac80211 has not used
internally.
Signed-off-by: Michael Wu <flamingice@sourmilk.net>
Signed-off-by: Johannes Berg <johannes@sipsolutions.net>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
Diffstat (limited to 'net/mac80211/rx.c')
-rw-r--r-- | net/mac80211/rx.c | 87 |
1 files changed, 86 insertions, 1 deletions
diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c index 3a3112f17832..b1fc112152c9 100644 --- a/net/mac80211/rx.c +++ b/net/mac80211/rx.c @@ -223,6 +223,9 @@ ieee80211_rx_monitor(struct ieee80211_local *local, struct sk_buff *origskb, if (sdata->vif.type != IEEE80211_IF_TYPE_MNTR) continue; + if (sdata->u.mntr_flags & MONITOR_FLAG_COOK_FRAMES) + continue; + if (prev_dev) { skb2 = skb_clone(skb, GFP_ATOMIC); if (skb2) { @@ -1520,6 +1523,86 @@ static void ieee80211_rx_michael_mic_report(struct net_device *dev, rx->skb = NULL; } +static void ieee80211_rx_cooked_monitor(struct ieee80211_txrx_data *rx) +{ + struct ieee80211_sub_if_data *sdata; + struct ieee80211_local *local = rx->local; + struct ieee80211_rtap_hdr { + struct ieee80211_radiotap_header hdr; + u8 flags; + u8 rate; + __le16 chan_freq; + __le16 chan_flags; + } __attribute__ ((packed)) *rthdr; + struct sk_buff *skb = rx->skb, *skb2; + struct net_device *prev_dev = NULL; + struct ieee80211_rx_status *status = rx->u.rx.status; + + if (rx->flags & IEEE80211_TXRXD_RX_CMNTR_REPORTED) + goto out_free_skb; + + if (skb_headroom(skb) < sizeof(*rthdr) && + pskb_expand_head(skb, sizeof(*rthdr), 0, GFP_ATOMIC)) + goto out_free_skb; + + 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)); + + rthdr->rate = rx->u.rx.rate->bitrate / 5; + rthdr->chan_freq = cpu_to_le16(status->freq); + + if (status->band == IEEE80211_BAND_5GHZ) + 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); + + 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->vif.type != IEEE80211_IF_TYPE_MNTR || + !(sdata->u.mntr_flags & MONITOR_FLAG_COOK_FRAMES)) + 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); + skb = NULL; + } else + goto out_free_skb; + + rx->flags |= IEEE80211_TXRXD_RX_CMNTR_REPORTED; + return; + + out_free_skb: + dev_kfree_skb(skb); +} + typedef ieee80211_rx_result (*ieee80211_rx_handler)(struct ieee80211_txrx_data *); static ieee80211_rx_handler ieee80211_rx_handlers[] = { @@ -1574,9 +1657,11 @@ static void ieee80211_invoke_rx_handlers(struct ieee80211_sub_if_data *sdata, } switch (res) { + case RX_CONTINUE: case RX_DROP_MONITOR: + ieee80211_rx_cooked_monitor(rx); + break; case RX_DROP_UNUSABLE: - case RX_CONTINUE: dev_kfree_skb(rx->skb); break; } |