Revert "[wpa_supplicant] cumilative patch from commit 4b755c967"

Revert submission 26533062-Supplicant_merge_June24

Reason for revert: https://b.corp.google.com/issues/349780869

Reverted changes: /q/submissionid:26533062-Supplicant_merge_June24
(cherry picked from https://googleplex-android-review.googlesource.com/q/commit:88611417f21e6329aadf8f0e300392dd3738e804)
Merged-In: I6c9b7a4323fa7edde47617da6c1e0d8f6e6d5101
Change-Id: I6c9b7a4323fa7edde47617da6c1e0d8f6e6d5101
diff --git a/src/drivers/driver.h b/src/drivers/driver.h
index 069e741..a55e8e3 100644
--- a/src/drivers/driver.h
+++ b/src/drivers/driver.h
@@ -43,7 +43,6 @@
 
 #define HOSTAPD_CHAN_VHT_80MHZ_SUBCHANNEL 0x00000800
 #define HOSTAPD_CHAN_VHT_160MHZ_SUBCHANNEL 0x00001000
-#define HOSTAPD_CHAN_EHT_320MHZ_SUBCHANNEL 0x00002000
 
 #define HOSTAPD_CHAN_INDOOR_ONLY 0x00010000
 #define HOSTAPD_CHAN_GO_CONCURRENT 0x00020000
@@ -248,11 +247,6 @@
 	enum hostapd_hw_mode mode;
 
 	/**
-	 * is_6ghz - Whether the mode information is for the 6 GHz band
-	 */
-	bool is_6ghz;
-
-	/**
 	 * num_channels - Number of entries in the channels array
 	 */
 	int num_channels;
@@ -701,14 +695,6 @@
 	 */
 	unsigned int min_probe_req_content:1;
 
-	/**
-	 * link_id - Specify the link that is requesting the scan on an MLD
-	 *
-	 * This is set when operating as an AP MLD and doing an OBSS scan.
-	 * -1 indicates that no particular link ID is set.
-	 */
-	s8 link_id;
-
 	/*
 	 * NOTE: Whenever adding new parameters here, please make sure
 	 * wpa_scan_clone_params() and wpa_scan_free_params() get updated with
@@ -3385,17 +3371,6 @@
 			     size_t ies_len);
 
 	/**
-	 * get_scan_results - Fetch the latest scan results
-	 * @priv: Private driver interface data
-	 * @bssid: Return results only for the specified BSSID, %NULL for all
-	 *
-	 * Returns: Allocated buffer of scan results (caller is responsible for
-	 * freeing the data structure) on success, NULL on failure
-	 */
-	struct wpa_scan_results * (*get_scan_results)(void *priv,
-						      const u8 *bssid);
-
-	/**
 	 * get_scan_results2 - Fetch the latest scan results
 	 * @priv: private driver interface data
 	 *
@@ -4579,14 +4554,13 @@
 	/**
 	 * stop_ap - Removes beacon from AP
 	 * @priv: Private driver interface data
-	 * @link_id: Link ID of the specified link; -1 for non-MLD
 	 * Returns: 0 on success, -1 on failure (or if not supported)
 	 *
 	 * This optional function can be used to disable AP mode related
 	 * configuration. Unlike deinit_ap, it does not change to station
 	 * mode.
 	 */
-	int (*stop_ap)(void *priv, int link_id);
+	int (*stop_ap)(void *priv);
 
 	/**
 	 * get_survey - Retrieve survey data
@@ -5166,44 +5140,9 @@
 	 * @priv: Private driver interface data
 	 * @link_id: The link ID
 	 * @addr: The MAC address to use for the link
-	 * @bss_ctx: BSS context for %WPA_IF_AP_BSS interfaces
 	 * Returns: 0 on success, negative value on failure
 	 */
-	int (*link_add)(void *priv, u8 link_id, const u8 *addr, void *bss_ctx);
-
-	/**
-	 * link_remove - Remove a link from the AP MLD interface
-	 * @priv: Private driver interface data
-	 * @type: Interface type
-	 * @ifname: Interface name of the virtual interface from where the link
-	 *	is to be removed.
-	 * @link_id: Valid link ID to remove
-	 * Returns: 0 on success, -1 on failure
-	 */
-	int (*link_remove)(void *priv, enum wpa_driver_if_type type,
-			   const char *ifname, u8 link_id);
-
-	/**
-	 * is_drv_shared - Check whether the driver interface is shared
-	 * @priv: Private driver interface data from init()
-	 * @bss_ctx: BSS context for %WPA_IF_AP_BSS interfaces
-	 *
-	 * Checks whether the driver interface is being used by other partner
-	 * BSS(s) or not. This is used to decide whether the driver interface
-	 * needs to be deinitilized when one interface is getting deinitialized.
-	 *
-	 * Returns: true if it is being used or else false.
-	 */
-	bool (*is_drv_shared)(void *priv, void *bss_ctx);
-
-	/**
-	 * link_sta_remove - Remove a link STA from an MLD STA
-	 * @priv: Private driver interface data
-	 * @link_id: The link ID which the link STA is using
-	 * @addr: The MLD MAC address of the MLD STA
-	 * Returns: 0 on success, negative value on failure
-	 */
-	int (*link_sta_remove)(void *priv, u8 link_id, const u8 *addr);
+	int (*link_add)(void *priv, u8 link_id, const u8 *addr);
 
 #ifdef CONFIG_TESTING_OPTIONS
 	int (*register_frame)(void *priv, u16 type,
@@ -6413,8 +6352,6 @@
 	 *	(if available).
 	 * @scan_start_tsf_bssid: The BSSID according to which %scan_start_tsf
 	 *	is set.
-	 * @scan_cookie: Unique identification representing the corresponding
-	 *      scan request. 0 if no unique identification is available.
 	 */
 	struct scan_info {
 		int aborted;
@@ -6426,7 +6363,6 @@
 		int nl_scan_event;
 		u64 scan_start_tsf;
 		u8 scan_start_tsf_bssid[ETH_ALEN];
-		u64 scan_cookie;
 	} scan_info;
 
 	/**
diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c
index 194a5cd..9b50b6f 100644
--- a/src/drivers/driver_nl80211.c
+++ b/src/drivers/driver_nl80211.c
@@ -18,6 +18,9 @@
 #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>
@@ -2292,6 +2295,7 @@
 {
 	struct wpa_driver_nl80211_data *drv;
 	struct i802_bss *bss;
+	unsigned int i;
 	char path[128], buf[200], *pos;
 	ssize_t len;
 	int ret;
@@ -2391,12 +2395,16 @@
 	}
 
 	/*
-	 * Use link ID 0 for the single "link" of a non-MLD.
+	 * Set the default link to be the first one, and set its address to that
+	 * of the interface.
 	 */
-	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:
@@ -3071,31 +3079,31 @@
 
 
 static int wpa_driver_nl80211_del_beacon(struct i802_bss *bss,
-					 int link_id)
+					 struct i802_link *link)
 {
 	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)",
-		   bss->ifindex);
+		   drv->ifindex);
 	link->beacon_set = 0;
 	link->freq = 0;
 
 	nl80211_put_wiphy_data_ap(bss);
-	msg = nl80211_bss_msg(bss, 0, NL80211_CMD_DEL_BEACON);
+	msg = nl80211_drv_msg(drv, 0, NL80211_CMD_DEL_BEACON);
 	if (!msg)
 		return -ENOBUFS;
 
-	if (link_id != NL80211_DRV_LINK_ID_NA) {
+	if (link->link_id != NL80211_DRV_LINK_ID_NA) {
 		wpa_printf(MSG_DEBUG,
 			   "nl80211: MLD: stop beaconing on link=%u",
-			   link_id);
+			   link->link_id);
 
-		if (nla_put_u8(msg, NL80211_ATTR_MLO_LINK_ID, link_id)) {
+		if (nla_put_u8(msg, NL80211_ATTR_MLO_LINK_ID,
+			       link->link_id)) {
 			nlmsg_free(msg);
 			return -ENOBUFS;
 		}
@@ -3107,10 +3115,10 @@
 
 static void wpa_driver_nl80211_del_beacon_all(struct i802_bss *bss)
 {
-	int link_id;
+	unsigned int i;
 
-	for_each_link_default(bss->valid_links, link_id, NL80211_DRV_LINK_ID_NA)
-		wpa_driver_nl80211_del_beacon(bss, link_id);
+	for (i = 0; i < bss->n_links; i++)
+		wpa_driver_nl80211_del_beacon(bss, &bss->links[i]);
 }
 
 
@@ -4250,11 +4258,14 @@
 
 struct i802_link * nl80211_get_link(struct i802_bss *bss, s8 link_id)
 {
-	if (link_id < 0 || link_id >= MAX_NUM_MLD_LINKS)
-		return bss->flink;
+	unsigned int i;
 
-	if (BIT(link_id) & bss->valid_links)
-		return &bss->links[link_id];
+	for (i = 0; i < bss->n_links; i++) {
+		if (bss->links[i].link_id != link_id)
+			continue;
+
+		return &bss->links[i];
+	}
 
 	return bss->flink;
 }
@@ -4271,9 +4282,9 @@
 static int nl80211_get_link_freq(struct i802_bss *bss, const u8 *addr,
 				 bool bss_freq_debug)
 {
-	u8 i;
+	size_t i;
 
-	for_each_link(bss->valid_links, i) {
+	for (i = 0; i < bss->n_links; i++) {
 		if (ether_addr_equal(bss->links[i].addr, addr)) {
 			wpa_printf(MSG_DEBUG,
 				   "nl80211: Use link freq=%d for address "
@@ -5114,18 +5125,20 @@
 #endif /* CONFIG_MESH */
 
 	if (params->mld_ap) {
-		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;
+		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;
+			}
 		}
 
-		link = nl80211_get_link(bss, params->mld_link_id);
-	} else if (bss->valid_links) {
-		wpa_printf(MSG_DEBUG, "nl80211: MLD configuration expected");
-		return -EINVAL;
+		if (i == bss->n_links) {
+			wpa_printf(MSG_DEBUG, "nl80211: Link ID=%u not found",
+				   params->mld_link_id);
+			return -EINVAL;
+		}
 	}
 
 	beacon_set = params->reenable ? 0 : link->beacon_set;
@@ -5166,12 +5179,13 @@
 			   params->mld_link_id);
 
 		if (nla_put_u8(msg, NL80211_ATTR_MLO_LINK_ID,
-			       params->mld_link_id))
+			       params->mld_link_id) ||
+		    (params->freq &&
+		     nl80211_put_freq_params(msg, params->freq) < 0))
 			goto fail;
 
-		if (params->freq)
-			nl80211_link_set_freq(bss, params->mld_link_id,
-					      params->freq->freq);
+		nl80211_link_set_freq(bss, params->mld_link_id,
+				      params->freq->freq);
 	}
 
 	if (params->proberesp && params->proberesp_len) {
@@ -5379,9 +5393,6 @@
 		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;
@@ -5416,6 +5427,9 @@
 		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;
@@ -5534,6 +5548,27 @@
 }
 
 
+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)
 {
@@ -5554,7 +5589,7 @@
 		return -1;
 	}
 
-	if (nl80211_link_valid(bss->valid_links, freq->link_id)) {
+	if (nl80211_link_valid(bss, freq->link_id)) {
 		wpa_printf(MSG_DEBUG, "nl80211: Set link_id=%u for freq",
 			   freq->link_id);
 
@@ -5920,25 +5955,26 @@
 
 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 ndmsg nhdr = {
-		.ndm_state = NUD_PERMANENT,
-		.ndm_ifindex = bss->ifindex,
-		.ndm_family = AF_BRIDGE,
-	};
-	struct nl_msg *msg;
+	struct rtnl_neigh *rn;
+	struct nl_addr *nl_addr;
 	int err;
 
-	msg = nlmsg_alloc_simple(RTM_DELNEIGH, NLM_F_CREATE);
-	if (!msg)
+	rn = rtnl_neigh_alloc();
+	if (!rn)
 		return;
 
-	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;
+	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);
 
-	err = nl_wait_for_ack(drv->rtnl_sk);
+	err = rtnl_neigh_delete(drv->rtnl_sk, rn, 0);
 	if (err < 0) {
 		wpa_printf(MSG_DEBUG, "nl80211: bridge FDB entry delete for "
 			   MACSTR " ifindex=%d failed: %s", MAC2STR(addr),
@@ -5948,8 +5984,9 @@
 			   MACSTR, MAC2STR(addr));
 	}
 
-errout:
-	nlmsg_free(msg);
+	nl_addr_put(nl_addr);
+	rtnl_neigh_put(rn);
+#endif /* CONFIG_LIBNL3_ROUTE */
 }
 
 
@@ -6746,6 +6783,7 @@
 	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,
@@ -6761,8 +6799,31 @@
 		if (!links)
 			return -1;
 
-		for_each_link(mld_params->valid_links, link_id) {
-			attr = nla_nest_start(msg, 0);
+		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);
 			if (!attr)
 				return -1;
 
@@ -6772,11 +6833,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[link_id].disabled &&
+			    (mld_params->mld_links[i].disabled &&
 			     nla_put_flag(msg,
 					  NL80211_ATTR_MLO_LINK_DISABLED)) ||
 			    (mld_params->mld_links[link_id].ies &&
-			     mld_params->mld_links[link_id].ies_len &&
+			     mld_params->mld_links[i].ies_len &&
 			     nla_put(msg, NL80211_ATTR_IE,
 				     mld_params->mld_links[link_id].ies_len,
 				     mld_params->mld_links[link_id].ies)))
@@ -6786,6 +6847,7 @@
 				  mld_params->mld_links[link_id].bssid,
 				  ETH_ALEN);
 			nla_nest_end(msg, attr);
+			i++;
 		}
 
 		nla_nest_end(msg, links);
@@ -7362,7 +7424,10 @@
 
 		/* Error and force TEST_FAIL checking for each link */
 		ret = -EINVAL;
-		for_each_link(params->mld_params.valid_links, i) {
+		for (i = 0; i < MAX_NUM_MLD_LINKS; i++) {
+			if (!(params->mld_params.valid_links & BIT(i)))
+				continue;
+
 			if (TEST_FAIL_TAG("link"))
 				err_info.link_id = i;
 		}
@@ -8697,6 +8762,7 @@
 	    (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;
 
@@ -8713,6 +8779,7 @@
 			goto failed;
 		}
 	}
+#endif /* CONFIG_LIBNL3_ROUTE */
 
 	if (drv->capa.flags2 & WPA_DRIVER_FLAGS2_CONTROL_PORT_RX) {
 		wpa_printf(MSG_DEBUG,
@@ -8937,6 +9004,7 @@
 
 	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)
@@ -8944,6 +9012,10 @@
 			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 "
@@ -8968,7 +9040,7 @@
 		new_bss->drv = drv;
 		new_bss->next = drv->first_bss->next;
 		new_bss->flink = &new_bss->links[0];
-		new_bss->valid_links = 0;
+		new_bss->n_links = 1;
 		os_memcpy(new_bss->flink->addr, new_bss->addr, ETH_ALEN);
 
 		new_bss->flink->freq = drv->first_bss->flink->freq;
@@ -9048,7 +9120,6 @@
 				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);
@@ -9063,7 +9134,6 @@
 	} 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);
@@ -9072,7 +9142,6 @@
 		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");
@@ -9313,7 +9382,10 @@
 		return 0;
 
 	/* First try to pick a link that uses the same band */
-	for_each_link(mlo->valid_links, i) {
+	for (i = 0; i < MAX_NUM_MLD_LINKS; i++) {
+		if (!(mlo->valid_links & BIT(i)))
+			continue;
+
 		if (any_valid_link_id == -1)
 			any_valid_link_id = i;
 
@@ -9508,81 +9580,59 @@
 }
 
 
-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)
 {
+	struct wpa_driver_nl80211_data *drv = bss->drv;
+	struct nl_msg *msg;
 	int ret;
 	u8 link_id;
 
-	for_each_link(bss->valid_links, link_id) {
-		ret = nl80211_remove_link(bss, link_id);
-		if (ret)
-			break;
-	}
+	if (bss->n_links == 0)
+		return;
 
-	if (bss->flink)
-		os_memcpy(bss->flink->addr, bss->addr, ETH_ALEN);
+	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;
+		}
+	}
 }
 
 
@@ -9595,7 +9645,7 @@
 		return -1;
 
 	/* Stop beaconing */
-	wpa_driver_nl80211_del_beacon(bss, NL80211_DRV_LINK_ID_NA);
+	wpa_driver_nl80211_del_beacon(bss, bss->flink);
 
 	nl80211_remove_links(bss);
 
@@ -9610,7 +9660,7 @@
 }
 
 
-static int wpa_driver_nl80211_stop_ap(void *priv, int link_id)
+static int wpa_driver_nl80211_stop_ap(void *priv)
 {
 	struct i802_bss *bss = priv;
 	struct wpa_driver_nl80211_data *drv = bss->drv;
@@ -9618,17 +9668,9 @@
 	if (!is_ap_interface(drv->nlmode))
 		return -1;
 
-	if (link_id == -1) {
-		wpa_driver_nl80211_del_beacon_all(bss);
-		return 0;
-	}
+	wpa_driver_nl80211_del_beacon_all(bss);
 
-	if (nl80211_link_valid(bss->valid_links, link_id)) {
-		wpa_driver_nl80211_del_beacon(bss, link_id);
-		return 0;
-	}
-
-	return -1;
+	return 0;
 }
 
 
@@ -9781,7 +9823,10 @@
 	if (!sinfo[NL80211_SURVEY_INFO_NOISE])
 		return NL_SKIP;
 
-	for_each_link(mlo_sig->valid_links, i) {
+	for (i = 0; i < MAX_NUM_MLD_LINKS; i++) {
+		if (!(mlo_sig->valid_links & BIT(i)))
+			continue;
+
 		if (nla_get_u32(sinfo[NL80211_SURVEY_INFO_FREQUENCY]) !=
 		    mlo_sig->links[i].frequency)
 			continue;
@@ -9874,7 +9919,10 @@
 	os_memset(mlo_si, 0, sizeof(*mlo_si));
 	mlo_si->valid_links = drv->sta_mlo_info.valid_links;
 
-	for_each_link(mlo_si->valid_links, i) {
+	for (i = 0; i < MAX_NUM_MLD_LINKS; i++) {
+		if (!(mlo_si->valid_links & BIT(i)))
+			continue;
+
 		res = nl80211_get_link_signal(drv,
 					      drv->sta_mlo_info.links[i].bssid,
 					      &mlo_si->links[i].data);
@@ -10798,73 +10846,6 @@
 }
 
 
-#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,
@@ -11133,7 +11114,10 @@
 			return pos - buf;
 		pos += res;
 
-		for_each_link(mlo->valid_links, i) {
+		for (i = 0; i < MAX_NUM_MLD_LINKS; i++) {
+			if (!(mlo->valid_links & BIT(i)))
+				continue;
+
 			res = os_snprintf(pos, end - pos,
 					  "link_addr[%u]=" MACSTR "\n"
 					  "link_bssid[%u]=" MACSTR "\n"
@@ -12264,14 +12248,13 @@
 				      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 ndmsg nhdr = {
-		.ndm_state = NUD_PERMANENT,
-		.ndm_ifindex = bss->br_ifindex,
-	};
-	struct nl_msg *msg;
-	int addrsize;
+	struct rtnl_neigh *rn;
+	struct nl_addr *nl_ipaddr = NULL;
+	struct nl_addr *nl_lladdr = NULL;
+	int family, addrsize;
 	int res;
 
 	if (!ipaddr || prefixlen == 0 || !addr)
@@ -12290,62 +12273,85 @@
 	}
 
 	if (version == 4) {
-		nhdr.ndm_family = AF_INET;
+		family = AF_INET;
 		addrsize = 4;
 	} else if (version == 6) {
-		nhdr.ndm_family = AF_INET6;
+		family = AF_INET6;
 		addrsize = 16;
 	} else {
 		return -EINVAL;
 	}
 
-	msg = nlmsg_alloc_simple(RTM_NEWNEIGH, NLM_F_CREATE);
-	if (!msg)
+	rn = rtnl_neigh_alloc();
+	if (rn == NULL)
 		return -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))
+	/* 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;
 		goto errout;
-
-	res = nl_send_auto_complete(drv->rtnl_sk, msg);
-	if (res < 0)
+	}
+	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");
 		goto errout;
+	}
 
-	res = nl_wait_for_ack(drv->rtnl_sk);
+	/* 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);
 	if (res) {
 		wpa_printf(MSG_DEBUG,
 			   "nl80211: Adding bridge ip neigh failed: %s",
 			   nl_geterror(res));
 	}
 errout:
-	nlmsg_free(msg);
+	if (nl_lladdr)
+		nl_addr_put(nl_lladdr);
+	if (nl_ipaddr)
+		nl_addr_put(nl_ipaddr);
+	if (rn)
+		rtnl_neigh_put(rn);
 	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 ndmsg nhdr = {
-		.ndm_state = NUD_PERMANENT,
-		.ndm_ifindex = bss->br_ifindex,
-	};
-	struct nl_msg *msg;
-	int addrsize;
+	struct rtnl_neigh *rn;
+	struct nl_addr *nl_ipaddr;
+	int family, addrsize;
 	int res;
 
 	if (!ipaddr)
 		return -EINVAL;
 
 	if (version == 4) {
-		nhdr.ndm_family = AF_INET;
+		family = AF_INET;
 		addrsize = 4;
 	} else if (version == 6) {
-		nhdr.ndm_family = AF_INET6;
+		family = AF_INET6;
 		addrsize = 16;
 	} else {
 		return -EINVAL;
@@ -12363,28 +12369,41 @@
 		return -1;
 	}
 
-	msg = nlmsg_alloc_simple(RTM_DELNEIGH, NLM_F_CREATE);
-	if (!msg)
+	rn = rtnl_neigh_alloc();
+	if (rn == NULL)
 		return -ENOMEM;
 
-	res = -ENOMEM;
-	if (nlmsg_append(msg, &nhdr, sizeof(nhdr), NLMSG_ALIGNTO) < 0 ||
-	    nla_put(msg, NDA_DST, addrsize, (void *) ipaddr))
+	/* 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;
 		goto errout;
-
-	res = nl_send_auto_complete(drv->rtnl_sk, msg);
-	if (res < 0)
+	}
+	res = rtnl_neigh_set_dst(rn, nl_ipaddr);
+	if (res) {
+		wpa_printf(MSG_DEBUG,
+			   "nl80211: neigh set destination addr failed");
 		goto errout;
+	}
 
-	res = nl_wait_for_ack(drv->rtnl_sk);
+	rtnl_neigh_set_ifindex(rn, bss->br_ifindex);
+
+	res = rtnl_neigh_delete(drv->rtnl_sk, rn, 0);
 	if (res) {
 		wpa_printf(MSG_DEBUG,
 			   "nl80211: Deleting bridge ip neigh failed: %s",
 			   nl_geterror(res));
 	}
 errout:
-	nlmsg_free(msg);
+	if (nl_ipaddr)
+		nl_addr_put(nl_ipaddr);
+	if (rn)
+		rtnl_neigh_put(rn);
 	return res;
+#else /* CONFIG_LIBNL3_ROUTE */
+	return -1;
+#endif /* CONFIG_LIBNL3_ROUTE */
 }
 
 
@@ -13967,12 +13986,12 @@
 }
 #endif /* CONFIG_DRIVER_NL80211_BRCM || CONFIG_DRIVER_NL80211_SYNA */
 
-static int nl80211_link_add(void *priv, u8 link_id, const u8 *addr,
-			    void *bss_ctx)
+static int nl80211_link_add(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;
+	unsigned int idx, i;
 	int ret;
 
 	wpa_printf(MSG_DEBUG, "nl80211: MLD: add link_id=%u, addr=" MACSTR,
@@ -13985,24 +14004,32 @@
 		return -EINVAL;
 	}
 
-	if (link_id >= MAX_NUM_MLD_LINKS) {
-		wpa_printf(MSG_DEBUG,
-			   "nl80211: invalid link_id=%u", link_id);
+	if (bss->n_links >= MAX_NUM_MLD_LINKS) {
+		wpa_printf(MSG_DEBUG, "nl80211: MLD: already have n_links=%zu",
+			   bss->n_links);
 		return -EINVAL;
 	}
 
-	if (bss->valid_links & BIT(link_id)) {
-		wpa_printf(MSG_DEBUG,
-			   "nl80211: MLD: Link %u already set", 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) {
-		/* Becoming MLD, verify we were not beaconing */
+	/* 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->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);
@@ -14020,51 +14047,16 @@
 		return ret;
 	}
 
-	os_memcpy(bss->links[link_id].addr, addr, ETH_ALEN);
+	bss->links[idx].link_id = link_id;
+	os_memcpy(bss->links[idx].addr, addr, ETH_ALEN);
 
-	/* The new link is the first one, make it the default */
-	if (!bss->valid_links)
-		bss->flink = &bss->links[link_id];
+	bss->n_links = idx + 1;
 
-	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);
+	wpa_printf(MSG_DEBUG, "nl80211: MLD: n_links=%zu", bss->n_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,
@@ -14119,7 +14111,7 @@
 	.scan2 = driver_nl80211_scan2,
 	.sched_scan = wpa_driver_nl80211_sched_scan,
 	.stop_sched_scan = wpa_driver_nl80211_stop_sched_scan,
-	.get_scan_results = wpa_driver_nl80211_get_scan_results,
+	.get_scan_results2 = wpa_driver_nl80211_get_scan_results,
 	.abort_scan = wpa_driver_nl80211_abort_scan,
 	.deauthenticate = driver_nl80211_deauthenticate,
 	.authenticate = driver_nl80211_authenticate,
@@ -14259,11 +14251,6 @@
 #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,
diff --git a/src/drivers/driver_nl80211.h b/src/drivers/driver_nl80211.h
index d2c1ffa..048c9a3 100644
--- a/src/drivers/driver_nl80211.h
+++ b/src/drivers/driver_nl80211.h
@@ -55,6 +55,7 @@
 struct i802_link {
 	unsigned int beacon_set:1;
 
+	s8 link_id;
 	int freq;
 	int bandwidth;
 	u8 addr[ETH_ALEN];
@@ -65,7 +66,7 @@
 	struct wpa_driver_nl80211_data *drv;
 	struct i802_bss *next;
 
-	u16 valid_links;
+	size_t n_links;
 	struct i802_link links[MAX_NUM_MLD_LINKS];
 	struct i802_link *flink;
 
@@ -291,21 +292,6 @@
 		  void *ack_data,
 		  struct nl80211_err_info *err_info);
 
-// This function is not used in supplicant anymore. But keeping this wrapper
-// functions for libraries outside wpa_supplicant to build (For eg: lib_driver_cmd_XX)
-static inline int
-send_and_recv_msgs(struct wpa_driver_nl80211_data *drv,
-		   struct nl_msg *msg,
-		   int (*valid_handler)(struct nl_msg *, void *),
-		   void *valid_data,
-		   int (*ack_handler_custom)(struct nl_msg *, void *),
-		   void *ack_data)
-{
-	return send_and_recv(drv->global, drv->global->nl, msg,
-			     valid_handler, valid_data,
-			     ack_handler_custom, ack_data, NULL);
-}
-
 static inline int
 send_and_recv_cmd(struct wpa_driver_nl80211_data *drv,
 		  struct nl_msg *msg)
@@ -371,18 +357,6 @@
 void nl80211_restore_ap_mode(struct i802_bss *bss);
 struct i802_link * nl80211_get_link(struct i802_bss *bss, s8 link_id);
 
-static inline bool nl80211_link_valid(u16 links, s8 link_id)
-{
-	if (link_id < 0 || link_id >= MAX_NUM_MLD_LINKS)
-		return false;
-
-	if (links & BIT(link_id))
-		return true;
-
-	return false;
-}
-
-
 static inline bool
 nl80211_attr_supported(struct wpa_driver_nl80211_data *drv, unsigned int attr)
 {
@@ -420,8 +394,7 @@
 int wpa_driver_nl80211_sched_scan(void *priv,
 				  struct wpa_driver_scan_params *params);
 int wpa_driver_nl80211_stop_sched_scan(void *priv);
-struct wpa_scan_results * wpa_driver_nl80211_get_scan_results(void *priv,
-							      const u8 *bssid);
+struct wpa_scan_results * wpa_driver_nl80211_get_scan_results(void *priv);
 void nl80211_dump_scan(struct wpa_driver_nl80211_data *drv);
 int wpa_driver_nl80211_abort_scan(void *priv, u64 scan_cookie);
 int wpa_driver_nl80211_vendor_scan(struct i802_bss *bss,
diff --git a/src/drivers/driver_nl80211_capa.c b/src/drivers/driver_nl80211_capa.c
index d8375cc..0b6f511 100644
--- a/src/drivers/driver_nl80211_capa.c
+++ b/src/drivers/driver_nl80211_capa.c
@@ -888,10 +888,6 @@
 				nla_get_u16(tb1[NL80211_ATTR_MLD_CAPA_AND_OPS]);
 		}
 
-		wpa_printf(MSG_DEBUG,
-			   "nl80211: EML Capability: 0x%x MLD Capability: 0x%x",
-			   capa->eml_capa, capa->mld_capa_and_ops);
-
 		drv->num_iface_capa++;
 		if (drv->num_iface_capa == NL80211_IFTYPE_MAX)
 			break;
@@ -2169,9 +2165,6 @@
 	for (m = 0; m < *num_modes; m++) {
 		if (!modes[m].num_channels)
 			continue;
-
-		modes[m].is_6ghz = false;
-
 		if (modes[m].channels[0].freq < 2000) {
 			modes[m].num_channels = 0;
 			continue;
@@ -2183,14 +2176,10 @@
 					break;
 				}
 			}
-		} else if (modes[m].channels[0].freq > 50000) {
+		} else if (modes[m].channels[0].freq > 50000)
 			modes[m].mode = HOSTAPD_MODE_IEEE80211AD;
-		} else if (is_6ghz_freq(modes[m].channels[0].freq)) {
+		else
 			modes[m].mode = HOSTAPD_MODE_IEEE80211A;
-			modes[m].is_6ghz = true;
-		} else {
-			modes[m].mode = HOSTAPD_MODE_IEEE80211A;
-		}
 	}
 
 	/* Remove unsupported bands */
@@ -2418,57 +2407,6 @@
 }
 
 
-static void nl80211_set_6ghz_mode(struct hostapd_hw_modes *mode, int start,
-				  int end, int max_bw)
-{
-	int c;
-
-	for (c = 0; c < mode->num_channels; c++) {
-		struct hostapd_channel_data *chan = &mode->channels[c];
-
-		if (chan->freq - 10 < start || chan->freq + 10 > end)
-			continue;
-
-		if (max_bw >= 80)
-			chan->flag |= HOSTAPD_CHAN_VHT_80MHZ_SUBCHANNEL;
-
-		if (max_bw >= 160)
-			chan->flag |= HOSTAPD_CHAN_VHT_160MHZ_SUBCHANNEL;
-
-		if (max_bw >= 320)
-			chan->flag |= HOSTAPD_CHAN_EHT_320MHZ_SUBCHANNEL;
-	}
-}
-
-
-static void nl80211_reg_rule_6ghz(struct nlattr *tb[],
-				 struct phy_info_arg *results)
-{
-	u32 start, end, max_bw;
-	u16 m;
-
-	if (!tb[NL80211_ATTR_FREQ_RANGE_START] ||
-	    !tb[NL80211_ATTR_FREQ_RANGE_END] ||
-	    !tb[NL80211_ATTR_FREQ_RANGE_MAX_BW])
-		return;
-
-	start = nla_get_u32(tb[NL80211_ATTR_FREQ_RANGE_START]) / 1000;
-	end = nla_get_u32(tb[NL80211_ATTR_FREQ_RANGE_END]) / 1000;
-	max_bw = nla_get_u32(tb[NL80211_ATTR_FREQ_RANGE_MAX_BW]) / 1000;
-
-	if (max_bw < 80)
-		return;
-
-	for (m = 0; m < *results->num_modes; m++) {
-		if (results->modes[m].num_channels == 0 ||
-		    !is_6ghz_freq(results->modes[m].channels[0].freq))
-			continue;
-
-		nl80211_set_6ghz_mode(&results->modes[m], start, end, max_bw);
-	}
-}
-
-
 static void nl80211_set_dfs_domain(enum nl80211_dfs_regions region,
 				   u8 *dfs_domain)
 {
@@ -2587,13 +2525,6 @@
 		nl80211_reg_rule_vht(tb_rule, results);
 	}
 
-	nla_for_each_nested(nl_rule, tb_msg[NL80211_ATTR_REG_RULES], rem_rule)
-	{
-		nla_parse(tb_rule, NL80211_FREQUENCY_ATTR_MAX,
-			  nla_data(nl_rule), nla_len(nl_rule), reg_policy);
-		nl80211_reg_rule_6ghz(tb_rule, results);
-	}
-
 	return NL_SKIP;
 }
 
diff --git a/src/drivers/driver_nl80211_event.c b/src/drivers/driver_nl80211_event.c
index 9ce73c6..4163f79 100644
--- a/src/drivers/driver_nl80211_event.c
+++ b/src/drivers/driver_nl80211_event.c
@@ -476,7 +476,10 @@
 	 * links when the link used for (re)association is removed.
 	 */
 	if (removed_links & BIT(drv->sta_mlo_info.assoc_link_id)) {
-		for_each_link(drv->sta_mlo_info.valid_links, i) {
+		for (i = 0; i < MAX_NUM_MLD_LINKS; i++) {
+			if (!(drv->sta_mlo_info.valid_links & BIT(i)))
+				continue;
+
 			os_memcpy(drv->bssid, drv->sta_mlo_info.links[i].bssid,
 				  ETH_ALEN);
 			drv->sta_mlo_info.assoc_link_id = i;
@@ -698,7 +701,10 @@
 	}
 
 	/* Get MLO links info for rejected links */
-	for_each_link((mlo->req_links & ~mlo->valid_links), i) {
+	for (i = 0; i < MAX_NUM_MLD_LINKS; i++) {
+		if (!((mlo->req_links & ~mlo->valid_links) & BIT(i)))
+			continue;
+
 		os_memcpy(mlo->links[i].bssid, resp_info.addr[i], ETH_ALEN);
 		os_memcpy(mlo->links[i].addr, req_info.addr[i], ETH_ALEN);
 	}
@@ -868,7 +874,9 @@
 		wpa_printf(MSG_DEBUG,
 			   "nl80211: TID-to-link: Received uplink %x downlink %x",
 			   uplink, downlink);
-		for_each_link(drv->sta_mlo_info.valid_links, i) {
+		for (i = 0; i < MAX_NUM_MLD_LINKS; i++) {
+			if (!(drv->sta_mlo_info.valid_links & BIT(i)))
+				continue;
 			if (uplink & BIT(i))
 				event.t2l_map_info.t2lmap[i].uplink |=
 					BIT(tidnum);
@@ -1260,23 +1268,14 @@
 	if (cf2)
 		data.ch_switch.cf2 = nla_get_u32(cf2);
 
+	if (finished)
+		bss->flink->freq = data.ch_switch.freq;
+
 	if (link)
 		data.ch_switch.link_id = nla_get_u8(link);
 	else
 		data.ch_switch.link_id = NL80211_DRV_LINK_ID_NA;
 
-	if (finished) {
-		if (data.ch_switch.link_id != NL80211_DRV_LINK_ID_NA) {
-			struct i802_link *mld_link;
-
-			mld_link = nl80211_get_link(bss,
-						    data.ch_switch.link_id);
-			mld_link->freq = data.ch_switch.freq;
-		} else {
-			bss->flink->freq = data.ch_switch.freq;
-		}
-	}
-
 	if (link && is_sta_interface(drv->nlmode)) {
 		u8 link_id = data.ch_switch.link_id;
 
@@ -1619,17 +1618,18 @@
 }
 
 
-static s8
-nl80211_get_link_id_by_freq(struct i802_bss *bss, unsigned int freq)
+static struct i802_link *
+nl80211_get_mld_link_by_freq(struct i802_bss *bss, unsigned int freq)
 {
 	unsigned int i;
 
-	for_each_link(bss->valid_links, i) {
-		if ((unsigned int) bss->links[i].freq == freq)
-			return i;
+	for (i = 0; i < bss->n_links; i++) {
+		if ((unsigned int) bss->links[i].freq == freq &&
+		    bss->links[i].link_id != -1)
+			return &bss->links[i];
 	}
 
-	return NL80211_DRV_LINK_ID_NA;
+	return NULL;
 }
 
 
@@ -1663,12 +1663,12 @@
 	/* Determine the MLD link either by an explicitly provided link id or
 	 * finding a match based on the frequency. */
 	if (link)
-		link_id = nla_get_u8(link);
+		mld_link = nl80211_get_link(bss, nla_get_u8(link));
 	else if (freq)
-		link_id = nl80211_get_link_id_by_freq(bss, nla_get_u32(freq));
+		mld_link = nl80211_get_mld_link_by_freq(bss, nla_get_u32(freq));
 
-	if (nl80211_link_valid(bss->valid_links, link_id))
-		mld_link = nl80211_get_link(bss, link_id);
+	if (mld_link)
+		link_id = mld_link->link_id;
 
 	data = nla_data(frame);
 	len = nla_len(frame);
@@ -1920,7 +1920,7 @@
 	os_memset(&data, 0, sizeof(data));
 	addr = nla_data(tb[NL80211_ATTR_MAC]);
 
-	if (!bss->valid_links &&
+	if (bss->links[0].link_id == NL80211_DRV_LINK_ID_NA &&
 	    (tb[NL80211_ATTR_MLO_LINK_ID] ||
 	     tb[NL80211_ATTR_MLD_ADDR])) {
 		wpa_printf(MSG_ERROR,
@@ -2184,7 +2184,7 @@
 		u8 *req_ies = NULL, *resp_ies = NULL;
 		size_t req_ies_len = 0, resp_ies_len = 0;
 
-		if (!bss->valid_links &&
+		if (bss->links[0].link_id == NL80211_DRV_LINK_ID_NA &&
 		    (tb[NL80211_ATTR_MLO_LINK_ID] ||
 		     tb[NL80211_ATTR_MLD_ADDR])) {
 			wpa_printf(MSG_ERROR,
@@ -2452,6 +2452,7 @@
 {
 	union wpa_event_data data;
 	enum nl80211_radar_event event_type;
+	struct i802_link *mld_link = NULL;
 
 	if (!tb[NL80211_ATTR_WIPHY_FREQ] || !tb[NL80211_ATTR_RADAR_EVENT])
 		return;
@@ -2461,13 +2462,11 @@
 	data.dfs_event.freq = nla_get_u32(tb[NL80211_ATTR_WIPHY_FREQ]);
 	event_type = nla_get_u32(tb[NL80211_ATTR_RADAR_EVENT]);
 
-	if (tb[NL80211_ATTR_MLO_LINK_ID]) {
-		data.dfs_event.link_id =
-			nla_get_u8(tb[NL80211_ATTR_MLO_LINK_ID]);
-	} else if (data.dfs_event.freq) {
-		data.dfs_event.link_id =
-			nl80211_get_link_id_by_freq(drv->first_bss,
-						    data.dfs_event.freq);
+	if (data.dfs_event.freq) {
+		mld_link = nl80211_get_mld_link_by_freq(drv->first_bss,
+							data.dfs_event.freq);
+		if (mld_link)
+			data.dfs_event.link_id = mld_link->link_id;
 	}
 
 	/* Check HT params */
@@ -2832,6 +2831,7 @@
 {
 	union wpa_event_data data;
 	struct nlattr *tb[NL80211_ATTR_MAX + 1];
+	struct i802_link *mld_link = NULL;
 
 	wpa_printf(MSG_DEBUG,
 		   "nl80211: DFS offload radar vendor event received");
@@ -2850,13 +2850,11 @@
 	data.dfs_event.freq = nla_get_u32(tb[NL80211_ATTR_WIPHY_FREQ]);
 	data.dfs_event.link_id = NL80211_DRV_LINK_ID_NA;
 
-	if (tb[NL80211_ATTR_MLO_LINK_ID]) {
-		data.dfs_event.link_id =
-			nla_get_u8(tb[NL80211_ATTR_MLO_LINK_ID]);
-	} else if (data.dfs_event.freq) {
-		data.dfs_event.link_id =
-			nl80211_get_link_id_by_freq(drv->first_bss,
-						    data.dfs_event.freq);
+	if (data.dfs_event.freq) {
+		mld_link = nl80211_get_mld_link_by_freq(drv->first_bss,
+							data.dfs_event.freq);
+		if (mld_link)
+			data.dfs_event.link_id = mld_link->link_id;
 	}
 
 	wpa_printf(MSG_DEBUG, "nl80211: DFS event on freq %d MHz, link=%d",
@@ -2968,7 +2966,6 @@
 	info = &event.scan_info;
 	info->aborted = aborted;
 	info->external_scan = external_scan;
-	info->scan_cookie = nla_get_u64(tb[QCA_WLAN_VENDOR_ATTR_SCAN_COOKIE]);
 
 	if (tb[QCA_WLAN_VENDOR_ATTR_SCAN_SSIDS]) {
 		nla_for_each_nested(nl,
@@ -3751,11 +3748,8 @@
 		   (long long unsigned int) cookie,
 		   match ? " (match)" : "",
 		   drv->send_frame_cookie == cookie ? " (match-saved)" : "");
-	if (drv->send_frame_cookie == cookie) {
+	if (drv->send_frame_cookie == cookie)
 		drv->send_frame_cookie = (u64) -1;
-		if (!match)
-			goto send_event;
-	}
 	if (!match)
 		return;
 
@@ -3765,7 +3759,6 @@
 			   (drv->num_send_frame_cookies - i - 1) * sizeof(u64));
 	drv->num_send_frame_cookies--;
 
-send_event:
 	wpa_supplicant_event(drv->ctx, EVENT_TX_WAIT_EXPIRE, NULL);
 }
 
diff --git a/src/drivers/driver_nl80211_scan.c b/src/drivers/driver_nl80211_scan.c
index 1eb4374..68ae579 100644
--- a/src/drivers/driver_nl80211_scan.c
+++ b/src/drivers/driver_nl80211_scan.c
@@ -728,7 +728,7 @@
 
 static struct wpa_scan_res *
 nl80211_parse_bss_info(struct wpa_driver_nl80211_data *drv,
-		       struct nl_msg *msg, const u8 *bssid)
+		       struct nl_msg *msg)
 {
 	struct nlattr *tb[NL80211_ATTR_MAX + 1];
 	struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
@@ -762,9 +762,6 @@
 	if (nla_parse_nested(bss, NL80211_BSS_MAX, tb[NL80211_ATTR_BSS],
 			     bss_policy))
 		return NULL;
-	if (bssid && bss[NL80211_BSS_BSSID] &&
-	    !ether_addr_equal(bssid, nla_data(bss[NL80211_BSS_BSSID])))
-		return NULL;
 	if (bss[NL80211_BSS_INFORMATION_ELEMENTS]) {
 		ie = nla_data(bss[NL80211_BSS_INFORMATION_ELEMENTS]);
 		ie_len = nla_len(bss[NL80211_BSS_INFORMATION_ELEMENTS]);
@@ -869,7 +866,6 @@
 struct nl80211_bss_info_arg {
 	struct wpa_driver_nl80211_data *drv;
 	struct wpa_scan_results *res;
-	const u8 *bssid;
 };
 
 static int bss_info_handler(struct nl_msg *msg, void *arg)
@@ -879,7 +875,7 @@
 	struct wpa_scan_res **tmp;
 	struct wpa_scan_res *r;
 
-	r = nl80211_parse_bss_info(_arg->drv, msg, _arg->bssid);
+	r = nl80211_parse_bss_info(_arg->drv, msg);
 	if (!r)
 		return NL_SKIP;
 
@@ -977,7 +973,7 @@
 
 
 static struct wpa_scan_results *
-nl80211_get_scan_results(struct wpa_driver_nl80211_data *drv, const u8 *bssid)
+nl80211_get_scan_results(struct wpa_driver_nl80211_data *drv)
 {
 	struct nl_msg *msg;
 	struct wpa_scan_results *res;
@@ -997,7 +993,6 @@
 
 	arg.drv = drv;
 	arg.res = res;
-	arg.bssid = bssid;
 	ret = send_and_recv_resp(drv, msg, bss_info_handler, &arg);
 	if (ret == -EAGAIN) {
 		count++;
@@ -1034,18 +1029,16 @@
 
 /**
  * wpa_driver_nl80211_get_scan_results - Fetch the latest scan results
- * @priv: Pointer to private nl80211 data from wpa_driver_nl80211_init()
- * @bssid: Return results only for the specified BSSID, %NULL for all
+ * @priv: Pointer to private wext data from wpa_driver_nl80211_init()
  * Returns: Scan results on success, -1 on failure
  */
-struct wpa_scan_results * wpa_driver_nl80211_get_scan_results(void *priv,
-							      const u8 *bssid)
+struct wpa_scan_results * wpa_driver_nl80211_get_scan_results(void *priv)
 {
 	struct i802_bss *bss = priv;
 	struct wpa_driver_nl80211_data *drv = bss->drv;
 	struct wpa_scan_results *res;
 
-	res = nl80211_get_scan_results(drv, bssid);
+	res = nl80211_get_scan_results(drv);
 	if (res)
 		wpa_driver_nl80211_check_bss_status(drv, res);
 	return res;
@@ -1062,7 +1055,7 @@
 	struct nl80211_dump_scan_ctx *ctx = arg;
 	struct wpa_scan_res *r;
 
-	r = nl80211_parse_bss_info(ctx->drv, msg, NULL);
+	r = nl80211_parse_bss_info(ctx->drv, msg);
 	if (!r)
 		return NL_SKIP;
 	wpa_printf(MSG_DEBUG, "nl80211: %d " MACSTR " %d%s",
@@ -1275,11 +1268,6 @@
 			goto fail;
 	}
 
-	if (is_ap_interface(drv->nlmode) &&
-	    params->link_id != NL80211_DRV_LINK_ID_NA &&
-	    nla_put_u8(msg, QCA_WLAN_VENDOR_ATTR_SCAN_LINK_ID, params->link_id))
-		goto fail;
-
 	nla_nest_end(msg, attr);
 
 	ret = send_and_recv_resp(drv, msg, scan_cookie_handler, &cookie);
diff --git a/src/drivers/drivers.mak b/src/drivers/drivers.mak
index 3b3098d..0186099 100644
--- a/src/drivers/drivers.mak
+++ b/src/drivers/drivers.mak
@@ -160,6 +160,7 @@
 NEED_LINUX_IOCTL=y
 ifdef CONFIG_VLAN_NETLINK
 NEED_LIBNL=y
+CONFIG_LIBNL3_ROUTE=y
 endif
 endif
 
diff --git a/src/drivers/drivers.mk b/src/drivers/drivers.mk
index 1cbe652..8c58456 100644
--- a/src/drivers/drivers.mk
+++ b/src/drivers/drivers.mk
@@ -154,6 +154,7 @@
 NEED_LINUX_IOCTL=y
 ifdef CONFIG_VLAN_NETLINK
 NEED_LIBNL=y
+CONFIG_LIBNL3_ROUTE=y
 endif
 endif