[PATCH 05/13] mbssid: add multiple BSSID elements

Aloka Dixit quic_alokad at quicinc.com
Wed Mar 2 14:26:26 PST 2022


From: John Crispin <john at phrozen.org>

Add data as per IEEE Std 802.11-2020 9.4.2.45 Multiple BSSID element.
Include this element in beacons and probe response frames when mbssid
is enabled. Split the BSSes into multiple elements if the data
does not fit in 255 bytes allowed for a single element.

Add support for enhanced multiple BSSID advertisements (EMA) by
sending offsets to the start of each MBSSID element to nl80211.
Mac80211 generates different EMA beacons by including only one
multiple BSSID element in each beacon frame.

Signed-off-by: John Crispin <john at phrozen.org>
Co-developed-by: Aloka Dixit <quic_alokad at quicinc.com>
Signed-off-by: Aloka Dixit <quic_alokad at quicinc.com>
---
 src/ap/beacon.c              |  66 ++++++++++-
 src/ap/ieee802_11.c          | 212 +++++++++++++++++++++++++++++++++++
 src/ap/ieee802_11.h          |   5 +-
 src/common/ieee802_11_defs.h |   2 +
 src/drivers/driver.h         |  27 +++++
 5 files changed, 309 insertions(+), 3 deletions(-)

diff --git a/src/ap/beacon.c b/src/ap/beacon.c
index 1d70cb2ead2c..3d45f4a69f2c 100644
--- a/src/ap/beacon.c
+++ b/src/ap/beacon.c
@@ -432,6 +432,9 @@ static void hostapd_set_mbssid_beacon(struct hostapd_data *hapd,
 				      struct wpa_driver_ap_params *params)
 {
 	struct hostapd_iface *iface = hapd->iface;
+	struct hostapd_data *tx_bss;
+	size_t len;
+	u8 num_mbssid = 0, *end;
 
 	if (!iface->conf->mbssid || iface->num_bss == 1)
 		return;
@@ -447,9 +450,54 @@ static void hostapd_set_mbssid_beacon(struct hostapd_data *hapd,
 		return;
 	}
 
-	params->mbssid_tx_iface = hostapd_mbssid_get_tx_bss(hapd)->conf->iface;
+	tx_bss = hostapd_mbssid_get_tx_bss(hapd);
+	params->mbssid_tx_iface = tx_bss->conf->iface;
 	params->mbssid_index = hostapd_mbssid_get_bss_index(hapd);
 	params->mbssid_count = iface->num_bss;
+
+	len = hostapd_eid_mbssid_len(tx_bss, WLAN_FC_STYPE_BEACON, &num_mbssid);
+	if (hapd->iconf->ema) {
+		if (!iface->ema_max_periodicity) {
+			wpa_printf(MSG_DEBUG,
+				   "MBSSID: Driver doesn't support enhanced multiple BSSID advertisements");
+			return;
+		}
+		if (num_mbssid > iface->ema_max_periodicity) {
+			wpa_printf(MSG_DEBUG,
+				   "MBSSID: Driver supports maximum %u EMA profile periodicity",
+				   iface->ema_max_periodicity);
+			return;
+		}
+		params->ema = 1;
+	}
+
+	if (hapd != tx_bss || !num_mbssid)
+		return;
+
+	params->mbssid_elem_count = num_mbssid;
+	params->mbssid_elem = os_zalloc(len);
+	if (!params->mbssid_elem) {
+		wpa_printf(MSG_ERROR,
+			   "Memory allocation failed for multiple BSSID elements");
+		return;
+	}
+
+	params->mbssid_elem_offset = os_zalloc(params->mbssid_elem_count *
+					       sizeof(u8 *));
+	if (!params->mbssid_elem_offset) {
+		wpa_printf(MSG_ERROR,
+			   "MBSSID: Memory allocation failed for multiple BSSID element offsets");
+		os_free(params->mbssid_elem);
+		params->mbssid_elem = NULL;
+		return;
+	}
+
+	end = hostapd_eid_mbssid(tx_bss, params->mbssid_elem,
+				 params->mbssid_elem + len,
+				 WLAN_FC_STYPE_BEACON,
+				 params->mbssid_elem_count,
+				 params->mbssid_elem_offset);
+	params->mbssid_elem_len = end - params->mbssid_elem;
 	return;
 }
 
@@ -462,8 +510,11 @@ static u8 * hostapd_gen_probe_resp(struct hostapd_data *hapd,
 	u8 *pos, *epos, *csa_pos;
 	size_t buflen;
 
+	hapd = hostapd_mbssid_get_tx_bss(hapd);
+
 #define MAX_PROBERESP_LEN 768
 	buflen = MAX_PROBERESP_LEN;
+
 #ifdef CONFIG_WPS
 	if (hapd->wps_probe_resp_ie)
 		buflen += wpabuf_len(hapd->wps_probe_resp_ie);
@@ -495,6 +546,7 @@ static u8 * hostapd_gen_probe_resp(struct hostapd_data *hapd,
 	}
 #endif /* CONFIG_IEEE80211AX */
 
+	buflen += hostapd_eid_mbssid_len(hapd, WLAN_FC_STYPE_PROBE_RESP, NULL);
 	buflen += hostapd_eid_rnr_len(hapd, WLAN_FC_STYPE_PROBE_RESP);
 	buflen += hostapd_mbo_ie_len(hapd);
 	buflen += hostapd_eid_owe_trans_len(hapd);
@@ -563,6 +615,8 @@ 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);
 
 	pos = hostapd_eid_ext_capab(hapd, pos);
 
@@ -1100,6 +1154,9 @@ void handle_probe_req(struct hostapd_data *hapd,
 	}
 #endif /* CONFIG_TESTING_OPTIONS */
 
+	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);
 
@@ -1126,7 +1183,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);
 
@@ -1794,6 +1852,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/ieee802_11.c b/src/ap/ieee802_11.c
index 6140a492c88b..7534fd027ec0 100644
--- a/src/ap/ieee802_11.c
+++ b/src/ap/ieee802_11.c
@@ -3956,6 +3956,23 @@ static void handle_auth(struct hostapd_data *hapd,
 }
 
 
+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;
@@ -7450,4 +7467,199 @@ 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)
+{
+	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, *rsnx;
+		size_t nontx_profile_len, auth_len;
+
+		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 ((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)
+{
+	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);
+
+		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)
+{
+	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, *rsnx;
+		size_t auth_len = 0;
+		u16 capab_info;
+
+		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;
+			*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]);
+			}
+		}
+
+		*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)
+{
+	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);
+	}
+	return eid;
+}
+
 #endif /* CONFIG_NATIVE_WINDOWS */
diff --git a/src/ap/ieee802_11.h b/src/ap/ieee802_11.h
index c59ad5e38e92..37df3faa724c 100644
--- a/src/ap/ieee802_11.h
+++ b/src/ap/ieee802_11.h
@@ -119,8 +119,11 @@ u8 * hostapd_eid_time_zone(struct hostapd_data *hapd, u8 *eid);
 int hostapd_update_time_adv(struct hostapd_data *hapd);
 void hostapd_client_poll_ok(struct hostapd_data *hapd, const u8 *addr);
 u8 * hostapd_eid_bss_max_idle_period(struct hostapd_data *hapd, u8 *eid);
-
 int auth_sae_init_committed(struct hostapd_data *hapd, struct sta_info *sta);
+size_t hostapd_eid_mbssid_len(struct hostapd_data *hapd, u32 frame_type,
+			      u8 *elem_count);
+u8 * hostapd_eid_mbssid(struct hostapd_data *hapd, u8 *eid, u8 *end,
+			u32 frame_type, u8 elem_count, u8 **elem_offset);
 #ifdef CONFIG_SAE
 void sae_clear_retransmit_timer(struct hostapd_data *hapd,
 				struct sta_info *sta);
diff --git a/src/common/ieee802_11_defs.h b/src/common/ieee802_11_defs.h
index 928b53500c5c..cd4d60a77d26 100644
--- a/src/common/ieee802_11_defs.h
+++ b/src/common/ieee802_11_defs.h
@@ -490,6 +490,8 @@
 #define WLAN_EID_EXT_ANTI_CLOGGING_TOKEN 93
 #define WLAN_EID_EXT_PASN_PARAMS 100
 
+#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 0527f120287b..a8f675633c0d 100644
--- a/src/drivers/driver.h
+++ b/src/drivers/driver.h
@@ -1597,6 +1597,33 @@ struct wpa_driver_ap_params {
 	 * mbssid_count - Total number of BSSs in the group
 	 */
 	unsigned int mbssid_count;
+
+	/**
+	 * mbssid_elem - Buffer containing all multiple BSSID elements
+	 */
+	u8 *mbssid_elem;
+
+	/**
+	 * mbssid_elem_len - Total length of all multiple BSSID elements
+	 */
+	size_t mbssid_elem_len;
+
+	/**
+	 * mbssid_elem_count - The number of multiple bssid 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 multi-bssid advertisements support.
+	 */
+	u8 ema;
+
 };
 
 struct wpa_driver_mesh_bss_params {
-- 
2.31.1




More information about the Hostap mailing list