mac80211: update to version 6.5
[openwrt/openwrt.git] / package / kernel / mac80211 / patches / subsys / 315-v6.3-wifi-mac80211-fix-receiving-A-MSDU-frames-on-mesh-in.patch
diff --git a/package/kernel/mac80211/patches/subsys/315-v6.3-wifi-mac80211-fix-receiving-A-MSDU-frames-on-mesh-in.patch b/package/kernel/mac80211/patches/subsys/315-v6.3-wifi-mac80211-fix-receiving-A-MSDU-frames-on-mesh-in.patch
deleted file mode 100644 (file)
index 59b799b..0000000
+++ /dev/null
@@ -1,762 +0,0 @@
-From 986e43b19ae9176093da35e0a844e65c8bf9ede7 Mon Sep 17 00:00:00 2001
-From: Felix Fietkau <nbd@nbd.name>
-Date: Mon, 13 Feb 2023 11:08:54 +0100
-Subject: [PATCH] wifi: mac80211: fix receiving A-MSDU frames on mesh
- interfaces
-
-The current mac80211 mesh A-MSDU receive path fails to parse A-MSDU packets
-on mesh interfaces, because it assumes that the Mesh Control field is always
-directly after the 802.11 header.
-802.11-2020 9.3.2.2.2 Figure 9-70 shows that the Mesh Control field is
-actually part of the A-MSDU subframe header.
-This makes more sense, since it allows packets for multiple different
-destinations to be included in the same A-MSDU, as long as RA and TID are
-still the same.
-Another issue is the fact that the A-MSDU subframe length field was apparently
-accidentally defined as little-endian in the standard.
-
-In order to fix this, the mesh forwarding path needs happen at a different
-point in the receive path.
-
-ieee80211_data_to_8023_exthdr is changed to ignore the mesh control field
-and leave it in after the ethernet header. This also affects the source/dest
-MAC address fields, which now in the case of mesh point to the mesh SA/DA.
-
-ieee80211_amsdu_to_8023s is changed to deal with the endian difference and
-to add the Mesh Control length to the subframe length, since it's not covered
-by the MSDU length field.
-
-With these changes, the mac80211 will get the same packet structure for
-converted regular data packets and unpacked A-MSDU subframes.
-
-The mesh forwarding checks are now only performed after the A-MSDU decap.
-For locally received packets, the Mesh Control header is stripped away.
-For forwarded packets, a new 802.11 header gets added.
-
-Signed-off-by: Felix Fietkau <nbd@nbd.name>
-Link: https://lore.kernel.org/r/20230213100855.34315-4-nbd@nbd.name
-[fix fortify build error]
-Signed-off-by: Johannes Berg <johannes.berg@intel.com>
----
- .../wireless/marvell/mwifiex/11n_rxreorder.c  |   2 +-
- include/net/cfg80211.h                        |  27 +-
- net/mac80211/rx.c                             | 350 ++++++++++--------
- net/wireless/util.c                           | 120 +++---
- 4 files changed, 297 insertions(+), 202 deletions(-)
-
---- a/drivers/net/wireless/marvell/mwifiex/11n_rxreorder.c
-+++ b/drivers/net/wireless/marvell/mwifiex/11n_rxreorder.c
-@@ -33,7 +33,7 @@ static int mwifiex_11n_dispatch_amsdu_pk
-               skb_trim(skb, le16_to_cpu(local_rx_pd->rx_pkt_length));
-               ieee80211_amsdu_to_8023s(skb, &list, priv->curr_addr,
--                                       priv->wdev.iftype, 0, NULL, NULL);
-+                                       priv->wdev.iftype, 0, NULL, NULL, false);
-               while (!skb_queue_empty(&list)) {
-                       struct rx_packet_hdr *rx_hdr;
---- a/include/net/cfg80211.h
-+++ b/include/net/cfg80211.h
-@@ -6208,11 +6208,36 @@ static inline int ieee80211_data_to_8023
-  * @extra_headroom: The hardware extra headroom for SKBs in the @list.
-  * @check_da: DA to check in the inner ethernet header, or NULL
-  * @check_sa: SA to check in the inner ethernet header, or NULL
-+ * @mesh_control: A-MSDU subframe header includes the mesh control field
-  */
- void ieee80211_amsdu_to_8023s(struct sk_buff *skb, struct sk_buff_head *list,
-                             const u8 *addr, enum nl80211_iftype iftype,
-                             const unsigned int extra_headroom,
--                            const u8 *check_da, const u8 *check_sa);
-+                            const u8 *check_da, const u8 *check_sa,
-+                            bool mesh_control);
-+
-+/**
-+ * ieee80211_get_8023_tunnel_proto - get RFC1042 or bridge tunnel encap protocol
-+ *
-+ * Check for RFC1042 or bridge tunnel header and fetch the encapsulated
-+ * protocol.
-+ *
-+ * @hdr: pointer to the MSDU payload
-+ * @proto: destination pointer to store the protocol
-+ * Return: true if encapsulation was found
-+ */
-+bool ieee80211_get_8023_tunnel_proto(const void *hdr, __be16 *proto);
-+
-+/**
-+ * ieee80211_strip_8023_mesh_hdr - strip mesh header from converted 802.3 frames
-+ *
-+ * Strip the mesh header, which was left in by ieee80211_data_to_8023 as part
-+ * of the MSDU data. Also move any source/destination addresses from the mesh
-+ * header to the ethernet header (if present).
-+ *
-+ * @skb: The 802.3 frame with embedded mesh header
-+ */
-+int ieee80211_strip_8023_mesh_hdr(struct sk_buff *skb);
- /**
-  * cfg80211_classify8021d - determine the 802.1p/1d tag for a data frame
---- a/net/mac80211/rx.c
-+++ b/net/mac80211/rx.c
-@@ -2720,6 +2720,174 @@ ieee80211_deliver_skb(struct ieee80211_r
-       }
- }
-+static ieee80211_rx_result
-+ieee80211_rx_mesh_data(struct ieee80211_sub_if_data *sdata, struct sta_info *sta,
-+                     struct sk_buff *skb)
-+{
-+#ifdef CPTCFG_MAC80211_MESH
-+      struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh;
-+      struct ieee80211_local *local = sdata->local;
-+      uint16_t fc = IEEE80211_FTYPE_DATA | IEEE80211_STYPE_QOS_DATA;
-+      struct ieee80211_hdr hdr = {
-+              .frame_control = cpu_to_le16(fc)
-+      };
-+      struct ieee80211_hdr *fwd_hdr;
-+      struct ieee80211s_hdr *mesh_hdr;
-+      struct ieee80211_tx_info *info;
-+      struct sk_buff *fwd_skb;
-+      struct ethhdr *eth;
-+      bool multicast;
-+      int tailroom = 0;
-+      int hdrlen, mesh_hdrlen;
-+      u8 *qos;
-+
-+      if (!ieee80211_vif_is_mesh(&sdata->vif))
-+              return RX_CONTINUE;
-+
-+      if (!pskb_may_pull(skb, sizeof(*eth) + 6))
-+              return RX_DROP_MONITOR;
-+
-+      mesh_hdr = (struct ieee80211s_hdr *)(skb->data + sizeof(*eth));
-+      mesh_hdrlen = ieee80211_get_mesh_hdrlen(mesh_hdr);
-+
-+      if (!pskb_may_pull(skb, sizeof(*eth) + mesh_hdrlen))
-+              return RX_DROP_MONITOR;
-+
-+      eth = (struct ethhdr *)skb->data;
-+      multicast = is_multicast_ether_addr(eth->h_dest);
-+
-+      mesh_hdr = (struct ieee80211s_hdr *)(eth + 1);
-+      if (!mesh_hdr->ttl)
-+              return RX_DROP_MONITOR;
-+
-+      /* frame is in RMC, don't forward */
-+      if (is_multicast_ether_addr(eth->h_dest) &&
-+          mesh_rmc_check(sdata, eth->h_source, mesh_hdr))
-+              return RX_DROP_MONITOR;
-+
-+      /* Frame has reached destination.  Don't forward */
-+      if (ether_addr_equal(sdata->vif.addr, eth->h_dest))
-+              goto rx_accept;
-+
-+      if (!ifmsh->mshcfg.dot11MeshForwarding) {
-+              if (is_multicast_ether_addr(eth->h_dest))
-+                      goto rx_accept;
-+
-+              return RX_DROP_MONITOR;
-+      }
-+
-+      /* forward packet */
-+      if (sdata->crypto_tx_tailroom_needed_cnt)
-+              tailroom = IEEE80211_ENCRYPT_TAILROOM;
-+
-+      if (!--mesh_hdr->ttl) {
-+              if (multicast)
-+                      goto rx_accept;
-+
-+              IEEE80211_IFSTA_MESH_CTR_INC(ifmsh, dropped_frames_ttl);
-+              return RX_DROP_MONITOR;
-+      }
-+
-+      if (mesh_hdr->flags & MESH_FLAGS_AE) {
-+              struct mesh_path *mppath;
-+              char *proxied_addr;
-+
-+              if (multicast)
-+                      proxied_addr = mesh_hdr->eaddr1;
-+              else if ((mesh_hdr->flags & MESH_FLAGS_AE) == MESH_FLAGS_AE_A5_A6)
-+                      /* has_a4 already checked in ieee80211_rx_mesh_check */
-+                      proxied_addr = mesh_hdr->eaddr2;
-+              else
-+                      return RX_DROP_MONITOR;
-+
-+              rcu_read_lock();
-+              mppath = mpp_path_lookup(sdata, proxied_addr);
-+              if (!mppath) {
-+                      mpp_path_add(sdata, proxied_addr, eth->h_source);
-+              } else {
-+                      spin_lock_bh(&mppath->state_lock);
-+                      if (!ether_addr_equal(mppath->mpp, eth->h_source))
-+                              memcpy(mppath->mpp, eth->h_source, ETH_ALEN);
-+                      mppath->exp_time = jiffies;
-+                      spin_unlock_bh(&mppath->state_lock);
-+              }
-+              rcu_read_unlock();
-+      }
-+
-+      skb_set_queue_mapping(skb, ieee802_1d_to_ac[skb->priority]);
-+
-+      ieee80211_fill_mesh_addresses(&hdr, &hdr.frame_control,
-+                                    eth->h_dest, eth->h_source);
-+      hdrlen = ieee80211_hdrlen(hdr.frame_control);
-+      if (multicast) {
-+              int extra_head = sizeof(struct ieee80211_hdr) - sizeof(*eth);
-+
-+              fwd_skb = skb_copy_expand(skb, local->tx_headroom + extra_head +
-+                                             IEEE80211_ENCRYPT_HEADROOM,
-+                                        tailroom, GFP_ATOMIC);
-+              if (!fwd_skb)
-+                      goto rx_accept;
-+      } else {
-+              fwd_skb = skb;
-+              skb = NULL;
-+
-+              if (skb_cow_head(fwd_skb, hdrlen - sizeof(struct ethhdr)))
-+                      return RX_DROP_UNUSABLE;
-+      }
-+
-+      fwd_hdr = skb_push(fwd_skb, hdrlen - sizeof(struct ethhdr));
-+      memcpy(fwd_hdr, &hdr, hdrlen - 2);
-+      qos = ieee80211_get_qos_ctl(fwd_hdr);
-+      qos[0] = qos[1] = 0;
-+
-+      skb_reset_mac_header(fwd_skb);
-+      hdrlen += mesh_hdrlen;
-+      if (ieee80211_get_8023_tunnel_proto(fwd_skb->data + hdrlen,
-+                                          &fwd_skb->protocol))
-+              hdrlen += ETH_ALEN;
-+      else
-+              fwd_skb->protocol = htons(fwd_skb->len - hdrlen);
-+      skb_set_network_header(fwd_skb, hdrlen);
-+
-+      info = IEEE80211_SKB_CB(fwd_skb);
-+      memset(info, 0, sizeof(*info));
-+      info->control.flags |= IEEE80211_TX_INTCFL_NEED_TXPROCESSING;
-+      info->control.vif = &sdata->vif;
-+      info->control.jiffies = jiffies;
-+      if (multicast) {
-+              IEEE80211_IFSTA_MESH_CTR_INC(ifmsh, fwded_mcast);
-+              memcpy(fwd_hdr->addr2, sdata->vif.addr, ETH_ALEN);
-+              /* update power mode indication when forwarding */
-+              ieee80211_mps_set_frame_flags(sdata, NULL, fwd_hdr);
-+      } else if (!mesh_nexthop_lookup(sdata, fwd_skb)) {
-+              /* mesh power mode flags updated in mesh_nexthop_lookup */
-+              IEEE80211_IFSTA_MESH_CTR_INC(ifmsh, fwded_unicast);
-+      } else {
-+              /* unable to resolve next hop */
-+              if (sta)
-+                      mesh_path_error_tx(sdata, ifmsh->mshcfg.element_ttl,
-+                                         hdr.addr3, 0,
-+                                         WLAN_REASON_MESH_PATH_NOFORWARD,
-+                                         sta->sta.addr);
-+              IEEE80211_IFSTA_MESH_CTR_INC(ifmsh, dropped_frames_no_route);
-+              kfree_skb(fwd_skb);
-+              goto rx_accept;
-+      }
-+
-+      IEEE80211_IFSTA_MESH_CTR_INC(ifmsh, fwded_frames);
-+      fwd_skb->dev = sdata->dev;
-+      ieee80211_add_pending_skb(local, fwd_skb);
-+
-+rx_accept:
-+      if (!skb)
-+              return RX_QUEUED;
-+
-+      ieee80211_strip_8023_mesh_hdr(skb);
-+#endif
-+
-+      return RX_CONTINUE;
-+}
-+
- static ieee80211_rx_result debug_noinline
- __ieee80211_rx_h_amsdu(struct ieee80211_rx_data *rx, u8 data_offset)
- {
-@@ -2728,8 +2896,10 @@ __ieee80211_rx_h_amsdu(struct ieee80211_
-       struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
-       __le16 fc = hdr->frame_control;
-       struct sk_buff_head frame_list;
-+      static ieee80211_rx_result res;
-       struct ethhdr ethhdr;
-       const u8 *check_da = ethhdr.h_dest, *check_sa = ethhdr.h_source;
-+      bool mesh = false;
-       if (unlikely(ieee80211_has_a4(hdr->frame_control))) {
-               check_da = NULL;
-@@ -2746,6 +2916,8 @@ __ieee80211_rx_h_amsdu(struct ieee80211_
-                       break;
-               case NL80211_IFTYPE_MESH_POINT:
-                       check_sa = NULL;
-+                      check_da = NULL;
-+                      mesh = true;
-                       break;
-               default:
-                       break;
-@@ -2763,17 +2935,29 @@ __ieee80211_rx_h_amsdu(struct ieee80211_
-       ieee80211_amsdu_to_8023s(skb, &frame_list, dev->dev_addr,
-                                rx->sdata->vif.type,
-                                rx->local->hw.extra_tx_headroom,
--                               check_da, check_sa);
-+                               check_da, check_sa, mesh);
-       while (!skb_queue_empty(&frame_list)) {
-               rx->skb = __skb_dequeue(&frame_list);
--              if (!ieee80211_frame_allowed(rx, fc)) {
--                      dev_kfree_skb(rx->skb);
-+              res = ieee80211_rx_mesh_data(rx->sdata, rx->sta, rx->skb);
-+              switch (res) {
-+              case RX_QUEUED:
-                       continue;
-+              case RX_CONTINUE:
-+                      break;
-+              default:
-+                      goto free;
-               }
-+              if (!ieee80211_frame_allowed(rx, fc))
-+                      goto free;
-+
-               ieee80211_deliver_skb(rx);
-+              continue;
-+
-+free:
-+              dev_kfree_skb(rx->skb);
-       }
-       return RX_QUEUED;
-@@ -2806,6 +2990,8 @@ ieee80211_rx_h_amsdu(struct ieee80211_rx
-                       if (!rx->sdata->u.mgd.use_4addr)
-                               return RX_DROP_UNUSABLE;
-                       break;
-+              case NL80211_IFTYPE_MESH_POINT:
-+                      break;
-               default:
-                       return RX_DROP_UNUSABLE;
-               }
-@@ -2834,155 +3020,6 @@ ieee80211_rx_h_amsdu(struct ieee80211_rx
-       return __ieee80211_rx_h_amsdu(rx, 0);
- }
--#ifdef CPTCFG_MAC80211_MESH
--static ieee80211_rx_result
--ieee80211_rx_h_mesh_fwding(struct ieee80211_rx_data *rx)
--{
--      struct ieee80211_hdr *fwd_hdr, *hdr;
--      struct ieee80211_tx_info *info;
--      struct ieee80211s_hdr *mesh_hdr;
--      struct sk_buff *skb = rx->skb, *fwd_skb;
--      struct ieee80211_local *local = rx->local;
--      struct ieee80211_sub_if_data *sdata = rx->sdata;
--      struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh;
--      u16 ac, q, hdrlen;
--      int tailroom = 0;
--
--      hdr = (struct ieee80211_hdr *) skb->data;
--      hdrlen = ieee80211_hdrlen(hdr->frame_control);
--
--      /* make sure fixed part of mesh header is there, also checks skb len */
--      if (!pskb_may_pull(rx->skb, hdrlen + 6))
--              return RX_DROP_MONITOR;
--
--      mesh_hdr = (struct ieee80211s_hdr *) (skb->data + hdrlen);
--
--      /* make sure full mesh header is there, also checks skb len */
--      if (!pskb_may_pull(rx->skb,
--                         hdrlen + ieee80211_get_mesh_hdrlen(mesh_hdr)))
--              return RX_DROP_MONITOR;
--
--      /* reload pointers */
--      hdr = (struct ieee80211_hdr *) skb->data;
--      mesh_hdr = (struct ieee80211s_hdr *) (skb->data + hdrlen);
--
--      if (ieee80211_drop_unencrypted(rx, hdr->frame_control)) {
--              int offset = hdrlen + ieee80211_get_mesh_hdrlen(mesh_hdr) +
--                           sizeof(rfc1042_header);
--              __be16 ethertype;
--
--              if (!ether_addr_equal(hdr->addr1, rx->sdata->vif.addr) ||
--                  skb_copy_bits(rx->skb, offset, &ethertype, 2) != 0 ||
--                  ethertype != rx->sdata->control_port_protocol)
--                      return RX_DROP_MONITOR;
--      }
--
--      /* frame is in RMC, don't forward */
--      if (ieee80211_is_data(hdr->frame_control) &&
--          is_multicast_ether_addr(hdr->addr1) &&
--          mesh_rmc_check(rx->sdata, hdr->addr3, mesh_hdr))
--              return RX_DROP_MONITOR;
--
--      if (!ieee80211_is_data(hdr->frame_control))
--              return RX_CONTINUE;
--
--      if (!mesh_hdr->ttl)
--              return RX_DROP_MONITOR;
--
--      if (mesh_hdr->flags & MESH_FLAGS_AE) {
--              struct mesh_path *mppath;
--              char *proxied_addr;
--              char *mpp_addr;
--
--              if (is_multicast_ether_addr(hdr->addr1)) {
--                      mpp_addr = hdr->addr3;
--                      proxied_addr = mesh_hdr->eaddr1;
--              } else if ((mesh_hdr->flags & MESH_FLAGS_AE) ==
--                          MESH_FLAGS_AE_A5_A6) {
--                      /* has_a4 already checked in ieee80211_rx_mesh_check */
--                      mpp_addr = hdr->addr4;
--                      proxied_addr = mesh_hdr->eaddr2;
--              } else {
--                      return RX_DROP_MONITOR;
--              }
--
--              rcu_read_lock();
--              mppath = mpp_path_lookup(sdata, proxied_addr);
--              if (!mppath) {
--                      mpp_path_add(sdata, proxied_addr, mpp_addr);
--              } else {
--                      spin_lock_bh(&mppath->state_lock);
--                      if (!ether_addr_equal(mppath->mpp, mpp_addr))
--                              memcpy(mppath->mpp, mpp_addr, ETH_ALEN);
--                      mppath->exp_time = jiffies;
--                      spin_unlock_bh(&mppath->state_lock);
--              }
--              rcu_read_unlock();
--      }
--
--      /* Frame has reached destination.  Don't forward */
--      if (!is_multicast_ether_addr(hdr->addr1) &&
--          ether_addr_equal(sdata->vif.addr, hdr->addr3))
--              return RX_CONTINUE;
--
--      ac = ieee802_1d_to_ac[skb->priority];
--      skb_set_queue_mapping(skb, ac);
--
--      if (!--mesh_hdr->ttl) {
--              if (!is_multicast_ether_addr(hdr->addr1))
--                      IEEE80211_IFSTA_MESH_CTR_INC(ifmsh,
--                                                   dropped_frames_ttl);
--              goto out;
--      }
--
--      if (!ifmsh->mshcfg.dot11MeshForwarding)
--              goto out;
--
--      if (sdata->crypto_tx_tailroom_needed_cnt)
--              tailroom = IEEE80211_ENCRYPT_TAILROOM;
--
--      fwd_skb = skb_copy_expand(skb, local->tx_headroom +
--                                     IEEE80211_ENCRYPT_HEADROOM,
--                                tailroom, GFP_ATOMIC);
--      if (!fwd_skb)
--              goto out;
--
--      fwd_skb->dev = sdata->dev;
--      fwd_hdr =  (struct ieee80211_hdr *) fwd_skb->data;
--      fwd_hdr->frame_control &= ~cpu_to_le16(IEEE80211_FCTL_RETRY);
--      info = IEEE80211_SKB_CB(fwd_skb);
--      memset(info, 0, sizeof(*info));
--      info->control.flags |= IEEE80211_TX_INTCFL_NEED_TXPROCESSING;
--      info->control.vif = &rx->sdata->vif;
--      info->control.jiffies = jiffies;
--      if (is_multicast_ether_addr(fwd_hdr->addr1)) {
--              IEEE80211_IFSTA_MESH_CTR_INC(ifmsh, fwded_mcast);
--              memcpy(fwd_hdr->addr2, sdata->vif.addr, ETH_ALEN);
--              /* update power mode indication when forwarding */
--              ieee80211_mps_set_frame_flags(sdata, NULL, fwd_hdr);
--      } else if (!mesh_nexthop_lookup(sdata, fwd_skb)) {
--              /* mesh power mode flags updated in mesh_nexthop_lookup */
--              IEEE80211_IFSTA_MESH_CTR_INC(ifmsh, fwded_unicast);
--      } else {
--              /* unable to resolve next hop */
--              mesh_path_error_tx(sdata, ifmsh->mshcfg.element_ttl,
--                                 fwd_hdr->addr3, 0,
--                                 WLAN_REASON_MESH_PATH_NOFORWARD,
--                                 fwd_hdr->addr2);
--              IEEE80211_IFSTA_MESH_CTR_INC(ifmsh, dropped_frames_no_route);
--              kfree_skb(fwd_skb);
--              return RX_DROP_MONITOR;
--      }
--
--      IEEE80211_IFSTA_MESH_CTR_INC(ifmsh, fwded_frames);
--      ieee80211_add_pending_skb(local, fwd_skb);
-- out:
--      if (is_multicast_ether_addr(hdr->addr1))
--              return RX_CONTINUE;
--      return RX_DROP_MONITOR;
--}
--#endif
--
- static ieee80211_rx_result debug_noinline
- ieee80211_rx_h_data(struct ieee80211_rx_data *rx)
- {
-@@ -2991,6 +3028,7 @@ ieee80211_rx_h_data(struct ieee80211_rx_
-       struct net_device *dev = sdata->dev;
-       struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)rx->skb->data;
-       __le16 fc = hdr->frame_control;
-+      static ieee80211_rx_result res;
-       bool port_control;
-       int err;
-@@ -3017,6 +3055,10 @@ ieee80211_rx_h_data(struct ieee80211_rx_
-       if (unlikely(err))
-               return RX_DROP_UNUSABLE;
-+      res = ieee80211_rx_mesh_data(rx->sdata, rx->sta, rx->skb);
-+      if (res != RX_CONTINUE)
-+              return res;
-+
-       if (!ieee80211_frame_allowed(rx, fc))
-               return RX_DROP_MONITOR;
-@@ -3987,10 +4029,6 @@ static void ieee80211_rx_handlers(struct
-               CALL_RXH(ieee80211_rx_h_defragment);
-               CALL_RXH(ieee80211_rx_h_michael_mic_verify);
-               /* must be after MMIC verify so header is counted in MPDU mic */
--#ifdef CPTCFG_MAC80211_MESH
--              if (ieee80211_vif_is_mesh(&rx->sdata->vif))
--                      CALL_RXH(ieee80211_rx_h_mesh_fwding);
--#endif
-               CALL_RXH(ieee80211_rx_h_amsdu);
-               CALL_RXH(ieee80211_rx_h_data);
---- a/net/wireless/util.c
-+++ b/net/wireless/util.c
-@@ -542,7 +542,7 @@ unsigned int ieee80211_get_mesh_hdrlen(s
- }
- EXPORT_SYMBOL(ieee80211_get_mesh_hdrlen);
--static bool ieee80211_get_8023_tunnel_proto(const void *hdr, __be16 *proto)
-+bool ieee80211_get_8023_tunnel_proto(const void *hdr, __be16 *proto)
- {
-       const __be16 *hdr_proto = hdr + ETH_ALEN;
-@@ -556,6 +556,49 @@ static bool ieee80211_get_8023_tunnel_pr
-       return true;
- }
-+EXPORT_SYMBOL(ieee80211_get_8023_tunnel_proto);
-+
-+int ieee80211_strip_8023_mesh_hdr(struct sk_buff *skb)
-+{
-+      const void *mesh_addr;
-+      struct {
-+              struct ethhdr eth;
-+              u8 flags;
-+      } payload;
-+      int hdrlen;
-+      int ret;
-+
-+      ret = skb_copy_bits(skb, 0, &payload, sizeof(payload));
-+      if (ret)
-+              return ret;
-+
-+      hdrlen = sizeof(payload.eth) + __ieee80211_get_mesh_hdrlen(payload.flags);
-+
-+      if (likely(pskb_may_pull(skb, hdrlen + 8) &&
-+                 ieee80211_get_8023_tunnel_proto(skb->data + hdrlen,
-+                                                 &payload.eth.h_proto)))
-+              hdrlen += ETH_ALEN + 2;
-+      else if (!pskb_may_pull(skb, hdrlen))
-+              return -EINVAL;
-+
-+      mesh_addr = skb->data + sizeof(payload.eth) + ETH_ALEN;
-+      switch (payload.flags & MESH_FLAGS_AE) {
-+      case MESH_FLAGS_AE_A4:
-+              memcpy(&payload.eth.h_source, mesh_addr, ETH_ALEN);
-+              break;
-+      case MESH_FLAGS_AE_A5_A6:
-+              memcpy(&payload.eth, mesh_addr, 2 * ETH_ALEN);
-+              break;
-+      default:
-+              break;
-+      }
-+
-+      pskb_pull(skb, hdrlen - sizeof(payload.eth));
-+      memcpy(skb->data, &payload.eth, sizeof(payload.eth));
-+
-+      return 0;
-+}
-+EXPORT_SYMBOL(ieee80211_strip_8023_mesh_hdr);
- int ieee80211_data_to_8023_exthdr(struct sk_buff *skb, struct ethhdr *ehdr,
-                                 const u8 *addr, enum nl80211_iftype iftype,
-@@ -568,7 +611,6 @@ int ieee80211_data_to_8023_exthdr(struct
-       } payload;
-       struct ethhdr tmp;
-       u16 hdrlen;
--      u8 mesh_flags = 0;
-       if (unlikely(!ieee80211_is_data_present(hdr->frame_control)))
-               return -1;
-@@ -589,12 +631,6 @@ int ieee80211_data_to_8023_exthdr(struct
-       memcpy(tmp.h_dest, ieee80211_get_DA(hdr), ETH_ALEN);
-       memcpy(tmp.h_source, ieee80211_get_SA(hdr), ETH_ALEN);
--      if (iftype == NL80211_IFTYPE_MESH_POINT &&
--          skb_copy_bits(skb, hdrlen, &mesh_flags, 1) < 0)
--              return -1;
--
--      mesh_flags &= MESH_FLAGS_AE;
--
-       switch (hdr->frame_control &
-               cpu_to_le16(IEEE80211_FCTL_TODS | IEEE80211_FCTL_FROMDS)) {
-       case cpu_to_le16(IEEE80211_FCTL_TODS):
-@@ -608,17 +644,6 @@ int ieee80211_data_to_8023_exthdr(struct
-                            iftype != NL80211_IFTYPE_AP_VLAN &&
-                            iftype != NL80211_IFTYPE_STATION))
-                       return -1;
--              if (iftype == NL80211_IFTYPE_MESH_POINT) {
--                      if (mesh_flags == MESH_FLAGS_AE_A4)
--                              return -1;
--                      if (mesh_flags == MESH_FLAGS_AE_A5_A6 &&
--                          skb_copy_bits(skb, hdrlen +
--                                        offsetof(struct ieee80211s_hdr, eaddr1),
--                                        tmp.h_dest, 2 * ETH_ALEN) < 0)
--                              return -1;
--
--                      hdrlen += __ieee80211_get_mesh_hdrlen(mesh_flags);
--              }
-               break;
-       case cpu_to_le16(IEEE80211_FCTL_FROMDS):
-               if ((iftype != NL80211_IFTYPE_STATION &&
-@@ -627,16 +652,6 @@ int ieee80211_data_to_8023_exthdr(struct
-                   (is_multicast_ether_addr(tmp.h_dest) &&
-                    ether_addr_equal(tmp.h_source, addr)))
-                       return -1;
--              if (iftype == NL80211_IFTYPE_MESH_POINT) {
--                      if (mesh_flags == MESH_FLAGS_AE_A5_A6)
--                              return -1;
--                      if (mesh_flags == MESH_FLAGS_AE_A4 &&
--                          skb_copy_bits(skb, hdrlen +
--                                        offsetof(struct ieee80211s_hdr, eaddr1),
--                                        tmp.h_source, ETH_ALEN) < 0)
--                              return -1;
--                      hdrlen += __ieee80211_get_mesh_hdrlen(mesh_flags);
--              }
-               break;
-       case cpu_to_le16(0):
-               if (iftype != NL80211_IFTYPE_ADHOC &&
-@@ -646,7 +661,7 @@ int ieee80211_data_to_8023_exthdr(struct
-               break;
-       }
--      if (likely(!is_amsdu &&
-+      if (likely(!is_amsdu && iftype != NL80211_IFTYPE_MESH_POINT &&
-                  skb_copy_bits(skb, hdrlen, &payload, sizeof(payload)) == 0 &&
-                  ieee80211_get_8023_tunnel_proto(&payload, &tmp.h_proto))) {
-               /* remove RFC1042 or Bridge-Tunnel encapsulation */
-@@ -722,7 +737,8 @@ __ieee80211_amsdu_copy_frag(struct sk_bu
- static struct sk_buff *
- __ieee80211_amsdu_copy(struct sk_buff *skb, unsigned int hlen,
--                     int offset, int len, bool reuse_frag)
-+                     int offset, int len, bool reuse_frag,
-+                     int min_len)
- {
-       struct sk_buff *frame;
-       int cur_len = len;
-@@ -736,7 +752,7 @@ __ieee80211_amsdu_copy(struct sk_buff *s
-        * in the stack later.
-        */
-       if (reuse_frag)
--              cur_len = min_t(int, len, 32);
-+              cur_len = min_t(int, len, min_len);
-       /*
-        * Allocate and reserve two bytes more for payload
-@@ -746,6 +762,7 @@ __ieee80211_amsdu_copy(struct sk_buff *s
-       if (!frame)
-               return NULL;
-+      frame->priority = skb->priority;
-       skb_reserve(frame, hlen + sizeof(struct ethhdr) + 2);
-       skb_copy_bits(skb, offset, skb_put(frame, cur_len), cur_len);
-@@ -762,23 +779,37 @@ __ieee80211_amsdu_copy(struct sk_buff *s
- void ieee80211_amsdu_to_8023s(struct sk_buff *skb, struct sk_buff_head *list,
-                             const u8 *addr, enum nl80211_iftype iftype,
-                             const unsigned int extra_headroom,
--                            const u8 *check_da, const u8 *check_sa)
-+                            const u8 *check_da, const u8 *check_sa,
-+                            bool mesh_control)
- {
-       unsigned int hlen = ALIGN(extra_headroom, 4);
-       struct sk_buff *frame = NULL;
-       int offset = 0, remaining;
--      struct ethhdr eth;
-+      struct {
-+              struct ethhdr eth;
-+              uint8_t flags;
-+      } hdr;
-       bool reuse_frag = skb->head_frag && !skb_has_frag_list(skb);
-       bool reuse_skb = false;
-       bool last = false;
-+      int copy_len = sizeof(hdr.eth);
-+
-+      if (iftype == NL80211_IFTYPE_MESH_POINT)
-+              copy_len = sizeof(hdr);
-       while (!last) {
-               unsigned int subframe_len;
--              int len;
-+              int len, mesh_len = 0;
-               u8 padding;
--              skb_copy_bits(skb, offset, &eth, sizeof(eth));
--              len = ntohs(eth.h_proto);
-+              skb_copy_bits(skb, offset, &hdr, copy_len);
-+              if (iftype == NL80211_IFTYPE_MESH_POINT)
-+                      mesh_len = __ieee80211_get_mesh_hdrlen(hdr.flags);
-+              if (mesh_control)
-+                      len = le16_to_cpu(*(__le16 *)&hdr.eth.h_proto) + mesh_len;
-+              else
-+                      len = ntohs(hdr.eth.h_proto);
-+
-               subframe_len = sizeof(struct ethhdr) + len;
-               padding = (4 - subframe_len) & 0x3;
-@@ -787,16 +818,16 @@ void ieee80211_amsdu_to_8023s(struct sk_
-               if (subframe_len > remaining)
-                       goto purge;
-               /* mitigate A-MSDU aggregation injection attacks */
--              if (ether_addr_equal(eth.h_dest, rfc1042_header))
-+              if (ether_addr_equal(hdr.eth.h_dest, rfc1042_header))
-                       goto purge;
-               offset += sizeof(struct ethhdr);
-               last = remaining <= subframe_len + padding;
-               /* FIXME: should we really accept multicast DA? */
--              if ((check_da && !is_multicast_ether_addr(eth.h_dest) &&
--                   !ether_addr_equal(check_da, eth.h_dest)) ||
--                  (check_sa && !ether_addr_equal(check_sa, eth.h_source))) {
-+              if ((check_da && !is_multicast_ether_addr(hdr.eth.h_dest) &&
-+                   !ether_addr_equal(check_da, hdr.eth.h_dest)) ||
-+                  (check_sa && !ether_addr_equal(check_sa, hdr.eth.h_source))) {
-                       offset += len + padding;
-                       continue;
-               }
-@@ -808,7 +839,7 @@ void ieee80211_amsdu_to_8023s(struct sk_
-                       reuse_skb = true;
-               } else {
-                       frame = __ieee80211_amsdu_copy(skb, hlen, offset, len,
--                                                     reuse_frag);
-+                                                     reuse_frag, 32 + mesh_len);
-                       if (!frame)
-                               goto purge;
-@@ -819,10 +850,11 @@ void ieee80211_amsdu_to_8023s(struct sk_
-               frame->dev = skb->dev;
-               frame->priority = skb->priority;
--              if (likely(ieee80211_get_8023_tunnel_proto(frame->data, &eth.h_proto)))
-+              if (likely(iftype != NL80211_IFTYPE_MESH_POINT &&
-+                         ieee80211_get_8023_tunnel_proto(frame->data, &hdr.eth.h_proto)))
-                       skb_pull(frame, ETH_ALEN + 2);
--              memcpy(skb_push(frame, sizeof(eth)), &eth, sizeof(eth));
-+              memcpy(skb_push(frame, sizeof(hdr.eth)), &hdr.eth, sizeof(hdr.eth));
-               __skb_queue_tail(list, frame);
-       }