summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--include/uapi/linux/nl80211.h6
-rw-r--r--net/mac80211/tx.c43
2 files changed, 47 insertions, 2 deletions
diff --git a/include/uapi/linux/nl80211.h b/include/uapi/linux/nl80211.h
index 3fa586e38f88..d4d6ba585b41 100644
--- a/include/uapi/linux/nl80211.h
+++ b/include/uapi/linux/nl80211.h
@@ -1119,6 +1119,12 @@
* has been received. %NL80211_ATTR_FRAME is used to specify the
* frame contents. The frame is the raw EAPoL data, without ethernet or
* 802.11 headers.
+ * For an MLD transmitter, the %NL80211_ATTR_MLO_LINK_ID may be given and
+ * its effect will depend on the destination: If the destination is known
+ * to be an MLD, this will be used as a hint to select the link to transmit
+ * the frame on. If the destination is not an MLD, this will select both
+ * the link to transmit on and the source address will be set to the link
+ * address of that link.
* When used as an event indication %NL80211_ATTR_CONTROL_PORT_ETHERTYPE,
* %NL80211_ATTR_CONTROL_PORT_NO_ENCRYPT and %NL80211_ATTR_MAC are added
* indicating the protocol type of the received frame; whether the frame
diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c
index 772108c2cc6b..06ec152e8188 100644
--- a/net/mac80211/tx.c
+++ b/net/mac80211/tx.c
@@ -2896,9 +2896,35 @@ static struct sk_buff *ieee80211_build_hdr(struct ieee80211_sub_if_data *sdata,
info->flags = info_flags;
info->ack_frame_id = info_id;
info->band = band;
- info->control.flags = ctrl_flags |
- u32_encode_bits(link_id,
+
+ if (likely(!cookie)) {
+ ctrl_flags |= u32_encode_bits(link_id,
IEEE80211_TX_CTRL_MLO_LINK);
+ } else {
+ unsigned int pre_conf_link_id;
+
+ /*
+ * ctrl_flags already have been set by
+ * ieee80211_tx_control_port(), here
+ * we just sanity check that
+ */
+
+ pre_conf_link_id = u32_get_bits(ctrl_flags,
+ IEEE80211_TX_CTRL_MLO_LINK);
+
+ if (pre_conf_link_id != link_id &&
+ link_id != IEEE80211_LINK_UNSPECIFIED) {
+#ifdef CPTCFG_MAC80211_VERBOSE_DEBUG
+ net_info_ratelimited("%s: dropped frame to %pM with bad link ID request (%d vs. %d)\n",
+ sdata->name, hdr.addr1,
+ pre_conf_link_id, link_id);
+#endif
+ ret = -EINVAL;
+ goto free;
+ }
+ }
+
+ info->control.flags = ctrl_flags;
return skb;
free:
@@ -5745,11 +5771,17 @@ int ieee80211_tx_control_port(struct wiphy *wiphy, struct net_device *dev,
ehdr = skb_push(skb, sizeof(struct ethhdr));
memcpy(ehdr->h_dest, dest, ETH_ALEN);
+ /* we may override the SA for MLO STA later */
if (link_id < 0) {
+ ctrl_flags |= u32_encode_bits(IEEE80211_LINK_UNSPECIFIED,
+ IEEE80211_TX_CTRL_MLO_LINK);
memcpy(ehdr->h_source, sdata->vif.addr, ETH_ALEN);
} else {
struct ieee80211_bss_conf *link_conf;
+ ctrl_flags |= u32_encode_bits(link_id,
+ IEEE80211_TX_CTRL_MLO_LINK);
+
rcu_read_lock();
link_conf = rcu_dereference(sdata->vif.link_conf[link_id]);
if (!link_conf) {
@@ -5784,6 +5816,13 @@ int ieee80211_tx_control_port(struct wiphy *wiphy, struct net_device *dev,
skb_set_queue_mapping(skb, queue);
skb_get_hash(skb);
+
+ /*
+ * for MLO STA, the SA should be the AP MLD address, but
+ * the link ID has been selected already
+ */
+ if (sta->sta.mlo)
+ memcpy(ehdr->h_source, sdata->vif.addr, ETH_ALEN);
}
rcu_read_unlock();