mac80211: fix virtual interfaces vs. injection
authorJohannes Berg <johannes@sipsolutions.net>
Fri, 12 Sep 2008 20:52:47 +0000 (22:52 +0200)
committerJohn W. Linville <linville@tuxdriver.com>
Mon, 15 Sep 2008 20:48:25 +0000 (16:48 -0400)
Currently, virtual interface pointers passed to drivers might be
from monitor interfaces and as such completely uninitialised
because we do not tell the driver about monitor interfaces when
those are created. Instead of passing them, we should therefore
indicate to the driver that there is no information; do that by
passing a NULL value and adjust drivers to cope with it.

As a result, some mac80211 API functions also need to cope with
a NULL vif pointer so drivers can still call them unconditionally.

Also, when injecting frames we really don't want to pass NULL all
the time, if we know we are the source address of a frame and have
a local interface for that address, we can to use that interface.
This also helps with processing the frame correctly for that
interface which will help the 802.11w implementation. It's not
entirely correct for VLANs or WDS interfaces because there the MAC
address isn't unique, but it's already a lot better than what we
do now.

Finally, when injecting without a matching local interface, don't
assign sequence numbers at all.

Signed-off-by: Johannes Berg <johannes@sipsolutions.net>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
drivers/net/wireless/mac80211_hwsim.c
drivers/net/wireless/rt2x00/rt2x00queue.c
include/net/mac80211.h
net/mac80211/tx.c
net/mac80211/util.c

index da8aa83481f7bafd3d825ba24448103b6e77f7d8..631d65b73edfb90732a6cc560ea8b64c9b805662 100644 (file)
@@ -258,7 +258,8 @@ static int mac80211_hwsim_tx(struct ieee80211_hw *hw, struct sk_buff *skb)
 
        txi = IEEE80211_SKB_CB(skb);
 
-       hwsim_check_magic(txi->control.vif);
+       if (txi->control.vif)
+               hwsim_check_magic(txi->control.vif);
        if (txi->control.sta)
                hwsim_check_sta_magic(txi->control.sta);
 
index a5e965068c83cda2a777cfac76e1f8466eb7fdf9..b7f4fe8fba6e680f6a081a83a050abbce261ce62 100644 (file)
@@ -155,7 +155,6 @@ static void rt2x00queue_create_tx_descriptor(struct queue_entry *entry,
 {
        struct rt2x00_dev *rt2x00dev = entry->queue->rt2x00dev;
        struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(entry->skb);
-       struct rt2x00_intf *intf = vif_to_intf(tx_info->control.vif);
        struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)entry->skb->data;
        struct ieee80211_rate *rate =
            ieee80211_get_tx_rate(rt2x00dev->hw, tx_info);
@@ -278,16 +277,22 @@ static void rt2x00queue_create_tx_descriptor(struct queue_entry *entry,
         * sequence counter given by mac80211.
         */
        if (tx_info->flags & IEEE80211_TX_CTL_ASSIGN_SEQ) {
-               spin_lock_irqsave(&intf->seqlock, irqflags);
+               if (likely(tx_info->control.vif)) {
+                       struct rt2x00_intf *intf;
 
-               if (test_bit(ENTRY_TXD_FIRST_FRAGMENT, &txdesc->flags))
-                       intf->seqno += 0x10;
-               hdr->seq_ctrl &= cpu_to_le16(IEEE80211_SCTL_FRAG);
-               hdr->seq_ctrl |= cpu_to_le16(intf->seqno);
+                       intf = vif_to_intf(tx_info->control.vif);
 
-               spin_unlock_irqrestore(&intf->seqlock, irqflags);
+                       spin_lock_irqsave(&intf->seqlock, irqflags);
 
-               __set_bit(ENTRY_TXD_GENERATE_SEQ, &txdesc->flags);
+                       if (test_bit(ENTRY_TXD_FIRST_FRAGMENT, &txdesc->flags))
+                               intf->seqno += 0x10;
+                       hdr->seq_ctrl &= cpu_to_le16(IEEE80211_SCTL_FRAG);
+                       hdr->seq_ctrl |= cpu_to_le16(intf->seqno);
+
+                       spin_unlock_irqrestore(&intf->seqlock, irqflags);
+
+                       __set_bit(ENTRY_TXD_GENERATE_SEQ, &txdesc->flags);
+               }
        }
 
        /*
index d6669fd3ffa898eef69754cbb665a610fc35d31b..003e4a03874e0012b9fbd597a792140cd59ac128 100644 (file)
@@ -330,6 +330,7 @@ struct ieee80211_tx_info {
 
        union {
                struct {
+                       /* NB: vif can be NULL for injected frames */
                        struct ieee80211_vif *vif;
                        struct ieee80211_key_conf *hw_key;
                        struct ieee80211_sta *sta;
index 698c8233e6b3d5604016ee21452e2ac1ee293b01..c12f361d71857c5fb2a7c9cbf5f8c4525806709d 100644 (file)
@@ -624,7 +624,14 @@ ieee80211_tx_h_sequence(struct ieee80211_tx_data *tx)
        u8 *qc;
        int tid;
 
-       /* only for injected frames */
+       /*
+        * Packet injection may want to control the sequence
+        * number, if we have no matching interface then we
+        * neither assign one ourselves nor ask the driver to.
+        */
+       if (unlikely(!info->control.vif))
+               return TX_CONTINUE;
+
        if (unlikely(ieee80211_is_ctl(hdr->frame_control)))
                return TX_CONTINUE;
 
@@ -849,7 +856,6 @@ __ieee80211_parse_tx_radiotap(struct ieee80211_tx_data *tx,
        sband = tx->local->hw.wiphy->bands[tx->channel->band];
 
        skb->do_not_encrypt = 1;
-       info->flags |= IEEE80211_TX_CTL_INJECTED;
        tx->flags &= ~IEEE80211_TX_FRAGMENTED;
 
        /*
@@ -981,7 +987,7 @@ __ieee80211_tx_prepare(struct ieee80211_tx_data *tx,
 
        /* process and remove the injection radiotap header */
        sdata = IEEE80211_DEV_TO_SUB_IF(dev);
-       if (unlikely(sdata->vif.type == NL80211_IFTYPE_MONITOR)) {
+       if (unlikely(info->flags & IEEE80211_TX_CTL_INJECTED)) {
                if (__ieee80211_parse_tx_radiotap(tx, skb) == TX_DROP)
                        return TX_DROP;
 
@@ -1300,6 +1306,11 @@ int ieee80211_master_start_xmit(struct sk_buff *skb,
        struct ieee80211_sub_if_data *osdata;
        int headroom;
        bool may_encrypt;
+       enum {
+               NOT_MONITOR,
+               FOUND_SDATA,
+               UNKNOWN_ADDRESS,
+       } monitor_iface = NOT_MONITOR;
        int ret;
 
        if (skb->iif)
@@ -1335,6 +1346,50 @@ int ieee80211_master_start_xmit(struct sk_buff *skb,
                                IEEE80211_IFSTA_MESH_CTR_INC(&osdata->u.mesh,
                                                             fwded_frames);
                }
+       } else if (unlikely(osdata->vif.type == NL80211_IFTYPE_MONITOR)) {
+               struct ieee80211_sub_if_data *sdata;
+               struct ieee80211_local *local = osdata->local;
+               struct ieee80211_hdr *hdr;
+               int hdrlen;
+               u16 len_rthdr;
+
+               info->flags |= IEEE80211_TX_CTL_INJECTED;
+               monitor_iface = UNKNOWN_ADDRESS;
+
+               len_rthdr = ieee80211_get_radiotap_len(skb->data);
+               hdr = (struct ieee80211_hdr *)skb->data + len_rthdr;
+               hdrlen = ieee80211_hdrlen(hdr->frame_control);
+
+               /* check the header is complete in the frame */
+               if (likely(skb->len >= len_rthdr + hdrlen)) {
+                       /*
+                        * We process outgoing injected frames that have a
+                        * local address we handle as though they are our
+                        * own frames.
+                        * This code here isn't entirely correct, the local
+                        * MAC address is not necessarily enough to find
+                        * the interface to use; for that proper VLAN/WDS
+                        * support we will need a different mechanism.
+                        */
+
+                       rcu_read_lock();
+                       list_for_each_entry_rcu(sdata, &local->interfaces,
+                                               list) {
+                               if (!netif_running(sdata->dev))
+                                       continue;
+                               if (compare_ether_addr(sdata->dev->dev_addr,
+                                                      hdr->addr2)) {
+                                       dev_hold(sdata->dev);
+                                       dev_put(odev);
+                                       osdata = sdata;
+                                       odev = osdata->dev;
+                                       skb->iif = sdata->dev->ifindex;
+                                       monitor_iface = FOUND_SDATA;
+                                       break;
+                               }
+                       }
+                       rcu_read_unlock();
+               }
        }
 
        may_encrypt = !skb->do_not_encrypt;
@@ -1355,7 +1410,8 @@ int ieee80211_master_start_xmit(struct sk_buff *skb,
                osdata = container_of(osdata->bss,
                                      struct ieee80211_sub_if_data,
                                      u.ap);
-       info->control.vif = &osdata->vif;
+       if (likely(monitor_iface != UNKNOWN_ADDRESS))
+               info->control.vif = &osdata->vif;
        ret = ieee80211_tx(odev, skb);
        dev_put(odev);
 
index 6eb222369bcbfd381ad4e0e35bdb1569d5ca5e2f..f32561ec224ca4fb1ec6a83a53d0b59dc7d63451 100644 (file)
@@ -231,16 +231,21 @@ __le16 ieee80211_generic_frame_duration(struct ieee80211_hw *hw,
                                        struct ieee80211_rate *rate)
 {
        struct ieee80211_local *local = hw_to_local(hw);
-       struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif);
+       struct ieee80211_sub_if_data *sdata;
        u16 dur;
        int erp;
+       bool short_preamble = false;
 
        erp = 0;
-       if (sdata->flags & IEEE80211_SDATA_OPERATING_GMODE)
-               erp = rate->flags & IEEE80211_RATE_ERP_G;
+       if (vif) {
+               sdata = vif_to_sdata(vif);
+               short_preamble = sdata->bss_conf.use_short_preamble;
+               if (sdata->flags & IEEE80211_SDATA_OPERATING_GMODE)
+                       erp = rate->flags & IEEE80211_RATE_ERP_G;
+       }
 
        dur = ieee80211_frame_duration(local, frame_len, rate->bitrate, erp,
-                                      sdata->bss_conf.use_short_preamble);
+                                      short_preamble);
 
        return cpu_to_le16(dur);
 }
@@ -252,7 +257,7 @@ __le16 ieee80211_rts_duration(struct ieee80211_hw *hw,
 {
        struct ieee80211_local *local = hw_to_local(hw);
        struct ieee80211_rate *rate;
-       struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif);
+       struct ieee80211_sub_if_data *sdata;
        bool short_preamble;
        int erp;
        u16 dur;
@@ -260,13 +265,17 @@ __le16 ieee80211_rts_duration(struct ieee80211_hw *hw,
 
        sband = local->hw.wiphy->bands[local->hw.conf.channel->band];
 
-       short_preamble = sdata->bss_conf.use_short_preamble;
+       short_preamble = false;
 
        rate = &sband->bitrates[frame_txctl->control.rts_cts_rate_idx];
 
        erp = 0;
-       if (sdata->flags & IEEE80211_SDATA_OPERATING_GMODE)
-               erp = rate->flags & IEEE80211_RATE_ERP_G;
+       if (vif) {
+               sdata = vif_to_sdata(vif);
+               short_preamble = sdata->bss_conf.use_short_preamble;
+               if (sdata->flags & IEEE80211_SDATA_OPERATING_GMODE)
+                       erp = rate->flags & IEEE80211_RATE_ERP_G;
+       }
 
        /* CTS duration */
        dur = ieee80211_frame_duration(local, 10, rate->bitrate,
@@ -289,7 +298,7 @@ __le16 ieee80211_ctstoself_duration(struct ieee80211_hw *hw,
 {
        struct ieee80211_local *local = hw_to_local(hw);
        struct ieee80211_rate *rate;
-       struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif);
+       struct ieee80211_sub_if_data *sdata;
        bool short_preamble;
        int erp;
        u16 dur;
@@ -297,12 +306,16 @@ __le16 ieee80211_ctstoself_duration(struct ieee80211_hw *hw,
 
        sband = local->hw.wiphy->bands[local->hw.conf.channel->band];
 
-       short_preamble = sdata->bss_conf.use_short_preamble;
+       short_preamble = false;
 
        rate = &sband->bitrates[frame_txctl->control.rts_cts_rate_idx];
        erp = 0;
-       if (sdata->flags & IEEE80211_SDATA_OPERATING_GMODE)
-               erp = rate->flags & IEEE80211_RATE_ERP_G;
+       if (vif) {
+               sdata = vif_to_sdata(vif);
+               short_preamble = sdata->bss_conf.use_short_preamble;
+               if (sdata->flags & IEEE80211_SDATA_OPERATING_GMODE)
+                       erp = rate->flags & IEEE80211_RATE_ERP_G;
+       }
 
        /* Data frame duration */
        dur = ieee80211_frame_duration(local, frame_len, rate->bitrate,