]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
wifi: mac80211: Add 802.3 multicast encapsulation offload support
authorTamizh Chelvam Raja <tamizh.raja@oss.qualcomm.com>
Thu, 4 Jun 2026 16:24:03 +0000 (21:54 +0530)
committerJohannes Berg <johannes.berg@intel.com>
Fri, 5 Jun 2026 14:12:26 +0000 (16:12 +0200)
mac80211 converts 802.3 multicast packets to 802.11 format
before driver TX, even when Ethernet encapsulation offload
is enabled. This prevents drivers that support multicast
Ethernet encapsulation offload from receiving frames in
native 802.3 format.

Introduce the IEEE80211_OFFLOAD_ENCAP_MCAST flag to bypass the
802.11 encapsulation step and pass the multicast packet to the
driver in 802.3 format. Drivers that support multicast Ethernet
encapsulation offload can advertise this flag.

Disable multicast encapsulation offload in MLO case for drivers not
advertising MLO_MCAST_MULTI_LINK_TX support for AP mode and for
3-address AP_VLAN multicast packets.

Signed-off-by: Tamizh Chelvam Raja <tamizh.raja@oss.qualcomm.com>
Link: https://patch.msgid.link/20260604162403.1563729-4-tamizh.raja@oss.qualcomm.com
[fix unlikely(), indentation]
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
include/net/mac80211.h
net/mac80211/tx.c

index 7dd558f4025b28470515d06b6fa28ea9a5475726..4f95da023746f9d57a89db80220f0b21a8c1c798 100644 (file)
@@ -2014,12 +2014,16 @@ enum ieee80211_vif_flags {
  * @IEEE80211_OFFLOAD_DECAP_ENABLED: rx encapsulation offload is enabled
  *     The driver supports passing received 802.11 frames as 802.3 frames to
  *     mac80211.
+ * @IEEE80211_OFFLOAD_ENCAP_MCAST: tx multicast encapsulation offload is enabled
+ *     The driver supports sending multicast frames passed as 802.3 frames
+ *     by mac80211.
  */
 
 enum ieee80211_offload_flags {
        IEEE80211_OFFLOAD_ENCAP_ENABLED         = BIT(0),
        IEEE80211_OFFLOAD_ENCAP_4ADDR           = BIT(1),
        IEEE80211_OFFLOAD_DECAP_ENABLED         = BIT(2),
+       IEEE80211_OFFLOAD_ENCAP_MCAST           = BIT(3),
 };
 
 #define IEEE80211_NAN_AVAIL_BLOB_MAX_LEN       54
index a353758f53ff2c66f8db3efc612242282a348f37..e96a5733ec51122d4c27e8b7f2e7c3b149b20663 100644 (file)
@@ -4746,11 +4746,32 @@ out_free:
        kfree_skb(skb);
 }
 
+static bool ieee80211_check_mcast_offload(struct ieee80211_sub_if_data *sdata,
+                                         struct sk_buff *skb)
+{
+       struct ethhdr *ehdr = (struct ethhdr *)skb->data;
+
+       if ((ieee80211_vif_is_mld(&sdata->vif) &&
+            (sdata->vif.type == NL80211_IFTYPE_AP &&
+             !ieee80211_hw_check(&sdata->local->hw, MLO_MCAST_MULTI_LINK_TX))) ||
+           (sdata->vif.type == NL80211_IFTYPE_AP_VLAN &&
+            !sdata->wdev.use_4addr))
+               return false;
+
+       if (!is_multicast_ether_addr(skb->data) ||
+           !(sdata->vif.offload_flags & IEEE80211_OFFLOAD_ENCAP_MCAST) ||
+           sdata->control_port_protocol == ehdr->h_proto)
+               return false;
+
+       return true;
+}
+
 static void __ieee80211_subif_start_xmit_8023(struct sk_buff *skb,
                                              struct net_device *dev)
 {
        struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
        struct ethhdr *ehdr = (struct ethhdr *)skb->data;
+       struct ieee80211_link_data *link;
        struct ieee80211_key *key;
        struct sta_info *sta;
 
@@ -4761,14 +4782,30 @@ static void __ieee80211_subif_start_xmit_8023(struct sk_buff *skb,
                goto out;
        }
 
-       if (unlikely(IS_ERR_OR_NULL(sta) || !sta->uploaded ||
-           !test_sta_flag(sta, WLAN_STA_AUTHORIZED) ||
-           sdata->control_port_protocol == ehdr->h_proto))
+       /*
+        * If STA is invalid, use the multicast offload path when applicable.
+        * In AP mode, drop the frame if there are no associated stations;
+        * otherwise use the default link and multicast key for transmission.
+        */
+       if (unlikely(IS_ERR_OR_NULL(sta) &&
+                    ieee80211_check_mcast_offload(sdata, skb))) {
+               sta = NULL;
+               if (ieee80211_vif_get_num_mcast_if(sdata) <= 0) {
+                       /* No associated STAs - no need to send multicast frames. */
+                       kfree_skb(skb);
+                       goto out;
+               }
+               link = &sdata->deflink;
+               key = rcu_dereference(link->default_multicast_key);
+       } else if (unlikely(IS_ERR_OR_NULL(sta) || !sta->uploaded ||
+                  !test_sta_flag(sta, WLAN_STA_AUTHORIZED) ||
+           sdata->control_port_protocol == ehdr->h_proto)) {
                goto skip_offload;
-
-       key = rcu_dereference(sta->ptk[sta->ptk_idx]);
-       if (!key)
-               key = rcu_dereference(sdata->default_unicast_key);
+       } else {
+               key = rcu_dereference(sta->ptk[sta->ptk_idx]);
+               if (!key)
+                       key = rcu_dereference(sdata->default_unicast_key);
+       }
 
        if (key && (!(key->flags & KEY_FLAG_UPLOADED_TO_HARDWARE) ||
                    key->conf.cipher == WLAN_CIPHER_SUITE_TKIP))