summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTamizh Chelvam Raja <tamizh.raja@oss.qualcomm.com>2026-06-04 21:54:03 +0530
committerJohannes Berg <johannes.berg@intel.com>2026-06-05 16:12:26 +0200
commit4cc0cc0b297c17c2b106d6892bd13d9c32fe66ce (patch)
treeed69424afc22e41e38e3fe651aab63e760fccdc1
parent2307b36ce34fd2166509ea2aeef0de5768ed03b7 (diff)
downloadlinux-4cc0cc0b297c17c2b106d6892bd13d9c32fe66ce.tar.gz
linux-4cc0cc0b297c17c2b106d6892bd13d9c32fe66ce.zip
wifi: mac80211: Add 802.3 multicast encapsulation offload support
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>
-rw-r--r--include/net/mac80211.h4
-rw-r--r--net/mac80211/tx.c51
2 files changed, 48 insertions, 7 deletions
diff --git a/include/net/mac80211.h b/include/net/mac80211.h
index 7dd558f4025b..4f95da023746 100644
--- a/include/net/mac80211.h
+++ b/include/net/mac80211.h
@@ -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
diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c
index a353758f53ff..e96a5733ec51 100644
--- a/net/mac80211/tx.c
+++ b/net/mac80211/tx.c
@@ -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))