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 <muna.sinada@oss.qualcomm.com>
Signed-off-by: Muna Sinada <muna.sinada@oss.qualcomm.com>
Signed-off-by: Tamizh Chelvam Raja <tamizh.raja@oss.qualcomm.com>
Link: https://patch.msgid.link/20260326164723.553927-2-tamizh.raja@oss.qualcomm.com
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
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)
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 &&
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))
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))
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);
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));
}