The current code does not provide any link-configuration support for 4-address mode WDS AP_VLAN interfaces in MLO setups, preventing MLD stations from being added correctly. Add the required handling to enable proper integration of 4-address WDS stations into an MLO environment. When a 4-address station associates with an MLO AP, compute the intersection of valid links between the master AP interface and the station's advertised capabilities. Configure the AP_VLAN interface with only these common links to ensure correct data-path operation. This update ensures AP_VLAN interfaces correctly track link-state transitions and maintain consistent addressing across all active MLO links. Co-developed-by: Muna Sinada Signed-off-by: Muna Sinada Signed-off-by: Tamizh Chelvam Raja --- net/mac80211/cfg.c | 67 +++++++++++++++++++++++++++++++++++++++++---- net/mac80211/chan.c | 8 ++++++ net/mac80211/link.c | 43 ++++++++++++++++++++--------- 3 files changed, 100 insertions(+), 18 deletions(-) diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index 13132215afb4..e4f28e33400c 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c @@ -2444,6 +2444,65 @@ static int ieee80211_del_station(struct wiphy *wiphy, struct wireless_dev *wdev, return 0; } +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; + struct wireless_dev *wdev; + unsigned long master_iter; + int link_id; + int err; + + lockdep_assert_wiphy(local->hw.wiphy); + + if (sdata->u.vlan.sta) + return -EBUSY; + + wdev = &sdata->wdev; + master = container_of(sdata->bss, + struct ieee80211_sub_if_data, + u.ap); + + if (sta->sta.valid_links) { + u16 sta_links = sta->sta.valid_links; + u16 new_links = master->vif.valid_links & sta_links; + u16 orig_links = wdev->valid_links; + + 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; + + 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]); + + ether_addr_copy(wdev->links[link_id].addr, + link_conf->bssid); + } + } + } + + rcu_assign_pointer(sdata->u.vlan.sta, sta); + __ieee80211_check_fast_rx_iface(sdata); + drv_sta_set_4addr(local, sta->sdata, &sta->sta, true); + + return 0; +} + static int ieee80211_change_station(struct wiphy *wiphy, struct wireless_dev *wdev, const u8 *mac, struct station_parameters *params) @@ -2500,12 +2559,10 @@ static int ieee80211_change_station(struct wiphy *wiphy, vlansdata = IEEE80211_DEV_TO_SUB_IF(params->vlan); if (params->vlan->ieee80211_ptr->use_4addr) { - if (vlansdata->u.vlan.sta) - return -EBUSY; + err = ieee80211_set_sta_4addr(local, vlansdata, sta); + if (err) + return err; - rcu_assign_pointer(vlansdata->u.vlan.sta, sta); - __ieee80211_check_fast_rx_iface(vlansdata); - drv_sta_set_4addr(local, sta->sdata, &sta->sta, true); } if (sta->sdata->vif.type == NL80211_IFTYPE_AP_VLAN && diff --git a/net/mac80211/chan.c b/net/mac80211/chan.c index 1e4bfcd25697..9bacc840bed7 100644 --- a/net/mac80211/chan.c +++ b/net/mac80211/chan.c @@ -1239,6 +1239,10 @@ __ieee80211_link_copy_chanctx_to_vlans(struct ieee80211_link_data *link, list_for_each_entry(vlan, &sdata->u.ap.vlans, u.vlan.list) { struct ieee80211_bss_conf *vlan_conf; + if (vlan->vif.valid_links && + !(vlan->vif.valid_links & BIT(link_id))) + continue; + vlan_conf = wiphy_dereference(local->hw.wiphy, vlan->vif.link_conf[link_id]); if (WARN_ON(!vlan_conf)) @@ -1482,6 +1486,10 @@ ieee80211_link_update_chanreq(struct ieee80211_link_data *link, list_for_each_entry(vlan, &sdata->u.ap.vlans, u.vlan.list) { struct ieee80211_bss_conf *vlan_conf; + if (vlan->vif.valid_links && + !(vlan->vif.valid_links & BIT(link_id))) + continue; + vlan_conf = wiphy_dereference(sdata->local->hw.wiphy, vlan->vif.link_conf[link_id]); if (WARN_ON(!vlan_conf)) diff --git a/net/mac80211/link.c b/net/mac80211/link.c index 03bfca27d205..93e290dd783f 100644 --- a/net/mac80211/link.c +++ b/net/mac80211/link.c @@ -14,26 +14,38 @@ static void ieee80211_update_apvlan_links(struct ieee80211_sub_if_data *sdata) { + unsigned long rem = ~sdata->vif.valid_links & + GENMASK(IEEE80211_MLD_MAX_NUM_LINKS - 1, 0); + struct ieee80211_local *local = sdata->local; + unsigned long add = sdata->vif.valid_links; + struct wiphy *wiphy = local->hw.wiphy; struct ieee80211_sub_if_data *vlan; struct ieee80211_link_data *link; - u16 ap_bss_links = sdata->vif.valid_links; - u16 new_links, vlan_links; - unsigned long add; + struct sta_info *sta; list_for_each_entry(vlan, &sdata->u.ap.vlans, u.vlan.list) { int link_id; - /* No support for 4addr with MLO yet */ - if (vlan->wdev.use_4addr) - return; + if (vlan->wdev.use_4addr) { + sta = wiphy_dereference(wiphy, + vlan->u.vlan.sta); + if (sta) + add = add & sta->sta.valid_links; + } - vlan_links = vlan->vif.valid_links; + if (add == vlan->vif.valid_links) + continue; - new_links = ap_bss_links; + for_each_set_bit(link_id, &add, IEEE80211_MLD_MAX_NUM_LINKS) { + vlan->wdev.valid_links |= BIT(link_id); + ether_addr_copy(vlan->wdev.links[link_id].addr, + sdata->wdev.links[link_id].addr); + } - add = new_links & ~vlan_links; - if (!add) - continue; + for_each_set_bit(link_id, &rem, IEEE80211_MLD_MAX_NUM_LINKS) { + vlan->wdev.valid_links &= ~BIT(link_id); + eth_zero_addr(vlan->wdev.links[link_id].addr); + } ieee80211_vif_set_links(vlan, add, 0); @@ -96,8 +108,13 @@ void ieee80211_link_init(struct ieee80211_sub_if_data *sdata, ap_bss = container_of(sdata->bss, struct ieee80211_sub_if_data, u.ap); - ap_bss_conf = sdata_dereference(ap_bss->vif.link_conf[link_id], - ap_bss); + + if (deflink) + ap_bss_conf = &ap_bss->vif.bss_conf; + else + ap_bss_conf = sdata_dereference(ap_bss->vif.link_conf[link_id], + ap_bss); + memcpy(link_conf, ap_bss_conf, sizeof(*link_conf)); } -- 2.34.1 Currently ieee80211_send_4addr_nullfunc() uses deflink.u.mgd.bssid for addr1 and addr3 fields. In MLO configurations, deflink.u.mgd.bssid represents link 0's BSSID and is not updated when link 0 is not an assoc link. This causes 4-address NULL frames to be sent to the wrong address, preventing WDS AP_VLAN interface creation on the peer AP. To fix this use sdata->vif.cfg.ap_addr instead, which contains the AP's MLD address populated during authentication/association and remains valid regardless of which links are active. This ensures 4-address NULL frames reach the correct AP, allowing proper WDS operation over MLO connections. Co-developed-by: Sathishkumar Muruganandam Signed-off-by: Sathishkumar Muruganandam Signed-off-by: Tamizh Chelvam Raja --- net/mac80211/mlme.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index 7fc5616cb244..9603fe4b612f 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -2514,9 +2514,9 @@ void ieee80211_send_4addr_nullfunc(struct ieee80211_local *local, fc = cpu_to_le16(IEEE80211_FTYPE_DATA | IEEE80211_STYPE_NULLFUNC | IEEE80211_FCTL_FROMDS | IEEE80211_FCTL_TODS); nullfunc->frame_control = fc; - memcpy(nullfunc->addr1, sdata->deflink.u.mgd.bssid, ETH_ALEN); + memcpy(nullfunc->addr1, sdata->vif.cfg.ap_addr, ETH_ALEN); memcpy(nullfunc->addr2, sdata->vif.addr, ETH_ALEN); - memcpy(nullfunc->addr3, sdata->deflink.u.mgd.bssid, ETH_ALEN); + memcpy(nullfunc->addr3, sdata->vif.cfg.ap_addr, ETH_ALEN); memcpy(nullfunc->addr4, sdata->vif.addr, ETH_ALEN); IEEE80211_SKB_CB(skb)->flags |= IEEE80211_TX_INTFL_DONT_ENCRYPT; -- 2.34.1 The current code does not support establishing MLO connections for interfaces operating in 4-address AP_VLAN mode. MLO bringup is blocked by sanity checks in cfg.c, iface.c, and mlme.c, which prevent MLD initialization when use_4addr is enabled. Remove these restrictions to allow 4-address AP_VLAN interfaces to initialize as part of an MLD and successfully participate in MLO connections. This patch series also adds the necessary changes to support WDS operation in MLO, making these modifications valid. Allow 4-address mode interfaces to: - Proceed with MLD initialization during interface setup - Add MLO links dynamically via ieee80211_add_intf_link() - Establish associations with MLO-capable access points - Support AP_VLAN interfaces with MLO parent APs Signed-off-by: Tamizh Chelvam Raja --- net/mac80211/cfg.c | 7 ------- net/mac80211/iface.c | 7 ------- net/mac80211/mlme.c | 4 ---- 3 files changed, 18 deletions(-) diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index e4f28e33400c..ccb41125b969 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c @@ -281,10 +281,6 @@ static int ieee80211_change_iface(struct wiphy *wiphy, if (params->use_4addr == ifmgd->use_4addr) return 0; - /* FIXME: no support for 4-addr MLO yet */ - if (ieee80211_vif_is_mld(&sdata->vif)) - return -EOPNOTSUPP; - sdata->u.mgd.use_4addr = params->use_4addr; if (!ifmgd->associated) return 0; @@ -5480,9 +5476,6 @@ static int ieee80211_add_intf_link(struct wiphy *wiphy, lockdep_assert_wiphy(sdata->local->hw.wiphy); - if (wdev->use_4addr) - return -EOPNOTSUPP; - return ieee80211_vif_set_links(sdata, wdev->valid_links, 0); } diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c index 125897717a4c..f6c65b88cabd 100644 --- a/net/mac80211/iface.c +++ b/net/mac80211/iface.c @@ -398,13 +398,6 @@ static int ieee80211_check_concurrent_iface(struct ieee80211_sub_if_data *sdata, nsdata->vif.type)) return -ENOTUNIQ; - /* No support for VLAN with MLO yet */ - if (iftype == NL80211_IFTYPE_AP_VLAN && - sdata->wdev.use_4addr && - nsdata->vif.type == NL80211_IFTYPE_AP && - nsdata->vif.valid_links) - return -EOPNOTSUPP; - /* * can only add VLANs to enabled APs */ diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index 9603fe4b612f..bc61369213f3 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -9858,10 +9858,6 @@ int ieee80211_mgd_assoc(struct ieee80211_sub_if_data *sdata, for (i = 0; i < IEEE80211_MLD_MAX_NUM_LINKS; i++) size += req->links[i].elems_len; - /* FIXME: no support for 4-addr MLO yet */ - if (sdata->u.mgd.use_4addr && req->link_id >= 0) - return -EOPNOTSUPP; - assoc_data = kzalloc(size, GFP_KERNEL); if (!assoc_data) return -ENOMEM; -- 2.34.1