[wpa_supplicant] cumilative patch from commit 4b755c967

Bug: 329004037
Test: Connect to open, WPA2, WPA3 and OWE
Test: Establish P2P connection
Test: Basic SoftAp tests
Test: Ran above tests on Pixel6
Test: Regression test (b/329003970)

BYPASS_INCLUSIVE_LANGUAGE_REASON=Merged from open source

4b755c967 build: De-duplicate _DIRS before calling mkdir
9a4423645 hostapd: Only attempt to set QoS map if supported by the driver
dec6fccf1 Support qos_map_set without CONFIG_INTERWORKING
8634e7343 mesh: Allow processing authentication frames in blocked state
a210fdb1c nl80211: Rewrite neigh code to not depend on libnl3-route
3ef057901 ndisc_snoop: Call dl_list_del() before freeing IPv6 addresses
e1cd3fe3c Cancel channel_list_update_timeout() in hostapd_cleanup_iface_partial()
47d7f3169 nl80211: Update drv->ifindex on removing the first BSS
1be706e86 hostapd: Add RRM link measurement request support
92fdb49b2 AP MLD: Set DTIM information properly in per-STA profile
36bd75dfd hostapd: Fix channel switch to a DFS channel
f4b84ecaf AP MLD: Track radar detection in offloaded DFS case
aaf879ef2 AP MLD: Do not update other links' RNR element if not enabled yet
32261721e nl80211: AP MLD: Parse link ID to determine the BSS for radar event
216cfd708 AP MLD: Fix missing check for legacy client case
d5e6f7998 AP MLD: Request Handle OBSS scan for a specific link
c9ad16870 AP MLD: Allow scan processing link to match the request
9b682e72d AP MLD: Find the link that is waiting for scan events
147f83692 PASN: Add set and get API for PASN data context
ab37a5731 Replace PMKSA cache inline stubs with wrapper function stubs
ba55088a7 Replace PTKSA cache inline stubs with wrapper function stubs
1f230a497 MBSSID: Include Extended Capabilities element in non-TX BSSID profile
37c00c3c5 AP MLD: Provide link addresses for non-AP MLDs in control interface
b818a1be1 Add a QCA vendor attribute to set avoid frequencies per netdev
9ac0e785c Revert "nl80211: Skip interface down/up when setting MAC address"
94506e8ed Use the latest updated BSS entry for sending ANQP requests
a9bc6e89d OpenSSL: Fix a memory leak in CMAC
4bc61b657 AP MLD: Remove restriction of having to disable the first link BSS last
a6d92da9a AP MLD: Support removal of link station from AP
1f88b3daf nl80211: Add callback function for removing link STAs
19e50f862 Export hostapd_sta_is_link_sta()
df34c2ced AP MLD: De-initialize/disable link BSS properly
63982fd09 nl80211: Print the MLD capabilities in debug
9fdbaf2ed AP MLD: Fix advertisement of MLD capabilities
7a0501d20 AP MLD: Refresh beacons for other links when one gets disabled/enabled
d2b62b3fe AP MLD: Support link removal before removing interface
55c30e8ab nl80211: Remove AP MLD links while removing the interface
a576180cd nl80211: Use per-BSS command for remove link
b162886fd nl80211: Re-factor nl80211_remove_links() function
b810426ea nl80211: Remove redundant put_freq call in set_ap() for AP MLD
420065733 nl80211: Fix set_ap() to add frequency without CONFIG_IEEE80211AX
f2f0dd354 nl80211: Cache hostapd_data context in per link BSS struct for AP MLD
60e1dca1e AP MLD: Clean up MLD when not required any further
fac34688a AP MLD: Assign link ID during BSS creation
b19aa9c42 AP MLD: Use MLD struct for MLD level information
2f0e5303e AP MLD: Add a separate MLD level structure
259b43a31 hostapd: MLO: Avoid use of mld_id as user configuration
69d53b8b6 nl80211: Fix potential NULL pointer dereference in set_ap()
666e954ca Remove unused wpa_drv_set_ap()
9be122d2e nl80211: Fix AP MLD frequency update on channel switch
9144f876a nl80211: Fix sending NL80211_CMD_DEL_BEACON command to wrong interface
ec4b755b0 wpa_cli: Don't select interface when using global socket
c24453dd9 Add a vendor attribute per MLO link ratemask bitmap configuration
77f39ed23 Document vendor command ratemask bitmap for EHT case
58017de69 Add QCA vendor sub-command and attribute for spectral scan completion
8f9da72d2 Add QCA vendor attribute indicating the spectral transport mode
5b4a78b1f Optimize internal BSS table updates based on a specific BSSID
8d0bd7f9c Update BSS entry on roaming only for actual BSS frequency change
024d4bca1 Multi-AP: WPS support for different Multi-AP profiles
69d086298 Multi-AP: Add support for VLAN related information
210c2b4bd Multi-AP: Add hostapd config option to disallow certain profiles
9a1512532 Multi-AP: Reject non-Multi-AP STA association on backhaul-only BSS
420afbdbd Multi-AP: Allow supported profile to be configured
c3e528653 Multi-AP: Parse Profile subelement
003411242 Multi-AP: Generation of Multi-AP Profile subelement
364cb7c94 Multi-AP: Parse the Multi-AP element using a shared helper function
0e2ca2e4e Multi-AP: Use proper length for remaining buffer for the element
61e46f860 Multi-AP: Move IE parameters into a struct for extensibility
2ae1e6f18 DBus: Add ANQP fields to BSS properties
2ea04435e DBus: Signal ANQP query done
d71c83851 DBus: Add a method to get ANQP fields
5eb409c4b DBus: Add dict helper for uint16 arrays
a438e5293 OpenSSL: Fix a memory leak on hpke_labeled_expand() error path
b35b1036f OpenSSL: Fix a memory leak on openssl_evp_pkey_ec_prime_len() error path
35df7ee09 DPP: Emit a DPP PB_STATUS event when push button starts
69dd408fb EHT: More accurate no-second-channel-offset checks when puncturing
131ee5926 EHT: Support punct_bitmap overriding in HE element generation
c96c3adc3 Move punct_update_legacy_bw() into src/common
9f43c1e26 Provide punct_bitmap to hostapd_set_freq_params()
47dad1ed1 EHT: Move puncturing bitmap determination into a helper function
010d8d10e EHT: Use eht_oper_puncturing_override when constructing VHT elements
c00abc69f Handle 6 GHz channels in Supported Operating Classes with freq_list
9e90486bc 2-octet operating classes in Support Operating Classes element
5dabc1018 Extend support for the 6 GHz operating class 137 (320 MHz)
e74d95e0a nl80211: Process 6 GHz regulatory rules to accurate channel flags
59951ebf0 Use a helper function to free neighbor DB entries
96f0af07e Clear all neighbor entry items explicitly
6f285fbaf Update own report in nr_db if SSID is changed
b653420a2 AP MLD: Set link address only when non-AP MLD is not added to driver
16abdac80 Fix INTERFACES command buffer size to allow more data
b483ceafc hostapd: Dump VHT/HE/EHT full capabilities in STA command output
040ba112a Use os_snprintf_error() more consistently in STA output generation
31bbc9391 Do not change out-of-range configuration parameters
fd24ed949 Fix valid range for disable_ht40
b1a880f38 Enforce valid range check for SET mbo_cell_capa and oce
e5b7e5b90 wpa_supplicant: Fix ignoring boundary 0 in config parser
10122e951 P2P: Fix pri/sec channel switch skipping for GO
e508c070c WNM: Keep BTM information until connection completes
17a2aa822 WNM: Follow BTM procedure if the last link is dropped
770f3cb30 WNM: Remove dialog_token parameter
c7cafef8a WNM: Set wnm_reply to 0 when sending it
261f7f9e5 WNM: Do not store coloc_intf_elems
033adbf83 WNM: Drop explicit wpa_is_bss_tmp_disallowed() check
8b51310f4 WNM: Drop explicit SSID check
939cd294b WNM: Drop check for current_ssid
436f07d02 WNM: Define a stub wnm_is_bss_excluded if WNM is disabled
e164943f4 WNM: Wait for BTM response TX status before roaming
40ef706e5 WNM: Don't scan frequencies of neighbors that should be ignored
20ed289a7 WNM: Clean up old scan data processing
4750a4f62 DPP: Wait for TX wait expiration on moving to neg_freq for Auth Resp
963dbad7d nl80211: Indicate EVENT_TX_WAIT_EXPIRE on match-saved
451d29952 DPP: Wait for ROC cancelled event on Auth Resp TX on another channel
45fffac0f BSS: Switch struct wpa_bss to use valid_links bitmask
e90f6678f nl80211: Remnove unused struct i802_link ctx
0d4288a00 nl80211: Use valid_links bitmask for bss->links array
9ed51186e Use a single define MAX_NUM_MLD_LINKS for the maximum number of links
11f26fed6 Use for_each_link() where possible
dbdf7ef67 Use for_each_link() in most cases
c9f8fe066 common: Introduce for_each_link() macro
6cb421c1f nl80211: Fix link indexing in nl80211_connect_common()
408a399aa nl80211: Explicitly differentiate between 5 GHz and 6 GHz modes
dbcf9ff15 P2P: Notify the IP address of the connected P2P Client
b18d95759 P2P: Disable pri/sec channel switch for GO with forced frequency
0e4aa28da hostapd_cli: Indentation cleanup
a01972a06 Add "stop_ap" command for hostapd_cli
8cdb0d3f2 AP MLD: Stop AP per link
d084ef36b AP MLD: Clean up disassoc handling for non-AP MLD link validity check
7ceafb6e9 AP MLD: Handle disassociation notification with SME offload to driver
95a825bc4 RADIUS: Preliminary support RADIUS/TLS as an alternative to RADIUS/UDP
87f33c26b RADIUS: Simplify IPv4/IPv6 socket handling in client
971b78147 RADIUS: Simplify radius_change_server() parameters
3386e1327 l2_packet_freebsd: Fix macOS build
86c242171 TDLS: Defer the start request until the discovery response RX for MLO
352ad5f1a Apply CHAN_SWITCH in all BSS for MBSSID case
87120a5b6 Add QCA_NL80211_VENDOR_SUBCMD_ADJUST_TX_POWER command
fe82a61ef Add QCA vendor attribute for BTM support configuration

Change-Id: I11d4eeb2e4ab44630e3359a2886361b3fccd7ead
diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c
index 9b50b6f..194a5cd 100644
--- a/src/drivers/driver_nl80211.c
+++ b/src/drivers/driver_nl80211.c
@@ -18,9 +18,6 @@
 #include <netlink/genl/genl.h>
 #include <netlink/genl/ctrl.h>
 #include <netlink/genl/family.h>
-#ifdef CONFIG_LIBNL3_ROUTE
-#include <netlink/route/neighbour.h>
-#endif /* CONFIG_LIBNL3_ROUTE */
 #include <linux/rtnetlink.h>
 #include <netpacket/packet.h>
 #include <linux/errqueue.h>
@@ -2295,7 +2292,6 @@
 {
 	struct wpa_driver_nl80211_data *drv;
 	struct i802_bss *bss;
-	unsigned int i;
 	char path[128], buf[200], *pos;
 	ssize_t len;
 	int ret;
@@ -2395,16 +2391,12 @@
 	}
 
 	/*
-	 * Set the default link to be the first one, and set its address to that
-	 * of the interface.
+	 * Use link ID 0 for the single "link" of a non-MLD.
 	 */
+	bss->valid_links = 0;
 	bss->flink = &bss->links[0];
-	bss->n_links = 1;
 	os_memcpy(bss->flink->addr, bss->addr, ETH_ALEN);
 
-	for (i = 0; i < MAX_NUM_MLD_LINKS; i++)
-		bss->links[i].link_id = NL80211_DRV_LINK_ID_NA;
-
 	return bss;
 
 failed:
@@ -3079,31 +3071,31 @@
 
 
 static int wpa_driver_nl80211_del_beacon(struct i802_bss *bss,
-					 struct i802_link *link)
+					 int link_id)
 {
 	struct nl_msg *msg;
 	struct wpa_driver_nl80211_data *drv = bss->drv;
+	struct i802_link *link = nl80211_get_link(bss, link_id);
 
 	if (!link->beacon_set)
 		return 0;
 
 	wpa_printf(MSG_DEBUG, "nl80211: Remove beacon (ifindex=%d)",
-		   drv->ifindex);
+		   bss->ifindex);
 	link->beacon_set = 0;
 	link->freq = 0;
 
 	nl80211_put_wiphy_data_ap(bss);
-	msg = nl80211_drv_msg(drv, 0, NL80211_CMD_DEL_BEACON);
+	msg = nl80211_bss_msg(bss, 0, NL80211_CMD_DEL_BEACON);
 	if (!msg)
 		return -ENOBUFS;
 
-	if (link->link_id != NL80211_DRV_LINK_ID_NA) {
+	if (link_id != NL80211_DRV_LINK_ID_NA) {
 		wpa_printf(MSG_DEBUG,
 			   "nl80211: MLD: stop beaconing on link=%u",
-			   link->link_id);
+			   link_id);
 
-		if (nla_put_u8(msg, NL80211_ATTR_MLO_LINK_ID,
-			       link->link_id)) {
+		if (nla_put_u8(msg, NL80211_ATTR_MLO_LINK_ID, link_id)) {
 			nlmsg_free(msg);
 			return -ENOBUFS;
 		}
@@ -3115,10 +3107,10 @@
 
 static void wpa_driver_nl80211_del_beacon_all(struct i802_bss *bss)
 {
-	unsigned int i;
+	int link_id;
 
-	for (i = 0; i < bss->n_links; i++)
-		wpa_driver_nl80211_del_beacon(bss, &bss->links[i]);
+	for_each_link_default(bss->valid_links, link_id, NL80211_DRV_LINK_ID_NA)
+		wpa_driver_nl80211_del_beacon(bss, link_id);
 }
 
 
@@ -4258,14 +4250,11 @@
 
 struct i802_link * nl80211_get_link(struct i802_bss *bss, s8 link_id)
 {
-	unsigned int i;
+	if (link_id < 0 || link_id >= MAX_NUM_MLD_LINKS)
+		return bss->flink;
 
-	for (i = 0; i < bss->n_links; i++) {
-		if (bss->links[i].link_id != link_id)
-			continue;
-
-		return &bss->links[i];
-	}
+	if (BIT(link_id) & bss->valid_links)
+		return &bss->links[link_id];
 
 	return bss->flink;
 }
@@ -4282,9 +4271,9 @@
 static int nl80211_get_link_freq(struct i802_bss *bss, const u8 *addr,
 				 bool bss_freq_debug)
 {
-	size_t i;
+	u8 i;
 
-	for (i = 0; i < bss->n_links; i++) {
+	for_each_link(bss->valid_links, i) {
 		if (ether_addr_equal(bss->links[i].addr, addr)) {
 			wpa_printf(MSG_DEBUG,
 				   "nl80211: Use link freq=%d for address "
@@ -5125,20 +5114,18 @@
 #endif /* CONFIG_MESH */
 
 	if (params->mld_ap) {
-		size_t i;
-
-		for (i = 0; i < bss->n_links; i++) {
-			if (bss->links[i].link_id == params->mld_link_id) {
-				link = &bss->links[i];
-				break;
-			}
-		}
-
-		if (i == bss->n_links) {
-			wpa_printf(MSG_DEBUG, "nl80211: Link ID=%u not found",
-				   params->mld_link_id);
+		if (!nl80211_link_valid(bss->valid_links,
+					params->mld_link_id)) {
+			wpa_printf(MSG_DEBUG,
+				   "nl80211: Link ID=%u invalid (valid: 0x%04x)",
+				   params->mld_link_id, bss->valid_links);
 			return -EINVAL;
 		}
+
+		link = nl80211_get_link(bss, params->mld_link_id);
+	} else if (bss->valid_links) {
+		wpa_printf(MSG_DEBUG, "nl80211: MLD configuration expected");
+		return -EINVAL;
 	}
 
 	beacon_set = params->reenable ? 0 : link->beacon_set;
@@ -5179,13 +5166,12 @@
 			   params->mld_link_id);
 
 		if (nla_put_u8(msg, NL80211_ATTR_MLO_LINK_ID,
-			       params->mld_link_id) ||
-		    (params->freq &&
-		     nl80211_put_freq_params(msg, params->freq) < 0))
+			       params->mld_link_id))
 			goto fail;
 
-		nl80211_link_set_freq(bss, params->mld_link_id,
-				      params->freq->freq);
+		if (params->freq)
+			nl80211_link_set_freq(bss, params->mld_link_id,
+					      params->freq->freq);
 	}
 
 	if (params->proberesp && params->proberesp_len) {
@@ -5393,6 +5379,9 @@
 		nla_nest_end(msg, ftm);
 	}
 
+	if (params->freq && nl80211_put_freq_params(msg, params->freq) < 0)
+		goto fail;
+
 #ifdef CONFIG_IEEE80211AX
 	if (params->he_spr_ctrl) {
 		struct nlattr *spr;
@@ -5427,9 +5416,6 @@
 		nla_nest_end(msg, spr);
 	}
 
-	if (params->freq && nl80211_put_freq_params(msg, params->freq) < 0)
-		goto fail;
-
 	if (params->freq && params->freq->he_enabled &&
 	    nl80211_attr_supported(drv, NL80211_ATTR_HE_BSS_COLOR)) {
 		struct nlattr *bss_color;
@@ -5548,27 +5534,6 @@
 }
 
 
-static bool nl80211_link_valid(struct i802_bss *bss, s8 link_id)
-{
-	unsigned int i;
-
-	if (link_id < 0)
-		return false;
-
-	for (i = 0; i < bss->n_links; i++) {
-		wpa_printf(MSG_DEBUG, "nl80211: %s - i=%u, link_id=%u",
-			   __func__, i, bss->links[i].link_id);
-		if (bss->links[i].link_id == NL80211_DRV_LINK_ID_NA)
-			continue;
-
-		if (bss->links[i].link_id == link_id)
-			return true;
-	}
-
-	return false;
-}
-
-
 static int nl80211_set_channel(struct i802_bss *bss,
 			       struct hostapd_freq_params *freq, int set_chan)
 {
@@ -5589,7 +5554,7 @@
 		return -1;
 	}
 
-	if (nl80211_link_valid(bss, freq->link_id)) {
+	if (nl80211_link_valid(bss->valid_links, freq->link_id)) {
 		wpa_printf(MSG_DEBUG, "nl80211: Set link_id=%u for freq",
 			   freq->link_id);
 
@@ -5955,26 +5920,25 @@
 
 static void rtnl_neigh_delete_fdb_entry(struct i802_bss *bss, const u8 *addr)
 {
-#ifdef CONFIG_LIBNL3_ROUTE
 	struct wpa_driver_nl80211_data *drv = bss->drv;
-	struct rtnl_neigh *rn;
-	struct nl_addr *nl_addr;
+	struct ndmsg nhdr = {
+		.ndm_state = NUD_PERMANENT,
+		.ndm_ifindex = bss->ifindex,
+		.ndm_family = AF_BRIDGE,
+	};
+	struct nl_msg *msg;
 	int err;
 
-	rn = rtnl_neigh_alloc();
-	if (!rn)
+	msg = nlmsg_alloc_simple(RTM_DELNEIGH, NLM_F_CREATE);
+	if (!msg)
 		return;
 
-	rtnl_neigh_set_family(rn, AF_BRIDGE);
-	rtnl_neigh_set_ifindex(rn, bss->ifindex);
-	nl_addr = nl_addr_build(AF_BRIDGE, (void *) addr, ETH_ALEN);
-	if (!nl_addr) {
-		rtnl_neigh_put(rn);
-		return;
-	}
-	rtnl_neigh_set_lladdr(rn, nl_addr);
+	if (nlmsg_append(msg, &nhdr, sizeof(nhdr), NLMSG_ALIGNTO) < 0 ||
+	    nla_put(msg, NDA_LLADDR, ETH_ALEN, (void *) addr) ||
+	    nl_send_auto_complete(drv->rtnl_sk, msg) < 0)
+		goto errout;
 
-	err = rtnl_neigh_delete(drv->rtnl_sk, rn, 0);
+	err = nl_wait_for_ack(drv->rtnl_sk);
 	if (err < 0) {
 		wpa_printf(MSG_DEBUG, "nl80211: bridge FDB entry delete for "
 			   MACSTR " ifindex=%d failed: %s", MAC2STR(addr),
@@ -5984,9 +5948,8 @@
 			   MACSTR, MAC2STR(addr));
 	}
 
-	nl_addr_put(nl_addr);
-	rtnl_neigh_put(rn);
-#endif /* CONFIG_LIBNL3_ROUTE */
+errout:
+	nlmsg_free(msg);
 }
 
 
@@ -6783,7 +6746,6 @@
 	if (params->mld_params.mld_addr && params->mld_params.valid_links > 0) {
 		struct wpa_driver_mld_params *mld_params = &params->mld_params;
 		struct nlattr *links, *attr;
-		int i;
 		u8 link_id;
 
 		wpa_printf(MSG_DEBUG, "  * MLD: MLD addr=" MACSTR,
@@ -6799,31 +6761,8 @@
 		if (!links)
 			return -1;
 
-		attr = nla_nest_start(msg, 0);
-		if (!attr)
-			return -1;
-
-		/* First add the association link ID */
-		link_id = mld_params->assoc_link_id;
-		if (nla_put_u8(msg, NL80211_ATTR_MLO_LINK_ID, link_id) ||
-		    nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN,
-			    mld_params->mld_links[link_id].bssid) ||
-		    nla_put_u32(msg, NL80211_ATTR_WIPHY_FREQ,
-				mld_params->mld_links[link_id].freq))
-			return -1;
-
-		os_memcpy(drv->sta_mlo_info.links[link_id].bssid,
-			  mld_params->mld_links[link_id].bssid, ETH_ALEN);
-
-		nla_nest_end(msg, attr);
-
-		for (i = 1, link_id = 0; link_id < MAX_NUM_MLD_LINKS;
-		     link_id++) {
-			if (!(mld_params->valid_links & BIT(link_id)) ||
-			    link_id == mld_params->assoc_link_id)
-				continue;
-
-			attr = nla_nest_start(msg, i);
+		for_each_link(mld_params->valid_links, link_id) {
+			attr = nla_nest_start(msg, 0);
 			if (!attr)
 				return -1;
 
@@ -6833,11 +6772,11 @@
 				    mld_params->mld_links[link_id].bssid) ||
 			    nla_put_u32(msg, NL80211_ATTR_WIPHY_FREQ,
 					mld_params->mld_links[link_id].freq) ||
-			    (mld_params->mld_links[i].disabled &&
+			    (mld_params->mld_links[link_id].disabled &&
 			     nla_put_flag(msg,
 					  NL80211_ATTR_MLO_LINK_DISABLED)) ||
 			    (mld_params->mld_links[link_id].ies &&
-			     mld_params->mld_links[i].ies_len &&
+			     mld_params->mld_links[link_id].ies_len &&
 			     nla_put(msg, NL80211_ATTR_IE,
 				     mld_params->mld_links[link_id].ies_len,
 				     mld_params->mld_links[link_id].ies)))
@@ -6847,7 +6786,6 @@
 				  mld_params->mld_links[link_id].bssid,
 				  ETH_ALEN);
 			nla_nest_end(msg, attr);
-			i++;
 		}
 
 		nla_nest_end(msg, links);
@@ -7424,10 +7362,7 @@
 
 		/* Error and force TEST_FAIL checking for each link */
 		ret = -EINVAL;
-		for (i = 0; i < MAX_NUM_MLD_LINKS; i++) {
-			if (!(params->mld_params.valid_links & BIT(i)))
-				continue;
-
+		for_each_link(params->mld_params.valid_links, i) {
 			if (TEST_FAIL_TAG("link"))
 				err_info.link_id = i;
 		}
@@ -8762,7 +8697,6 @@
 	    (params->num_bridge == 0 || !params->bridge[0]))
 		add_ifidx(drv, br_ifindex, drv->ifindex);
 
-#ifdef CONFIG_LIBNL3_ROUTE
 	if (bss->added_if_into_bridge || bss->already_in_bridge) {
 		int err;
 
@@ -8779,7 +8713,6 @@
 			goto failed;
 		}
 	}
-#endif /* CONFIG_LIBNL3_ROUTE */
 
 	if (drv->capa.flags2 & WPA_DRIVER_FLAGS2_CONTROL_PORT_RX) {
 		wpa_printf(MSG_DEBUG,
@@ -9004,7 +8937,6 @@
 
 	if (type == WPA_IF_AP_BSS && setup_ap) {
 		struct i802_bss *new_bss = os_zalloc(sizeof(*new_bss));
-		unsigned int i;
 
 		if (new_bss == NULL) {
 			if (added)
@@ -9012,10 +8944,6 @@
 			return -1;
 		}
 
-		/* Initialize here before any failure path */
-		for (i = 0; i < MAX_NUM_MLD_LINKS; i++)
-			new_bss->links[i].link_id = NL80211_DRV_LINK_ID_NA;
-
 		if (bridge &&
 		    i802_check_bridge(drv, new_bss, bridge, ifname) < 0) {
 			wpa_printf(MSG_ERROR, "nl80211: Failed to add the new "
@@ -9040,7 +8968,7 @@
 		new_bss->drv = drv;
 		new_bss->next = drv->first_bss->next;
 		new_bss->flink = &new_bss->links[0];
-		new_bss->n_links = 1;
+		new_bss->valid_links = 0;
 		os_memcpy(new_bss->flink->addr, new_bss->addr, ETH_ALEN);
 
 		new_bss->flink->freq = drv->first_bss->flink->freq;
@@ -9120,6 +9048,7 @@
 				tbss->next = bss->next;
 				/* Unsubscribe management frames */
 				nl80211_teardown_ap(bss);
+				nl80211_remove_links(bss);
 				nl80211_destroy_bss(bss);
 				if (!bss->added_if)
 					i802_set_iface_flags(bss, 0);
@@ -9134,6 +9063,7 @@
 	} else {
 		wpa_printf(MSG_DEBUG, "nl80211: First BSS - reassign context");
 		nl80211_teardown_ap(bss);
+		nl80211_remove_links(bss);
 		if (!bss->added_if && !drv->first_bss->next)
 			wpa_driver_nl80211_del_beacon_all(bss);
 		nl80211_destroy_bss(bss);
@@ -9142,6 +9072,7 @@
 		if (drv->first_bss->next) {
 			drv->first_bss = drv->first_bss->next;
 			drv->ctx = drv->first_bss->ctx;
+			drv->ifindex = drv->first_bss->ifindex;
 			os_free(bss);
 		} else {
 			wpa_printf(MSG_DEBUG, "nl80211: No second BSS to reassign context to");
@@ -9382,10 +9313,7 @@
 		return 0;
 
 	/* First try to pick a link that uses the same band */
-	for (i = 0; i < MAX_NUM_MLD_LINKS; i++) {
-		if (!(mlo->valid_links & BIT(i)))
-			continue;
-
+	for_each_link(mlo->valid_links, i) {
 		if (any_valid_link_id == -1)
 			any_valid_link_id = i;
 
@@ -9580,59 +9508,81 @@
 }
 
 
-static void nl80211_remove_links(struct i802_bss *bss)
+static int nl80211_remove_link(struct i802_bss *bss, int link_id)
 {
 	struct wpa_driver_nl80211_data *drv = bss->drv;
+	struct i802_link *link;
 	struct nl_msg *msg;
+	size_t i;
+	int ret;
+
+	wpa_printf(MSG_DEBUG, "nl80211: Remove link (ifindex=%d link_id=%u)",
+		   bss->ifindex, link_id);
+
+	if (!(bss->valid_links & BIT(link_id))) {
+		wpa_printf(MSG_DEBUG,
+			   "nl80211: MLD: remove link: Link not found");
+		return -1;
+	}
+
+	link = &bss->links[link_id];
+
+	wpa_driver_nl80211_del_beacon(bss, link_id);
+
+	/* First remove the link locally */
+	bss->valid_links &= ~BIT(link_id);
+	os_memset(link->addr, 0, ETH_ALEN);
+
+	/* Choose new deflink if we are removing that link */
+	if (bss->flink == link) {
+		for_each_link_default(bss->valid_links, i, 0) {
+			bss->flink = &bss->links[i];
+			break;
+		}
+	}
+
+	/* If this was the last link, reset default link */
+	if (!bss->valid_links) {
+		/* TODO: Does keeping freq/bandwidth make sense? */
+		if (bss->flink != link)
+			os_memcpy(bss->flink, link, sizeof(*link));
+
+		os_memcpy(bss->flink->addr, bss->addr, ETH_ALEN);
+	}
+
+	/* Remove the link from the kernel */
+	msg = nl80211_bss_msg(bss, 0, NL80211_CMD_REMOVE_LINK);
+	if (!msg ||
+	    nla_put_u8(msg, NL80211_ATTR_MLO_LINK_ID, link_id)) {
+		nlmsg_free(msg);
+		wpa_printf(MSG_ERROR,
+			   "nl80211: remove link (%d) failed", link_id);
+		return -1;
+	}
+
+	ret = send_and_recv_cmd(drv, msg);
+	if (ret)
+		wpa_printf(MSG_ERROR,
+			   "nl80211: remove link (%d) failed. ret=%d (%s)",
+			   link_id, ret, strerror(-ret));
+
+	return ret;
+}
+
+
+static void nl80211_remove_links(struct i802_bss *bss)
+{
 	int ret;
 	u8 link_id;
 
-	if (bss->n_links == 0)
-		return;
-
-	while (bss->links[0].link_id != NL80211_DRV_LINK_ID_NA) {
-		struct i802_link *link = &bss->links[0];
-
-		wpa_printf(MSG_DEBUG, "nl80211: MLD: remove link_id=%u",
-			   link->link_id);
-
-		wpa_driver_nl80211_del_beacon(bss, link);
-
-		link_id = link->link_id;
-
-		/* First remove the link locally */
-		if (bss->n_links == 1) {
-			bss->flink->link_id = NL80211_DRV_LINK_ID_NA;
-			os_memcpy(bss->flink->addr, bss->addr, ETH_ALEN);
-		} else {
-			struct i802_link *other = &bss->links[bss->n_links - 1];
-
-			os_memcpy(link, other, sizeof(*link));
-			other->link_id = NL80211_DRV_LINK_ID_NA;
-			os_memset(other->addr, 0, ETH_ALEN);
-
-			bss->n_links--;
-		}
-
-		/* Remove the link from the kernel */
-		msg = nl80211_drv_msg(drv, 0, NL80211_CMD_REMOVE_LINK);
-		if (!msg ||
-		    nla_put_u8(msg, NL80211_ATTR_MLO_LINK_ID, link_id)) {
-			nlmsg_free(msg);
-			wpa_printf(MSG_ERROR,
-				   "nl80211: remove link (%d) failed",
-				   link_id);
-			return;
-		}
-
-		ret = send_and_recv_cmd(drv, msg);
-		if (ret) {
-			wpa_printf(MSG_ERROR,
-				   "nl80211: remove link (%d) failed. ret=%d (%s)",
-				   link_id, ret, strerror(-ret));
-			return;
-		}
+	for_each_link(bss->valid_links, link_id) {
+		ret = nl80211_remove_link(bss, link_id);
+		if (ret)
+			break;
 	}
+
+	if (bss->flink)
+		os_memcpy(bss->flink->addr, bss->addr, ETH_ALEN);
 }
 
 
@@ -9645,7 +9595,7 @@
 		return -1;
 
 	/* Stop beaconing */
-	wpa_driver_nl80211_del_beacon(bss, bss->flink);
+	wpa_driver_nl80211_del_beacon(bss, NL80211_DRV_LINK_ID_NA);
 
 	nl80211_remove_links(bss);
 
@@ -9660,7 +9610,7 @@
 }
 
 
-static int wpa_driver_nl80211_stop_ap(void *priv)
+static int wpa_driver_nl80211_stop_ap(void *priv, int link_id)
 {
 	struct i802_bss *bss = priv;
 	struct wpa_driver_nl80211_data *drv = bss->drv;
@@ -9668,9 +9618,17 @@
 	if (!is_ap_interface(drv->nlmode))
 		return -1;
 
-	wpa_driver_nl80211_del_beacon_all(bss);
+	if (link_id == -1) {
+		wpa_driver_nl80211_del_beacon_all(bss);
+		return 0;
+	}
 
-	return 0;
+	if (nl80211_link_valid(bss->valid_links, link_id)) {
+		wpa_driver_nl80211_del_beacon(bss, link_id);
+		return 0;
+	}
+
+	return -1;
 }
 
 
@@ -9823,10 +9781,7 @@
 	if (!sinfo[NL80211_SURVEY_INFO_NOISE])
 		return NL_SKIP;
 
-	for (i = 0; i < MAX_NUM_MLD_LINKS; i++) {
-		if (!(mlo_sig->valid_links & BIT(i)))
-			continue;
-
+	for_each_link(mlo_sig->valid_links, i) {
 		if (nla_get_u32(sinfo[NL80211_SURVEY_INFO_FREQUENCY]) !=
 		    mlo_sig->links[i].frequency)
 			continue;
@@ -9919,10 +9874,7 @@
 	os_memset(mlo_si, 0, sizeof(*mlo_si));
 	mlo_si->valid_links = drv->sta_mlo_info.valid_links;
 
-	for (i = 0; i < MAX_NUM_MLD_LINKS; i++) {
-		if (!(mlo_si->valid_links & BIT(i)))
-			continue;
-
+	for_each_link(mlo_si->valid_links, i) {
 		res = nl80211_get_link_signal(drv,
 					      drv->sta_mlo_info.links[i].bssid,
 					      &mlo_si->links[i].data);
@@ -10846,6 +10798,73 @@
 }
 
 
+#ifdef CONFIG_IEEE80211BE
+
+static int driver_nl80211_link_remove(void *priv, enum wpa_driver_if_type type,
+				      const char *ifname, u8 link_id)
+{
+	struct i802_bss *bss = priv;
+	struct wpa_driver_nl80211_data *drv = bss->drv;
+
+	if (type != WPA_IF_AP_BSS ||
+	    !nl80211_link_valid(bss->valid_links, link_id))
+		return -1;
+
+	wpa_printf(MSG_DEBUG,
+		   "nl80211: Teardown AP(%s) link %d (type=%d ifname=%s links=0x%x)",
+		   bss->ifname, link_id, type, ifname, bss->valid_links);
+
+	nl80211_remove_link(bss, link_id);
+
+	bss->ctx = bss->flink->ctx;
+
+	if (drv->first_bss == bss && !bss->valid_links)
+		drv->ctx = bss->ctx;
+
+	if (!bss->valid_links) {
+		wpa_printf(MSG_DEBUG,
+			   "nl80211: No more links remaining, so remove interface");
+		return wpa_driver_nl80211_if_remove(bss, type, ifname);
+	}
+
+	return 0;
+}
+
+
+static bool nl80211_is_drv_shared(void *priv, void *bss_ctx)
+{
+	struct i802_bss *bss = priv;
+	struct wpa_driver_nl80211_data *drv = bss->drv;
+	unsigned int num_bss = 0;
+
+	/* If any other BSS exist, someone else is using this since at this
+	 * time, we would have removed all BSSs created by this driver and only
+	 * this BSS should be remaining if the driver is not shared by anyone.
+	 */
+	for (bss = drv->first_bss; bss; bss = bss->next) {
+		num_bss++;
+		if (num_bss > 1)
+			return true;
+	}
+
+	/* This is the only BSS present */
+	bss = priv;
+
+	/* If only one/no link is there no one is sharing */
+	if (bss->valid_links <= 1)
+		return false;
+
+	/* More than one link means someone is still using. To check if
+	 * only 1 bit is set, power of 2 condition can be checked. */
+	if (!(bss->valid_links & (bss->valid_links - 1)))
+		return false;
+
+	return true;
+}
+
+#endif /* CONFIG_IEEE80211BE */
+
+
 static int driver_nl80211_send_mlme(void *priv, const u8 *data,
 				    size_t data_len, int noack,
 				    unsigned int freq,
@@ -11114,10 +11133,7 @@
 			return pos - buf;
 		pos += res;
 
-		for (i = 0; i < MAX_NUM_MLD_LINKS; i++) {
-			if (!(mlo->valid_links & BIT(i)))
-				continue;
-
+		for_each_link(mlo->valid_links, i) {
 			res = os_snprintf(pos, end - pos,
 					  "link_addr[%u]=" MACSTR "\n"
 					  "link_bssid[%u]=" MACSTR "\n"
@@ -12248,13 +12264,14 @@
 				      const u8 *ipaddr, int prefixlen,
 				      const u8 *addr)
 {
-#ifdef CONFIG_LIBNL3_ROUTE
 	struct i802_bss *bss = priv;
 	struct wpa_driver_nl80211_data *drv = bss->drv;
-	struct rtnl_neigh *rn;
-	struct nl_addr *nl_ipaddr = NULL;
-	struct nl_addr *nl_lladdr = NULL;
-	int family, addrsize;
+	struct ndmsg nhdr = {
+		.ndm_state = NUD_PERMANENT,
+		.ndm_ifindex = bss->br_ifindex,
+	};
+	struct nl_msg *msg;
+	int addrsize;
 	int res;
 
 	if (!ipaddr || prefixlen == 0 || !addr)
@@ -12273,85 +12290,62 @@
 	}
 
 	if (version == 4) {
-		family = AF_INET;
+		nhdr.ndm_family = AF_INET;
 		addrsize = 4;
 	} else if (version == 6) {
-		family = AF_INET6;
+		nhdr.ndm_family = AF_INET6;
 		addrsize = 16;
 	} else {
 		return -EINVAL;
 	}
 
-	rn = rtnl_neigh_alloc();
-	if (rn == NULL)
+	msg = nlmsg_alloc_simple(RTM_NEWNEIGH, NLM_F_CREATE);
+	if (!msg)
 		return -ENOMEM;
 
-	/* set the destination ip address for neigh */
-	nl_ipaddr = nl_addr_build(family, (void *) ipaddr, addrsize);
-	if (nl_ipaddr == NULL) {
-		wpa_printf(MSG_DEBUG, "nl80211: nl_ipaddr build failed");
-		res = -ENOMEM;
+	res = -ENOMEM;
+	if (nlmsg_append(msg, &nhdr, sizeof(nhdr), NLMSG_ALIGNTO) < 0 ||
+	    nla_put(msg, NDA_DST, addrsize, (void *) ipaddr) ||
+	    nla_put(msg, NDA_LLADDR, ETH_ALEN, (void *) addr))
 		goto errout;
-	}
-	nl_addr_set_prefixlen(nl_ipaddr, prefixlen);
-	res = rtnl_neigh_set_dst(rn, nl_ipaddr);
-	if (res) {
-		wpa_printf(MSG_DEBUG,
-			   "nl80211: neigh set destination addr failed");
+
+	res = nl_send_auto_complete(drv->rtnl_sk, msg);
+	if (res < 0)
 		goto errout;
-	}
 
-	/* set the corresponding lladdr for neigh */
-	nl_lladdr = nl_addr_build(AF_BRIDGE, (u8 *) addr, ETH_ALEN);
-	if (nl_lladdr == NULL) {
-		wpa_printf(MSG_DEBUG, "nl80211: neigh set lladdr failed");
-		res = -ENOMEM;
-		goto errout;
-	}
-	rtnl_neigh_set_lladdr(rn, nl_lladdr);
-
-	rtnl_neigh_set_ifindex(rn, bss->br_ifindex);
-	rtnl_neigh_set_state(rn, NUD_PERMANENT);
-
-	res = rtnl_neigh_add(drv->rtnl_sk, rn, NLM_F_CREATE);
+	res = nl_wait_for_ack(drv->rtnl_sk);
 	if (res) {
 		wpa_printf(MSG_DEBUG,
 			   "nl80211: Adding bridge ip neigh failed: %s",
 			   nl_geterror(res));
 	}
 errout:
-	if (nl_lladdr)
-		nl_addr_put(nl_lladdr);
-	if (nl_ipaddr)
-		nl_addr_put(nl_ipaddr);
-	if (rn)
-		rtnl_neigh_put(rn);
+	nlmsg_free(msg);
 	return res;
-#else /* CONFIG_LIBNL3_ROUTE */
-	return -1;
-#endif /* CONFIG_LIBNL3_ROUTE */
 }
 
 
 static int wpa_driver_br_delete_ip_neigh(void *priv, u8 version,
 					 const u8 *ipaddr)
 {
-#ifdef CONFIG_LIBNL3_ROUTE
 	struct i802_bss *bss = priv;
 	struct wpa_driver_nl80211_data *drv = bss->drv;
-	struct rtnl_neigh *rn;
-	struct nl_addr *nl_ipaddr;
-	int family, addrsize;
+	struct ndmsg nhdr = {
+		.ndm_state = NUD_PERMANENT,
+		.ndm_ifindex = bss->br_ifindex,
+	};
+	struct nl_msg *msg;
+	int addrsize;
 	int res;
 
 	if (!ipaddr)
 		return -EINVAL;
 
 	if (version == 4) {
-		family = AF_INET;
+		nhdr.ndm_family = AF_INET;
 		addrsize = 4;
 	} else if (version == 6) {
-		family = AF_INET6;
+		nhdr.ndm_family = AF_INET6;
 		addrsize = 16;
 	} else {
 		return -EINVAL;
@@ -12369,41 +12363,28 @@
 		return -1;
 	}
 
-	rn = rtnl_neigh_alloc();
-	if (rn == NULL)
+	msg = nlmsg_alloc_simple(RTM_DELNEIGH, NLM_F_CREATE);
+	if (!msg)
 		return -ENOMEM;
 
-	/* set the destination ip address for neigh */
-	nl_ipaddr = nl_addr_build(family, (void *) ipaddr, addrsize);
-	if (nl_ipaddr == NULL) {
-		wpa_printf(MSG_DEBUG, "nl80211: nl_ipaddr build failed");
-		res = -ENOMEM;
+	res = -ENOMEM;
+	if (nlmsg_append(msg, &nhdr, sizeof(nhdr), NLMSG_ALIGNTO) < 0 ||
+	    nla_put(msg, NDA_DST, addrsize, (void *) ipaddr))
 		goto errout;
-	}
-	res = rtnl_neigh_set_dst(rn, nl_ipaddr);
-	if (res) {
-		wpa_printf(MSG_DEBUG,
-			   "nl80211: neigh set destination addr failed");
+
+	res = nl_send_auto_complete(drv->rtnl_sk, msg);
+	if (res < 0)
 		goto errout;
-	}
 
-	rtnl_neigh_set_ifindex(rn, bss->br_ifindex);
-
-	res = rtnl_neigh_delete(drv->rtnl_sk, rn, 0);
+	res = nl_wait_for_ack(drv->rtnl_sk);
 	if (res) {
 		wpa_printf(MSG_DEBUG,
 			   "nl80211: Deleting bridge ip neigh failed: %s",
 			   nl_geterror(res));
 	}
 errout:
-	if (nl_ipaddr)
-		nl_addr_put(nl_ipaddr);
-	if (rn)
-		rtnl_neigh_put(rn);
+	nlmsg_free(msg);
 	return res;
-#else /* CONFIG_LIBNL3_ROUTE */
-	return -1;
-#endif /* CONFIG_LIBNL3_ROUTE */
 }
 
 
@@ -13986,12 +13967,12 @@
 }
 #endif /* CONFIG_DRIVER_NL80211_BRCM || CONFIG_DRIVER_NL80211_SYNA */
 
-static int nl80211_link_add(void *priv, u8 link_id, const u8 *addr)
+static int nl80211_link_add(void *priv, u8 link_id, const u8 *addr,
+			    void *bss_ctx)
 {
 	struct i802_bss *bss = priv;
 	struct wpa_driver_nl80211_data *drv = bss->drv;
 	struct nl_msg *msg;
-	unsigned int idx, i;
 	int ret;
 
 	wpa_printf(MSG_DEBUG, "nl80211: MLD: add link_id=%u, addr=" MACSTR,
@@ -14004,32 +13985,24 @@
 		return -EINVAL;
 	}
 
-	if (bss->n_links >= MAX_NUM_MLD_LINKS) {
-		wpa_printf(MSG_DEBUG, "nl80211: MLD: already have n_links=%zu",
-			   bss->n_links);
+	if (link_id >= MAX_NUM_MLD_LINKS) {
+		wpa_printf(MSG_DEBUG,
+			   "nl80211: invalid link_id=%u", link_id);
 		return -EINVAL;
 	}
 
-	for (i = 0; i < bss->n_links; i++) {
-		if (bss->links[i].link_id == link_id &&
-		    bss->links[i].beacon_set) {
-			wpa_printf(MSG_DEBUG,
-				   "nl80211: MLD: link already set");
-			return -EINVAL;
-		}
+	if (bss->valid_links & BIT(link_id)) {
+		wpa_printf(MSG_DEBUG,
+			   "nl80211: MLD: Link %u already set", link_id);
+		return -EINVAL;
 	}
 
-	/* try using the first link entry, assuming it is not beaconing yet */
-	if (bss->n_links == 1 &&
-	    bss->flink->link_id == NL80211_DRV_LINK_ID_NA) {
+	if (!bss->valid_links) {
+		/* Becoming MLD, verify we were not beaconing */
 		if (bss->flink->beacon_set) {
 			wpa_printf(MSG_DEBUG, "nl80211: BSS already beaconing");
 			return -EINVAL;
 		}
-
-		idx = 0;
-	} else {
-		idx = bss->n_links;
 	}
 
 	msg = nl80211_drv_msg(drv, 0, NL80211_CMD_ADD_LINK);
@@ -14047,16 +14020,51 @@
 		return ret;
 	}
 
-	bss->links[idx].link_id = link_id;
-	os_memcpy(bss->links[idx].addr, addr, ETH_ALEN);
+	os_memcpy(bss->links[link_id].addr, addr, ETH_ALEN);
 
-	bss->n_links = idx + 1;
+	/* The new link is the first one, make it the default */
+	if (!bss->valid_links)
+		bss->flink = &bss->links[link_id];
 
-	wpa_printf(MSG_DEBUG, "nl80211: MLD: n_links=%zu", bss->n_links);
+	bss->valid_links |= BIT(link_id);
+	bss->links[link_id].ctx = bss_ctx;
+
+	wpa_printf(MSG_DEBUG, "nl80211: MLD: valid_links=0x%04x",
+		   bss->valid_links);
 	return 0;
 }
 
 
+#ifdef CONFIG_IEEE80211BE
+static int wpa_driver_nl80211_link_sta_remove(void *priv, u8 link_id,
+					      const u8 *addr)
+{
+	struct i802_bss *bss = priv;
+	struct wpa_driver_nl80211_data *drv = bss->drv;
+	struct nl_msg *msg;
+	int ret;
+
+	if (!(bss->valid_links & BIT(link_id)))
+		return -ENOLINK;
+
+	if (!(msg = nl80211_bss_msg(bss, 0, NL80211_CMD_REMOVE_LINK_STA)) ||
+	    nla_put(msg, NL80211_ATTR_MLD_ADDR, ETH_ALEN, addr) ||
+	    nla_put_u8(msg, NL80211_ATTR_MLO_LINK_ID, link_id)) {
+		nlmsg_free(msg);
+		return -ENOBUFS;
+	}
+
+	ret = send_and_recv_cmd(drv, msg);
+	wpa_printf(MSG_DEBUG,
+		   "nl80211: link_sta_remove -> REMOVE_LINK_STA on link_id %u from MLD STA "
+		   MACSTR ", from %s --> %d (%s)",
+		   link_id, MAC2STR(addr), bss->ifname, ret, strerror(-ret));
+
+	return ret;
+}
+#endif /* CONFIG_IEEE80211BE */
+
+
 #ifdef CONFIG_TESTING_OPTIONS
 
 static int testing_nl80211_register_frame(void *priv, u16 type,
@@ -14111,7 +14119,7 @@
 	.scan2 = driver_nl80211_scan2,
 	.sched_scan = wpa_driver_nl80211_sched_scan,
 	.stop_sched_scan = wpa_driver_nl80211_stop_sched_scan,
-	.get_scan_results2 = wpa_driver_nl80211_get_scan_results,
+	.get_scan_results = wpa_driver_nl80211_get_scan_results,
 	.abort_scan = wpa_driver_nl80211_abort_scan,
 	.deauthenticate = driver_nl80211_deauthenticate,
 	.authenticate = driver_nl80211_authenticate,
@@ -14251,6 +14259,11 @@
 #endif /* CONFIG_DPP */
 	.get_sta_mlo_info = nl80211_get_sta_mlo_info,
 	.link_add = nl80211_link_add,
+#ifdef CONFIG_IEEE80211BE
+	.link_remove = driver_nl80211_link_remove,
+	.is_drv_shared = nl80211_is_drv_shared,
+	.link_sta_remove = wpa_driver_nl80211_link_sta_remove,
+#endif /* CONFIG_IEEE80211BE */
 #ifdef CONFIG_TESTING_OPTIONS
 	.register_frame = testing_nl80211_register_frame,
 	.radio_disable = testing_nl80211_radio_disable,