mac80211: merge a few performance enhancements
authorFelix Fietkau <nbd@openwrt.org>
Fri, 20 Mar 2015 16:24:14 +0000 (16:24 +0000)
committerFelix Fietkau <nbd@openwrt.org>
Fri, 20 Mar 2015 16:24:14 +0000 (16:24 +0000)
Signed-off-by: Felix Fietkau <nbd@openwrt.org>
SVN-Revision: 44910

package/kernel/mac80211/patches/310-mac80211-don-t-look-up-stations-for-multicast-addres.patch [new file with mode: 0644]
package/kernel/mac80211/patches/311-mac80211-remove-drop_unencrypted-code.patch [new file with mode: 0644]
package/kernel/mac80211/patches/312-mac80211-don-t-look-up-destination-station-twice.patch [new file with mode: 0644]
package/kernel/mac80211/patches/313-mac80211-drop-4-addr-VLAN-frames-earlier-if-not-conn.patch [new file with mode: 0644]
package/kernel/mac80211/patches/314-mac80211-mesh-avoid-pointless-station-lookup.patch [new file with mode: 0644]
package/kernel/mac80211/patches/315-mac80211-avoid-duplicate-TX-path-station-lookup.patch [new file with mode: 0644]
package/kernel/mac80211/patches/522-mac80211_configure_antenna_gain.patch

diff --git a/package/kernel/mac80211/patches/310-mac80211-don-t-look-up-stations-for-multicast-addres.patch b/package/kernel/mac80211/patches/310-mac80211-don-t-look-up-stations-for-multicast-addres.patch
new file mode 100644 (file)
index 0000000..7a039d6
--- /dev/null
@@ -0,0 +1,21 @@
+From: Johannes Berg <johannes.berg@intel.com>
+Date: Tue, 24 Feb 2015 00:28:18 +0100
+Subject: [PATCH] mac80211: don't look up stations for multicast addresses
+
+Since multicast addresses don't exist as stations, don't attempt
+to look them up in the hashtable on TX.
+
+Signed-off-by: Johannes Berg <johannes.berg@intel.com>
+---
+
+--- a/net/mac80211/tx.c
++++ b/net/mac80211/tx.c
+@@ -1162,7 +1162,7 @@ ieee80211_tx_prepare(struct ieee80211_su
+                  tx->sdata->control_port_protocol == tx->skb->protocol) {
+               tx->sta = sta_info_get_bss(sdata, hdr->addr1);
+       }
+-      if (!tx->sta)
++      if (!tx->sta && !is_multicast_ether_addr(hdr->addr1))
+               tx->sta = sta_info_get(sdata, hdr->addr1);
+       if (tx->sta && ieee80211_is_data_qos(hdr->frame_control) &&
diff --git a/package/kernel/mac80211/patches/311-mac80211-remove-drop_unencrypted-code.patch b/package/kernel/mac80211/patches/311-mac80211-remove-drop_unencrypted-code.patch
new file mode 100644 (file)
index 0000000..d6bc082
--- /dev/null
@@ -0,0 +1,130 @@
+From: Johannes Berg <johannes.berg@intel.com>
+Date: Fri, 20 Mar 2015 11:41:58 +0100
+Subject: [PATCH] mac80211: remove drop_unencrypted code
+
+This mechanism was historic, and only ever used by IBSS, which
+also doesn't need to have it as it properly manages station's
+802.1X PAE state (or, with WEP, always has a key.)
+
+Remove the mechanism to clean up the code.
+
+Signed-off-by: Johannes Berg <johannes.berg@intel.com>
+---
+
+--- a/net/mac80211/debugfs.c
++++ b/net/mac80211/debugfs.c
+@@ -274,8 +274,6 @@ void debugfs_hw_add(struct ieee80211_loc
+ #ifdef CPTCFG_MAC80211_DEBUG_COUNTERS
+       DEBUGFS_STATS_ADD(tx_handlers_drop, local->tx_handlers_drop);
+       DEBUGFS_STATS_ADD(tx_handlers_queued, local->tx_handlers_queued);
+-      DEBUGFS_STATS_ADD(tx_handlers_drop_unencrypted,
+-              local->tx_handlers_drop_unencrypted);
+       DEBUGFS_STATS_ADD(tx_handlers_drop_fragment,
+               local->tx_handlers_drop_fragment);
+       DEBUGFS_STATS_ADD(tx_handlers_drop_wep,
+--- a/net/mac80211/debugfs_netdev.c
++++ b/net/mac80211/debugfs_netdev.c
+@@ -177,7 +177,6 @@ static ssize_t ieee80211_if_write_##name
+       IEEE80211_IF_FILE_R(name)
+ /* common attributes */
+-IEEE80211_IF_FILE(drop_unencrypted, drop_unencrypted, DEC);
+ IEEE80211_IF_FILE(rc_rateidx_mask_2ghz, rc_rateidx_mask[IEEE80211_BAND_2GHZ],
+                 HEX);
+ IEEE80211_IF_FILE(rc_rateidx_mask_5ghz, rc_rateidx_mask[IEEE80211_BAND_5GHZ],
+@@ -562,7 +561,6 @@ IEEE80211_IF_FILE(dot11MeshAwakeWindowDu
+ static void add_common_files(struct ieee80211_sub_if_data *sdata)
+ {
+-      DEBUGFS_ADD(drop_unencrypted);
+       DEBUGFS_ADD(rc_rateidx_mask_2ghz);
+       DEBUGFS_ADD(rc_rateidx_mask_5ghz);
+       DEBUGFS_ADD(rc_rateidx_mcs_mask_2ghz);
+--- a/net/mac80211/ibss.c
++++ b/net/mac80211/ibss.c
+@@ -249,8 +249,6 @@ static void __ieee80211_sta_join_ibss(st
+       if (presp)
+               kfree_rcu(presp, rcu_head);
+-      sdata->drop_unencrypted = capability & WLAN_CAPABILITY_PRIVACY ? 1 : 0;
+-
+       /* make a copy of the chandef, it could be modified below. */
+       chandef = *req_chandef;
+       chan = chandef.chan;
+@@ -1289,8 +1287,6 @@ static void ieee80211_sta_create_ibss(st
+       if (ifibss->privacy)
+               capability |= WLAN_CAPABILITY_PRIVACY;
+-      else
+-              sdata->drop_unencrypted = 0;
+       __ieee80211_sta_join_ibss(sdata, bssid, sdata->vif.bss_conf.beacon_int,
+                                 &ifibss->chandef, ifibss->basic_rates,
+--- a/net/mac80211/ieee80211_i.h
++++ b/net/mac80211/ieee80211_i.h
+@@ -842,8 +842,6 @@ struct ieee80211_sub_if_data {
+       unsigned long state;
+-      int drop_unencrypted;
+-
+       char name[IFNAMSIZ];
+       /* Fragment table for host-based reassembly */
+@@ -1289,7 +1287,6 @@ struct ieee80211_local {
+       /* TX/RX handler statistics */
+       unsigned int tx_handlers_drop;
+       unsigned int tx_handlers_queued;
+-      unsigned int tx_handlers_drop_unencrypted;
+       unsigned int tx_handlers_drop_fragment;
+       unsigned int tx_handlers_drop_wep;
+       unsigned int tx_handlers_drop_not_assoc;
+--- a/net/mac80211/iface.c
++++ b/net/mac80211/iface.c
+@@ -1535,7 +1535,6 @@ int ieee80211_if_change_type(struct ieee
+       }
+       /* reset some values that shouldn't be kept across type changes */
+-      sdata->drop_unencrypted = 0;
+       if (type == NL80211_IFTYPE_STATION)
+               sdata->u.mgd.use_4addr = false;
+--- a/net/mac80211/rx.c
++++ b/net/mac80211/rx.c
+@@ -1897,8 +1897,7 @@ static int ieee80211_drop_unencrypted(st
+       /* Drop unencrypted frames if key is set. */
+       if (unlikely(!ieee80211_has_protected(fc) &&
+                    !ieee80211_is_nullfunc(fc) &&
+-                   ieee80211_is_data(fc) &&
+-                   (rx->key || rx->sdata->drop_unencrypted)))
++                   ieee80211_is_data(fc) && rx->key))
+               return -EACCES;
+       return 0;
+--- a/net/mac80211/tx.c
++++ b/net/mac80211/tx.c
+@@ -594,23 +594,8 @@ ieee80211_tx_h_select_key(struct ieee802
+       else if (!is_multicast_ether_addr(hdr->addr1) &&
+                (key = rcu_dereference(tx->sdata->default_unicast_key)))
+               tx->key = key;
+-      else if (info->flags & IEEE80211_TX_CTL_INJECTED)
+-              tx->key = NULL;
+-      else if (!tx->sdata->drop_unencrypted)
+-              tx->key = NULL;
+-      else if (tx->skb->protocol == tx->sdata->control_port_protocol)
+-              tx->key = NULL;
+-      else if (ieee80211_is_robust_mgmt_frame(tx->skb) &&
+-               !(ieee80211_is_action(hdr->frame_control) &&
+-                 tx->sta && test_sta_flag(tx->sta, WLAN_STA_MFP)))
+-              tx->key = NULL;
+-      else if (ieee80211_is_mgmt(hdr->frame_control) &&
+-               !ieee80211_is_robust_mgmt_frame(tx->skb))
++      else
+               tx->key = NULL;
+-      else {
+-              I802_DEBUG_INC(tx->local->tx_handlers_drop_unencrypted);
+-              return TX_DROP;
+-      }
+       if (tx->key) {
+               bool skip_hw = false;
diff --git a/package/kernel/mac80211/patches/312-mac80211-don-t-look-up-destination-station-twice.patch b/package/kernel/mac80211/patches/312-mac80211-don-t-look-up-destination-station-twice.patch
new file mode 100644 (file)
index 0000000..6109bc2
--- /dev/null
@@ -0,0 +1,71 @@
+From: Johannes Berg <johannes.berg@intel.com>
+Date: Fri, 20 Mar 2015 16:24:21 +0100
+Subject: [PATCH] mac80211: don't look up destination station twice
+
+There's no need to look up the destination station twice while
+building the 802.11 header for a given frame if the frame will
+actually be transmitted to the station we initially looked up.
+
+This happens for 4-addr VLAN interfaces and TDLS connections, which
+both directly send the frame to the station they looked up, though
+in the case of TDLS some station conditions need to be checked.
+
+To avoid that, add a variable indicating that we've looked up the
+station that the frame is going to be transmitted to, and avoid the
+lookup/flag checking if it already has been done.
+
+In the TDLS case, also move the authorized/wme_sta flag assignment
+to the correct place, i.e. only when that station is really used.
+Before this change, the new lookup should always have succeeded so
+that the potentially erroneous data would be overwritten.
+
+Signed-off-by: Johannes Berg <johannes.berg@intel.com>
+---
+
+--- a/net/mac80211/tx.c
++++ b/net/mac80211/tx.c
+@@ -1895,6 +1895,7 @@ static struct sk_buff *ieee80211_build_h
+       bool wme_sta = false, authorized = false, tdls_auth = false;
+       bool tdls_peer = false, tdls_setup_frame = false;
+       bool multicast;
++      bool have_station = false;
+       u16 info_id = 0;
+       struct ieee80211_chanctx_conf *chanctx_conf;
+       struct ieee80211_sub_if_data *ap_sdata;
+@@ -1919,6 +1920,7 @@ static struct sk_buff *ieee80211_build_h
+                       hdrlen = 30;
+                       authorized = test_sta_flag(sta, WLAN_STA_AUTHORIZED);
+                       wme_sta = sta->sta.wme;
++                      have_station = true;
+               }
+               ap_sdata = container_of(sdata->bss, struct ieee80211_sub_if_data,
+                                       u.ap);
+@@ -2035,9 +2037,6 @@ static struct sk_buff *ieee80211_build_h
+               if (sdata->wdev.wiphy->flags & WIPHY_FLAG_SUPPORTS_TDLS) {
+                       sta = sta_info_get(sdata, skb->data);
+                       if (sta) {
+-                              authorized = test_sta_flag(sta,
+-                                                      WLAN_STA_AUTHORIZED);
+-                              wme_sta = sta->sta.wme;
+                               tdls_peer = test_sta_flag(sta,
+                                                         WLAN_STA_TDLS_PEER);
+                               tdls_auth = test_sta_flag(sta,
+@@ -2069,6 +2068,9 @@ static struct sk_buff *ieee80211_build_h
+                       memcpy(hdr.addr2, skb->data + ETH_ALEN, ETH_ALEN);
+                       memcpy(hdr.addr3, sdata->u.mgd.bssid, ETH_ALEN);
+                       hdrlen = 24;
++                      have_station = true;
++                      authorized = test_sta_flag(sta, WLAN_STA_AUTHORIZED);
++                      wme_sta = sta->sta.wme;
+               }  else if (sdata->u.mgd.use_4addr &&
+                           cpu_to_be16(ethertype) != sdata->control_port_protocol) {
+                       fc |= cpu_to_le16(IEEE80211_FCTL_FROMDS |
+@@ -2131,7 +2133,7 @@ static struct sk_buff *ieee80211_build_h
+        * in AP mode)
+        */
+       multicast = is_multicast_ether_addr(hdr.addr1);
+-      if (!multicast) {
++      if (!multicast && !have_station) {
+               sta = sta_info_get(sdata, hdr.addr1);
+               if (sta) {
+                       authorized = test_sta_flag(sta, WLAN_STA_AUTHORIZED);
diff --git a/package/kernel/mac80211/patches/313-mac80211-drop-4-addr-VLAN-frames-earlier-if-not-conn.patch b/package/kernel/mac80211/patches/313-mac80211-drop-4-addr-VLAN-frames-earlier-if-not-conn.patch
new file mode 100644 (file)
index 0000000..f493f65
--- /dev/null
@@ -0,0 +1,27 @@
+From: Johannes Berg <johannes.berg@intel.com>
+Date: Fri, 20 Mar 2015 16:24:22 +0100
+Subject: [PATCH] mac80211: drop 4-addr VLAN frames earlier if not
+ connected
+
+If there's no station on the 4-addr VLAN interface, then frames
+cannot be transmitted. Drop such frames earlier, before setting
+up all the information for them.
+
+We should keep the old check though since that code might be used
+for other internally-generated frames.
+
+Signed-off-by: Johannes Berg <johannes.berg@intel.com>
+---
+
+--- a/net/mac80211/tx.c
++++ b/net/mac80211/tx.c
+@@ -1921,6 +1921,9 @@ static struct sk_buff *ieee80211_build_h
+                       authorized = test_sta_flag(sta, WLAN_STA_AUTHORIZED);
+                       wme_sta = sta->sta.wme;
+                       have_station = true;
++              } else if (sdata->wdev.use_4addr) {
++                      ret = -ENOLINK;
++                      goto free;
+               }
+               ap_sdata = container_of(sdata->bss, struct ieee80211_sub_if_data,
+                                       u.ap);
diff --git a/package/kernel/mac80211/patches/314-mac80211-mesh-avoid-pointless-station-lookup.patch b/package/kernel/mac80211/patches/314-mac80211-mesh-avoid-pointless-station-lookup.patch
new file mode 100644 (file)
index 0000000..c476dd4
--- /dev/null
@@ -0,0 +1,33 @@
+From: Johannes Berg <johannes.berg@intel.com>
+Date: Fri, 20 Mar 2015 16:24:23 +0100
+Subject: [PATCH] mac80211: mesh: avoid pointless station lookup
+
+In ieee80211_build_hdr(), the station is looked up to build the
+header correctly (QoS field) and to check for authorization. For
+mesh, authorization isn't checked here, and QoS capability is
+mandatory, so the station lookup can be avoided.
+
+Signed-off-by: Johannes Berg <johannes.berg@intel.com>
+---
+
+--- a/net/mac80211/tx.c
++++ b/net/mac80211/tx.c
+@@ -2131,12 +2131,14 @@ static struct sk_buff *ieee80211_build_h
+       }
+       /*
+-       * There's no need to try to look up the destination
+-       * if it is a multicast address (which can only happen
+-       * in AP mode)
++       * There's no need to try to look up the destination station
++       * if it is a multicast address. In mesh, there's no need to
++       * look up the station at all as it always must be QoS capable
++       * and mesh mode checks authorization later.
+        */
+       multicast = is_multicast_ether_addr(hdr.addr1);
+-      if (!multicast && !have_station) {
++      if (!multicast && !have_station &&
++          !ieee80211_vif_is_mesh(&sdata->vif)) {
+               sta = sta_info_get(sdata, hdr.addr1);
+               if (sta) {
+                       authorized = test_sta_flag(sta, WLAN_STA_AUTHORIZED);
diff --git a/package/kernel/mac80211/patches/315-mac80211-avoid-duplicate-TX-path-station-lookup.patch b/package/kernel/mac80211/patches/315-mac80211-avoid-duplicate-TX-path-station-lookup.patch
new file mode 100644 (file)
index 0000000..31ed830
--- /dev/null
@@ -0,0 +1,267 @@
+From: Johannes Berg <johannes.berg@intel.com>
+Date: Fri, 20 Mar 2015 14:18:27 +0100
+Subject: [PATCH] mac80211: avoid duplicate TX path station lookup
+
+Instead of looking up the destination station twice in the TX path
+(first to build the header, and then for control processing), save
+it when building the header and use it later in the TX path.
+
+To avoid having to look up the station in the many callers, allow
+those to pass %NULL which keeps the existing lookup.
+
+Signed-off-by: Johannes Berg <johannes.berg@intel.com>
+---
+
+--- a/net/mac80211/cfg.c
++++ b/net/mac80211/cfg.c
+@@ -3565,7 +3565,7 @@ static int ieee80211_probe_client(struct
+               nullfunc->qos_ctrl = cpu_to_le16(7);
+       local_bh_disable();
+-      ieee80211_xmit(sdata, skb);
++      ieee80211_xmit(sdata, sta, skb);
+       local_bh_enable();
+       rcu_read_unlock();
+--- a/net/mac80211/ieee80211_i.h
++++ b/net/mac80211/ieee80211_i.h
+@@ -1775,7 +1775,8 @@ void mac80211_ev_michael_mic_failure(str
+                                    gfp_t gfp);
+ void ieee80211_set_wmm_default(struct ieee80211_sub_if_data *sdata,
+                              bool bss_notify);
+-void ieee80211_xmit(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb);
++void ieee80211_xmit(struct ieee80211_sub_if_data *sdata,
++                  struct sta_info *sta, struct sk_buff *skb);
+ void __ieee80211_tx_skb_tid_band(struct ieee80211_sub_if_data *sdata,
+                                struct sk_buff *skb, int tid,
+--- a/net/mac80211/sta_info.c
++++ b/net/mac80211/sta_info.c
+@@ -1279,7 +1279,7 @@ static void ieee80211_send_null_response
+       }
+       info->band = chanctx_conf->def.chan->band;
+-      ieee80211_xmit(sdata, skb);
++      ieee80211_xmit(sdata, sta, skb);
+       rcu_read_unlock();
+ }
+--- a/net/mac80211/tx.c
++++ b/net/mac80211/tx.c
+@@ -1111,11 +1111,13 @@ static bool ieee80211_tx_prep_agg(struct
+ /*
+  * initialises @tx
++ * pass %NULL for the station if unknown, a valid pointer if known
++ * or an ERR_PTR() if the station is known not to exist
+  */
+ static ieee80211_tx_result
+ ieee80211_tx_prepare(struct ieee80211_sub_if_data *sdata,
+                    struct ieee80211_tx_data *tx,
+-                   struct sk_buff *skb)
++                   struct sta_info *sta, struct sk_buff *skb)
+ {
+       struct ieee80211_local *local = sdata->local;
+       struct ieee80211_hdr *hdr;
+@@ -1138,17 +1140,22 @@ ieee80211_tx_prepare(struct ieee80211_su
+       hdr = (struct ieee80211_hdr *) skb->data;
+-      if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN) {
+-              tx->sta = rcu_dereference(sdata->u.vlan.sta);
+-              if (!tx->sta && sdata->dev->ieee80211_ptr->use_4addr)
+-                      return TX_DROP;
+-      } else if (info->flags & (IEEE80211_TX_CTL_INJECTED |
+-                                IEEE80211_TX_INTFL_NL80211_FRAME_TX) ||
+-                 tx->sdata->control_port_protocol == tx->skb->protocol) {
+-              tx->sta = sta_info_get_bss(sdata, hdr->addr1);
++      if (likely(sta)) {
++              if (!IS_ERR(sta))
++                      tx->sta = sta;
++      } else {
++              if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN) {
++                      tx->sta = rcu_dereference(sdata->u.vlan.sta);
++                      if (!tx->sta && sdata->wdev.use_4addr)
++                              return TX_DROP;
++              } else if (info->flags & (IEEE80211_TX_INTFL_NL80211_FRAME_TX |
++                                        IEEE80211_TX_CTL_INJECTED) ||
++                         tx->sdata->control_port_protocol == tx->skb->protocol) {
++                      tx->sta = sta_info_get_bss(sdata, hdr->addr1);
++              }
++              if (!tx->sta && !is_multicast_ether_addr(hdr->addr1))
++                      tx->sta = sta_info_get(sdata, hdr->addr1);
+       }
+-      if (!tx->sta && !is_multicast_ether_addr(hdr->addr1))
+-              tx->sta = sta_info_get(sdata, hdr->addr1);
+       if (tx->sta && ieee80211_is_data_qos(hdr->frame_control) &&
+           !ieee80211_is_qos_nullfunc(hdr->frame_control) &&
+@@ -1486,7 +1493,7 @@ bool ieee80211_tx_prepare_skb(struct iee
+       struct ieee80211_tx_data tx;
+       struct sk_buff *skb2;
+-      if (ieee80211_tx_prepare(sdata, &tx, skb) == TX_DROP)
++      if (ieee80211_tx_prepare(sdata, &tx, NULL, skb) == TX_DROP)
+               return false;
+       info->band = band;
+@@ -1519,7 +1526,8 @@ EXPORT_SYMBOL(ieee80211_tx_prepare_skb);
+  * Returns false if the frame couldn't be transmitted but was queued instead.
+  */
+ static bool ieee80211_tx(struct ieee80211_sub_if_data *sdata,
+-                       struct sk_buff *skb, bool txpending)
++                       struct sta_info *sta, struct sk_buff *skb,
++                       bool txpending)
+ {
+       struct ieee80211_local *local = sdata->local;
+       struct ieee80211_tx_data tx;
+@@ -1535,7 +1543,7 @@ static bool ieee80211_tx(struct ieee8021
+       /* initialises tx */
+       led_len = skb->len;
+-      res_prepare = ieee80211_tx_prepare(sdata, &tx, skb);
++      res_prepare = ieee80211_tx_prepare(sdata, &tx, sta, skb);
+       if (unlikely(res_prepare == TX_DROP)) {
+               ieee80211_free_txskb(&local->hw, skb);
+@@ -1591,7 +1599,8 @@ static int ieee80211_skb_resize(struct i
+       return 0;
+ }
+-void ieee80211_xmit(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb)
++void ieee80211_xmit(struct ieee80211_sub_if_data *sdata,
++                  struct sta_info *sta, struct sk_buff *skb)
+ {
+       struct ieee80211_local *local = sdata->local;
+       struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+@@ -1626,7 +1635,7 @@ void ieee80211_xmit(struct ieee80211_sub
+       }
+       ieee80211_set_qos_hdr(sdata, skb);
+-      ieee80211_tx(sdata, skb, false);
++      ieee80211_tx(sdata, sta, skb, false);
+ }
+ static bool ieee80211_parse_tx_radiotap(struct sk_buff *skb)
+@@ -1847,7 +1856,7 @@ netdev_tx_t ieee80211_monitor_start_xmit
+               goto fail_rcu;
+       info->band = chandef->chan->band;
+-      ieee80211_xmit(sdata, skb);
++      ieee80211_xmit(sdata, NULL, skb);
+       rcu_read_unlock();
+       return NETDEV_TX_OK;
+@@ -1878,7 +1887,8 @@ fail:
+  * Returns: the (possibly reallocated) skb or an ERR_PTR() code
+  */
+ static struct sk_buff *ieee80211_build_hdr(struct ieee80211_sub_if_data *sdata,
+-                                         struct sk_buff *skb, u32 info_flags)
++                                         struct sk_buff *skb, u32 info_flags,
++                                         struct sta_info **sta_out)
+ {
+       struct ieee80211_local *local = sdata->local;
+       struct ieee80211_tx_info *info;
+@@ -1921,6 +1931,7 @@ static struct sk_buff *ieee80211_build_h
+                       authorized = test_sta_flag(sta, WLAN_STA_AUTHORIZED);
+                       wme_sta = sta->sta.wme;
+                       have_station = true;
++                      *sta_out = sta;
+               } else if (sdata->wdev.use_4addr) {
+                       ret = -ENOLINK;
+                       goto free;
+@@ -2074,6 +2085,7 @@ static struct sk_buff *ieee80211_build_h
+                       have_station = true;
+                       authorized = test_sta_flag(sta, WLAN_STA_AUTHORIZED);
+                       wme_sta = sta->sta.wme;
++                      *sta_out = sta;
+               }  else if (sdata->u.mgd.use_4addr &&
+                           cpu_to_be16(ethertype) != sdata->control_port_protocol) {
+                       fc |= cpu_to_le16(IEEE80211_FCTL_FROMDS |
+@@ -2137,13 +2149,18 @@ static struct sk_buff *ieee80211_build_h
+        * and mesh mode checks authorization later.
+        */
+       multicast = is_multicast_ether_addr(hdr.addr1);
+-      if (!multicast && !have_station &&
+-          !ieee80211_vif_is_mesh(&sdata->vif)) {
+-              sta = sta_info_get(sdata, hdr.addr1);
++      if (multicast) {
++              *sta_out = ERR_PTR(-ENOENT);
++      } else if (!have_station && !ieee80211_vif_is_mesh(&sdata->vif)) {
++              if (sdata->control_port_protocol == skb->protocol)
++                      sta = sta_info_get_bss(sdata, hdr.addr1);
++              else
++                      sta = sta_info_get(sdata, hdr.addr1);
+               if (sta) {
+                       authorized = test_sta_flag(sta, WLAN_STA_AUTHORIZED);
+                       wme_sta = sta->sta.wme;
+               }
++              *sta_out = sta ?: ERR_PTR(-ENOENT);
+       }
+       /* For mesh, the use of the QoS header is mandatory */
+@@ -2321,6 +2338,7 @@ void __ieee80211_subif_start_xmit(struct
+                                 u32 info_flags)
+ {
+       struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
++      struct sta_info *sta = NULL;
+       if (unlikely(skb->len < ETH_HLEN)) {
+               kfree_skb(skb);
+@@ -2329,7 +2347,7 @@ void __ieee80211_subif_start_xmit(struct
+       rcu_read_lock();
+-      skb = ieee80211_build_hdr(sdata, skb, info_flags);
++      skb = ieee80211_build_hdr(sdata, skb, info_flags, &sta);
+       if (IS_ERR(skb))
+               goto out;
+@@ -2337,7 +2355,7 @@ void __ieee80211_subif_start_xmit(struct
+       dev->stats.tx_bytes += skb->len;
+       dev->trans_start = jiffies;
+-      ieee80211_xmit(sdata, skb);
++      ieee80211_xmit(sdata, sta, skb);
+  out:
+       rcu_read_unlock();
+ }
+@@ -2365,10 +2383,11 @@ ieee80211_build_data_template(struct iee
+               .local = sdata->local,
+               .sdata = sdata,
+       };
++      struct sta_info *sta_ignore;
+       rcu_read_lock();
+-      skb = ieee80211_build_hdr(sdata, skb, info_flags);
++      skb = ieee80211_build_hdr(sdata, skb, info_flags, &sta_ignore);
+       if (IS_ERR(skb))
+               goto out;
+@@ -2426,7 +2445,7 @@ static bool ieee80211_tx_pending_skb(str
+                       return true;
+               }
+               info->band = chanctx_conf->def.chan->band;
+-              result = ieee80211_tx(sdata, skb, true);
++              result = ieee80211_tx(sdata, NULL, skb, true);
+       } else {
+               struct sk_buff_head skbs;
+@@ -3164,7 +3183,7 @@ ieee80211_get_buffered_bc(struct ieee802
+               if (sdata->vif.type == NL80211_IFTYPE_AP)
+                       sdata = IEEE80211_DEV_TO_SUB_IF(skb->dev);
+-              if (!ieee80211_tx_prepare(sdata, &tx, skb))
++              if (!ieee80211_tx_prepare(sdata, &tx, NULL, skb))
+                       break;
+               dev_kfree_skb_any(skb);
+       }
+@@ -3296,6 +3315,6 @@ void __ieee80211_tx_skb_tid_band(struct 
+        */
+       local_bh_disable();
+       IEEE80211_SKB_CB(skb)->band = band;
+-      ieee80211_xmit(sdata, skb);
++      ieee80211_xmit(sdata, NULL, skb);
+       local_bh_enable();
+ }
index 673c6dc4226e47734f38d7774326ab18a0250020..e13fe31d3fda47b12567cfecbd6cc63f0a278cc5 100644 (file)
@@ -87,7 +87,7 @@
        CFG80211_TESTMODE_CMD(ieee80211_testmode_cmd)
 --- a/net/mac80211/ieee80211_i.h
 +++ b/net/mac80211/ieee80211_i.h
-@@ -1336,6 +1336,7 @@ struct ieee80211_local {
+@@ -1333,6 +1333,7 @@ struct ieee80211_local {
        int dynamic_ps_forced_timeout;
  
        int user_power_level; /* in dBm, for all interfaces */