mac80211: fix VLAN handling with TXQs
authorJohannes Berg <johannes.berg@intel.com>
Thu, 22 Jun 2017 10:20:30 +0000 (12:20 +0200)
committerJohannes Berg <johannes.berg@intel.com>
Tue, 5 Sep 2017 09:28:43 +0000 (11:28 +0200)
With TXQs, the AP_VLAN interfaces are resolved to their owner AP
interface when enqueuing the frame, which makes sense since the
frame really goes out on that as far as the driver is concerned.

However, this introduces a problem: frames to be encrypted with
a VLAN-specific GTK will now be encrypted with the AP GTK, since
the information about which virtual interface to use to select
the key is taken from the TXQ.

Fix this by preserving info->control.vif and using that in the
dequeue function. This now requires doing the driver-mapping
in the dequeue as well.

Since there's no way to filter the frames that are sitting on a
TXQ, drop all frames, which may affect other interfaces, when an
AP_VLAN is removed.

Cc: stable@vger.kernel.org
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
include/net/mac80211.h
net/mac80211/iface.c
net/mac80211/tx.c

index f8149ca192b430c181406fa9a3e4db8fc0a17252..885690fa39c8d8f4a48f2fa25447646d3d0a7856 100644 (file)
@@ -919,21 +919,10 @@ struct ieee80211_tx_info {
                                unsigned long jiffies;
                        };
                        /* NB: vif can be NULL for injected frames */
-                       union {
-                               /* NB: vif can be NULL for injected frames */
-                               struct ieee80211_vif *vif;
-
-                               /* When packets are enqueued on txq it's easy
-                                * to re-construct the vif pointer. There's no
-                                * more space in tx_info so it can be used to
-                                * store the necessary enqueue time for packet
-                                * sojourn time computation.
-                                */
-                               codel_time_t enqueue_time;
-                       };
+                       struct ieee80211_vif *vif;
                        struct ieee80211_key_conf *hw_key;
                        u32 flags;
-                       /* 4 bytes free */
+                       codel_time_t enqueue_time;
                } control;
                struct {
                        u64 cookie;
index 9228ac73c429921673bf2c058e821a07dcdfec92..44399322f356b007f2e7d60f6605ba4762eb379c 100644 (file)
@@ -792,6 +792,7 @@ static int ieee80211_open(struct net_device *dev)
 static void ieee80211_do_stop(struct ieee80211_sub_if_data *sdata,
                              bool going_down)
 {
+       struct ieee80211_sub_if_data *txq_sdata = sdata;
        struct ieee80211_local *local = sdata->local;
        struct fq *fq = &local->fq;
        unsigned long flags;
@@ -937,6 +938,9 @@ static void ieee80211_do_stop(struct ieee80211_sub_if_data *sdata,
 
        switch (sdata->vif.type) {
        case NL80211_IFTYPE_AP_VLAN:
+               txq_sdata = container_of(sdata->bss,
+                                        struct ieee80211_sub_if_data, u.ap);
+
                mutex_lock(&local->mtx);
                list_del(&sdata->u.vlan.list);
                mutex_unlock(&local->mtx);
@@ -1007,8 +1011,17 @@ static void ieee80211_do_stop(struct ieee80211_sub_if_data *sdata,
        }
        spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags);
 
-       if (sdata->vif.txq) {
-               struct txq_info *txqi = to_txq_info(sdata->vif.txq);
+       if (txq_sdata->vif.txq) {
+               struct txq_info *txqi = to_txq_info(txq_sdata->vif.txq);
+
+               /*
+                * FIXME FIXME
+                *
+                * We really shouldn't purge the *entire* txqi since that
+                * contains frames for the other AP_VLANs (and possibly
+                * the AP itself) as well, but there's no API in FQ now
+                * to be able to filter.
+                */
 
                spin_lock_bh(&fq->lock);
                ieee80211_txq_purge(local, txqi);
index 8858f4f185e90953d096ec28debc21f5dd2bc20d..94826680cf2b54e2a6254146856b495d0ba5e861 100644 (file)
@@ -1276,11 +1276,6 @@ static void ieee80211_set_skb_enqueue_time(struct sk_buff *skb)
        IEEE80211_SKB_CB(skb)->control.enqueue_time = codel_get_time();
 }
 
-static void ieee80211_set_skb_vif(struct sk_buff *skb, struct txq_info *txqi)
-{
-       IEEE80211_SKB_CB(skb)->control.vif = txqi->txq.vif;
-}
-
 static u32 codel_skb_len_func(const struct sk_buff *skb)
 {
        return skb->len;
@@ -3414,6 +3409,7 @@ struct sk_buff *ieee80211_tx_dequeue(struct ieee80211_hw *hw,
        struct ieee80211_tx_info *info;
        struct ieee80211_tx_data tx;
        ieee80211_tx_result r;
+       struct ieee80211_vif *vif;
 
        spin_lock_bh(&fq->lock);
 
@@ -3430,8 +3426,6 @@ begin:
        if (!skb)
                goto out;
 
-       ieee80211_set_skb_vif(skb, txqi);
-
        hdr = (struct ieee80211_hdr *)skb->data;
        info = IEEE80211_SKB_CB(skb);
 
@@ -3488,6 +3482,34 @@ begin:
                }
        }
 
+       switch (tx.sdata->vif.type) {
+       case NL80211_IFTYPE_MONITOR:
+               if (tx.sdata->u.mntr.flags & MONITOR_FLAG_ACTIVE) {
+                       vif = &tx.sdata->vif;
+                       break;
+               }
+               tx.sdata = rcu_dereference(local->monitor_sdata);
+               if (tx.sdata) {
+                       vif = &tx.sdata->vif;
+                       info->hw_queue =
+                               vif->hw_queue[skb_get_queue_mapping(skb)];
+               } else if (ieee80211_hw_check(&local->hw, QUEUE_CONTROL)) {
+                       ieee80211_free_txskb(&local->hw, skb);
+                       goto begin;
+               } else {
+                       vif = NULL;
+               }
+               break;
+       case NL80211_IFTYPE_AP_VLAN:
+               tx.sdata = container_of(tx.sdata->bss,
+                                       struct ieee80211_sub_if_data, u.ap);
+               /* fall through */
+       default:
+               vif = &tx.sdata->vif;
+               break;
+       }
+
+       IEEE80211_SKB_CB(skb)->control.vif = vif;
 out:
        spin_unlock_bh(&fq->lock);