}
+static void hostapd_global_cleanup_mld(struct hapd_interfaces *interfaces)
+{
+#ifdef CONFIG_IEEE80211BE
+ size_t i;
+
+ if (!interfaces || !interfaces->mld)
+ return;
+
+ for (i = 0; i < interfaces->mld_count; i++) {
+ if (!interfaces->mld[i])
+ continue;
+
+ os_free(interfaces->mld[i]);
+ interfaces->mld[i] = NULL;
+ }
+
+ os_free(interfaces->mld);
+ interfaces->mld = NULL;
+ interfaces->mld_count = 0;
+#endif /* CONFIG_IEEE80211BE */
+}
+
+
int main(int argc, char *argv[])
{
struct hapd_interfaces interfaces;
interfaces.iface = NULL;
interfaces.count = 0;
+ hostapd_global_cleanup_mld(&interfaces);
+
#ifdef CONFIG_DPP
dpp_global_deinit(interfaces.dpp);
#endif /* CONFIG_DPP */
}
+static void hostapd_bss_setup_multi_link(struct hostapd_data *hapd,
+ struct hapd_interfaces *interfaces)
+{
+#ifdef CONFIG_IEEE80211BE
+ struct hostapd_mld *mld, **all_mld;
+ struct hostapd_bss_config *conf;
+ size_t i;
+
+ conf = hapd->conf;
+
+ if (!hapd->iconf || !hapd->iconf->ieee80211be || !conf->mld_ap ||
+ conf->disable_11be)
+ return;
+
+ for (i = 0; i < interfaces->mld_count; i++) {
+ mld = interfaces->mld[i];
+
+ if (!mld || os_strcmp(conf->iface, mld->name) != 0)
+ continue;
+
+ hapd->mld = mld;
+ break;
+ }
+
+ if (hapd->mld)
+ return;
+
+ mld = os_zalloc(sizeof(struct hostapd_mld));
+ if (!mld)
+ goto fail;
+
+ os_strlcpy(mld->name, conf->iface, sizeof(conf->iface));
+ dl_list_init(&mld->links);
+
+ wpa_printf(MSG_DEBUG, "AP MLD %s created", mld->name);
+
+ hapd->mld = mld;
+
+ all_mld = os_realloc_array(interfaces->mld, interfaces->mld_count + 1,
+ sizeof(struct hostapd_mld *));
+ if (!all_mld)
+ goto fail;
+
+ interfaces->mld = all_mld;
+ interfaces->mld[interfaces->mld_count] = mld;
+ interfaces->mld_count++;
+
+ return;
+fail:
+ if (!mld)
+ return;
+
+ wpa_printf(MSG_DEBUG, "AP MLD %s: free mld %p", mld->name, mld);
+ os_free(mld);
+ hapd->mld = NULL;
+#endif /* CONFIG_IEEE80211BE */
+}
+
+
/**
* hostapd_init - Allocate and initialize per-interface data
* @config_file: Path to the configuration file
if (hapd == NULL)
goto fail;
hapd->msg_ctx = hapd;
+ hostapd_bss_setup_multi_link(hapd, interfaces);
}
return hapd_iface;
iface->conf->last_bss = bss;
iface->bss[iface->num_bss] = hapd;
hapd->msg_ctx = hapd;
+ hostapd_bss_setup_multi_link(hapd, interfaces);
+
bss_idx = iface->num_bss++;
conf->num_bss--;
return -1;
}
hapd->msg_ctx = hapd;
+ hostapd_bss_setup_multi_link(hapd, hapd_iface->interfaces);
}
hapd_iface->conf = conf;
/* TODO: MLD ID for Multiple BSS cases */
}
+
+int hostapd_mld_add_link(struct hostapd_data *hapd)
+{
+ struct hostapd_mld *mld = hapd->mld;
+
+ if (!hapd->conf->mld_ap)
+ return 0;
+
+ /* Should not happen */
+ if (!mld)
+ return -1;
+
+ dl_list_add_tail(&mld->links, &hapd->link);
+ mld->num_links++;
+
+ wpa_printf(MSG_DEBUG, "AP MLD %s: Link ID %d added. num_links: %d",
+ mld->name, hapd->mld_link_id, mld->num_links);
+
+ if (mld->fbss)
+ return 0;
+
+ mld->fbss = hapd;
+ wpa_printf(MSG_DEBUG, "AP MLD %s: First link BSS set to %p",
+ mld->name, mld->fbss);
+ return 0;
+}
+
+
+int hostapd_mld_remove_link(struct hostapd_data *hapd)
+{
+ struct hostapd_mld *mld = hapd->mld;
+ struct hostapd_data *next_fbss;
+
+ if (!hapd->conf->mld_ap)
+ return 0;
+
+ /* Should not happen */
+ if (!mld)
+ return -1;
+
+ dl_list_del(&hapd->link);
+ mld->num_links--;
+
+ wpa_printf(MSG_DEBUG, "AP MLD %s: Link ID %d removed. num_links: %d",
+ mld->name, hapd->mld_link_id, mld->num_links);
+
+ if (mld->fbss != hapd)
+ return 0;
+
+ /* If the list is empty, all links are removed */
+ if (dl_list_empty(&mld->links)) {
+ mld->fbss = NULL;
+ } else {
+ next_fbss = dl_list_entry(mld->links.next, struct hostapd_data,
+ link);
+ mld->fbss = next_fbss;
+ }
+
+ wpa_printf(MSG_DEBUG, "AP MLD %s: First link BSS set to %p",
+ mld->name, mld->fbss);
+ return 0;
+}
+
+
+bool hostapd_mld_is_first_bss(struct hostapd_data *hapd)
+{
+ struct hostapd_mld *mld = hapd->mld;
+
+ if (!hapd->conf->mld_ap)
+ return true;
+
+ /* Should not happen */
+ if (!mld)
+ return false;
+
+ /* If fbss is not set, it is safe to assume the caller is the first BSS.
+ */
+ if (!mld->fbss)
+ return true;
+
+ return hapd == mld->fbss;
+}
+
+
+struct hostapd_data * hostapd_mld_get_first_bss(struct hostapd_data *hapd)
+{
+ struct hostapd_mld *mld = hapd->mld;
+
+ if (!hapd->conf->mld_ap)
+ return NULL;
+
+ /* Should not happen */
+ if (!mld)
+ return NULL;
+
+ return mld->fbss;
+}
+
#endif /* CONFIG_IEEE80211BE */
#endif /* CONFIG_CTRL_IFACE_UDP */
struct hostapd_iface;
+struct hostapd_mld;
struct hapd_interfaces {
int (*reload_config)(struct hostapd_iface *iface);
unsigned char ctrl_iface_cookie[CTRL_IFACE_COOKIE_LEN];
#endif /* CONFIG_CTRL_IFACE_UDP */
+#ifdef CONFIG_IEEE80211BE
+ struct hostapd_mld **mld;
+ size_t mld_count;
+#endif /* CONFIG_IEEE80211BE */
};
enum hostapd_chan_status {
#ifdef CONFIG_IEEE80211BE
u8 eht_mld_bss_param_change;
+ struct hostapd_mld *mld;
+ struct dl_list link;
#ifdef CONFIG_TESTING_OPTIONS
u8 eht_mld_link_removal_count;
#endif /* CONFIG_TESTING_OPTIONS */
#endif /* CONFIG_TAXONOMY */
};
+#ifdef CONFIG_IEEE80211BE
+/**
+ * struct hostapd_mld - hostapd per-mld data structure
+ */
+struct hostapd_mld {
+ char name[IFNAMSIZ + 1];
+ u8 mld_addr[ETH_ALEN];
+ u8 next_link_id;
+ u8 num_links;
+
+ struct hostapd_data *fbss;
+ struct dl_list links; /* List head of all affiliated links */
+};
+#endif /* CONFIG_IEEE80211BE */
+
/**
* struct hostapd_iface - hostapd per-interface data structure
*/
bool hostapd_is_ml_partner(struct hostapd_data *hapd1,
struct hostapd_data *hapd2);
u8 hostapd_get_mld_id(struct hostapd_data *hapd);
+int hostapd_mld_add_link(struct hostapd_data *hapd);
+int hostapd_mld_remove_link(struct hostapd_data *hapd);
+struct hostapd_data * hostapd_mld_get_first_bss(struct hostapd_data *hapd);
#ifdef CONFIG_IEEE80211BE
+
+bool hostapd_mld_is_first_bss(struct hostapd_data *hapd);
+
#define for_each_mld_link(_link, _bss_idx, _iface_idx, _ifaces, _mld_id) \
for (_iface_idx = 0; \
_iface_idx < (_ifaces)->count; \
_link && _link->conf->mld_ap && \
hostapd_get_mld_id(_link) == _mld_id; \
_link = NULL)
+
#else /* CONFIG_IEEE80211BE */
+
+static inline bool hostapd_mld_is_first_bss(struct hostapd_data *hapd)
+{
+ return true;
+}
+
#define for_each_mld_link(_link, _bss_idx, _iface_idx, _ifaces, _mld_id) \
if (false)
+
#endif /* CONFIG_IEEE80211BE */
u16 hostapd_get_punct_bitmap(struct hostapd_data *hapd);