mac80211: fix virtual interfaces vs. injection
[safe/jmp/linux-2.6] / net / mac80211 / tx.c
index 698c823..c12f361 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);