ieee80211_set_sta_4addr() gives the AP_VLAN of a 4-address MLD station the links of that station. It added the links first and set wdev->links[].addr afterwards, from the link_conf of the AP_VLAN itself. ieee80211_link_init() takes the address of a new link from wdev->links[].addr, so a link that the station gained later came up with the zero address that an earlier call had set, and the copy from its own link_conf kept it at zero. With two such links, ieee80211_check_dup_link_addrs() refuses the change with -EALREADY, and the station stays on the AP interface instead of its AP_VLAN. Set the addresses from the link_conf of the AP before the links are added, as the AP_VLAN link update in link.c does. Fixes: 469d5d5a3b7a ("wifi: mac80211: synchronize valid links for WDS AP_VLAN interfaces") Cc: stable@vger.kernel.org Signed-off-by: Piotr Kubik --- v2: remove the now unused vif variable. net/mac80211/cfg.c | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index a1753335eb9d..371f441d6b7f 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c @@ -2736,7 +2736,6 @@ static int ieee80211_set_sta_4addr(struct ieee80211_local *local, struct ieee80211_sub_if_data *sdata, struct sta_info *sta) { - struct ieee80211_vif *vif = &sdata->vif; struct wiphy *wiphy = local->hw.wiphy; struct ieee80211_sub_if_data *master; struct ieee80211_bss_conf *link_conf; @@ -2762,26 +2761,29 @@ static int ieee80211_set_sta_4addr(struct ieee80211_local *local, wdev->valid_links = new_links; - err = ieee80211_vif_set_links(sdata, new_links, 0); - if (err) { - wdev->valid_links = orig_links; - return err; - } - master_iter = master->vif.valid_links; + /* ieee80211_link_init() takes the address of a new link from + * wdev->links, so set it before the links are added. + */ for_each_set_bit(link_id, &master_iter, IEEE80211_MLD_MAX_NUM_LINKS) { if (!(sta_links & BIT(link_id))) { eth_zero_addr(wdev->links[link_id].addr); } else { link_conf = wiphy_dereference(wiphy, - vif->link_conf[link_id]); + master->vif.link_conf[link_id]); ether_addr_copy(wdev->links[link_id].addr, link_conf->bssid); } } + + err = ieee80211_vif_set_links(sdata, new_links, 0); + if (err) { + wdev->valid_links = orig_links; + return err; + } } rcu_assign_pointer(sdata->u.vlan.sta, sta); -- 2.55.0