From ec33a6ca2cd16e92d2a544f38704405c043c822d Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Sat, 10 Dec 2022 14:55:33 +0100 Subject: [PATCH] mac80211: add fixes for receiving A-MSDU packets on mesh interfaces The standard defines the A-MSDU header length field differently for mesh compared to other modes. Deal with this accordingly and work around broken implementations (e.g. ath10k, ath11k). Signed-off-by: Felix Fietkau --- ...x-and-simplify-unencrypted-drop-chec.patch | 87 ++ ...ve-A-MSDU-check-in-ieee80211_data_to.patch | 25 + ...ctor-out-bridge-tunnel-RFC1042-heade.patch | 76 ++ ...move-mesh-forwarding-congestion-chec.patch | 54 ++ ...x-receiving-A-MSDU-frames-on-mesh-in.patch | 753 ++++++++++++++++++ ...d-a-workaround-for-receiving-non-sta.patch | 145 ++++ .../500-mac80211_configure_antenna_gain.patch | 2 +- ...-mask-nested-A-MSDU-support-for-mesh.patch | 33 - 8 files changed, 1141 insertions(+), 34 deletions(-) create mode 100644 package/kernel/mac80211/patches/subsys/311-wifi-mac80211-fix-and-simplify-unencrypted-drop-chec.patch create mode 100644 package/kernel/mac80211/patches/subsys/312-wifi-cfg80211-move-A-MSDU-check-in-ieee80211_data_to.patch create mode 100644 package/kernel/mac80211/patches/subsys/313-wifi-cfg80211-factor-out-bridge-tunnel-RFC1042-heade.patch create mode 100644 package/kernel/mac80211/patches/subsys/314-wifi-mac80211-remove-mesh-forwarding-congestion-chec.patch create mode 100644 package/kernel/mac80211/patches/subsys/315-wifi-mac80211-fix-receiving-A-MSDU-frames-on-mesh-in.patch create mode 100644 package/kernel/mac80211/patches/subsys/316-wifi-mac80211-add-a-workaround-for-receiving-non-sta.patch delete mode 100644 package/kernel/mac80211/patches/subsys/800-mac80211-mask-nested-A-MSDU-support-for-mesh.patch diff --git a/package/kernel/mac80211/patches/subsys/311-wifi-mac80211-fix-and-simplify-unencrypted-drop-chec.patch b/package/kernel/mac80211/patches/subsys/311-wifi-mac80211-fix-and-simplify-unencrypted-drop-chec.patch new file mode 100644 index 0000000000..804b02eb30 --- /dev/null +++ b/package/kernel/mac80211/patches/subsys/311-wifi-mac80211-fix-and-simplify-unencrypted-drop-chec.patch @@ -0,0 +1,87 @@ +From: Felix Fietkau +Date: Thu, 1 Dec 2022 14:57:30 +0100 +Subject: [PATCH] wifi: mac80211: fix and simplify unencrypted drop check for + mesh + +ieee80211_drop_unencrypted is called from ieee80211_rx_h_mesh_fwding and +ieee80211_frame_allowed. + +Since ieee80211_rx_h_mesh_fwding can forward packets for other mesh nodes +and is called earlier, it needs to check the decryptions status and if the +packet is using the control protocol on its own, instead of deferring to +the later call from ieee80211_frame_allowed. + +Because of that, ieee80211_drop_unencrypted has a mesh specific check +that skips over the mesh header in order to check the payload protocol. +This code is invalid when called from ieee80211_frame_allowed, since that +happens after the 802.11->802.3 conversion. + +Fix this by moving the mesh specific check directly into +ieee80211_rx_h_mesh_fwding. + +Signed-off-by: Felix Fietkau +Link: https://lore.kernel.org/r/20221201135730.19723-1-nbd@nbd.name +Signed-off-by: Johannes Berg +--- + +--- a/net/mac80211/rx.c ++++ b/net/mac80211/rx.c +@@ -2403,7 +2403,6 @@ static int ieee80211_802_1x_port_control + + static int ieee80211_drop_unencrypted(struct ieee80211_rx_data *rx, __le16 fc) + { +- struct ieee80211_hdr *hdr = (void *)rx->skb->data; + struct sk_buff *skb = rx->skb; + struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb); + +@@ -2414,31 +2413,6 @@ static int ieee80211_drop_unencrypted(st + if (status->flag & RX_FLAG_DECRYPTED) + return 0; + +- /* check mesh EAPOL frames first */ +- if (unlikely(rx->sta && ieee80211_vif_is_mesh(&rx->sdata->vif) && +- ieee80211_is_data(fc))) { +- struct ieee80211s_hdr *mesh_hdr; +- u16 hdr_len = ieee80211_hdrlen(fc); +- u16 ethertype_offset; +- __be16 ethertype; +- +- if (!ether_addr_equal(hdr->addr1, rx->sdata->vif.addr)) +- goto drop_check; +- +- /* make sure fixed part of mesh header is there, also checks skb len */ +- if (!pskb_may_pull(rx->skb, hdr_len + 6)) +- goto drop_check; +- +- mesh_hdr = (struct ieee80211s_hdr *)(skb->data + hdr_len); +- ethertype_offset = hdr_len + ieee80211_get_mesh_hdrlen(mesh_hdr) + +- sizeof(rfc1042_header); +- +- if (skb_copy_bits(rx->skb, ethertype_offset, ðertype, 2) == 0 && +- ethertype == rx->sdata->control_port_protocol) +- return 0; +- } +- +-drop_check: + /* Drop unencrypted frames if key is set. */ + if (unlikely(!ieee80211_has_protected(fc) && + !ieee80211_is_any_nullfunc(fc) && +@@ -2892,8 +2866,16 @@ ieee80211_rx_h_mesh_fwding(struct ieee80 + hdr = (struct ieee80211_hdr *) skb->data; + mesh_hdr = (struct ieee80211s_hdr *) (skb->data + hdrlen); + +- if (ieee80211_drop_unencrypted(rx, hdr->frame_control)) +- return RX_DROP_MONITOR; ++ 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, ðertype, 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) && diff --git a/package/kernel/mac80211/patches/subsys/312-wifi-cfg80211-move-A-MSDU-check-in-ieee80211_data_to.patch b/package/kernel/mac80211/patches/subsys/312-wifi-cfg80211-move-A-MSDU-check-in-ieee80211_data_to.patch new file mode 100644 index 0000000000..f668905cca --- /dev/null +++ b/package/kernel/mac80211/patches/subsys/312-wifi-cfg80211-move-A-MSDU-check-in-ieee80211_data_to.patch @@ -0,0 +1,25 @@ +From: Felix Fietkau +Date: Fri, 2 Dec 2022 13:53:11 +0100 +Subject: [PATCH] wifi: cfg80211: move A-MSDU check in + ieee80211_data_to_8023_exthdr + +When parsing the outer A-MSDU header, don't check for inner bridge tunnel +or RFC1042 headers. This is handled by ieee80211_amsdu_to_8023s already. + +Signed-off-by: Felix Fietkau +--- + +--- a/net/wireless/util.c ++++ b/net/wireless/util.c +@@ -631,8 +631,9 @@ int ieee80211_data_to_8023_exthdr(struct + break; + } + +- if (likely(skb_copy_bits(skb, hdrlen, &payload, sizeof(payload)) == 0 && +- ((!is_amsdu && ether_addr_equal(payload.hdr, rfc1042_header) && ++ if (likely(!is_amsdu && ++ skb_copy_bits(skb, hdrlen, &payload, sizeof(payload)) == 0 && ++ ((ether_addr_equal(payload.hdr, rfc1042_header) && + payload.proto != htons(ETH_P_AARP) && + payload.proto != htons(ETH_P_IPX)) || + ether_addr_equal(payload.hdr, bridge_tunnel_header)))) { diff --git a/package/kernel/mac80211/patches/subsys/313-wifi-cfg80211-factor-out-bridge-tunnel-RFC1042-heade.patch b/package/kernel/mac80211/patches/subsys/313-wifi-cfg80211-factor-out-bridge-tunnel-RFC1042-heade.patch new file mode 100644 index 0000000000..8641057869 --- /dev/null +++ b/package/kernel/mac80211/patches/subsys/313-wifi-cfg80211-factor-out-bridge-tunnel-RFC1042-heade.patch @@ -0,0 +1,76 @@ +From: Felix Fietkau +Date: Fri, 2 Dec 2022 13:54:15 +0100 +Subject: [PATCH] wifi: cfg80211: factor out bridge tunnel / RFC1042 header + check + +The same check is done in multiple places, unify it. + +Signed-off-by: Felix Fietkau +--- + +--- a/net/wireless/util.c ++++ b/net/wireless/util.c +@@ -542,6 +542,21 @@ 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) ++{ ++ const __be16 *hdr_proto = hdr + ETH_ALEN; ++ ++ if (!(ether_addr_equal(hdr, rfc1042_header) && ++ *hdr_proto != htons(ETH_P_AARP) && ++ *hdr_proto != htons(ETH_P_IPX)) && ++ !ether_addr_equal(hdr, bridge_tunnel_header)) ++ return false; ++ ++ *proto = *hdr_proto; ++ ++ return true; ++} ++ + int ieee80211_data_to_8023_exthdr(struct sk_buff *skb, struct ethhdr *ehdr, + const u8 *addr, enum nl80211_iftype iftype, + u8 data_offset, bool is_amsdu) +@@ -633,14 +648,9 @@ int ieee80211_data_to_8023_exthdr(struct + + if (likely(!is_amsdu && + skb_copy_bits(skb, hdrlen, &payload, sizeof(payload)) == 0 && +- ((ether_addr_equal(payload.hdr, rfc1042_header) && +- payload.proto != htons(ETH_P_AARP) && +- payload.proto != htons(ETH_P_IPX)) || +- ether_addr_equal(payload.hdr, bridge_tunnel_header)))) { +- /* remove RFC1042 or Bridge-Tunnel encapsulation and +- * replace EtherType */ ++ ieee80211_get_8023_tunnel_proto(&payload, &tmp.h_proto))) { ++ /* remove RFC1042 or Bridge-Tunnel encapsulation */ + hdrlen += ETH_ALEN + 2; +- tmp.h_proto = payload.proto; + skb_postpull_rcsum(skb, &payload, ETH_ALEN + 2); + } else { + tmp.h_proto = htons(skb->len - hdrlen); +@@ -756,8 +766,6 @@ void ieee80211_amsdu_to_8023s(struct sk_ + { + unsigned int hlen = ALIGN(extra_headroom, 4); + struct sk_buff *frame = NULL; +- u16 ethertype; +- u8 *payload; + int offset = 0, remaining; + struct ethhdr eth; + bool reuse_frag = skb->head_frag && !skb_has_frag_list(skb); +@@ -811,14 +819,8 @@ void ieee80211_amsdu_to_8023s(struct sk_ + frame->dev = skb->dev; + frame->priority = skb->priority; + +- payload = frame->data; +- ethertype = (payload[6] << 8) | payload[7]; +- if (likely((ether_addr_equal(payload, rfc1042_header) && +- ethertype != ETH_P_AARP && ethertype != ETH_P_IPX) || +- ether_addr_equal(payload, bridge_tunnel_header))) { +- eth.h_proto = htons(ethertype); ++ if (likely(ieee80211_get_8023_tunnel_proto(frame->data, ð.h_proto))) + skb_pull(frame, ETH_ALEN + 2); +- } + + memcpy(skb_push(frame, sizeof(eth)), ð, sizeof(eth)); + __skb_queue_tail(list, frame); diff --git a/package/kernel/mac80211/patches/subsys/314-wifi-mac80211-remove-mesh-forwarding-congestion-chec.patch b/package/kernel/mac80211/patches/subsys/314-wifi-mac80211-remove-mesh-forwarding-congestion-chec.patch new file mode 100644 index 0000000000..515176f0de --- /dev/null +++ b/package/kernel/mac80211/patches/subsys/314-wifi-mac80211-remove-mesh-forwarding-congestion-chec.patch @@ -0,0 +1,54 @@ +From: Felix Fietkau +Date: Fri, 2 Dec 2022 17:01:46 +0100 +Subject: [PATCH] wifi: mac80211: remove mesh forwarding congestion check + +Now that all drivers use iTXQ, it does not make sense to check to drop +tx forwarding packets when the driver has stopped the queues. +fq_codel will take care of dropping packets when the queues fill up + +Signed-off-by: Felix Fietkau +--- + +--- a/net/mac80211/debugfs_netdev.c ++++ b/net/mac80211/debugfs_netdev.c +@@ -603,8 +603,6 @@ IEEE80211_IF_FILE(fwded_mcast, u.mesh.ms + IEEE80211_IF_FILE(fwded_unicast, u.mesh.mshstats.fwded_unicast, DEC); + IEEE80211_IF_FILE(fwded_frames, u.mesh.mshstats.fwded_frames, DEC); + IEEE80211_IF_FILE(dropped_frames_ttl, u.mesh.mshstats.dropped_frames_ttl, DEC); +-IEEE80211_IF_FILE(dropped_frames_congestion, +- u.mesh.mshstats.dropped_frames_congestion, DEC); + IEEE80211_IF_FILE(dropped_frames_no_route, + u.mesh.mshstats.dropped_frames_no_route, DEC); + +@@ -740,7 +738,6 @@ static void add_mesh_stats(struct ieee80 + MESHSTATS_ADD(fwded_frames); + MESHSTATS_ADD(dropped_frames_ttl); + MESHSTATS_ADD(dropped_frames_no_route); +- MESHSTATS_ADD(dropped_frames_congestion); + #undef MESHSTATS_ADD + } + +--- a/net/mac80211/ieee80211_i.h ++++ b/net/mac80211/ieee80211_i.h +@@ -329,7 +329,6 @@ struct mesh_stats { + __u32 fwded_frames; /* Mesh total forwarded frames */ + __u32 dropped_frames_ttl; /* Not transmitted since mesh_ttl == 0*/ + __u32 dropped_frames_no_route; /* Not transmitted, no route found */ +- __u32 dropped_frames_congestion;/* Not forwarded due to congestion */ + }; + + #define PREQ_Q_F_START 0x1 +--- a/net/mac80211/rx.c ++++ b/net/mac80211/rx.c +@@ -2926,11 +2926,6 @@ ieee80211_rx_h_mesh_fwding(struct ieee80 + return RX_CONTINUE; + + ac = ieee802_1d_to_ac[skb->priority]; +- q = sdata->vif.hw_queue[ac]; +- if (ieee80211_queue_stopped(&local->hw, q)) { +- IEEE80211_IFSTA_MESH_CTR_INC(ifmsh, dropped_frames_congestion); +- return RX_DROP_MONITOR; +- } + skb_set_queue_mapping(skb, ac); + + if (!--mesh_hdr->ttl) { diff --git a/package/kernel/mac80211/patches/subsys/315-wifi-mac80211-fix-receiving-A-MSDU-frames-on-mesh-in.patch b/package/kernel/mac80211/patches/subsys/315-wifi-mac80211-fix-receiving-A-MSDU-frames-on-mesh-in.patch new file mode 100644 index 0000000000..6aec9bc85f --- /dev/null +++ b/package/kernel/mac80211/patches/subsys/315-wifi-mac80211-fix-receiving-A-MSDU-frames-on-mesh-in.patch @@ -0,0 +1,753 @@ +From: Felix Fietkau +Date: Tue, 6 Dec 2022 11:15:02 +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 +--- + +--- 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, ðertype, 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.h_dest, 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, ð, 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, ð.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)), ð, sizeof(eth)); ++ memcpy(skb_push(frame, sizeof(hdr.eth)), &hdr.eth, sizeof(hdr.eth)); + __skb_queue_tail(list, frame); + } + diff --git a/package/kernel/mac80211/patches/subsys/316-wifi-mac80211-add-a-workaround-for-receiving-non-sta.patch b/package/kernel/mac80211/patches/subsys/316-wifi-mac80211-add-a-workaround-for-receiving-non-sta.patch new file mode 100644 index 0000000000..6dc98ae16a --- /dev/null +++ b/package/kernel/mac80211/patches/subsys/316-wifi-mac80211-add-a-workaround-for-receiving-non-sta.patch @@ -0,0 +1,145 @@ +From: Felix Fietkau +Date: Fri, 9 Dec 2022 21:15:04 +0100 +Subject: [PATCH] wifi: mac80211: add a workaround for receiving + non-standard mesh A-MSDU + +At least ath10k and ath11k supported hardware (maybe more) does not implement +mesh A-MSDU aggregation in a standard compliant way. +802.11-2020 9.3.2.2.2 declares that the Mesh Control field is part of the +A-MSDU header. As such, its length must not be included in the subframe +length field. +Hardware affected by this bug treats the mesh control field as part of the +MSDU data and sets the length accordingly. +In order to avoid packet loss, keep track of which stations are affected +by this and take it into account when converting A-MSDU to 802.3 + mesh control +packets. + +Signed-off-by: Felix Fietkau +--- + +--- a/include/net/cfg80211.h ++++ b/include/net/cfg80211.h +@@ -6194,6 +6194,19 @@ static inline int ieee80211_data_to_8023 + } + + /** ++ * ieee80211_is_valid_amsdu - check if subframe lengths of an A-MSDU are valid ++ * ++ * This is used to detect non-standard A-MSDU frames, e.g. the ones generated ++ * by ath10k and ath11k, where the subframe length includes the length of the ++ * mesh control field. ++ * ++ * @skb: The input A-MSDU frame without any headers. ++ * @mesh_hdr: use standard compliant mesh A-MSDU subframe header ++ * Returns: true if subframe header lengths are valid for the @mesh_hdr mode ++ */ ++bool ieee80211_is_valid_amsdu(struct sk_buff *skb, bool mesh_hdr); ++ ++/** + * ieee80211_amsdu_to_8023s - decode an IEEE 802.11n A-MSDU frame + * + * Decode an IEEE 802.11 A-MSDU and convert it to a list of 802.3 frames. +--- a/net/mac80211/rx.c ++++ b/net/mac80211/rx.c +@@ -2899,7 +2899,6 @@ __ieee80211_rx_h_amsdu(struct ieee80211_ + 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; +@@ -2917,7 +2916,6 @@ __ieee80211_rx_h_amsdu(struct ieee80211_ + case NL80211_IFTYPE_MESH_POINT: + check_sa = NULL; + check_da = NULL; +- mesh = true; + break; + default: + break; +@@ -2932,10 +2930,21 @@ __ieee80211_rx_h_amsdu(struct ieee80211_ + data_offset, true)) + return RX_DROP_UNUSABLE; + ++ if (rx->sta && rx->sta->amsdu_mesh_control < 0) { ++ bool valid_std = ieee80211_is_valid_amsdu(skb, true); ++ bool valid_nonstd = ieee80211_is_valid_amsdu(skb, false); ++ ++ if (valid_std && !valid_nonstd) ++ rx->sta->amsdu_mesh_control = 1; ++ else if (valid_nonstd && !valid_std) ++ rx->sta->amsdu_mesh_control = 0; ++ } ++ + ieee80211_amsdu_to_8023s(skb, &frame_list, dev->dev_addr, + rx->sdata->vif.type, + rx->local->hw.extra_tx_headroom, +- check_da, check_sa, mesh); ++ check_da, check_sa, ++ rx->sta->amsdu_mesh_control); + + while (!skb_queue_empty(&frame_list)) { + rx->skb = __skb_dequeue(&frame_list); +--- a/net/mac80211/sta_info.c ++++ b/net/mac80211/sta_info.c +@@ -591,6 +591,9 @@ __sta_info_alloc(struct ieee80211_sub_if + + sta->sta_state = IEEE80211_STA_NONE; + ++ if (sdata->vif.type == NL80211_IFTYPE_MESH_POINT) ++ sta->amsdu_mesh_control = -1; ++ + /* Mark TID as unreserved */ + sta->reserved_tid = IEEE80211_TID_UNRESERVED; + +--- a/net/mac80211/sta_info.h ++++ b/net/mac80211/sta_info.h +@@ -702,6 +702,7 @@ struct sta_info { + struct codel_params cparams; + + u8 reserved_tid; ++ s8 amsdu_mesh_control; + + struct cfg80211_chan_def tdls_chandef; + +--- a/net/wireless/util.c ++++ b/net/wireless/util.c +@@ -776,6 +776,38 @@ __ieee80211_amsdu_copy(struct sk_buff *s + return frame; + } + ++bool ieee80211_is_valid_amsdu(struct sk_buff *skb, bool mesh_hdr) ++{ ++ int offset = 0, remaining, subframe_len, padding; ++ ++ for (offset = 0; offset < skb->len; offset += subframe_len + padding) { ++ struct { ++ __be16 len; ++ u8 mesh_flags; ++ } hdr; ++ u16 len; ++ ++ if (skb_copy_bits(skb, offset + 2 * ETH_ALEN, &hdr, sizeof(hdr)) < 0) ++ return false; ++ ++ if (mesh_hdr) ++ len = le16_to_cpu(*(__le16 *)&hdr.len) + ++ __ieee80211_get_mesh_hdrlen(hdr.mesh_flags); ++ else ++ len = ntohs(hdr.len); ++ ++ subframe_len = sizeof(struct ethhdr) + len; ++ padding = (4 - subframe_len) & 0x3; ++ remaining = skb->len - offset; ++ ++ if (subframe_len > remaining) ++ return false; ++ } ++ ++ return true; ++} ++EXPORT_SYMBOL(ieee80211_is_valid_amsdu); ++ + 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, diff --git a/package/kernel/mac80211/patches/subsys/500-mac80211_configure_antenna_gain.patch b/package/kernel/mac80211/patches/subsys/500-mac80211_configure_antenna_gain.patch index 13b374aae7..70d4e89c90 100644 --- a/package/kernel/mac80211/patches/subsys/500-mac80211_configure_antenna_gain.patch +++ b/package/kernel/mac80211/patches/subsys/500-mac80211_configure_antenna_gain.patch @@ -87,7 +87,7 @@ CFG80211_TESTMODE_DUMP(ieee80211_testmode_dump) --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h -@@ -1521,6 +1521,7 @@ struct ieee80211_local { +@@ -1520,6 +1520,7 @@ struct ieee80211_local { int dynamic_ps_forced_timeout; int user_power_level; /* in dBm, for all interfaces */ diff --git a/package/kernel/mac80211/patches/subsys/800-mac80211-mask-nested-A-MSDU-support-for-mesh.patch b/package/kernel/mac80211/patches/subsys/800-mac80211-mask-nested-A-MSDU-support-for-mesh.patch deleted file mode 100644 index 56cc523022..0000000000 --- a/package/kernel/mac80211/patches/subsys/800-mac80211-mask-nested-A-MSDU-support-for-mesh.patch +++ /dev/null @@ -1,33 +0,0 @@ -From 313d8c18385f10957402b475f9b0c209ceab6c5a Mon Sep 17 00:00:00 2001 -From: David Bauer -Date: Fri, 8 Oct 2021 00:25:19 +0200 -Subject: [PATCH] mac80211: mask nested A-MSDU support for mesh - -mac80211 incorrectly processes A-MSDUs contained in A-MPDU frames. This -results in dropped packets and severely impacted throughput. - -As a workaround, don't indicate support for A-MSDUs contained in -A-MPDUs. This improves throughput over mesh links by factor 10. - -Ref: https://github.com/openwrt/mt76/issues/450 - -Signed-off-by: David Bauer ---- - net/mac80211/agg-rx.c | 4 +++- - 1 file changed, 3 insertions(+), 1 deletion(-) - ---- a/net/mac80211/agg-rx.c -+++ b/net/mac80211/agg-rx.c -@@ -254,7 +254,11 @@ static void ieee80211_send_addba_resp(st - mgmt->u.action.u.addba_resp.action_code = WLAN_ACTION_ADDBA_RESP; - mgmt->u.action.u.addba_resp.dialog_token = dialog_token; - -- capab = u16_encode_bits(amsdu, IEEE80211_ADDBA_PARAM_AMSDU_MASK); -+ capab = 0; -+#ifdef CPTCFG_MAC80211_MESH -+ if (!sta->mesh) -+#endif -+ capab = u16_encode_bits(amsdu, IEEE80211_ADDBA_PARAM_AMSDU_MASK); - capab |= u16_encode_bits(policy, IEEE80211_ADDBA_PARAM_POLICY_MASK); - capab |= u16_encode_bits(tid, IEEE80211_ADDBA_PARAM_TID_MASK); - capab |= u16_encode_bits(buf_size, IEEE80211_ADDBA_PARAM_BUF_SIZE_MASK); -- 2.30.2