A 4-address AP_VLAN always sets A2 to sdata->vif.addr, which on an MLD AP is the AP MLD address. For a non-MLO station, A2 must be the link address it associated to. Use the link address for a non-MLO station in the 4-address case as well, like the AP case already does in ieee80211_build_hdr() and ieee80211_check_fast_xmit(). Share the address selection between both cases and both TX paths. Fixes: 3e0278b717b0 ("wifi: mac80211: select link when transmitting to non-MLO stations") Signed-off-by: Felix Fietkau --- net/mac80211/tx.c | 68 ++++++++++++++++++++++++++++------------------- 1 file changed, 40 insertions(+), 28 deletions(-) diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c index efe12384c59b..8bebda8038c5 100644 --- a/net/mac80211/tx.c +++ b/net/mac80211/tx.c @@ -2698,6 +2698,32 @@ static void ieee80211_store_ack_skb(struct ieee80211_local *local, } } +static int ieee80211_ap_tx_addr_get(struct ieee80211_sub_if_data *sdata, + struct sta_info *sta, u8 *addr, u8 *link_id) +{ + struct ieee80211_link_data *link; + int ret = 0; + + if (!ieee80211_vif_is_mld(&sdata->vif) || sta->sta.mlo) { + memcpy(addr, sdata->vif.addr, ETH_ALEN); + return 0; + } + + rcu_read_lock(); + link = rcu_dereference(sdata->link[sta->deflink.link_id]); + if (WARN_ON(!link)) { + ret = -ENOLINK; + goto out; + } + + memcpy(addr, link->conf->addr, ETH_ALEN); + if (link_id) + *link_id = sta->deflink.link_id; +out: + rcu_read_unlock(); + return ret; +} + /** * ieee80211_build_hdr - build 802.11 header in the given frame * @sdata: virtual interface to build the header for @@ -2762,7 +2788,10 @@ static struct sk_buff *ieee80211_build_hdr(struct ieee80211_sub_if_data *sdata, fc |= cpu_to_le16(IEEE80211_FCTL_FROMDS | IEEE80211_FCTL_TODS); /* RA TA DA SA */ memcpy(hdr.addr1, sta->sta.addr, ETH_ALEN); - memcpy(hdr.addr2, sdata->vif.addr, ETH_ALEN); + ret = ieee80211_ap_tx_addr_get(sdata, sta, hdr.addr2, + &link_id); + if (ret) + goto free; memcpy(hdr.addr3, skb->data, ETH_ALEN); memcpy(hdr.addr4, skb->data + ETH_ALEN, ETH_ALEN); hdrlen = 30; @@ -2787,18 +2816,12 @@ static struct sk_buff *ieee80211_build_hdr(struct ieee80211_sub_if_data *sdata, /* DA BSSID SA */ memcpy(hdr.addr1, skb->data, ETH_ALEN); - if (ieee80211_vif_is_mld(&sdata->vif) && sta && !sta->sta.mlo) { - struct ieee80211_link_data *link; - - link_id = sta->deflink.link_id; - link = rcu_dereference(sdata->link[link_id]); - if (WARN_ON(!link)) { - ret = -ENOLINK; + if (sta) { + ret = ieee80211_ap_tx_addr_get(sdata, sta, hdr.addr2, + &link_id); + if (ret) goto free; - } - memcpy(hdr.addr2, link->conf->addr, ETH_ALEN); - } else if (link_id == IEEE80211_LINK_UNSPECIFIED || - (sta && sta->sta.mlo)) { + } else if (link_id == IEEE80211_LINK_UNSPECIFIED) { memcpy(hdr.addr2, sdata->vif.addr, ETH_ALEN); } else { struct ieee80211_bss_conf *conf; @@ -3230,7 +3253,9 @@ void ieee80211_check_fast_xmit(struct sta_info *sta) IEEE80211_FCTL_TODS); /* RA TA DA SA */ memcpy(hdr->addr1, sta->sta.addr, ETH_ALEN); - memcpy(hdr->addr2, sdata->vif.addr, ETH_ALEN); + if (ieee80211_ap_tx_addr_get(sdata, sta, hdr->addr2, + NULL)) + goto out; build.da_offs = offsetof(struct ieee80211_hdr, addr3); build.sa_offs = offsetof(struct ieee80211_hdr, addr4); build.hdr_len = 30; @@ -3241,21 +3266,8 @@ void ieee80211_check_fast_xmit(struct sta_info *sta) fc |= cpu_to_le16(IEEE80211_FCTL_FROMDS); /* DA BSSID SA */ build.da_offs = offsetof(struct ieee80211_hdr, addr1); - if (sta->sta.mlo || !ieee80211_vif_is_mld(&sdata->vif)) { - memcpy(hdr->addr2, sdata->vif.addr, ETH_ALEN); - } else { - unsigned int link_id = sta->deflink.link_id; - struct ieee80211_link_data *link; - - rcu_read_lock(); - link = rcu_dereference(sdata->link[link_id]); - if (WARN_ON(!link)) { - rcu_read_unlock(); - goto out; - } - memcpy(hdr->addr2, link->conf->addr, ETH_ALEN); - rcu_read_unlock(); - } + if (ieee80211_ap_tx_addr_get(sdata, sta, hdr->addr2, NULL)) + goto out; build.sa_offs = offsetof(struct ieee80211_hdr, addr3); build.hdr_len = 24; break; -- 2.47.3