This commit enables Dynamic VLAN for Multi-Link Operation (MLO) in hostapd. When Dynamic VLAN is enabled for AP MLD (Multi-Link Device), VLAN list will be maintained in the first BSS (Basic Service Set). Key changes include: - Utilize MLD address for creating new VLAN interfaces. - Access the VLAN list from first BSS for the MLD. - If first bss is removed, move reference to VLAN list to new first bss. - When setting Group Temporal Key (GTK) per link, reference the first BSS for the VLAN list. - For non-AP MLD, copy the vlan_id from the primary link to other links. - Pass first bss when calling functions that iterate through the VLAN list such as vlan_add_dynamic() and vlan_remove_dynamic() Signed-off-by: Muna Sinada <quic_msinada@xxxxxxxxxxx> Signed-off-by: Pradeep Kumar Chitrapu <quic_pradeepc@xxxxxxxxxxx> --- src/ap/ap_drv_ops.c | 11 ++++++- src/ap/hostapd.c | 19 +++++++++++ src/ap/ieee802_11.c | 1 + src/ap/sta_info.c | 71 +++++++++++++++++++++++++++++++----------- src/ap/vlan_full.c | 22 +++++++++++-- src/ap/wpa_auth_glue.c | 11 ++++++- 6 files changed, 112 insertions(+), 23 deletions(-) diff --git a/src/ap/ap_drv_ops.c b/src/ap/ap_drv_ops.c index 65e83f468baa..fc044a4c7d93 100644 --- a/src/ap/ap_drv_ops.c +++ b/src/ap/ap_drv_ops.c @@ -372,7 +372,16 @@ int hostapd_vlan_if_add(struct hostapd_data *hapd, const char *ifname) { char force_ifname[IFNAMSIZ]; u8 if_addr[ETH_ALEN]; - return hostapd_if_add(hapd, WPA_IF_AP_VLAN, ifname, hapd->own_addr, + const u8 *addr; + +#ifdef CONFIG_IEEE80211BE + if (hapd->conf->mld_ap) + addr = hapd->mld->mld_addr; + else +#endif /* CONFIG_IEEE80211BE */ + addr = hapd->own_addr; + + return hostapd_if_add(hapd, WPA_IF_AP_VLAN, ifname, addr, NULL, NULL, force_ifname, if_addr, NULL, 0); } diff --git a/src/ap/hostapd.c b/src/ap/hostapd.c index 9dfc21e00f3e..bf15bdaa4b12 100644 --- a/src/ap/hostapd.c +++ b/src/ap/hostapd.c @@ -616,6 +616,24 @@ void hostapd_free_hapd_data(struct hostapd_data *hapd) #endif /* CONFIG_IEEE80211AX */ } +/* hostapd_mld_move_vlan_list - Move vlan list to new first bss + * @old_fbss + * @new_fbss + * + * This function is used to copy reference to vlan list from old first bss to + * new first bss when the first bss is removed. + */ +static void hostapd_mld_move_vlan_list(struct hostapd_data *old_fbss, + struct hostapd_data *new_fbss) +{ +#ifdef CONFIG_IEEE80211BE + if (!old_fbss || !new_fbss) + return; + + new_fbss->conf->vlan = old_fbss->conf->vlan; + old_fbss->conf->vlan = NULL; +#endif /* CONFIG_IEEE80211BE */ +} /* hostapd_bss_link_deinit - Per-BSS ML cleanup (deinitialization) * @hapd: Pointer to BSS data @@ -5006,6 +5024,7 @@ int hostapd_mld_remove_link(struct hostapd_data *hapd) if (dl_list_empty(&mld->links)) { mld->fbss = NULL; } else { + hostapd_mld_move_vlan_list(mld->fbss, hapd); next_fbss = dl_list_entry(mld->links.next, struct hostapd_data, link); mld->fbss = next_fbss; diff --git a/src/ap/ieee802_11.c b/src/ap/ieee802_11.c index 00fd5e4bf6ef..7ad59276ab84 100644 --- a/src/ap/ieee802_11.c +++ b/src/ap/ieee802_11.c @@ -4674,6 +4674,7 @@ static int ieee80211_ml_process_link(struct hostapd_data *hapd, hapd->mld_link_id, sta->aid); sta->flags |= WLAN_STA_AUTH | WLAN_STA_ASSOC_REQ_OK; + sta->vlan_id = origin_sta->vlan_id; /* TODO: What other processing is required? */ diff --git a/src/ap/sta_info.c b/src/ap/sta_info.c index 857d3de50fd0..cc9786d6f361 100644 --- a/src/ap/sta_info.c +++ b/src/ap/sta_info.c @@ -224,6 +224,9 @@ static void clear_wpa_sm_for_each_partner_link(struct hostapd_data *hapd, void ap_free_sta(struct hostapd_data *hapd, struct sta_info *sta) { +#ifndef CONFIG_NO_VLAN + struct hostapd_data *vlan_bss = hapd; +#endif /* CONFIG_NO_VLAN */ int set_beacon = 0; accounting_sta_stop(hapd, sta); @@ -362,13 +365,20 @@ void ap_free_sta(struct hostapd_data *hapd, struct sta_info *sta) #endif /* CONFIG_NO_RADIUS */ #ifndef CONFIG_NO_VLAN +#ifdef CONFIG_IEEE80211BE + if (hapd->conf->mld_ap) { + vlan_bss = hostapd_mld_get_first_bss(hapd); + if (vlan_bss == NULL) + vlan_bss = hapd; + } +#endif /* * sta->wpa_sm->group needs to be released before so that * vlan_remove_dynamic() can check that no stations are left on the * AP_VLAN netdev. */ if (sta->vlan_id) - vlan_remove_dynamic(hapd, sta->vlan_id); + vlan_remove_dynamic(vlan_bss, sta->vlan_id); if (sta->vlan_id_bound) { /* * Need to remove the STA entry before potentially removing the @@ -379,7 +389,7 @@ void ap_free_sta(struct hostapd_data *hapd, struct sta_info *sta) hostapd_drv_sta_remove(hapd, sta->addr); sta->added_unassoc = 0; } - vlan_remove_dynamic(hapd, sta->vlan_id_bound); + vlan_remove_dynamic(vlan_bss, sta->vlan_id_bound); } #endif /* CONFIG_NO_VLAN */ @@ -1174,10 +1184,19 @@ int ap_sta_wps_cancel(struct hostapd_data *hapd, static int ap_sta_get_free_vlan_id(struct hostapd_data *hapd) { struct hostapd_vlan *vlan; + struct hostapd_data *vlan_bss = hapd; int vlan_id = MAX_VLAN_ID + 2; +#ifdef CONFIG_IEEE80211BE + if (hapd->conf->mld_ap) { + vlan_bss = hostapd_mld_get_first_bss(hapd); + if (vlan_bss == NULL) + vlan_bss = hapd; + } +#endif + retry: - for (vlan = hapd->conf->vlan; vlan; vlan = vlan->next) { + for (vlan = vlan_bss->conf->vlan; vlan; vlan = vlan->next) { if (vlan->vlan_id == vlan_id) { vlan_id++; goto retry; @@ -1191,8 +1210,17 @@ int ap_sta_set_vlan(struct hostapd_data *hapd, struct sta_info *sta, struct vlan_description *vlan_desc) { struct hostapd_vlan *vlan = NULL, *wildcard_vlan = NULL; + struct hostapd_data *vlan_bss = hapd; int old_vlan_id, vlan_id = 0, ret = 0; +#ifdef CONFIG_IEEE80211BE + if (hapd->conf->mld_ap) { + vlan_bss = hostapd_mld_get_first_bss(hapd); + if (vlan_bss == NULL) + vlan_bss = hapd; + } +#endif + /* Check if there is something to do */ if (hapd->conf->ssid.per_sta_vif && !sta->vlan_id) { /* This sta is lacking its own vif */ @@ -1209,7 +1237,7 @@ int ap_sta_set_vlan(struct hostapd_data *hapd, struct sta_info *sta, /* find a free vlan_id sufficiently big */ vlan_id = ap_sta_get_free_vlan_id(hapd); /* Get wildcard VLAN */ - for (vlan = hapd->conf->vlan; vlan; vlan = vlan->next) { + for (vlan = vlan_bss->conf->vlan; vlan; vlan = vlan->next) { if (vlan->vlan_id == VLAN_ID_WILDCARD) break; } @@ -1223,7 +1251,7 @@ int ap_sta_set_vlan(struct hostapd_data *hapd, struct sta_info *sta, goto done; } } else if (vlan_desc && vlan_desc->notempty) { - for (vlan = hapd->conf->vlan; vlan; vlan = vlan->next) { + for (vlan = vlan_bss->conf->vlan; vlan; vlan = vlan->next) { if (!vlan_compare(&vlan->vlan_desc, vlan_desc)) break; if (vlan->vlan_id == VLAN_ID_WILDCARD) @@ -1236,10 +1264,10 @@ int ap_sta_set_vlan(struct hostapd_data *hapd, struct sta_info *sta, vlan_id = vlan_desc->untagged; if (vlan_desc->tagged[0]) { /* Tagged VLAN configuration */ - vlan_id = ap_sta_get_free_vlan_id(hapd); + vlan_id = ap_sta_get_free_vlan_id(vlan_bss); } } else { - hostapd_logger(hapd, sta->addr, + hostapd_logger(vlan_bss, sta->addr, HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_DEBUG, "missing vlan and wildcard for vlan=%d%s", @@ -1252,9 +1280,9 @@ int ap_sta_set_vlan(struct hostapd_data *hapd, struct sta_info *sta, } if (vlan && vlan->vlan_id == VLAN_ID_WILDCARD) { - vlan = vlan_add_dynamic(hapd, vlan, vlan_id, vlan_desc); + vlan = vlan_add_dynamic(vlan_bss, vlan, vlan_id, vlan_desc); if (vlan == NULL) { - hostapd_logger(hapd, sta->addr, + hostapd_logger(vlan_bss, sta->addr, HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_DEBUG, "could not add dynamic VLAN interface for vlan=%d%s", @@ -1266,13 +1294,13 @@ int ap_sta_set_vlan(struct hostapd_data *hapd, struct sta_info *sta, goto done; } - hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, + hostapd_logger(vlan_bss, sta->addr, HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_DEBUG, "added new dynamic VLAN interface '%s'", vlan->ifname); } else if (vlan && vlan->dynamic_vlan > 0) { vlan->dynamic_vlan++; - hostapd_logger(hapd, sta->addr, + hostapd_logger(vlan_bss, sta->addr, HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_DEBUG, "updated existing dynamic VLAN interface '%s'", @@ -1284,7 +1312,7 @@ done: sta->vlan_desc = vlan ? &vlan->vlan_desc : NULL; if (vlan_id != old_vlan_id && old_vlan_id) - vlan_remove_dynamic(hapd, old_vlan_id); + vlan_remove_dynamic(vlan_bss, old_vlan_id); return ret; } @@ -1295,13 +1323,18 @@ int ap_sta_bind_vlan(struct hostapd_data *hapd, struct sta_info *sta) #ifndef CONFIG_NO_VLAN const char *iface; struct hostapd_vlan *vlan = NULL; + struct hostapd_data *vlan_bss = hapd; int ret; int old_vlanid = sta->vlan_id_bound; int mld_link_id = -1; #ifdef CONFIG_IEEE80211BE - if (hapd->conf->mld_ap) + if (hapd->conf->mld_ap) { mld_link_id = hapd->mld_link_id; + vlan_bss = hostapd_mld_get_first_bss(hapd); + if (vlan_bss == NULL) + vlan_bss = hapd; + } #endif /* CONFIG_IEEE80211BE */ if ((sta->flags & WLAN_STA_WDS) && sta->vlan_id == 0) { @@ -1316,7 +1349,7 @@ int ap_sta_bind_vlan(struct hostapd_data *hapd, struct sta_info *sta) iface = hapd->conf->ssid.vlan; if (sta->vlan_id > 0) { - for (vlan = hapd->conf->vlan; vlan; vlan = vlan->next) { + for (vlan = vlan_bss->conf->vlan; vlan; vlan = vlan->next) { if (vlan->vlan_id == sta->vlan_id) break; } @@ -1334,7 +1367,7 @@ int ap_sta_bind_vlan(struct hostapd_data *hapd, struct sta_info *sta) if (sta->vlan_id > 0 && !vlan && !(hapd->iface->drv_flags & WPA_DRIVER_FLAGS_VLAN_OFFLOAD)) { - hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, + hostapd_logger(vlan_bss, sta->addr, HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_DEBUG, "could not find VLAN for " "binding station to (vlan_id=%d)", sta->vlan_id); @@ -1342,7 +1375,7 @@ int ap_sta_bind_vlan(struct hostapd_data *hapd, struct sta_info *sta) goto done; } else if (vlan && vlan->dynamic_vlan > 0) { vlan->dynamic_vlan++; - hostapd_logger(hapd, sta->addr, + hostapd_logger(vlan_bss, sta->addr, HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_DEBUG, "updated existing dynamic VLAN interface '%s'", @@ -1353,7 +1386,7 @@ int ap_sta_bind_vlan(struct hostapd_data *hapd, struct sta_info *sta) sta->vlan_id_bound = sta->vlan_id; skip_counting: - hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, + hostapd_logger(vlan_bss, sta->addr, HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_DEBUG, "binding station to interface " "'%s'", iface); @@ -1363,14 +1396,14 @@ skip_counting: ret = hostapd_drv_set_sta_vlan(iface, hapd, sta->addr, sta->vlan_id, mld_link_id); if (ret < 0) { - hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, + hostapd_logger(vlan_bss, sta->addr, HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_DEBUG, "could not bind the STA " "entry to vlan_id=%d", sta->vlan_id); } /* During 1x reauth, if the vlan id changes, then remove the old id. */ if (old_vlanid > 0 && old_vlanid != sta->vlan_id) - vlan_remove_dynamic(hapd, old_vlanid); + vlan_remove_dynamic(vlan_bss, old_vlanid); done: return ret; diff --git a/src/ap/vlan_full.c b/src/ap/vlan_full.c index 19aa3c649a5d..bd79b35c9c10 100644 --- a/src/ap/vlan_full.c +++ b/src/ap/vlan_full.c @@ -462,11 +462,19 @@ void vlan_newlink(const char *ifname, struct hostapd_data *hapd) { char br_name[IFNAMSIZ]; struct hostapd_vlan *vlan; + struct hostapd_data *vlan_bss = hapd; int untagged, *tagged, i, notempty; wpa_printf(MSG_DEBUG, "VLAN: vlan_newlink(%s)", ifname); - for (vlan = hapd->conf->vlan; vlan; vlan = vlan->next) { +#ifdef CONFIG_IEEE80211BE + if (hapd->conf->mld_ap) { + vlan_bss = hostapd_mld_get_first_bss(hapd); + if (vlan_bss == NULL) + vlan_bss = hapd; + } +#endif + for (vlan = vlan_bss->conf->vlan; vlan; vlan = vlan->next) { if (vlan->configured || os_strcmp(ifname, vlan->ifname) != 0) continue; @@ -563,10 +571,20 @@ static void vlan_put_bridge(const char *br_name, struct hostapd_data *hapd, void vlan_dellink(const char *ifname, struct hostapd_data *hapd) { - struct hostapd_vlan *first, *prev, *vlan = hapd->conf->vlan; + struct hostapd_vlan *first, *prev, *vlan; + struct hostapd_data *vlan_bss = hapd; wpa_printf(MSG_DEBUG, "VLAN: vlan_dellink(%s)", ifname); +#ifdef CONFIG_IEEE80211BE + if (hapd->conf->mld_ap) { + vlan_bss = hostapd_mld_get_first_bss(hapd); + if (vlan_bss == NULL) + vlan_bss = hapd; + } +#endif + + vlan = vlan_bss->conf->vlan; first = prev = vlan; while (vlan) { diff --git a/src/ap/wpa_auth_glue.c b/src/ap/wpa_auth_glue.c index 9fa9f19b7bc5..5d7884199f17 100644 --- a/src/ap/wpa_auth_glue.c +++ b/src/ap/wpa_auth_glue.c @@ -508,10 +508,19 @@ static int hostapd_wpa_auth_set_key(void *ctx, int vlan_id, enum wpa_alg alg, size_t key_len, enum key_flag key_flag) { struct hostapd_data *hapd = ctx; + struct hostapd_data *link_bss = hapd; const char *ifname = hapd->conf->iface; +#ifdef CONFIG_IEEE80211BE + if (hapd->conf->mld_ap) { + link_bss = hostapd_mld_get_first_bss(hapd); + if (link_bss == NULL) + link_bss = hapd; + } +#endif if (vlan_id > 0) { - ifname = hostapd_get_vlan_id_ifname(hapd->conf->vlan, vlan_id); + ifname = hostapd_get_vlan_id_ifname(link_bss->conf->vlan, + vlan_id); if (!ifname) { if (!(hapd->iface->drv_flags & WPA_DRIVER_FLAGS_VLAN_OFFLOAD)) -- 2.34.1 _______________________________________________ Hostap mailing list Hostap@xxxxxxxxxxxxxxxxxxx http://lists.infradead.org/mailman/listinfo/hostap