[][mt76][eagle][hostapd mbssid and ema support]

[Description]
Add patch fot 11v mbss and ema
porting from https://patchwork.ozlabs.org/project/hostap/cover/20221201031847.26480-1-quic_alokad@quicinc.com/

[Release-log]

Change-Id: I033f0787427b777032875f906e8bb3934e570ce2
Reviewed-on: https://gerrit.mediatek.inc/c/openwrt/feeds/mtk_openwrt_feeds/+/7197107
diff --git a/autobuild_mac80211_release/package/network/services/hostapd/patches/mtk-0020-hostapd-mtk-add-11v_mbss-and-ema-support.patch b/autobuild_mac80211_release/package/network/services/hostapd/patches/mtk-0020-hostapd-mtk-add-11v_mbss-and-ema-support.patch
new file mode 100644
index 0000000..17fe005
--- /dev/null
+++ b/autobuild_mac80211_release/package/network/services/hostapd/patches/mtk-0020-hostapd-mtk-add-11v_mbss-and-ema-support.patch
@@ -0,0 +1,1175 @@
+From 52606532643a6db7206ff66fc5a5717e859cf76c Mon Sep 17 00:00:00 2001
+From: mtk20656 <chank.chen@mediatek.com>
+Date: Thu, 2 Mar 2023 10:51:43 +0800
+Subject: [PATCH] add 11v_mbss and ema support for hostapd
+
+Signed-off-by: mtk20656 <chank.chen@mediatek.com>
+---
+ hostapd/config_file.c             |   9 +
+ hostapd/hostapd.conf              |  58 +++++++
+ hostapd/main.c                    |   3 +
+ src/ap/ap_config.c                |  12 ++
+ src/ap/ap_config.h                |   6 +
+ src/ap/beacon.c                   | 124 ++++++++++++--
+ src/ap/hostapd.c                  |  76 ++++++--
+ src/ap/hostapd.h                  |   7 +
+ src/ap/ieee802_11.c               | 276 +++++++++++++++++++++++++++++-
+ src/ap/ieee802_11.h               |   7 +-
+ src/ap/ieee802_11_shared.c        |  11 ++
+ src/common/ieee802_11_common.c    |   4 +
+ src/common/ieee802_11_common.h    |   3 +
+ src/common/ieee802_11_defs.h      |   5 +
+ src/drivers/driver.h              |  42 +++++
+ src/drivers/driver_nl80211.c      |  52 ++++++
+ src/drivers/driver_nl80211_capa.c |  27 +++
+ 17 files changed, 700 insertions(+), 22 deletions(-)
+
+diff --git a/hostapd/config_file.c b/hostapd/config_file.c
+index 649618b..3f26191
+--- a/hostapd/config_file.c
++++ b/hostapd/config_file.c
+@@ -3663,6 +3663,15 @@ static int hostapd_config_fill(struct hostapd_config *conf,
+ 			return 1;
+ 		}
+ 		bss->unsol_bcast_probe_resp_interval = val;
++	} else if (os_strcmp(buf, "mbssid") == 0) {
++		int mbssid = atoi(pos);
++		if (mbssid < 0 || mbssid > ENHANCED_MBSSID_ENABLED) {
++			wpa_printf(MSG_ERROR,
++				   "Line %d: invalid mbssid (%d): '%s'.",
++				   line, mbssid, pos);
++			return 1;
++		}
++		conf->mbssid = mbssid;
+ 	} else if (os_strcmp(buf, "mu_onoff") == 0) {
+ 		int val = atoi(pos);
+ 		if (val < 0 || val > 15) {
+diff --git a/hostapd/hostapd.conf b/hostapd/hostapd.conf
+index e3a5eb3..f926029
+--- a/hostapd/hostapd.conf
++++ b/hostapd/hostapd.conf
+@@ -3123,3 +3123,61 @@ own_ip_addr=127.0.0.1
+ #bss=wlan0_1
+ #bssid=00:13:10:95:fe:0b
+ # ...
++#
++# Multiple BSSID Advertisement in 802.11ax
++# IEEE Std 802.11ax-2021 added a feature where instead of multiple interfaces
++# on a common radio transmitting individual beacons, those can form a set with
++# a common beacon is transmitted for all. The interface which is brought up
++# first is called as the transmitting profile of the MBSSID set which transmits
++# the beacons. The remaining interfaces are called as the non-transmitting
++# profiles and these are advertised inside the multiple BSSID element in the
++# beacons and probe response frames.
++# The transmitting interface is visible to all clients in the vicinity, however
++# the clients which do not support parsing of the multiple BSSID element will
++# not be able to connect to the non-transmitting interfaces.
++#
++# Enhanced Multiple BSSID Advertisements (EMA)
++# When enabled, the non-transmitting interfaces are split into multiple
++# beacons. The number of beacons required to cover all the non-transmitting
++# profiles is called as the profile periodicity.
++#
++# Refer to IEEE Std 802.11-2020 for details regarding the procedure and
++# required MAC address assignment.
++#
++# Following configuration is per radio.
++# 0 = Disabled (Default)
++# 1 = Multiple BSSID advertisements enabled.
++# 2 = Enhanced multiple BSSID advertisements enabled.
++#mbssid=0
++#
++# The transmitting interface should be added with 'interface' option while
++# the non-transmitting interfaces should be added using 'bss' option.
++# Security configuration should be added separately per interface, if required.
++#
++# Example:
++#mbssid=2
++#interface=wlan2
++#ctrl_interface=/var/run/hostapd
++#wpa_passphrase=0123456789
++#ieee80211w=2
++#sae_pwe=1
++#auth_algs=1
++#wpa=2
++#wpa_pairwise=CCMP
++#ssid=<SSID-0>
++#bridge=br-lan
++#wpa_key_mgmt=SAE
++#bssid=00:03:7f:12:84:84
++#
++#bss=wlan2-1
++#ctrl_interface=/var/run/hostapd
++#wpa_passphrase=0123456789
++#ieee80211w=2
++#sae_pwe=1
++#auth_algs=1
++#wpa=2
++#wpa_pairwise=CCMP
++#ssid=<SSID-1>
++#bridge=br-lan
++#wpa_key_mgmt=SAE
++#bssid=00:03:7f:12:84:85
+diff --git a/hostapd/main.c b/hostapd/main.c
+index 70a4b32..1b6474a
+--- a/hostapd/main.c
++++ b/hostapd/main.c
+@@ -253,6 +253,9 @@ static int hostapd_driver_init(struct hostapd_iface *iface)
+ 				wpa_printf(MSG_ERROR, "set_wowlan failed");
+ 		}
+ 		os_free(triggs);
++
++		iface->mbssid_max_interfaces = capa.mbssid_max_interfaces;
++		iface->ema_max_periodicity = capa.ema_max_periodicity;
+ 	}
+ 
+ 	return 0;
+diff --git a/src/ap/ap_config.c b/src/ap/ap_config.c
+index 4e46a62..7d9d5cb
+--- a/src/ap/ap_config.c
++++ b/src/ap/ap_config.c
+@@ -1462,6 +1462,12 @@ static int hostapd_config_check_bss(struct hostapd_bss_config *bss,
+ 	}
+ #endif /* CONFIG_IEEE80211BE */
+ 
++	if (full_config && bss->ignore_broadcast_ssid && conf->mbssid) {
++		wpa_printf(MSG_ERROR,
++			   "Hidden SSID is not suppored when MBSSID is enabled");
++		return -1;
++	}
++
+ 	return 0;
+ }
+ 
+@@ -1545,6 +1551,12 @@ int hostapd_config_check(struct hostapd_config *conf, int full_config)
+ 	}
+ #endif /* CONFIG_IEEE80211BE */
+ 
++	if (full_config && conf->mbssid && !conf->ieee80211ax) {
++		wpa_printf(MSG_ERROR,
++			   "Cannot enable multiple BSSID support without ieee80211ax");
++		return -1;
++	}
++
+ 	for (i = 0; i < conf->num_bss; i++) {
+ 		if (hostapd_config_check_bss(conf->bss[i], conf, full_config))
+ 			return -1;
+diff --git a/src/ap/ap_config.h b/src/ap/ap_config.h
+index 7aeb176..51476b8
+--- a/src/ap/ap_config.h
++++ b/src/ap/ap_config.h
+@@ -923,6 +923,8 @@ struct hostapd_bss_config {
+ 	u8 ext_capa[EXT_CAPA_MAX_LEN];
+ 
+ 	u8 rnr;
++
++	bool xrates_supported;
+ };
+ 
+ /**
+@@ -1163,6 +1165,10 @@ struct hostapd_config {
+ 	u8 ibf_enable;
+ 	u8 dfs_detect_mode;
+ 	u8 amsdu;
++
++#define MBSSID_ENABLED          1
++#define ENHANCED_MBSSID_ENABLED 2
++	u8 mbssid;
+ };
+ 
+ enum three_wire_mode {
+diff --git a/src/ap/beacon.c b/src/ap/beacon.c
+index f3ea5c2..9263950
+--- a/src/ap/beacon.c
++++ b/src/ap/beacon.c
+@@ -462,15 +462,77 @@ static u8 * hostapd_eid_supported_op_classes(struct hostapd_data *hapd, u8 *eid)
+ }
+ 
+ 
++static int ieee802_11_build_ap_params_mbssid(struct hostapd_data *hapd,
++					     struct wpa_driver_ap_params *params,
++					     u8 **eid)
++{
++	struct hostapd_iface *iface = hapd->iface;
++	struct hostapd_data *tx_bss;
++	size_t len;
++	u8 elem_count = 0, *elem = NULL, **elem_offset = NULL, *end;
++	u8 *tailpos = *eid;
++
++	if (!iface->mbssid_max_interfaces ||
++	    iface->num_bss > iface->mbssid_max_interfaces ||
++	    (iface->conf->mbssid == ENHANCED_MBSSID_ENABLED &&
++	     !iface->ema_max_periodicity))
++		goto fail;
++
++	tx_bss = hostapd_mbssid_get_tx_bss(hapd);
++	len = hostapd_eid_mbssid_len(tx_bss, WLAN_FC_STYPE_BEACON, &elem_count,
++				     NULL, 0);
++	if (!len || (iface->conf->mbssid == ENHANCED_MBSSID_ENABLED &&
++		     elem_count > iface->ema_max_periodicity))
++		goto fail;
++
++	elem = os_zalloc(len);
++	if (!elem)
++		goto fail;
++
++	elem_offset = os_zalloc(elem_count * sizeof(u8 *));
++	if (!elem_offset)
++		goto fail;
++
++	end = hostapd_eid_mbssid(tx_bss, elem, elem + len, WLAN_FC_STYPE_BEACON,
++				 elem_count, elem_offset, NULL, 0);
++
++	params->mbssid_tx_iface = tx_bss->conf->iface;
++	params->mbssid_index = hostapd_mbssid_get_bss_index(hapd);
++	params->mbssid_elem = elem;
++	params->mbssid_elem_len = end - elem;
++	params->mbssid_elem_count = elem_count;
++	params->mbssid_elem_offset = elem_offset;
++	if (iface->conf->mbssid == ENHANCED_MBSSID_ENABLED) {
++		params->ema = true;
++		*tailpos++ = WLAN_EID_EXTENSION;
++		*tailpos++ = 3;
++		*tailpos++ = WLAN_EID_EXT_MULTIPLE_BSSID_CONFIGURATION;
++		*tailpos++ = iface->num_bss;
++		*tailpos++ = params->mbssid_elem_count;
++		*eid = tailpos;
++	}
++
++	return 0;
++
++fail:
++	os_free(elem);
++	wpa_printf(MSG_ERROR, "MBSSID: Configuration failed");
++	return -1;
++}
++
++
+ static u8 * hostapd_gen_probe_resp(struct hostapd_data *hapd,
+ 				   const struct ieee80211_mgmt *req,
+ 				   int is_p2p, size_t *resp_len,
+-				   bool bcast_probe_resp)
++				   bool bcast_probe_resp, const u8 *known_bss,
++				   u8 known_bss_len)
+ {
+ 	struct ieee80211_mgmt *resp;
+-	u8 *pos, *epos, *csa_pos;
++	u8 *pos, *epos, *csa_pos, *ext_cap_pos;
+ 	size_t buflen;
+ 
++	hapd = hostapd_mbssid_get_tx_bss(hapd);
++
+ #define MAX_PROBERESP_LEN 768
+ 	buflen = MAX_PROBERESP_LEN;
+ #ifdef CONFIG_WPS
+@@ -517,6 +579,8 @@ static u8 * hostapd_gen_probe_resp(struct hostapd_data *hapd,
+ 	}
+ #endif /* CONFIG_IEEE80211BE */
+ 
++	buflen += hostapd_eid_mbssid_len(hapd, WLAN_FC_STYPE_PROBE_RESP, NULL,
++					 known_bss, known_bss_len);
+ 	buflen += hostapd_eid_rnr_len(hapd, WLAN_FC_STYPE_PROBE_RESP);
+ 	buflen += hostapd_mbo_ie_len(hapd);
+ 	buflen += hostapd_eid_owe_trans_len(hapd);
+@@ -588,8 +652,16 @@ static u8 * hostapd_gen_probe_resp(struct hostapd_data *hapd,
+ 	pos = hostapd_eid_supported_op_classes(hapd, pos);
+ 	pos = hostapd_eid_ht_capabilities(hapd, pos);
+ 	pos = hostapd_eid_ht_operation(hapd, pos);
++	pos = hostapd_eid_mbssid(hapd, pos, epos, WLAN_FC_STYPE_PROBE_RESP, 0,
++				 NULL, known_bss, known_bss_len);
+ 
++	ext_cap_pos = pos;
+ 	pos = hostapd_eid_ext_capab(hapd, pos);
++	if (hapd->iconf->mbssid >= MBSSID_ENABLED && !known_bss_len)
++		ext_cap_pos[12] |= 0x01; /* Probe responses always include all
++					  * non-tx profiles except when a list
++					  * of known BSSes is included in the
++					  * probe request. */
+ 
+ 	pos = hostapd_eid_time_adv(hapd, pos);
+ 	pos = hostapd_eid_time_zone(hapd, pos);
+@@ -1153,16 +1225,23 @@ void handle_probe_req(struct hostapd_data *hapd,
+ 	}
+ #endif /* CONFIG_TESTING_OPTIONS */
+ 
++	/* Do not send probe response from a non-transmitting multiple BSSID
++	 * profile unless the probe request is directed at that paticular BSS */
++	if (hapd != hostapd_mbssid_get_tx_bss(hapd) && res != EXACT_SSID_MATCH)
++		return;
++
+ 	wpa_msg_ctrl(hapd->msg_ctx, MSG_INFO, RX_PROBE_REQUEST "sa=" MACSTR
+ 		     " signal=%d", MAC2STR(mgmt->sa), ssi_signal);
+ 
+ 	if (is_6ghz_op_class(hapd->iconf->op_class) &&
+ 	    is_broadcast_ether_addr(mgmt->da))
+ 		resp = hostapd_gen_probe_resp(hapd, NULL, elems.p2p != NULL,
+-					      &resp_len, true);
++				      	&resp_len, false, elems.mbssid_known_bss,
++				      	elems.mbssid_known_bss_len);
+ 	else
+ 		resp = hostapd_gen_probe_resp(hapd, mgmt, elems.p2p != NULL,
+-					      &resp_len, false);
++				      	&resp_len, false, elems.mbssid_known_bss,
++				      	elems.mbssid_known_bss_len);
+ 	if (resp == NULL)
+ 		return;
+ 
+@@ -1184,7 +1263,8 @@ void handle_probe_req(struct hostapd_data *hapd,
+ 				hapd->cs_c_off_ecsa_proberesp;
+ 	}
+ 
+-	ret = hostapd_drv_send_mlme(hapd, resp, resp_len, noack,
++	ret = hostapd_drv_send_mlme(hostapd_mbssid_get_tx_bss(hapd), resp,
++				    resp_len, noack,
+ 				    csa_offs_len ? csa_offs : NULL,
+ 				    csa_offs_len, 0);
+ 
+@@ -1231,7 +1311,7 @@ static u8 * hostapd_probe_resp_offloads(struct hostapd_data *hapd,
+ 			   "this");
+ 
+ 	/* Generate a Probe Response template for the non-P2P case */
+-	return hostapd_gen_probe_resp(hapd, NULL, 0, resp_len, false);
++	return hostapd_gen_probe_resp(hapd, NULL, 0, resp_len, false, NULL, 0);
+ }
+ 
+ #endif /* NEED_AP_MLME */
+@@ -1250,7 +1330,7 @@ static u8 * hostapd_unsol_bcast_probe_resp(struct hostapd_data *hapd,
+ 
+ 	return hostapd_gen_probe_resp(hapd, NULL, 0,
+ 				      &params->unsol_bcast_probe_resp_tmpl_len,
+-				      true);
++				      true, NULL, 0);
+ }
+ #endif /* CONFIG_IEEE80211AX */
+ 
+@@ -1533,8 +1613,12 @@ int ieee802_11_build_ap_params(struct hostapd_data *hapd,
+ 	size_t resp_len = 0;
+ #ifdef NEED_AP_MLME
+ 	u16 capab_info;
+-	u8 *pos, *tailpos, *tailend, *csa_pos;
++	u8 *pos, *tailpos, *tailend, *csa_pos, *ext_cap_pos;
++#endif /* NEED_AP_MLME */
+ 
++	os_memset(params, 0, sizeof(*params));
++
++#ifdef NEED_AP_MLME
+ #define BEACON_HEAD_BUF_SIZE 256
+ #define BEACON_TAIL_BUF_SIZE 512
+ 	head = os_zalloc(BEACON_HEAD_BUF_SIZE);
+@@ -1586,6 +1670,9 @@ int ieee802_11_build_ap_params(struct hostapd_data *hapd,
+ 	}
+ #endif /* CONFIG_IEEE80211BE */
+ 
++	if (hapd->iconf->mbssid == ENHANCED_MBSSID_ENABLED &&
++	    hapd == hostapd_mbssid_get_tx_bss(hapd))
++		tail_len += 5; /* Multiple BSSID Configuration element */
+ 	tail_len += hostapd_eid_rnr_len(hapd, WLAN_FC_STYPE_BEACON);
+ 	tail_len += hostapd_mbo_ie_len(hapd);
+ 	tail_len += hostapd_eid_owe_trans_len(hapd);
+@@ -1671,9 +1758,23 @@ int ieee802_11_build_ap_params(struct hostapd_data *hapd,
+ 	tailpos = hostapd_eid_supported_op_classes(hapd, tailpos);
+ 	tailpos = hostapd_eid_ht_capabilities(hapd, tailpos);
+ 	tailpos = hostapd_eid_ht_operation(hapd, tailpos);
+-
++	ext_cap_pos = tailpos;
+ 	tailpos = hostapd_eid_ext_capab(hapd, tailpos);
+ 
++	if (hapd->iconf->mbssid && hapd->iconf->num_bss > 1) {
++		if (ieee802_11_build_ap_params_mbssid(hapd, params, &tailpos)) {
++			os_free(head);
++			os_free(tail);
++			wpa_printf(MSG_ERROR, "Failed to set beacon data");
++			return -1;
++		} else if (hapd->iconf->mbssid == MBSSID_ENABLED ||
++			   (hapd->iconf->mbssid == ENHANCED_MBSSID_ENABLED &&
++			   params->mbssid_elem_count == 1)) {
++			/* Set the extended capability bit for "complete list
++			 * of non-tx profiles" */
++			ext_cap_pos[12] |= 0x01;
++		}
++	}
+ 	/*
+ 	 * TODO: Time Advertisement element should only be included in some
+ 	 * DTIM Beacon frames.
+@@ -1794,7 +1895,6 @@ int ieee802_11_build_ap_params(struct hostapd_data *hapd,
+ 	resp = hostapd_probe_resp_offloads(hapd, &resp_len);
+ #endif /* NEED_AP_MLME */
+ 
+-	os_memset(params, 0, sizeof(*params));
+ 	params->head = (u8 *) head;
+ 	params->head_len = head_len;
+ 	params->tail = tail;
+@@ -1897,6 +1997,10 @@ void ieee802_11_free_ap_params(struct wpa_driver_ap_params *params)
+ 	params->head = NULL;
+ 	os_free(params->proberesp);
+ 	params->proberesp = NULL;
++	os_free(params->mbssid_elem);
++	params->mbssid_elem = NULL;
++	os_free(params->mbssid_elem_offset);
++	params->mbssid_elem_offset = NULL;
+ #ifdef CONFIG_FILS
+ 	os_free(params->fd_frame_tmpl);
+ 	params->fd_frame_tmpl = NULL;
+diff --git a/src/ap/hostapd.c b/src/ap/hostapd.c
+index da1e7a4..308bfb8
+--- a/src/ap/hostapd.c
++++ b/src/ap/hostapd.c
+@@ -91,6 +91,29 @@ int hostapd_for_each_interface(struct hapd_interfaces *interfaces,
+ }
+ 
+ 
++struct hostapd_data * hostapd_mbssid_get_tx_bss(struct hostapd_data *hapd)
++{
++	if (hapd->iconf->mbssid)
++		return hapd->iface->bss[0];
++
++	return hapd;
++}
++
++
++int hostapd_mbssid_get_bss_index(struct hostapd_data *hapd)
++{
++	if (hapd->iconf->mbssid) {
++		size_t i;
++
++		for (i = 1; i < hapd->iface->num_bss; i++)
++			if (hapd->iface->bss[i] == hapd)
++				return i;
++	}
++
++	return 0;
++}
++
++
+ void hostapd_reconfig_encryption(struct hostapd_data *hapd)
+ {
+ 	if (hapd->wpa_auth)
+@@ -1179,19 +1202,37 @@ static int db_table_create_radius_attributes(sqlite3 *db)
+ 
+ #endif /* CONFIG_NO_RADIUS */
+ 
++static int hostapd_start_beacon(struct hostapd_data *hapd)
++{
++	struct hostapd_bss_config *conf = hapd->conf;
++
++	if (!conf->start_disabled && ieee802_11_set_beacon(hapd) < 0)
++		return -1;
++
++	if (hapd->driver && hapd->driver->set_operstate)
++		hapd->driver->set_operstate(hapd->drv_priv, 1);
++
++	return 0;
++}
+ 
+ /**
+  * hostapd_setup_bss - Per-BSS setup (initialization)
+  * @hapd: Pointer to BSS data
+  * @first: Whether this BSS is the first BSS of an interface; -1 = not first,
+  *	but interface may exist
++ * @start_beacon: Whether beacons should be configured and transmission started
++ *	at this time. This is used when MBSSID IE is enabled where the
++ *	information regarding all BSSes should be retrieved before configuring
++ *	the beacons. The calling functions are responsible to configure the
++ *	beacon explicitly if this is set to 'false'.
+  *
+  * This function is used to initialize all per-BSS data structures and
+  * resources. This gets called in a loop for each BSS when an interface is
+  * initialized. Most of the modules that are initialized here will be
+  * deinitialized in hostapd_cleanup().
+  */
+-static int hostapd_setup_bss(struct hostapd_data *hapd, int first)
++static int hostapd_setup_bss(struct hostapd_data *hapd, int first,
++			     bool start_beacon)
+ {
+ 	struct hostapd_bss_config *conf = hapd->conf;
+ 	u8 ssid[SSID_MAX_LEN + 1];
+@@ -1464,9 +1507,6 @@ static int hostapd_setup_bss(struct hostapd_data *hapd, int first)
+ 		return -1;
+ 	}
+ 
+-	if (!conf->start_disabled && ieee802_11_set_beacon(hapd) < 0)
+-		return -1;
+-
+ 	if (flush_old_stations && !conf->start_disabled &&
+ 	    conf->broadcast_deauth) {
+ 		u8 addr[ETH_ALEN];
+@@ -1487,8 +1525,8 @@ static int hostapd_setup_bss(struct hostapd_data *hapd, int first)
+ 	if (hapd->wpa_auth && wpa_init_keys(hapd->wpa_auth) < 0)
+ 		return -1;
+ 
+-	if (hapd->driver && hapd->driver->set_operstate)
+-		hapd->driver->set_operstate(hapd->drv_priv, 1);
++	if (start_beacon)
++		return hostapd_start_beacon(hapd);
+ 
+ 	hostapd_ubus_add_bss(hapd);
+ 
+@@ -2216,7 +2254,7 @@ static int hostapd_setup_interface_complete_sync(struct hostapd_iface *iface,
+ 		hapd = iface->bss[j];
+ 		if (j)
+ 			os_memcpy(hapd->own_addr, prev_addr, ETH_ALEN);
+-		if (hostapd_setup_bss(hapd, j == 0)) {
++		if (hostapd_setup_bss(hapd, j == 0, !iface->conf->mbssid)) {
+ 			for (;;) {
+ 				hapd = iface->bss[j];
+ 				hostapd_bss_deinit_no_free(hapd);
+@@ -2230,6 +2268,24 @@ static int hostapd_setup_interface_complete_sync(struct hostapd_iface *iface,
+ 		if (is_zero_ether_addr(hapd->conf->bssid))
+ 			prev_addr = hapd->own_addr;
+ 	}
++
++	if (hapd->iconf->mbssid) {
++		for (j = 0; j < iface->num_bss; j++) {
++			hapd = iface->bss[j];
++			if (hostapd_start_beacon(hapd)) {
++				for (;;) {
++					hapd = iface->bss[j];
++					hostapd_bss_deinit_no_free(hapd);
++					hostapd_free_hapd_data(hapd);
++					if (j == 0)
++						break;
++					j--;
++				}
++				goto fail;
++			}
++		}
++	}
++
+ 	hapd = iface->bss[0];
+ 
+ 	hostapd_tx_queue_params(iface);
+@@ -3132,7 +3188,7 @@ int hostapd_add_iface(struct hapd_interfaces *interfaces, char *buf)
+ 
+ 			if (start_ctrl_iface_bss(hapd) < 0 ||
+ 			    (hapd_iface->state == HAPD_IFACE_ENABLED &&
+-			     hostapd_setup_bss(hapd, -1))) {
++			     hostapd_setup_bss(hapd, -1, true))) {
+ 				hostapd_cleanup(hapd);
+ 				hapd_iface->bss[hapd_iface->num_bss - 1] = NULL;
+ 				hapd_iface->conf->num_bss--;
+diff --git a/src/ap/hostapd.h b/src/ap/hostapd.h
+index 56d96a5..093c28a
+--- a/src/ap/hostapd.h
++++ b/src/ap/hostapd.h
+@@ -660,6 +660,11 @@ struct hostapd_iface {
+ 	/* Previous WMM element information */
+ 	struct hostapd_wmm_ac_params prev_wmm[WMM_AC_NUM];
+ 
++	/* Maximum number of interfaces supported for MBSSID advertisements */
++	u8 mbssid_max_interfaces;
++	/* Maximum profile periodicity for enhanced MBSSID advertisements */
++	u8 ema_max_periodicity;
++
+ 	int (*enable_iface_cb)(struct hostapd_iface *iface);
+ 	int (*disable_iface_cb)(struct hostapd_iface *iface);
+ };
+@@ -762,5 +767,7 @@ void fst_hostapd_fill_iface_obj(struct hostapd_data *hapd,
+ #endif /* CONFIG_FST */
+ 
+ int hostapd_set_acl(struct hostapd_data *hapd);
++struct hostapd_data * hostapd_mbssid_get_tx_bss(struct hostapd_data *hapd);
++int hostapd_mbssid_get_bss_index(struct hostapd_data *hapd);
+ 
+ #endif /* HOSTAPD_H */
+diff --git a/src/ap/ieee802_11.c b/src/ap/ieee802_11.c
+index 098793e..30bfa30
+--- a/src/ap/ieee802_11.c
++++ b/src/ap/ieee802_11.c
+@@ -165,6 +165,7 @@ u8 * hostapd_eid_ext_supp_rates(struct hostapd_data *hapd, u8 *eid)
+ 	int i, num, count;
+ 	int h2e_required;
+ 
++	hapd->conf->xrates_supported = 0;
+ 	if (hapd->iface->current_rates == NULL)
+ 		return eid;
+ 
+@@ -214,6 +215,7 @@ u8 * hostapd_eid_ext_supp_rates(struct hostapd_data *hapd, u8 *eid)
+ 			*pos++ = 0x80 | BSS_MEMBERSHIP_SELECTOR_SAE_H2E_ONLY;
+ 	}
+ 
++	hapd->conf->xrates_supported = 1;
+ 	return pos;
+ }
+ 
+@@ -3979,6 +3981,23 @@ static void handle_auth(struct hostapd_data *hapd,
+ }
+ 
+ 
++static u8 hostapd_max_bssid_indicator(struct hostapd_data *hapd)
++{
++	size_t num_bss_nontx;
++	u8 max_bssid_ind = 0;
++
++	if (!hapd->iconf->mbssid || hapd->iface->num_bss <= 1)
++		return 0;
++
++	num_bss_nontx = hapd->iface->num_bss - 1;
++	while (num_bss_nontx > 0) {
++		max_bssid_ind++;
++		num_bss_nontx >>= 1;
++	}
++	return max_bssid_ind;
++}
++
++
+ int hostapd_get_aid(struct hostapd_data *hapd, struct sta_info *sta)
+ {
+ 	int i, j = 32, aid;
+@@ -4004,7 +4023,10 @@ int hostapd_get_aid(struct hostapd_data *hapd, struct sta_info *sta)
+ 	}
+ 	if (j == 32)
+ 		return -1;
+-	aid = i * 32 + j + 1;
++	aid = i * 32 + j;
++
++	aid += (1 << hostapd_max_bssid_indicator(hapd));
++
+ 	if (aid > 2007)
+ 		return -1;
+ 
+@@ -7579,4 +7601,256 @@ u8 * hostapd_eid_rnr(struct hostapd_data *hapd, u8 *eid, u32 type)
+ 	return eid;
+ }
+ 
++
++static size_t hostapd_eid_mbssid_elem_len(struct hostapd_data *hapd,
++					  u32 frame_type, size_t *bss_index,
++					  const u8 *known_bss,
++					  size_t known_bss_len)
++{
++	struct hostapd_data *tx_bss = hostapd_mbssid_get_tx_bss(hapd);
++	size_t len = 3, i;
++
++	for (i = *bss_index; i < hapd->iface->num_bss; i++) {
++		struct hostapd_data *bss = hapd->iface->bss[i];
++		const u8 *auth, *rsn = NULL, *rsnx = NULL;
++		size_t nontx_profile_len, auth_len;
++		u8 ie_count = 0;
++
++		if (known_bss && (known_bss_len > (i / 8))) {
++			known_bss = &known_bss[i / 8];
++			if (*known_bss & (u8)(BIT(i % 8)))
++				continue;
++		}
++
++		if (!bss || !bss->conf || !bss->started)
++			continue;
++
++		/*
++		 * Sublement ID: 1 byte
++		 * Length: 1 byte
++		 * Nontransmitted capabilities: 4 bytes
++		 * SSID element: 2 + variable
++		 * Multiple BSSID Index Element: 3 bytes (+2 bytes in beacons)
++		 * Fixed length = 1 + 1 + 4 + 2 + 3 = 11
++		 */
++		nontx_profile_len = 11 + bss->conf->ssid.ssid_len;
++
++		if (frame_type == WLAN_FC_STYPE_BEACON)
++			nontx_profile_len += 2;
++
++		auth = wpa_auth_get_wpa_ie(bss->wpa_auth, &auth_len);
++		if (auth) {
++			rsn = get_ie(auth, auth_len, WLAN_EID_RSN);
++			if (rsn)
++				nontx_profile_len += (2 + rsn[1]);
++
++			rsnx = get_ie(auth, auth_len, WLAN_EID_RSNX);
++			if (rsnx)
++				nontx_profile_len += (2 + rsnx[1]);
++		}
++		if (!rsn && hostapd_wpa_ie(tx_bss, WLAN_EID_RSN))
++			ie_count++;
++		if (!rsnx && hostapd_wpa_ie(tx_bss, WLAN_EID_RSNX))
++			ie_count++;
++		if (bss->conf->xrates_supported)
++			nontx_profile_len += 8;
++		else if (hapd->conf->xrates_supported)
++			ie_count++;
++		if (ie_count)
++			nontx_profile_len += (4 + ie_count);
++
++		if ((len + nontx_profile_len) > 255)
++			goto mbssid_too_big;
++
++		len += nontx_profile_len;
++	}
++
++mbssid_too_big:
++	*bss_index = i;
++	return len;
++}
++
++
++size_t hostapd_eid_mbssid_len(struct hostapd_data *hapd, u32 frame_type,
++			      u8 *elem_count, const u8 *known_bss,
++			      size_t known_bss_len)
++{
++	size_t len = 0, bss_index = 1;
++
++	if (!hapd->iconf->mbssid || hapd->iface->num_bss <= 1 ||
++	    (frame_type != WLAN_FC_STYPE_BEACON &&
++	     frame_type != WLAN_FC_STYPE_PROBE_RESP))
++		return 0;
++
++	if (frame_type == WLAN_FC_STYPE_BEACON) {
++		if (!elem_count) {
++			wpa_printf(MSG_ERROR,
++				   "MBSSID: Insufficient data for beacons");
++			return 0;
++		}
++		*elem_count = 0;
++	}
++
++	while (bss_index < hapd->iface->num_bss) {
++		len += hostapd_eid_mbssid_elem_len(hapd, frame_type,
++						   &bss_index, known_bss,
++						   known_bss_len);
++
++		if (frame_type == WLAN_FC_STYPE_BEACON)
++			*elem_count += 1;
++	}
++	return len;
++}
++
++
++static u8 * hostapd_eid_mbssid_elem(struct hostapd_data *hapd, u8 *eid, u8 *end,
++				    u32 frame_type, u8 max_bssid_indicator,
++				    size_t *bss_index, u8 elem_count,
++				    const u8 *known_bss, size_t known_bss_len)
++{
++	struct hostapd_data *tx_bss = hostapd_mbssid_get_tx_bss(hapd);
++	size_t i;
++	u8 *eid_len_offset, *max_bssid_indicator_offset;
++
++	*eid++ = WLAN_EID_MULTIPLE_BSSID;
++	eid_len_offset = eid++;
++	max_bssid_indicator_offset = eid++;
++
++	for (i = *bss_index; i < hapd->iface->num_bss; i++) {
++		struct hostapd_data *bss = hapd->iface->bss[i];
++		struct hostapd_bss_config *conf;
++		u8 *eid_len_pos, *nontx_bss_start = eid;
++		const u8 *auth, *rsn = NULL, *rsnx = NULL;
++		u8 ie_count = 0, non_inherit_ie[3];
++		size_t auth_len = 0;
++		u16 capab_info;
++
++		if (known_bss && (known_bss_len > (i / 8))) {
++			known_bss = &known_bss[i / 8];
++			if (*known_bss & (u8)(BIT(i % 8)))
++				continue;
++		}
++
++		if (!bss || !bss->conf || !bss->started)
++			continue;
++		conf = bss->conf;
++
++		*eid++ = WLAN_EID_SUBELEMENT_NONTRANSMITTED_BSSID_PROFILE;
++		eid_len_pos = eid++;
++
++		*eid++ = WLAN_EID_NONTRANSMITTED_BSSID_CAPA;
++		*eid++ = sizeof(capab_info);
++		capab_info = host_to_le16(hostapd_own_capab_info(bss));
++		os_memcpy(eid, (const void *)&capab_info, sizeof(capab_info));
++		eid += sizeof(capab_info);
++
++		*eid++ = WLAN_EID_SSID;
++		*eid++ = conf->ssid.ssid_len;
++		os_memcpy(eid, conf->ssid.ssid, conf->ssid.ssid_len);
++		eid += conf->ssid.ssid_len;
++
++		*eid++ = WLAN_EID_MULTIPLE_BSSID_INDEX;
++		if (frame_type == WLAN_FC_STYPE_BEACON) {
++			*eid++ = 3;
++			*eid++ = i;
++			if (hapd->iconf->mbssid == ENHANCED_MBSSID_ENABLED &&
++			    (conf->dtim_period % elem_count))
++				conf->dtim_period = elem_count;
++			*eid++ = conf->dtim_period;
++			*eid++ = 0xFF;
++		} else {
++			*eid++ = 1;
++			*eid++ = i;
++		}
++
++		auth = wpa_auth_get_wpa_ie(bss->wpa_auth, &auth_len);
++		if (auth) {
++			rsn = get_ie(auth, auth_len, WLAN_EID_RSN);
++			if (rsn) {
++				os_memcpy(eid, rsn, 2 + rsn[1]);
++				eid += (2 + rsn[1]);
++			}
++
++			rsnx = get_ie(auth, auth_len, WLAN_EID_RSNX);
++			if (rsnx) {
++				os_memcpy(eid, rsnx, 2 + rsnx[1]);
++				eid += (2 + rsnx[1]);
++			}
++		}
++		if (!rsn && hostapd_wpa_ie(tx_bss, WLAN_EID_RSN)) {
++			non_inherit_ie[ie_count] = WLAN_EID_RSN;
++			ie_count++;
++		}
++		if (!rsnx && hostapd_wpa_ie(tx_bss, WLAN_EID_RSNX)) {
++			non_inherit_ie[ie_count] = WLAN_EID_RSNX;
++			ie_count++;
++		}
++		if (hapd->conf->xrates_supported &&
++		    !bss->conf->xrates_supported) {
++			non_inherit_ie[ie_count] = WLAN_EID_EXT_SUPP_RATES;
++			ie_count++;
++		}
++		if (ie_count) {
++			*eid++ = WLAN_EID_EXTENSION;
++			*eid++ = 2 + ie_count;
++			*eid++ = WLAN_EID_EXT_NON_INHERITANCE;
++			*eid++ = ie_count;
++			os_memcpy(eid, non_inherit_ie, ie_count);
++			eid += ie_count;
++		}
++
++		*eid_len_pos = (eid - eid_len_pos) - 1;
++
++		if (((eid - eid_len_offset) - 1) > 255) {
++			eid = nontx_bss_start;
++			goto mbssid_too_big;
++		}
++	}
++
++mbssid_too_big:
++	*bss_index = i;
++	*max_bssid_indicator_offset = max_bssid_indicator;
++	if (*max_bssid_indicator_offset < 1)
++		*max_bssid_indicator_offset = 1;
++	*eid_len_offset = (eid - eid_len_offset) - 1;
++	return eid;
++}
++
++
++u8 * hostapd_eid_mbssid(struct hostapd_data *hapd, u8 *eid, u8 *end,
++			u32 frame_type, u8 elem_count, u8 **elem_offset,
++			const u8 *known_bss, size_t known_bss_len)
++{
++	size_t bss_index = 1;
++	u8 elem_index = 0;
++
++	if (!hapd->iconf->mbssid || hapd->iface->num_bss <= 1 ||
++	    (frame_type != WLAN_FC_STYPE_BEACON &&
++	     frame_type != WLAN_FC_STYPE_PROBE_RESP))
++		return eid;
++
++	if (frame_type == WLAN_FC_STYPE_BEACON && !elem_offset) {
++		wpa_printf(MSG_ERROR, "MBSSID: Insufficient data for beacons");
++		return eid;
++	}
++
++	while (bss_index < hapd->iface->num_bss) {
++		if (frame_type == WLAN_FC_STYPE_BEACON) {
++			if (elem_index == elem_count) {
++				wpa_printf(MSG_WARNING,
++					   "MBSSID: More number of elements than provided array");
++				break;
++			}
++
++			elem_offset[elem_index] = eid;
++			elem_index = elem_index + 1;
++		}
++		eid = hostapd_eid_mbssid_elem(hapd, eid, end, frame_type,
++					      hostapd_max_bssid_indicator(hapd),
++					      &bss_index, elem_count,
++					      known_bss, known_bss_len);
++	}
++	return eid;
++}
++
+ #endif /* CONFIG_NATIVE_WINDOWS */
+diff --git a/src/ap/ieee802_11.h b/src/ap/ieee802_11.h
+index fa1f47b..bb454bb
+--- a/src/ap/ieee802_11.h
++++ b/src/ap/ieee802_11.h
+@@ -214,5 +214,10 @@ u16 copy_sta_eht_capab(struct hostapd_data *hapd, struct sta_info *sta,
+ 		       enum ieee80211_op_mode opmode,
+ 		       const u8 *he_capab, size_t he_capab_len,
+ 		       const u8 *eht_capab, size_t eht_capab_len);
+-
++size_t hostapd_eid_mbssid_len(struct hostapd_data *hapd, u32 frame_type,
++			      u8 *elem_count, const u8 *known_bss,
++			      size_t known_bss_len);
++u8 * hostapd_eid_mbssid(struct hostapd_data *hapd, u8 *eid, u8 *end,
++			u32 frame_type, u8 elem_count, u8 **elem_offset,
++			const u8 *known_bss, size_t known_bss_len);
+ #endif /* IEEE802_11_H */
+diff --git a/src/ap/ieee802_11_shared.c b/src/ap/ieee802_11_shared.c
+index 4f85d78..7f5b475
+--- a/src/ap/ieee802_11_shared.c
++++ b/src/ap/ieee802_11_shared.c
+@@ -364,6 +364,8 @@ static void hostapd_ext_capab_byte(struct hostapd_data *hapd, u8 *pos, int idx)
+ 			*pos |= 0x02; /* Bit 17 - WNM-Sleep Mode */
+ 		if (hapd->conf->bss_transition)
+ 			*pos |= 0x08; /* Bit 19 - BSS Transition */
++		if (hapd->iconf->mbssid)
++			*pos |= 0x40; /* Bit 22 - Multiple BSSID */
+ 		break;
+ 	case 3: /* Bits 24-31 */
+ #ifdef CONFIG_WNM_AP
+@@ -436,6 +438,8 @@ static void hostapd_ext_capab_byte(struct hostapd_data *hapd, u8 *pos, int idx)
+ 		    (hapd->iface->drv_flags &
+ 		     WPA_DRIVER_FLAGS_BEACON_PROTECTION))
+ 			*pos |= 0x10; /* Bit 84 - Beacon Protection Enabled */
++		if (hapd->iconf->mbssid == ENHANCED_MBSSID_ENABLED)
++			*pos |= 0x08; /* Bit 83 - Enhanced multiple BSSID */
+ 		break;
+ 	case 11: /* Bits 88-95 */
+ #ifdef CONFIG_SAE_PK
+@@ -471,6 +475,13 @@ u8 * hostapd_eid_ext_capab(struct hostapd_data *hapd, u8 *eid)
+ 			*pos &= ~hapd->conf->ext_capa_mask[i];
+ 			*pos |= hapd->conf->ext_capa[i];
+ 		}
++
++		/* Clear bits 83 and 22 if EMA and MBSSID are not enabled
++		 * otherwise association fails with some clients */
++		if (i == 10 && hapd->iconf->mbssid < ENHANCED_MBSSID_ENABLED)
++			*pos &= ~0x08;
++		if (i == 2 && !hapd->iconf->mbssid)
++			*pos &= ~0x40;
+ 	}
+ 
+ 	while (len > 0 && eid[1 + len] == 0) {
+diff --git a/src/common/ieee802_11_common.c b/src/common/ieee802_11_common.c
+index c8ee90c..2fab7c3
+--- a/src/common/ieee802_11_common.c
++++ b/src/common/ieee802_11_common.c
+@@ -315,6 +315,10 @@ static int ieee802_11_parse_extension(const u8 *pos, size_t elen,
+ 		elems->eht_operation = pos;
+ 		elems->eht_operation_len = elen;
+ 		break;
++	case WLAN_EID_EXT_KNOWN_BSSID:
++		elems->mbssid_known_bss = pos;
++		elems->mbssid_known_bss_len = elen;
++		break;
+ 	default:
+ 		if (show_errors) {
+ 			wpa_printf(MSG_MSGDUMP,
+diff --git a/src/common/ieee802_11_common.h b/src/common/ieee802_11_common.h
+index 94e1d7b..1e4e27d
+--- a/src/common/ieee802_11_common.h
++++ b/src/common/ieee802_11_common.h
+@@ -119,6 +119,7 @@ struct ieee802_11_elems {
+ 	const u8 *pasn_params;
+ 	const u8 *eht_capabilities;
+ 	const u8 *eht_operation;
++	const u8 *mbssid_known_bss;
+ 
+ 	u8 ssid_len;
+ 	u8 supp_rates_len;
+@@ -176,6 +177,8 @@ struct ieee802_11_elems {
+ 	u8 eht_capabilities_len;
+ 	u8 eht_operation_len;
+ 
++	u8 mbssid_known_bss_len;
++
+ 	struct mb_ies_info mb_ies;
+ 	struct frag_ies_info frag_ies;
+ };
+diff --git a/src/common/ieee802_11_defs.h b/src/common/ieee802_11_defs.h
+index 78e7bee..f7ec11b
+--- a/src/common/ieee802_11_defs.h
++++ b/src/common/ieee802_11_defs.h
+@@ -481,6 +481,9 @@
+ #define WLAN_EID_EXT_SPATIAL_REUSE 39
+ #define WLAN_EID_EXT_COLOR_CHANGE_ANNOUNCEMENT 42
+ #define WLAN_EID_EXT_OCV_OCI 54
++#define WLAN_EID_EXT_MULTIPLE_BSSID_CONFIGURATION 55
++#define WLAN_EID_EXT_NON_INHERITANCE 56
++#define WLAN_EID_EXT_KNOWN_BSSID 57
+ #define WLAN_EID_EXT_SHORT_SSID_LIST 58
+ #define WLAN_EID_EXT_HE_6GHZ_BAND_CAP 59
+ #define WLAN_EID_EXT_EDMG_CAPABILITIES 61
+@@ -497,6 +500,8 @@
+ #define WLAN_EID_EXT_MULTI_LINK_TRAFFIC_INDICATION 110
+ #define WLAN_EID_EXT_AKM_SUITE_SELECTOR 114
+ 
++#define WLAN_EID_SUBELEMENT_NONTRANSMITTED_BSSID_PROFILE 0
++
+ /* Extended Capabilities field */
+ #define WLAN_EXT_CAPAB_20_40_COEX 0
+ #define WLAN_EXT_CAPAB_GLK 1
+diff --git a/src/drivers/driver.h b/src/drivers/driver.h
+index bb879ff..aa7d31d
+--- a/src/drivers/driver.h
++++ b/src/drivers/driver.h
+@@ -1633,6 +1633,43 @@ struct wpa_driver_ap_params {
+ 	 * ibf_enable=<val>
+ 	 */
+ 	u8 ibf_enable;
++
++	/**
++	 * mbssid_tx_iface - Transmitting interface of the MBSSID set
++	 */
++	const char *mbssid_tx_iface;
++
++	/**
++	 * mbssid_index - The index of this BSS in the MBSSID set
++	 */
++	unsigned int mbssid_index;
++
++	/**
++	 * mbssid_elem - Buffer containing all MBSSID elements
++	 */
++	u8 *mbssid_elem;
++
++	/**
++	 * mbssid_elem_len - Total length of all MBSSID elements
++	 */
++	size_t mbssid_elem_len;
++
++	/**
++	 * mbssid_elem_count - The number of MBSSID elements
++	 */
++	u8 mbssid_elem_count;
++
++	/**
++	 * mbssid_elem_offset - Offsets to elements in mbssid_elem.
++	 * Kernel will use these offsets to generate multiple BSSID beacons.
++	 */
++	u8 **mbssid_elem_offset;
++
++	/**
++	 * ema - Enhanced MBSSID advertisements support.
++	 */
++	bool ema;
++
+ };
+ 
+ struct wpa_driver_mesh_bss_params {
+@@ -2197,6 +2234,11 @@ struct wpa_driver_capa {
+ 
+ 	/* Maximum number of supported CSA counters */
+ 	u16 max_csa_counters;
++
++	/* Maximum number of interfaces supported for MBSSID advertisements */
++	u8 mbssid_max_interfaces;
++	/* Maximum profile periodicity for enhanced MBSSID advertisements */
++	u8 ema_max_periodicity;
+ };
+ 
+ 
+diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c
+index 3e2e7a6..268e1ec
+--- a/src/drivers/driver_nl80211.c
++++ b/src/drivers/driver_nl80211.c
+@@ -4540,6 +4540,55 @@ static int nl80211_unsol_bcast_probe_resp(struct i802_bss *bss,
+ 	nla_nest_end(msg, attr);
+ 	return 0;
+ }
++
++
++static int nl80211_mbssid(struct nl_msg *msg,
++			 struct wpa_driver_ap_params *params)
++{
++	struct nlattr *config, *elems;
++	int ifidx;
++
++	if (!params->mbssid_tx_iface)
++		return 0;
++
++	config = nla_nest_start(msg, NL80211_ATTR_MBSSID_CONFIG);
++	if (!config)
++		return -1;
++
++	nla_put_u8(msg, NL80211_MBSSID_CONFIG_ATTR_INDEX, params->mbssid_index);
++	if (params->mbssid_tx_iface) {
++		ifidx = if_nametoindex(params->mbssid_tx_iface);
++		if (ifidx <= 0)
++			return -1;
++		nla_put_u32(msg,
++			    NL80211_MBSSID_CONFIG_ATTR_TX_IFINDEX,
++			    ifidx);
++	}
++
++	if (params->ema)
++		nla_put_flag(msg, NL80211_MBSSID_CONFIG_ATTR_EMA);
++
++	nla_nest_end(msg, config);
++
++	if (params->mbssid_elem_count && params->mbssid_elem_len &&
++	    params->mbssid_elem_offset && *params->mbssid_elem_offset) {
++		u8 i, **offs = params->mbssid_elem_offset;
++
++		elems = nla_nest_start(msg, NL80211_ATTR_MBSSID_ELEMS);
++		if (!elems)
++			return -1;
++
++		for (i = 0; i < params->mbssid_elem_count - 1; i++)
++			nla_put(msg, i + 1, offs[i + 1] - offs[i], offs[i]);
++
++		nla_put(msg, i + 1,
++			*offs + params->mbssid_elem_len - offs[i],
++			offs[i]);
++
++		nla_nest_end(msg, elems);
++	}
++	return 0;
++}
+ #endif /* CONFIG_IEEE80211AX */
+ 
+ 
+@@ -4838,6 +4887,9 @@ static int wpa_driver_nl80211_set_ap(void *priv,
+ 	if (params->unsol_bcast_probe_resp_interval &&
+ 	    nl80211_unsol_bcast_probe_resp(bss, msg, params) < 0)
+ 		goto fail;
++
++	if (nl80211_mbssid(msg, params) < 0)
++		goto fail;
+ #endif /* CONFIG_IEEE80211AX */
+ 
+ #ifdef CONFIG_SAE
+diff --git a/src/drivers/driver_nl80211_capa.c b/src/drivers/driver_nl80211_capa.c
+index 06a52db..d8078bc
+--- a/src/drivers/driver_nl80211_capa.c
++++ b/src/drivers/driver_nl80211_capa.c
+@@ -857,6 +857,30 @@ err:
+ }
+ 
+ 
++static void wiphy_info_mbssid(struct wpa_driver_capa *cap, struct nlattr *attr)
++{
++	struct nlattr *config[NL80211_MBSSID_CONFIG_ATTR_MAX + 1];
++
++	if (nla_parse_nested(config, NL80211_MBSSID_CONFIG_ATTR_MAX, attr,
++			     NULL))
++		return;
++
++	if (!config[NL80211_MBSSID_CONFIG_ATTR_MAX_INTERFACES])
++		return;
++
++	cap->mbssid_max_interfaces =
++		nla_get_u8(config[NL80211_MBSSID_CONFIG_ATTR_MAX_INTERFACES]);
++
++	if (config[NL80211_MBSSID_CONFIG_ATTR_MAX_EMA_PROFILE_PERIODICITY])
++		cap->ema_max_periodicity =
++		nla_get_u8(config[NL80211_MBSSID_CONFIG_ATTR_MAX_EMA_PROFILE_PERIODICITY]);
++
++	wpa_printf(MSG_DEBUG,
++		   "multiple_bssid: max interfaces %u, max profile periodicity %u\n",
++		   cap->mbssid_max_interfaces, cap->ema_max_periodicity);
++}
++
++
+ static int wiphy_info_handler(struct nl_msg *msg, void *arg)
+ {
+ 	struct nlattr *tb[NL80211_ATTR_MAX + 1];
+@@ -1106,6 +1130,9 @@ static int wiphy_info_handler(struct nl_msg *msg, void *arg)
+ 	if (tb[NL80211_ATTR_WIPHY_SELF_MANAGED_REG])
+ 		capa->flags |= WPA_DRIVER_FLAGS_SELF_MANAGED_REGULATORY;
+ 
++	if (tb[NL80211_ATTR_MBSSID_CONFIG])
++		wiphy_info_mbssid(capa, tb[NL80211_ATTR_MBSSID_CONFIG]);
++
+ 	return NL_SKIP;
+ }
+ 
+-- 
+2.18.0
+