]> git.ipfire.org Git - thirdparty/hostap.git/blobdiff - src/drivers/driver_nl80211.c
hostapd: Add ctrl iface indications for WDS STA interface
[thirdparty/hostap.git] / src / drivers / driver_nl80211.c
index 4f7069bf28c89d0e91230b74c773a6bbf9fb4613..0c5c6bb6f7981098a518de044382f49b12c40d8b 100644 (file)
@@ -29,6 +29,7 @@
 #include "common/qca-vendor-attr.h"
 #include "common/ieee802_11_defs.h"
 #include "common/ieee802_11_common.h"
+#include "common/wpa_common.h"
 #include "l2_packet/l2_packet.h"
 #include "netlink.h"
 #include "linux_defines.h"
@@ -129,7 +130,7 @@ static void nl_destroy_handles(struct nl_handle **handle)
 
 static void nl80211_register_eloop_read(struct nl_handle **handle,
                                        eloop_sock_handler handler,
-                                       void *eloop_data)
+                                       void *eloop_data, int persist)
 {
 #ifdef CONFIG_LIBNL20
        /*
@@ -150,13 +151,17 @@ static void nl80211_register_eloop_read(struct nl_handle **handle,
        nl_socket_set_nonblocking(*handle);
        eloop_register_read_sock(nl_socket_get_fd(*handle), handler,
                                 eloop_data, *handle);
-       *handle = (void *) (((intptr_t) *handle) ^ ELOOP_SOCKET_INVALID);
+       if (!persist)
+               *handle = (void *) (((intptr_t) *handle) ^
+                                   ELOOP_SOCKET_INVALID);
 }
 
 
-static void nl80211_destroy_eloop_handle(struct nl_handle **handle)
+static void nl80211_destroy_eloop_handle(struct nl_handle **handle, int persist)
 {
-       *handle = (void *) (((intptr_t) *handle) ^ ELOOP_SOCKET_INVALID);
+       if (!persist)
+               *handle = (void *) (((intptr_t) *handle) ^
+                                   ELOOP_SOCKET_INVALID);
        eloop_unregister_read_sock(nl_socket_get_fd(*handle));
        nl_destroy_handles(handle);
 }
@@ -204,6 +209,8 @@ static int nl80211_set_param(void *priv, const char *param);
 static int nl80211_put_mesh_config(struct nl_msg *msg,
                                   struct wpa_driver_mesh_bss_params *params);
 #endif /* CONFIG_MESH */
+static int i802_sta_disassoc(void *priv, const u8 *own_addr, const u8 *addr,
+                            int reason);
 
 
 /* Converts nl80211_chan_width to a common format */
@@ -673,6 +680,7 @@ nl80211_get_wiphy_data_ap(struct i802_bss *bss)
        struct nl80211_wiphy_data *w;
        int wiphy_idx, found = 0;
        struct i802_bss *tmp_bss;
+       u8 channel;
 
        if (bss->wiphy_data != NULL)
                return bss->wiphy_data;
@@ -692,30 +700,36 @@ nl80211_get_wiphy_data_ap(struct i802_bss *bss)
        dl_list_init(&w->bsss);
        dl_list_init(&w->drvs);
 
-       w->nl_cb = nl_cb_alloc(NL_CB_DEFAULT);
-       if (!w->nl_cb) {
-               os_free(w);
-               return NULL;
-       }
-       nl_cb_set(w->nl_cb, NL_CB_SEQ_CHECK, NL_CB_CUSTOM, no_seq_check, NULL);
-       nl_cb_set(w->nl_cb, NL_CB_VALID, NL_CB_CUSTOM, process_beacon_event,
-                 w);
+       /* Beacon frames not supported in IEEE 802.11ad */
+       if (ieee80211_freq_to_chan(bss->freq, &channel) !=
+           HOSTAPD_MODE_IEEE80211AD) {
+               w->nl_cb = nl_cb_alloc(NL_CB_DEFAULT);
+               if (!w->nl_cb) {
+                       os_free(w);
+                       return NULL;
+               }
+               nl_cb_set(w->nl_cb, NL_CB_SEQ_CHECK, NL_CB_CUSTOM,
+                         no_seq_check, NULL);
+               nl_cb_set(w->nl_cb, NL_CB_VALID, NL_CB_CUSTOM,
+                         process_beacon_event, w);
+
+               w->nl_beacons = nl_create_handle(bss->drv->global->nl_cb,
+                                                "wiphy beacons");
+               if (w->nl_beacons == NULL) {
+                       os_free(w);
+                       return NULL;
+               }
 
-       w->nl_beacons = nl_create_handle(bss->drv->global->nl_cb,
-                                        "wiphy beacons");
-       if (w->nl_beacons == NULL) {
-               os_free(w);
-               return NULL;
-       }
+               if (nl80211_register_beacons(bss->drv, w)) {
+                       nl_destroy_handles(&w->nl_beacons);
+                       os_free(w);
+                       return NULL;
+               }
 
-       if (nl80211_register_beacons(bss->drv, w)) {
-               nl_destroy_handles(&w->nl_beacons);
-               os_free(w);
-               return NULL;
+               nl80211_register_eloop_read(&w->nl_beacons,
+                                           nl80211_recv_beacons, w, 0);
        }
 
-       nl80211_register_eloop_read(&w->nl_beacons, nl80211_recv_beacons, w);
-
        dl_list_add(&nl80211_wiphys, &w->list);
 
 add:
@@ -761,7 +775,8 @@ static void nl80211_put_wiphy_data_ap(struct i802_bss *bss)
        if (!dl_list_empty(&w->bsss))
                return;
 
-       nl80211_destroy_eloop_handle(&w->nl_beacons);
+       if (w->nl_beacons)
+               nl80211_destroy_eloop_handle(&w->nl_beacons, 0);
 
        nl_cb_put(w->nl_cb);
        dl_list_del(&w->list);
@@ -900,7 +915,8 @@ static int wpa_driver_nl80211_own_ifindex(struct wpa_driver_nl80211_data *drv,
                nl80211_check_global(drv->global);
                wpa_printf(MSG_DEBUG, "nl80211: Update ifindex for a removed "
                           "interface");
-               wpa_driver_nl80211_finish_drv_init(drv, NULL, 0, NULL);
+               if (wpa_driver_nl80211_finish_drv_init(drv, NULL, 0, NULL) < 0)
+                       return -1;
                return 1;
        }
 
@@ -909,19 +925,58 @@ static int wpa_driver_nl80211_own_ifindex(struct wpa_driver_nl80211_data *drv,
 
 
 static struct wpa_driver_nl80211_data *
-nl80211_find_drv(struct nl80211_global *global, int idx, u8 *buf, size_t len)
+nl80211_find_drv(struct nl80211_global *global, int idx, u8 *buf, size_t len,
+                int *init_failed)
 {
        struct wpa_driver_nl80211_data *drv;
+       int res;
+
+       if (init_failed)
+               *init_failed = 0;
        dl_list_for_each(drv, &global->interfaces,
                         struct wpa_driver_nl80211_data, list) {
-               if (wpa_driver_nl80211_own_ifindex(drv, idx, buf, len) ||
-                   have_ifidx(drv, idx, IFIDX_ANY))
+               res = wpa_driver_nl80211_own_ifindex(drv, idx, buf, len);
+               if (res < 0) {
+                       wpa_printf(MSG_DEBUG,
+                                  "nl80211: Found matching own interface, but failed to complete reinitialization");
+                       if (init_failed)
+                               *init_failed = 1;
+                       return drv;
+               }
+               if (res > 0 || have_ifidx(drv, idx, IFIDX_ANY))
                        return drv;
        }
        return NULL;
 }
 
 
+static void nl80211_refresh_mac(struct wpa_driver_nl80211_data *drv,
+                               int ifindex, int notify)
+{
+       struct i802_bss *bss;
+       u8 addr[ETH_ALEN];
+
+       bss = get_bss_ifindex(drv, ifindex);
+       if (bss &&
+           linux_get_ifhwaddr(drv->global->ioctl_sock,
+                              bss->ifname, addr) < 0) {
+               wpa_printf(MSG_DEBUG,
+                          "nl80211: %s: failed to re-read MAC address",
+                          bss->ifname);
+       } else if (bss && os_memcmp(addr, bss->addr, ETH_ALEN) != 0) {
+               wpa_printf(MSG_DEBUG,
+                          "nl80211: Own MAC address on ifindex %d (%s) changed from "
+                          MACSTR " to " MACSTR,
+                          ifindex, bss->ifname,
+                          MAC2STR(bss->addr), MAC2STR(addr));
+               os_memcpy(bss->addr, addr, ETH_ALEN);
+               if (notify)
+                       wpa_supplicant_event(drv->ctx,
+                                            EVENT_INTERFACE_MAC_CHANGED, NULL);
+       }
+}
+
+
 static void wpa_driver_nl80211_event_rtm_newlink(void *ctx,
                                                 struct ifinfomsg *ifi,
                                                 u8 *buf, size_t len)
@@ -934,6 +989,7 @@ static void wpa_driver_nl80211_event_rtm_newlink(void *ctx,
        char namebuf[IFNAMSIZ];
        char ifname[IFNAMSIZ + 1];
        char extra[100], *pos, *end;
+       int init_failed;
 
        extra[0] = '\0';
        pos = extra;
@@ -978,9 +1034,11 @@ static void wpa_driver_nl80211_event_rtm_newlink(void *ctx,
                   (ifi->ifi_flags & IFF_LOWER_UP) ? "[LOWER_UP]" : "",
                   (ifi->ifi_flags & IFF_DORMANT) ? "[DORMANT]" : "");
 
-       drv = nl80211_find_drv(global, ifi->ifi_index, buf, len);
+       drv = nl80211_find_drv(global, ifi->ifi_index, buf, len, &init_failed);
        if (!drv)
                goto event_newlink;
+       if (init_failed)
+               return; /* do not update interface state */
 
        if (!drv->if_disabled && !(ifi->ifi_flags & IFF_UP)) {
                namebuf[0] = '\0';
@@ -989,6 +1047,8 @@ static void wpa_driver_nl80211_event_rtm_newlink(void *ctx,
                        wpa_printf(MSG_DEBUG, "nl80211: Ignore interface down "
                                   "event since interface %s is up", namebuf);
                        drv->ignore_if_down_event = 0;
+                       /* Re-read MAC address as it may have changed */
+                       nl80211_refresh_mac(drv, ifi->ifi_index, 1);
                        return;
                }
                wpa_printf(MSG_DEBUG, "nl80211: Interface down (%s/%s)",
@@ -1012,7 +1072,7 @@ static void wpa_driver_nl80211_event_rtm_newlink(void *ctx,
                         * dynamic interfaces
                         */
                        drv = nl80211_find_drv(global, ifi->ifi_index,
-                                              buf, len);
+                                              buf, len, NULL);
                        if (!drv)
                                return;
                }
@@ -1033,27 +1093,8 @@ static void wpa_driver_nl80211_event_rtm_newlink(void *ctx,
                                   "event since interface %s is marked "
                                   "removed", drv->first_bss->ifname);
                } else {
-                       struct i802_bss *bss;
-                       u8 addr[ETH_ALEN];
-
                        /* Re-read MAC address as it may have changed */
-                       bss = get_bss_ifindex(drv, ifi->ifi_index);
-                       if (bss &&
-                           linux_get_ifhwaddr(drv->global->ioctl_sock,
-                                              bss->ifname, addr) < 0) {
-                               wpa_printf(MSG_DEBUG,
-                                          "nl80211: %s: failed to re-read MAC address",
-                                          bss->ifname);
-                       } else if (bss &&
-                                  os_memcmp(addr, bss->addr, ETH_ALEN) != 0) {
-                               wpa_printf(MSG_DEBUG,
-                                          "nl80211: Own MAC address on ifindex %d (%s) changed from "
-                                          MACSTR " to " MACSTR,
-                                          ifi->ifi_index, bss->ifname,
-                                          MAC2STR(bss->addr),
-                                          MAC2STR(addr));
-                               os_memcpy(bss->addr, addr, ETH_ALEN);
-                       }
+                       nl80211_refresh_mac(drv, ifi->ifi_index, 0);
 
                        wpa_printf(MSG_DEBUG, "nl80211: Interface up");
                        drv->if_disabled = 0;
@@ -1157,7 +1198,7 @@ static void wpa_driver_nl80211_event_rtm_dellink(void *ctx,
                   (ifi->ifi_flags & IFF_LOWER_UP) ? "[LOWER_UP]" : "",
                   (ifi->ifi_flags & IFF_DORMANT) ? "[DORMANT]" : "");
 
-       drv = nl80211_find_drv(global, ifi->ifi_index, buf, len);
+       drv = nl80211_find_drv(global, ifi->ifi_index, buf, len, NULL);
 
        if (ifi->ifi_family == AF_BRIDGE && brid && drv) {
                /* device has been removed from bridge */
@@ -1181,16 +1222,108 @@ static void wpa_driver_nl80211_event_rtm_dellink(void *ctx,
 }
 
 
+struct nl80211_get_assoc_freq_arg {
+       struct wpa_driver_nl80211_data *drv;
+       unsigned int assoc_freq;
+       unsigned int ibss_freq;
+       u8 assoc_bssid[ETH_ALEN];
+       u8 assoc_ssid[SSID_MAX_LEN];
+       u8 assoc_ssid_len;
+};
+
+static int nl80211_get_assoc_freq_handler(struct nl_msg *msg, void *arg)
+{
+       struct nlattr *tb[NL80211_ATTR_MAX + 1];
+       struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
+       struct nlattr *bss[NL80211_BSS_MAX + 1];
+       static struct nla_policy bss_policy[NL80211_BSS_MAX + 1] = {
+               [NL80211_BSS_BSSID] = { .type = NLA_UNSPEC },
+               [NL80211_BSS_FREQUENCY] = { .type = NLA_U32 },
+               [NL80211_BSS_INFORMATION_ELEMENTS] = { .type = NLA_UNSPEC },
+               [NL80211_BSS_STATUS] = { .type = NLA_U32 },
+       };
+       struct nl80211_get_assoc_freq_arg *ctx = arg;
+       enum nl80211_bss_status status;
+
+       nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
+                 genlmsg_attrlen(gnlh, 0), NULL);
+       if (!tb[NL80211_ATTR_BSS] ||
+           nla_parse_nested(bss, NL80211_BSS_MAX, tb[NL80211_ATTR_BSS],
+                            bss_policy) ||
+           !bss[NL80211_BSS_STATUS])
+               return NL_SKIP;
+
+       status = nla_get_u32(bss[NL80211_BSS_STATUS]);
+       if (status == NL80211_BSS_STATUS_ASSOCIATED &&
+           bss[NL80211_BSS_FREQUENCY]) {
+               ctx->assoc_freq = nla_get_u32(bss[NL80211_BSS_FREQUENCY]);
+               wpa_printf(MSG_DEBUG, "nl80211: Associated on %u MHz",
+                          ctx->assoc_freq);
+       }
+       if (status == NL80211_BSS_STATUS_IBSS_JOINED &&
+           bss[NL80211_BSS_FREQUENCY]) {
+               ctx->ibss_freq = nla_get_u32(bss[NL80211_BSS_FREQUENCY]);
+               wpa_printf(MSG_DEBUG, "nl80211: IBSS-joined on %u MHz",
+                          ctx->ibss_freq);
+       }
+       if (status == NL80211_BSS_STATUS_ASSOCIATED &&
+           bss[NL80211_BSS_BSSID]) {
+               os_memcpy(ctx->assoc_bssid,
+                         nla_data(bss[NL80211_BSS_BSSID]), ETH_ALEN);
+               wpa_printf(MSG_DEBUG, "nl80211: Associated with "
+                          MACSTR, MAC2STR(ctx->assoc_bssid));
+       }
+
+       if (status == NL80211_BSS_STATUS_ASSOCIATED &&
+           bss[NL80211_BSS_INFORMATION_ELEMENTS]) {
+               const u8 *ie, *ssid;
+               size_t ie_len;
+
+               ie = nla_data(bss[NL80211_BSS_INFORMATION_ELEMENTS]);
+               ie_len = nla_len(bss[NL80211_BSS_INFORMATION_ELEMENTS]);
+               ssid = get_ie(ie, ie_len, WLAN_EID_SSID);
+               if (ssid && ssid[1] > 0 && ssid[1] <= SSID_MAX_LEN) {
+                       ctx->assoc_ssid_len = ssid[1];
+                       os_memcpy(ctx->assoc_ssid, ssid + 2, ssid[1]);
+               }
+       }
+
+       return NL_SKIP;
+}
+
+
+int nl80211_get_assoc_ssid(struct wpa_driver_nl80211_data *drv, u8 *ssid)
+{
+       struct nl_msg *msg;
+       int ret;
+       struct nl80211_get_assoc_freq_arg arg;
+
+       msg = nl80211_drv_msg(drv, NLM_F_DUMP, NL80211_CMD_GET_SCAN);
+       os_memset(&arg, 0, sizeof(arg));
+       arg.drv = drv;
+       ret = send_and_recv_msgs(drv, msg, nl80211_get_assoc_freq_handler,
+                                &arg);
+       if (ret == 0) {
+               os_memcpy(ssid, arg.assoc_ssid, arg.assoc_ssid_len);
+               return arg.assoc_ssid_len;
+       }
+       wpa_printf(MSG_DEBUG, "nl80211: Scan result fetch failed: ret=%d (%s)",
+                  ret, strerror(-ret));
+       return ret;
+}
+
+
 unsigned int nl80211_get_assoc_freq(struct wpa_driver_nl80211_data *drv)
 {
        struct nl_msg *msg;
        int ret;
-       struct nl80211_bss_info_arg arg;
+       struct nl80211_get_assoc_freq_arg arg;
 
        msg = nl80211_drv_msg(drv, NLM_F_DUMP, NL80211_CMD_GET_SCAN);
        os_memset(&arg, 0, sizeof(arg));
        arg.drv = drv;
-       ret = send_and_recv_msgs(drv, msg, bss_info_handler, &arg);
+       ret = send_and_recv_msgs(drv, msg, nl80211_get_assoc_freq_handler,
+                                &arg);
        if (ret == 0) {
                unsigned int freq = drv->nlmode == NL80211_IFTYPE_ADHOC ?
                        arg.ibss_freq : arg.assoc_freq;
@@ -1505,7 +1638,7 @@ static int wpa_driver_nl80211_init_nl_global(struct nl80211_global *global)
 
        nl80211_register_eloop_read(&global->nl_event,
                                    wpa_driver_nl80211_event_receive,
-                                   global->nl_cb);
+                                   global->nl_cb, 0);
 
        return 0;
 
@@ -1871,7 +2004,7 @@ static void nl80211_mgmt_handle_register_eloop(struct i802_bss *bss)
 {
        nl80211_register_eloop_read(&bss->nl_mgmt,
                                    wpa_driver_nl80211_event_receive,
-                                   bss->nl_cb);
+                                   bss->nl_cb, 0);
 }
 
 
@@ -1884,6 +2017,25 @@ static int nl80211_register_action_frame(struct i802_bss *bss,
 }
 
 
+static int nl80211_init_connect_handle(struct i802_bss *bss)
+{
+       if (bss->nl_connect) {
+               wpa_printf(MSG_DEBUG,
+                          "nl80211: Connect handle already created (nl_connect=%p)",
+                          bss->nl_connect);
+               return -1;
+       }
+
+       bss->nl_connect = nl_create_handle(bss->nl_cb, "connect");
+       if (!bss->nl_connect)
+               return -1;
+       nl80211_register_eloop_read(&bss->nl_connect,
+                                   wpa_driver_nl80211_event_receive,
+                                   bss->nl_cb, 1);
+       return 0;
+}
+
+
 static int nl80211_mgmt_subscribe_non_ap(struct i802_bss *bss)
 {
        struct wpa_driver_nl80211_data *drv = bss->drv;
@@ -1894,7 +2046,9 @@ static int nl80211_mgmt_subscribe_non_ap(struct i802_bss *bss)
        wpa_printf(MSG_DEBUG, "nl80211: Subscribe to mgmt frames with non-AP "
                   "handle %p", bss->nl_mgmt);
 
-       if (drv->nlmode == NL80211_IFTYPE_ADHOC) {
+       if (drv->nlmode == NL80211_IFTYPE_ADHOC ||
+           ((drv->capa.flags & WPA_DRIVER_FLAGS_SAE) &&
+            !(drv->capa.flags & WPA_DRIVER_FLAGS_SME))) {
                u16 type = (WLAN_FC_TYPE_MGMT << 2) | (WLAN_FC_STYPE_AUTH << 4);
 
                /* register for any AUTH message */
@@ -1906,7 +2060,7 @@ static int nl80211_mgmt_subscribe_non_ap(struct i802_bss *bss)
        if (nl80211_register_action_frame(bss, (u8 *) "\x01\x04", 2) < 0)
                ret = -1;
 #endif /* CONFIG_INTERWORKING */
-#if defined(CONFIG_P2P) || defined(CONFIG_INTERWORKING)
+#if defined(CONFIG_P2P) || defined(CONFIG_INTERWORKING) || defined(CONFIG_DPP)
        /* GAS Initial Request */
        if (nl80211_register_action_frame(bss, (u8 *) "\x04\x0a", 2) < 0)
                ret = -1;
@@ -1931,7 +2085,7 @@ static int nl80211_mgmt_subscribe_non_ap(struct i802_bss *bss)
        /* Protected GAS Comeback Response */
        if (nl80211_register_action_frame(bss, (u8 *) "\x09\x0d", 2) < 0)
                ret = -1;
-#endif /* CONFIG_P2P || CONFIG_INTERWORKING */
+#endif /* CONFIG_P2P || CONFIG_INTERWORKING || CONFIG_DPP */
 #ifdef CONFIG_P2P
        /* P2P Public Action */
        if (nl80211_register_action_frame(bss,
@@ -1944,6 +2098,13 @@ static int nl80211_mgmt_subscribe_non_ap(struct i802_bss *bss)
                                          5) < 0)
                ret = -1;
 #endif /* CONFIG_P2P */
+#ifdef CONFIG_DPP
+       /* DPP Public Action */
+       if (nl80211_register_action_frame(bss,
+                                         (u8 *) "\x04\x09\x50\x6f\x9a\x1a",
+                                         6) < 0)
+               ret = -1;
+#endif /* CONFIG_DPP */
 #ifdef CONFIG_IEEE80211W
        /* SA Query Response */
        if (nl80211_register_action_frame(bss, (u8 *) "\x08\x01", 2) < 0)
@@ -2070,6 +2231,9 @@ static int nl80211_action_subscribe_ap(struct i802_bss *bss)
        /* RRM Measurement Report */
        if (nl80211_register_action_frame(bss, (u8 *) "\x05\x01", 2) < 0)
                ret = -1;
+       /* RRM Link Measurement Report */
+       if (nl80211_register_action_frame(bss, (u8 *) "\x05\x03", 2) < 0)
+               ret = -1;
        /* RRM Neighbor Report Request */
        if (nl80211_register_action_frame(bss, (u8 *) "\x05\x04", 2) < 0)
                ret = -1;
@@ -2141,9 +2305,6 @@ static int nl80211_mgmt_subscribe_ap(struct i802_bss *bss)
        if (nl80211_register_spurious_class3(bss))
                goto out_err;
 
-       if (nl80211_get_wiphy_data_ap(bss) == NULL)
-               goto out_err;
-
        nl80211_mgmt_handle_register_eloop(bss);
        return 0;
 
@@ -2178,7 +2339,7 @@ static void nl80211_mgmt_unsubscribe(struct i802_bss *bss, const char *reason)
                return;
        wpa_printf(MSG_DEBUG, "nl80211: Unsubscribe mgmt frames handle %p "
                   "(%s)", bss->nl_mgmt, reason);
-       nl80211_destroy_eloop_handle(&bss->nl_mgmt);
+       nl80211_destroy_eloop_handle(&bss->nl_mgmt, 0);
 
        nl80211_put_wiphy_data_ap(bss);
 }
@@ -2394,16 +2555,20 @@ wpa_driver_nl80211_finish_drv_init(struct wpa_driver_nl80211_data *drv,
        if (drv->vendor_cmd_test_avail)
                qca_vendor_test(drv);
 
+       nl80211_init_connect_handle(bss);
+
        return 0;
 }
 
 
-static int wpa_driver_nl80211_del_beacon(struct wpa_driver_nl80211_data *drv)
+static int wpa_driver_nl80211_del_beacon(struct i802_bss *bss)
 {
        struct nl_msg *msg;
+       struct wpa_driver_nl80211_data *drv = bss->drv;
 
        wpa_printf(MSG_DEBUG, "nl80211: Remove beacon (ifindex=%d)",
                   drv->ifindex);
+       nl80211_put_wiphy_data_ap(bss);
        msg = nl80211_drv_msg(drv, 0, NL80211_CMD_DEL_BEACON);
        return send_and_recv_msgs(drv, msg, NULL, NULL);
 }
@@ -2456,7 +2621,7 @@ static void wpa_driver_nl80211_deinit(struct i802_bss *bss)
        nl80211_remove_monitor_interface(drv);
 
        if (is_ap_interface(drv->nlmode))
-               wpa_driver_nl80211_del_beacon(drv);
+               wpa_driver_nl80211_del_beacon(bss);
 
        if (drv->eapol_sock >= 0) {
                eloop_unregister_read_sock(drv->eapol_sock);
@@ -2505,6 +2670,9 @@ static void wpa_driver_nl80211_deinit(struct i802_bss *bss)
                nl80211_del_p2pdev(bss);
        }
 
+       if (bss->nl_connect)
+               nl80211_destroy_eloop_handle(&bss->nl_connect, 1);
+
        nl80211_destroy_bss(drv->first_bss);
 
        os_free(drv->filter_ssids);
@@ -2530,30 +2698,30 @@ static u32 wpa_alg_to_cipher_suite(enum wpa_alg alg, size_t key_len)
        switch (alg) {
        case WPA_ALG_WEP:
                if (key_len == 5)
-                       return WLAN_CIPHER_SUITE_WEP40;
-               return WLAN_CIPHER_SUITE_WEP104;
+                       return RSN_CIPHER_SUITE_WEP40;
+               return RSN_CIPHER_SUITE_WEP104;
        case WPA_ALG_TKIP:
-               return WLAN_CIPHER_SUITE_TKIP;
+               return RSN_CIPHER_SUITE_TKIP;
        case WPA_ALG_CCMP:
-               return WLAN_CIPHER_SUITE_CCMP;
+               return RSN_CIPHER_SUITE_CCMP;
        case WPA_ALG_GCMP:
-               return WLAN_CIPHER_SUITE_GCMP;
+               return RSN_CIPHER_SUITE_GCMP;
        case WPA_ALG_CCMP_256:
-               return WLAN_CIPHER_SUITE_CCMP_256;
+               return RSN_CIPHER_SUITE_CCMP_256;
        case WPA_ALG_GCMP_256:
-               return WLAN_CIPHER_SUITE_GCMP_256;
+               return RSN_CIPHER_SUITE_GCMP_256;
        case WPA_ALG_IGTK:
-               return WLAN_CIPHER_SUITE_AES_CMAC;
+               return RSN_CIPHER_SUITE_AES_128_CMAC;
        case WPA_ALG_BIP_GMAC_128:
-               return WLAN_CIPHER_SUITE_BIP_GMAC_128;
+               return RSN_CIPHER_SUITE_BIP_GMAC_128;
        case WPA_ALG_BIP_GMAC_256:
-               return WLAN_CIPHER_SUITE_BIP_GMAC_256;
+               return RSN_CIPHER_SUITE_BIP_GMAC_256;
        case WPA_ALG_BIP_CMAC_256:
-               return WLAN_CIPHER_SUITE_BIP_CMAC_256;
+               return RSN_CIPHER_SUITE_BIP_CMAC_256;
        case WPA_ALG_SMS4:
-               return WLAN_CIPHER_SUITE_SMS4;
+               return RSN_CIPHER_SUITE_SMS4;
        case WPA_ALG_KRK:
-               return WLAN_CIPHER_SUITE_KRK;
+               return RSN_CIPHER_SUITE_KRK;
        case WPA_ALG_NONE:
        case WPA_ALG_PMK:
                wpa_printf(MSG_ERROR, "nl80211: Unexpected encryption algorithm %d",
@@ -2571,21 +2739,21 @@ static u32 wpa_cipher_to_cipher_suite(unsigned int cipher)
 {
        switch (cipher) {
        case WPA_CIPHER_CCMP_256:
-               return WLAN_CIPHER_SUITE_CCMP_256;
+               return RSN_CIPHER_SUITE_CCMP_256;
        case WPA_CIPHER_GCMP_256:
-               return WLAN_CIPHER_SUITE_GCMP_256;
+               return RSN_CIPHER_SUITE_GCMP_256;
        case WPA_CIPHER_CCMP:
-               return WLAN_CIPHER_SUITE_CCMP;
+               return RSN_CIPHER_SUITE_CCMP;
        case WPA_CIPHER_GCMP:
-               return WLAN_CIPHER_SUITE_GCMP;
+               return RSN_CIPHER_SUITE_GCMP;
        case WPA_CIPHER_TKIP:
-               return WLAN_CIPHER_SUITE_TKIP;
+               return RSN_CIPHER_SUITE_TKIP;
        case WPA_CIPHER_WEP104:
-               return WLAN_CIPHER_SUITE_WEP104;
+               return RSN_CIPHER_SUITE_WEP104;
        case WPA_CIPHER_WEP40:
-               return WLAN_CIPHER_SUITE_WEP40;
+               return RSN_CIPHER_SUITE_WEP40;
        case WPA_CIPHER_GTK_NOT_USED:
-               return WLAN_CIPHER_SUITE_NO_GROUP_ADDR;
+               return RSN_CIPHER_SUITE_NO_GROUP_ADDRESSED;
        }
 
        return 0;
@@ -2598,19 +2766,19 @@ static int wpa_cipher_to_cipher_suites(unsigned int ciphers, u32 suites[],
        int num_suites = 0;
 
        if (num_suites < max_suites && ciphers & WPA_CIPHER_CCMP_256)
-               suites[num_suites++] = WLAN_CIPHER_SUITE_CCMP_256;
+               suites[num_suites++] = RSN_CIPHER_SUITE_CCMP_256;
        if (num_suites < max_suites && ciphers & WPA_CIPHER_GCMP_256)
-               suites[num_suites++] = WLAN_CIPHER_SUITE_GCMP_256;
+               suites[num_suites++] = RSN_CIPHER_SUITE_GCMP_256;
        if (num_suites < max_suites && ciphers & WPA_CIPHER_CCMP)
-               suites[num_suites++] = WLAN_CIPHER_SUITE_CCMP;
+               suites[num_suites++] = RSN_CIPHER_SUITE_CCMP;
        if (num_suites < max_suites && ciphers & WPA_CIPHER_GCMP)
-               suites[num_suites++] = WLAN_CIPHER_SUITE_GCMP;
+               suites[num_suites++] = RSN_CIPHER_SUITE_GCMP;
        if (num_suites < max_suites && ciphers & WPA_CIPHER_TKIP)
-               suites[num_suites++] = WLAN_CIPHER_SUITE_TKIP;
+               suites[num_suites++] = RSN_CIPHER_SUITE_TKIP;
        if (num_suites < max_suites && ciphers & WPA_CIPHER_WEP104)
-               suites[num_suites++] = WLAN_CIPHER_SUITE_WEP104;
+               suites[num_suites++] = RSN_CIPHER_SUITE_WEP104;
        if (num_suites < max_suites && ciphers & WPA_CIPHER_WEP40)
-               suites[num_suites++] = WLAN_CIPHER_SUITE_WEP40;
+               suites[num_suites++] = RSN_CIPHER_SUITE_WEP40;
 
        return num_suites;
 }
@@ -2647,6 +2815,44 @@ static int issue_key_mgmt_set_key(struct wpa_driver_nl80211_data *drv,
 #endif /* CONFIG_DRIVER_NL80211_QCA */
 
 
+static int nl80211_set_pmk(struct wpa_driver_nl80211_data *drv,
+                          const u8 *key, size_t key_len,
+                          const u8 *addr)
+{
+       struct nl_msg *msg = NULL;
+       int ret;
+
+       /*
+        * If the authenticator address is not set, assume it is
+        * the current BSSID.
+        */
+       if (!addr && drv->associated)
+               addr = drv->bssid;
+       else if (!addr)
+               return -1;
+
+       wpa_printf(MSG_DEBUG, "nl80211: Set PMK to the driver for " MACSTR,
+                  MAC2STR(addr));
+       wpa_hexdump_key(MSG_DEBUG, "nl80211: PMK", key, key_len);
+       msg = nl80211_drv_msg(drv, 0, NL80211_CMD_SET_PMK);
+       if (!msg ||
+           nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, addr) ||
+           nla_put(msg, NL80211_ATTR_PMK, key_len, key)) {
+               nl80211_nlmsg_clear(msg);
+               nlmsg_free(msg);
+               return -ENOBUFS;
+       }
+
+       ret = send_and_recv_msgs(drv, msg, NULL, (void *) -1);
+       if (ret) {
+               wpa_printf(MSG_DEBUG, "nl80211: Set PMK failed: ret=%d (%s)",
+                          ret, strerror(-ret));
+       }
+
+       return ret;
+}
+
+
 static int wpa_driver_nl80211_set_key(const char *ifname, struct i802_bss *bss,
                                      enum wpa_alg alg, const u8 *addr,
                                      int key_idx, int set_tx,
@@ -2685,6 +2891,10 @@ static int wpa_driver_nl80211_set_key(const char *ifname, struct i802_bss *bss,
        }
 #endif /* CONFIG_DRIVER_NL80211_QCA */
 
+       if (alg == WPA_ALG_PMK &&
+           (drv->capa.flags & WPA_DRIVER_FLAGS_4WAY_HANDSHAKE))
+               return nl80211_set_pmk(drv, key, key_len, addr);
+
        if (alg == WPA_ALG_NONE) {
                msg = nl80211_ifindex_msg(drv, ifindex, 0, NL80211_CMD_DEL_KEY);
                if (!msg)
@@ -2868,8 +3078,8 @@ static int nl80211_set_conn_keys(struct wpa_driver_associate_params *params,
                            params->wep_key[i]) ||
                    nla_put_u32(msg, NL80211_KEY_CIPHER,
                                params->wep_key_len[i] == 5 ?
-                               WLAN_CIPHER_SUITE_WEP40 :
-                               WLAN_CIPHER_SUITE_WEP104) ||
+                               RSN_CIPHER_SUITE_WEP40 :
+                               RSN_CIPHER_SUITE_WEP104) ||
                    nla_put_u8(msg, NL80211_KEY_IDX, i) ||
                    (i == params->wep_tx_keyidx &&
                     nla_put_flag(msg, NL80211_KEY_DEFAULT)))
@@ -3020,6 +3230,27 @@ static void nl80211_unmask_11b_rates(struct i802_bss *bss)
 }
 
 
+static enum nl80211_auth_type get_nl_auth_type(int wpa_auth_alg)
+{
+       if (wpa_auth_alg & WPA_AUTH_ALG_OPEN)
+               return NL80211_AUTHTYPE_OPEN_SYSTEM;
+       if (wpa_auth_alg & WPA_AUTH_ALG_SHARED)
+               return NL80211_AUTHTYPE_SHARED_KEY;
+       if (wpa_auth_alg & WPA_AUTH_ALG_LEAP)
+               return NL80211_AUTHTYPE_NETWORK_EAP;
+       if (wpa_auth_alg & WPA_AUTH_ALG_FT)
+               return NL80211_AUTHTYPE_FT;
+       if (wpa_auth_alg & WPA_AUTH_ALG_SAE)
+               return NL80211_AUTHTYPE_SAE;
+       if (wpa_auth_alg & WPA_AUTH_ALG_FILS)
+               return NL80211_AUTHTYPE_FILS_SK;
+       if (wpa_auth_alg & WPA_AUTH_ALG_FILS_SK_PFS)
+               return NL80211_AUTHTYPE_FILS_SK_PFS;
+
+       return NL80211_AUTHTYPE_MAX;
+}
+
+
 static int wpa_driver_nl80211_authenticate(
        struct i802_bss *bss, struct wpa_driver_auth_params *params)
 {
@@ -3102,22 +3333,10 @@ retry:
                            params->auth_data))
                        goto fail;
        }
-       if (params->auth_alg & WPA_AUTH_ALG_OPEN)
-               type = NL80211_AUTHTYPE_OPEN_SYSTEM;
-       else if (params->auth_alg & WPA_AUTH_ALG_SHARED)
-               type = NL80211_AUTHTYPE_SHARED_KEY;
-       else if (params->auth_alg & WPA_AUTH_ALG_LEAP)
-               type = NL80211_AUTHTYPE_NETWORK_EAP;
-       else if (params->auth_alg & WPA_AUTH_ALG_FT)
-               type = NL80211_AUTHTYPE_FT;
-       else if (params->auth_alg & WPA_AUTH_ALG_SAE)
-               type = NL80211_AUTHTYPE_SAE;
-       else if (params->auth_alg & WPA_AUTH_ALG_FILS)
-               type = NL80211_AUTHTYPE_FILS_SK;
-       else
-               goto fail;
+       type = get_nl_auth_type(params->auth_alg);
        wpa_printf(MSG_DEBUG, "  * Auth Type %d", type);
-       if (nla_put_u32(msg, NL80211_ATTR_AUTH_TYPE, type))
+       if (type == NL80211_AUTHTYPE_MAX ||
+           nla_put_u32(msg, NL80211_ATTR_AUTH_TYPE, type))
                goto fail;
        if (params->local_state_change) {
                wpa_printf(MSG_DEBUG, "  * Local state change only");
@@ -3129,11 +3348,11 @@ retry:
        msg = NULL;
        if (ret) {
                wpa_dbg(drv->ctx, MSG_DEBUG,
-                       "nl80211: MLME command failed (auth): ret=%d (%s)",
-                       ret, strerror(-ret));
+                       "nl80211: MLME command failed (auth): count=%d ret=%d (%s)",
+                       count, ret, strerror(-ret));
                count++;
-               if (ret == -EALREADY && count == 1 && params->bssid &&
-                   !params->local_state_change) {
+               if ((ret == -EALREADY || ret == -EEXIST) && count == 1 &&
+                   params->bssid && !params->local_state_change) {
                        /*
                         * mac80211 does not currently accept new
                         * authentication if we are already authenticated. As a
@@ -3612,6 +3831,52 @@ static int nl80211_put_beacon_rate(struct nl_msg *msg, const u64 flags,
 }
 
 
+static int nl80211_set_multicast_to_unicast(struct i802_bss *bss,
+                                           int multicast_to_unicast)
+{
+       struct wpa_driver_nl80211_data *drv = bss->drv;
+       struct nl_msg *msg;
+       int ret;
+
+       msg = nl80211_bss_msg(bss, 0, NL80211_CMD_SET_MULTICAST_TO_UNICAST);
+       if (!msg ||
+           (multicast_to_unicast &&
+            nla_put_flag(msg, NL80211_ATTR_MULTICAST_TO_UNICAST_ENABLED))) {
+               wpa_printf(MSG_ERROR,
+                          "nl80211: Failed to build NL80211_CMD_SET_MULTICAST_TO_UNICAST msg for %s",
+                          bss->ifname);
+               nlmsg_free(msg);
+               return -ENOBUFS;
+       }
+
+       ret = send_and_recv_msgs(drv, msg, NULL, NULL);
+
+       switch (ret) {
+       case 0:
+               wpa_printf(MSG_DEBUG,
+                          "nl80211: multicast to unicast %s on interface %s",
+                          multicast_to_unicast ? "enabled" : "disabled",
+                          bss->ifname);
+               break;
+       case -EOPNOTSUPP:
+               if (!multicast_to_unicast)
+                       break;
+               wpa_printf(MSG_INFO,
+                          "nl80211: multicast to unicast not supported on interface %s",
+                          bss->ifname);
+               break;
+       default:
+               wpa_printf(MSG_ERROR,
+                          "nl80211: %s multicast to unicast failed with %d (%s) on interface %s",
+                          multicast_to_unicast ? "enabling" : "disabling",
+                          ret, strerror(-ret), bss->ifname);
+               break;
+       }
+
+       return ret;
+}
+
+
 static int wpa_driver_nl80211_set_ap(void *priv,
                                     struct wpa_driver_ap_params *params)
 {
@@ -3635,6 +3900,9 @@ static int wpa_driver_nl80211_set_ap(void *priv,
                   beacon_set);
        if (beacon_set)
                cmd = NL80211_CMD_SET_BEACON;
+       else if (!drv->device_ap_sme && !drv->use_monitor &&
+                !nl80211_get_wiphy_data_ap(bss))
+               return -ENOBUFS;
 
        wpa_hexdump(MSG_DEBUG, "nl80211: Beacon head",
                    params->head, params->head_len);
@@ -3716,9 +3984,9 @@ static int wpa_driver_nl80211_set_ap(void *priv,
                   params->key_mgmt_suites);
        num_suites = 0;
        if (params->key_mgmt_suites & WPA_KEY_MGMT_IEEE8021X)
-               suites[num_suites++] = WLAN_AKM_SUITE_8021X;
+               suites[num_suites++] = RSN_AUTH_KEY_MGMT_UNSPEC_802_1X;
        if (params->key_mgmt_suites & WPA_KEY_MGMT_PSK)
-               suites[num_suites++] = WLAN_AKM_SUITE_PSK;
+               suites[num_suites++] = RSN_AUTH_KEY_MGMT_PSK_OVER_802_1X;
        if (num_suites &&
            nla_put(msg, NL80211_ATTR_AKM_SUITES, num_suites * sizeof(u32),
                    suites))
@@ -3764,7 +4032,7 @@ static int wpa_driver_nl80211_set_ap(void *priv,
                        smps_mode = NL80211_SMPS_OFF;
                        break;
                }
-               if (nla_put_u32(msg, NL80211_ATTR_SMPS_MODE, smps_mode))
+               if (nla_put_u8(msg, NL80211_ATTR_SMPS_MODE, smps_mode))
                        goto fail;
        }
 
@@ -3831,6 +4099,8 @@ static int wpa_driver_nl80211_set_ap(void *priv,
                nl80211_set_bss(bss, params->cts_protect, params->preamble,
                                params->short_slot_time, params->ht_opmode,
                                params->isolate, params->basic_rates);
+               nl80211_set_multicast_to_unicast(bss,
+                                                params->multicast_to_unicast);
                if (beacon_set && params->freq &&
                    params->freq->bandwidth != bss->bandwidth) {
                        wpa_printf(MSG_DEBUG,
@@ -4564,6 +4834,7 @@ static void nl80211_teardown_ap(struct i802_bss *bss)
        else
                nl80211_mgmt_unsubscribe(bss, "AP teardown");
 
+       nl80211_put_wiphy_data_ap(bss);
        bss->beacon_set = 0;
 }
 
@@ -4908,10 +5179,54 @@ fail:
 }
 
 
+static int nl80211_put_fils_connect_params(struct wpa_driver_nl80211_data *drv,
+                                          struct wpa_driver_associate_params *params,
+                                          struct nl_msg *msg)
+{
+       if (params->fils_erp_username_len) {
+               wpa_hexdump_ascii(MSG_DEBUG, "  * FILS ERP EMSKname/username",
+                                 params->fils_erp_username,
+                                 params->fils_erp_username_len);
+               if (nla_put(msg, NL80211_ATTR_FILS_ERP_USERNAME,
+                           params->fils_erp_username_len,
+                           params->fils_erp_username))
+                       return -1;
+       }
+
+       if (params->fils_erp_realm_len) {
+               wpa_hexdump_ascii(MSG_DEBUG, "  * FILS ERP Realm",
+                                 params->fils_erp_realm,
+                                 params->fils_erp_realm_len);
+               if (nla_put(msg, NL80211_ATTR_FILS_ERP_REALM,
+                           params->fils_erp_realm_len, params->fils_erp_realm))
+                       return -1;
+       }
+
+       wpa_printf(MSG_DEBUG, "  * FILS ERP next seq %u",
+                  params->fils_erp_next_seq_num);
+       if (nla_put_u16(msg, NL80211_ATTR_FILS_ERP_NEXT_SEQ_NUM,
+                       params->fils_erp_next_seq_num))
+               return -1;
+
+       if (params->fils_erp_rrk_len) {
+               wpa_printf(MSG_DEBUG, "  * FILS ERP rRK (len=%lu)",
+                          (unsigned long) params->fils_erp_rrk_len);
+               if (nla_put(msg, NL80211_ATTR_FILS_ERP_RRK,
+                           params->fils_erp_rrk_len, params->fils_erp_rrk))
+                       return -1;
+       }
+
+       return 0;
+}
+
+
 static int nl80211_connect_common(struct wpa_driver_nl80211_data *drv,
                                  struct wpa_driver_associate_params *params,
                                  struct nl_msg *msg)
 {
+       if (nla_put_flag(msg, NL80211_ATTR_IFACE_SOCKET_OWNER))
+               return -1;
+
        if (params->bssid) {
                wpa_printf(MSG_DEBUG, "  * bssid=" MACSTR,
                           MAC2STR(params->bssid));
@@ -5012,40 +5327,64 @@ static int nl80211_connect_common(struct wpa_driver_nl80211_data *drv,
            params->key_mgmt_suite == WPA_KEY_MGMT_IEEE8021X_SHA256 ||
            params->key_mgmt_suite == WPA_KEY_MGMT_PSK_SHA256 ||
            params->key_mgmt_suite == WPA_KEY_MGMT_IEEE8021X_SUITE_B ||
-           params->key_mgmt_suite == WPA_KEY_MGMT_IEEE8021X_SUITE_B_192) {
-               int mgmt = WLAN_AKM_SUITE_PSK;
+           params->key_mgmt_suite == WPA_KEY_MGMT_IEEE8021X_SUITE_B_192 ||
+           params->key_mgmt_suite == WPA_KEY_MGMT_FILS_SHA256 ||
+           params->key_mgmt_suite == WPA_KEY_MGMT_FILS_SHA384 ||
+           params->key_mgmt_suite == WPA_KEY_MGMT_FT_FILS_SHA256 ||
+           params->key_mgmt_suite == WPA_KEY_MGMT_FT_FILS_SHA384 ||
+           params->key_mgmt_suite == WPA_KEY_MGMT_OWE ||
+           params->key_mgmt_suite == WPA_KEY_MGMT_DPP) {
+               int mgmt = RSN_AUTH_KEY_MGMT_PSK_OVER_802_1X;
 
                switch (params->key_mgmt_suite) {
                case WPA_KEY_MGMT_CCKM:
-                       mgmt = WLAN_AKM_SUITE_CCKM;
+                       mgmt = RSN_AUTH_KEY_MGMT_CCKM;
                        break;
                case WPA_KEY_MGMT_IEEE8021X:
-                       mgmt = WLAN_AKM_SUITE_8021X;
+                       mgmt = RSN_AUTH_KEY_MGMT_UNSPEC_802_1X;
                        break;
                case WPA_KEY_MGMT_FT_IEEE8021X:
-                       mgmt = WLAN_AKM_SUITE_FT_8021X;
+                       mgmt = RSN_AUTH_KEY_MGMT_FT_802_1X;
                        break;
                case WPA_KEY_MGMT_FT_PSK:
-                       mgmt = WLAN_AKM_SUITE_FT_PSK;
+                       mgmt = RSN_AUTH_KEY_MGMT_FT_PSK;
                        break;
                case WPA_KEY_MGMT_IEEE8021X_SHA256:
-                       mgmt = WLAN_AKM_SUITE_8021X_SHA256;
+                       mgmt = RSN_AUTH_KEY_MGMT_802_1X_SHA256;
                        break;
                case WPA_KEY_MGMT_PSK_SHA256:
-                       mgmt = WLAN_AKM_SUITE_PSK_SHA256;
+                       mgmt = RSN_AUTH_KEY_MGMT_PSK_SHA256;
                        break;
                case WPA_KEY_MGMT_OSEN:
-                       mgmt = WLAN_AKM_SUITE_OSEN;
+                       mgmt = RSN_AUTH_KEY_MGMT_OSEN;
                        break;
                case WPA_KEY_MGMT_IEEE8021X_SUITE_B:
-                       mgmt = WLAN_AKM_SUITE_8021X_SUITE_B;
+                       mgmt = RSN_AUTH_KEY_MGMT_802_1X_SUITE_B;
                        break;
                case WPA_KEY_MGMT_IEEE8021X_SUITE_B_192:
-                       mgmt = WLAN_AKM_SUITE_8021X_SUITE_B_192;
+                       mgmt = RSN_AUTH_KEY_MGMT_802_1X_SUITE_B_192;
+                       break;
+               case WPA_KEY_MGMT_FILS_SHA256:
+                       mgmt = RSN_AUTH_KEY_MGMT_FILS_SHA256;
+                       break;
+               case WPA_KEY_MGMT_FILS_SHA384:
+                       mgmt = RSN_AUTH_KEY_MGMT_FILS_SHA384;
+                       break;
+               case WPA_KEY_MGMT_FT_FILS_SHA256:
+                       mgmt = RSN_AUTH_KEY_MGMT_FT_FILS_SHA256;
+                       break;
+               case WPA_KEY_MGMT_FT_FILS_SHA384:
+                       mgmt = RSN_AUTH_KEY_MGMT_FT_FILS_SHA384;
+                       break;
+               case WPA_KEY_MGMT_OWE:
+                       mgmt = RSN_AUTH_KEY_MGMT_OWE;
+                       break;
+               case WPA_KEY_MGMT_DPP:
+                       mgmt = RSN_AUTH_KEY_MGMT_DPP;
                        break;
                case WPA_KEY_MGMT_PSK:
                default:
-                       mgmt = WLAN_AKM_SUITE_PSK;
+                       mgmt = RSN_AUTH_KEY_MGMT_PSK_OVER_802_1X;
                        break;
                }
                wpa_printf(MSG_DEBUG, "  * akm=0x%x", mgmt);
@@ -5053,6 +5392,14 @@ static int nl80211_connect_common(struct wpa_driver_nl80211_data *drv,
                        return -1;
        }
 
+       /* Add PSK in case of 4-way handshake offload */
+       if (params->psk &&
+           (drv->capa.flags & WPA_DRIVER_FLAGS_4WAY_HANDSHAKE)) {
+               wpa_hexdump_key(MSG_DEBUG, "  * PSK", params->psk, 32);
+               if (nla_put(msg, NL80211_ATTR_PMK, 32, params->psk))
+                       return -1;
+       }
+
        if (nla_put_flag(msg, NL80211_ATTR_CONTROL_PORT))
                return -1;
 
@@ -5064,10 +5411,6 @@ static int nl80211_connect_common(struct wpa_driver_nl80211_data *drv,
             nla_put_flag(msg, NL80211_ATTR_CONTROL_PORT_NO_ENCRYPT)))
                return -1;
 
-       if (params->mgmt_frame_protection == MGMT_FRAME_PROTECTION_REQUIRED &&
-           nla_put_u32(msg, NL80211_ATTR_USE_MFP, NL80211_MFP_REQUIRED))
-               return -1;
-
        if (params->rrm_used) {
                u32 drv_rrm_flags = drv->capa.rrm_flags;
                if ((!((drv_rrm_flags &
@@ -5100,13 +5443,23 @@ static int nl80211_connect_common(struct wpa_driver_nl80211_data *drv,
                drv->connect_reassoc = 1;
        }
 
+       if ((params->auth_alg & WPA_AUTH_ALG_FILS) &&
+           nl80211_put_fils_connect_params(drv, params, msg) != 0)
+               return -1;
+
+       if ((params->auth_alg & WPA_AUTH_ALG_SAE) &&
+           (!(drv->capa.flags & WPA_DRIVER_FLAGS_SME)) &&
+           nla_put_flag(msg, NL80211_ATTR_EXTERNAL_AUTH_SUPPORT))
+               return -1;
+
        return 0;
 }
 
 
 static int wpa_driver_nl80211_try_connect(
        struct wpa_driver_nl80211_data *drv,
-       struct wpa_driver_associate_params *params)
+       struct wpa_driver_associate_params *params,
+       struct nl_handle *nl_connect)
 {
        struct nl_msg *msg;
        enum nl80211_auth_type type;
@@ -5134,6 +5487,15 @@ static int wpa_driver_nl80211_try_connect(
        if (ret)
                goto fail;
 
+       if (params->mgmt_frame_protection == MGMT_FRAME_PROTECTION_REQUIRED &&
+           nla_put_u32(msg, NL80211_ATTR_USE_MFP, NL80211_MFP_REQUIRED))
+               goto fail;
+
+       if (params->mgmt_frame_protection == MGMT_FRAME_PROTECTION_OPTIONAL &&
+           (drv->capa.flags & WPA_DRIVER_FLAGS_MFP_OPTIONAL) &&
+           nla_put_u32(msg, NL80211_ATTR_USE_MFP, NL80211_MFP_OPTIONAL))
+               goto fail;
+
        algs = 0;
        if (params->auth_alg & WPA_AUTH_ALG_OPEN)
                algs++;
@@ -5141,25 +5503,20 @@ static int wpa_driver_nl80211_try_connect(
                algs++;
        if (params->auth_alg & WPA_AUTH_ALG_LEAP)
                algs++;
+       if (params->auth_alg & WPA_AUTH_ALG_FILS)
+               algs++;
+       if (params->auth_alg & WPA_AUTH_ALG_FT)
+               algs++;
        if (algs > 1) {
                wpa_printf(MSG_DEBUG, "  * Leave out Auth Type for automatic "
                           "selection");
                goto skip_auth_type;
        }
 
-       if (params->auth_alg & WPA_AUTH_ALG_OPEN)
-               type = NL80211_AUTHTYPE_OPEN_SYSTEM;
-       else if (params->auth_alg & WPA_AUTH_ALG_SHARED)
-               type = NL80211_AUTHTYPE_SHARED_KEY;
-       else if (params->auth_alg & WPA_AUTH_ALG_LEAP)
-               type = NL80211_AUTHTYPE_NETWORK_EAP;
-       else if (params->auth_alg & WPA_AUTH_ALG_FT)
-               type = NL80211_AUTHTYPE_FT;
-       else
-               goto fail;
-
+       type = get_nl_auth_type(params->auth_alg);
        wpa_printf(MSG_DEBUG, "  * Auth Type %d", type);
-       if (nla_put_u32(msg, NL80211_ATTR_AUTH_TYPE, type))
+       if (type == NL80211_AUTHTYPE_MAX ||
+           nla_put_u32(msg, NL80211_ATTR_AUTH_TYPE, type))
                goto fail;
 
 skip_auth_type:
@@ -5167,7 +5524,11 @@ skip_auth_type:
        if (ret)
                goto fail;
 
-       ret = send_and_recv_msgs(drv, msg, NULL, NULL);
+       if (nl_connect)
+               ret = send_and_recv(drv->global, nl_connect, msg, NULL, NULL);
+       else
+               ret = send_and_recv_msgs(drv, msg, NULL, NULL);
+
        msg = NULL;
        if (ret) {
                wpa_printf(MSG_DEBUG, "nl80211: MLME connect failed: ret=%d "
@@ -5186,7 +5547,8 @@ fail:
 
 static int wpa_driver_nl80211_connect(
        struct wpa_driver_nl80211_data *drv,
-       struct wpa_driver_associate_params *params)
+       struct wpa_driver_associate_params *params,
+       struct nl_handle *nl_connect)
 {
        int ret;
 
@@ -5196,7 +5558,7 @@ static int wpa_driver_nl80211_connect(
        else
                os_memset(drv->auth_attempt_bssid, 0, ETH_ALEN);
 
-       ret = wpa_driver_nl80211_try_connect(drv, params);
+       ret = wpa_driver_nl80211_try_connect(drv, params, nl_connect);
        if (ret == -EALREADY) {
                /*
                 * cfg80211 does not currently accept new connections if
@@ -5209,7 +5571,7 @@ static int wpa_driver_nl80211_connect(
                if (wpa_driver_nl80211_disconnect(
                            drv, WLAN_REASON_PREV_AUTH_NOT_VALID))
                        return -1;
-               ret = wpa_driver_nl80211_try_connect(drv, params);
+               ret = wpa_driver_nl80211_try_connect(drv, params, nl_connect);
        }
        return ret;
 }
@@ -5234,10 +5596,13 @@ static int wpa_driver_nl80211_associate(
        if (!(drv->capa.flags & WPA_DRIVER_FLAGS_SME)) {
                enum nl80211_iftype nlmode = params->p2p ?
                        NL80211_IFTYPE_P2P_CLIENT : NL80211_IFTYPE_STATION;
+               struct nl_handle *nl_connect = NULL;
 
                if (wpa_driver_nl80211_set_mode(priv, nlmode) < 0)
                        return -1;
-               return wpa_driver_nl80211_connect(drv, params);
+               if (params->auth_alg & WPA_AUTH_ALG_SAE)
+                       nl_connect = bss->nl_connect;
+               return wpa_driver_nl80211_connect(drv, params, nl_connect);
        }
 
        nl80211_mark_disconnected(drv);
@@ -5252,6 +5617,10 @@ static int wpa_driver_nl80211_associate(
        if (ret)
                goto fail;
 
+       if (params->mgmt_frame_protection == MGMT_FRAME_PROTECTION_REQUIRED &&
+           nla_put_u32(msg, NL80211_ATTR_USE_MFP, NL80211_MFP_REQUIRED))
+               goto fail;
+
        if (params->fils_kek) {
                wpa_printf(MSG_DEBUG, "  * FILS KEK (len=%u)",
                           (unsigned int) params->fils_kek_len);
@@ -5684,6 +6053,17 @@ static int get_sta_handler(struct nl_msg *msg, void *arg)
                [NL80211_STA_INFO_TX_FAILED] = { .type = NLA_U32 },
                [NL80211_STA_INFO_RX_BYTES64] = { .type = NLA_U64 },
                [NL80211_STA_INFO_TX_BYTES64] = { .type = NLA_U64 },
+               [NL80211_STA_INFO_SIGNAL] = { .type = NLA_U8 },
+               [NL80211_STA_INFO_ACK_SIGNAL] = { .type = NLA_U8 },
+       };
+       struct nlattr *rate[NL80211_RATE_INFO_MAX + 1];
+       static struct nla_policy rate_policy[NL80211_RATE_INFO_MAX + 1] = {
+               [NL80211_RATE_INFO_BITRATE] = { .type = NLA_U16 },
+               [NL80211_RATE_INFO_BITRATE32] = { .type = NLA_U32 },
+               [NL80211_RATE_INFO_MCS] = { .type = NLA_U8 },
+               [NL80211_RATE_INFO_VHT_MCS] = { .type = NLA_U8 },
+               [NL80211_RATE_INFO_SHORT_GI] = { .type = NLA_FLAG },
+               [NL80211_RATE_INFO_VHT_NSS] = { .type = NLA_U8 },
        };
 
        nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
@@ -5735,6 +6115,72 @@ static int get_sta_handler(struct nl_msg *msg, void *arg)
        if (stats[NL80211_STA_INFO_TX_FAILED])
                data->tx_retry_failed =
                        nla_get_u32(stats[NL80211_STA_INFO_TX_FAILED]);
+       if (stats[NL80211_STA_INFO_SIGNAL])
+               data->signal = nla_get_u8(stats[NL80211_STA_INFO_SIGNAL]);
+       if (stats[NL80211_STA_INFO_ACK_SIGNAL]) {
+               data->last_ack_rssi =
+                       nla_get_u8(stats[NL80211_STA_INFO_ACK_SIGNAL]);
+               data->flags |= STA_DRV_DATA_LAST_ACK_RSSI;
+       }
+
+       if (stats[NL80211_STA_INFO_TX_BITRATE] &&
+           nla_parse_nested(rate, NL80211_RATE_INFO_MAX,
+                            stats[NL80211_STA_INFO_TX_BITRATE],
+                            rate_policy) == 0) {
+               if (rate[NL80211_RATE_INFO_BITRATE32])
+                       data->current_tx_rate =
+                               nla_get_u32(rate[NL80211_RATE_INFO_BITRATE32]);
+               else if (rate[NL80211_RATE_INFO_BITRATE])
+                       data->current_tx_rate =
+                               nla_get_u16(rate[NL80211_RATE_INFO_BITRATE]);
+
+               if (rate[NL80211_RATE_INFO_MCS]) {
+                       data->tx_mcs = nla_get_u8(rate[NL80211_RATE_INFO_MCS]);
+                       data->flags |= STA_DRV_DATA_TX_MCS;
+               }
+               if (rate[NL80211_RATE_INFO_VHT_MCS]) {
+                       data->tx_vhtmcs =
+                               nla_get_u8(rate[NL80211_RATE_INFO_VHT_MCS]);
+                       data->flags |= STA_DRV_DATA_TX_VHT_MCS;
+               }
+               if (rate[NL80211_RATE_INFO_SHORT_GI])
+                       data->flags |= STA_DRV_DATA_TX_SHORT_GI;
+               if (rate[NL80211_RATE_INFO_VHT_NSS]) {
+                       data->tx_vht_nss =
+                               nla_get_u8(rate[NL80211_RATE_INFO_VHT_NSS]);
+                       data->flags |= STA_DRV_DATA_TX_VHT_NSS;
+               }
+       }
+
+       if (stats[NL80211_STA_INFO_RX_BITRATE] &&
+           nla_parse_nested(rate, NL80211_RATE_INFO_MAX,
+                            stats[NL80211_STA_INFO_RX_BITRATE],
+                            rate_policy) == 0) {
+               if (rate[NL80211_RATE_INFO_BITRATE32])
+                       data->current_rx_rate =
+                               nla_get_u32(rate[NL80211_RATE_INFO_BITRATE32]);
+               else if (rate[NL80211_RATE_INFO_BITRATE])
+                       data->current_rx_rate =
+                               nla_get_u16(rate[NL80211_RATE_INFO_BITRATE]);
+
+               if (rate[NL80211_RATE_INFO_MCS]) {
+                       data->rx_mcs =
+                               nla_get_u8(rate[NL80211_RATE_INFO_MCS]);
+                       data->flags |= STA_DRV_DATA_RX_MCS;
+               }
+               if (rate[NL80211_RATE_INFO_VHT_MCS]) {
+                       data->rx_vhtmcs =
+                               nla_get_u8(rate[NL80211_RATE_INFO_VHT_MCS]);
+                       data->flags |= STA_DRV_DATA_RX_VHT_MCS;
+               }
+               if (rate[NL80211_RATE_INFO_SHORT_GI])
+                       data->flags |= STA_DRV_DATA_RX_SHORT_GI;
+               if (rate[NL80211_RATE_INFO_VHT_NSS]) {
+                       data->rx_vht_nss =
+                               nla_get_u8(rate[NL80211_RATE_INFO_VHT_NSS]);
+                       data->flags |= STA_DRV_DATA_RX_VHT_NSS;
+               }
+       }
 
        return NL_SKIP;
 }
@@ -5876,6 +6322,14 @@ static int i802_sta_deauth(void *priv, const u8 *own_addr, const u8 *addr,
        struct i802_bss *bss = priv;
        struct wpa_driver_nl80211_data *drv = bss->drv;
        struct ieee80211_mgmt mgmt;
+       u8 channel;
+
+       if (ieee80211_freq_to_chan(bss->freq, &channel) ==
+           HOSTAPD_MODE_IEEE80211AD) {
+               /* Deauthentication is not used in DMG/IEEE 802.11ad;
+                * disassociate the STA instead. */
+               return i802_sta_disassoc(priv, own_addr, addr, reason);
+       }
 
        if (is_mesh_interface(drv->nlmode))
                return -1;
@@ -6057,6 +6511,7 @@ static int i802_set_wds_sta(void *priv, const u8 *addr, int aid, int val,
        struct i802_bss *bss = priv;
        struct wpa_driver_nl80211_data *drv = bss->drv;
        char name[IFNAMSIZ + 1];
+       union wpa_event_data event;
 
        os_snprintf(name, sizeof(name), "%s.sta%d", bss->ifname, aid);
        if (ifname_wds)
@@ -6075,6 +6530,14 @@ static int i802_set_wds_sta(void *priv, const u8 *addr, int aid, int val,
                            linux_br_add_if(drv->global->ioctl_sock,
                                            bridge_ifname, name) < 0)
                                return -1;
+
+                       os_memset(&event, 0, sizeof(event));
+                       event.wds_sta_interface.sta_addr = addr;
+                       event.wds_sta_interface.ifname = name;
+                       event.wds_sta_interface.istatus = INTERFACE_ADDED;
+                       wpa_supplicant_event(drv->ctx,
+                                            EVENT_WDS_STA_INTERFACE_STATUS,
+                                            &event);
                }
                if (linux_set_iface_flags(drv->global->ioctl_sock, name, 1)) {
                        wpa_printf(MSG_ERROR, "nl80211: Failed to set WDS STA "
@@ -6088,6 +6551,12 @@ static int i802_set_wds_sta(void *priv, const u8 *addr, int aid, int val,
 
                i802_set_sta_vlan(priv, addr, bss->ifname, 0);
                nl80211_remove_iface(drv, if_nametoindex(name));
+               os_memset(&event, 0, sizeof(event));
+               event.wds_sta_interface.sta_addr = addr;
+               event.wds_sta_interface.ifname = name;
+               event.wds_sta_interface.istatus = INTERFACE_REMOVED;
+               wpa_supplicant_event(drv->ctx, EVENT_WDS_STA_INTERFACE_STATUS,
+                                    &event);
                return 0;
        }
 }
@@ -6151,7 +6620,7 @@ static int i802_check_bridge(struct wpa_driver_nl80211_data *drv,
                        wpa_printf(MSG_ERROR, "nl80211: Failed to "
                                   "remove interface %s from bridge "
                                   "%s: %s",
-                                  ifname, brname, strerror(errno));
+                                  ifname, in_br, strerror(errno));
                        return -1;
                }
        }
@@ -6589,7 +7058,7 @@ static int wpa_driver_nl80211_if_remove(struct i802_bss *bss,
                wpa_printf(MSG_DEBUG, "nl80211: First BSS - reassign context");
                nl80211_teardown_ap(bss);
                if (!bss->added_if && !drv->first_bss->next)
-                       wpa_driver_nl80211_del_beacon(drv);
+                       wpa_driver_nl80211_del_beacon(bss);
                nl80211_destroy_bss(bss);
                if (!bss->added_if)
                        i802_set_iface_flags(bss, 0);
@@ -6713,6 +7182,14 @@ static int wpa_driver_nl80211_send_action(struct i802_bss *bss,
        os_memcpy(hdr->addr2, src, ETH_ALEN);
        os_memcpy(hdr->addr3, bssid, ETH_ALEN);
 
+       if (os_memcmp(bss->addr, src, ETH_ALEN) != 0) {
+               wpa_printf(MSG_DEBUG, "nl80211: Use random TA " MACSTR,
+                          MAC2STR(src));
+               os_memcpy(bss->rand_addr, src, ETH_ALEN);
+       } else {
+               os_memset(bss->rand_addr, 0, ETH_ALEN);
+       }
+
        if (is_ap_interface(drv->nlmode) &&
            (!(drv->capa.flags & WPA_DRIVER_FLAGS_OFFCHANNEL_TX) ||
             (int) freq == bss->freq || drv->device_ap_sme ||
@@ -6860,7 +7337,7 @@ static int wpa_driver_nl80211_probe_req_report(struct i802_bss *bss, int report)
                } else if (bss->nl_preq) {
                        wpa_printf(MSG_DEBUG, "nl80211: Disable Probe Request "
                                   "reporting nl_preq=%p", bss->nl_preq);
-                       nl80211_destroy_eloop_handle(&bss->nl_preq);
+                       nl80211_destroy_eloop_handle(&bss->nl_preq, 0);
                }
                return 0;
        }
@@ -6885,7 +7362,7 @@ static int wpa_driver_nl80211_probe_req_report(struct i802_bss *bss, int report)
 
        nl80211_register_eloop_read(&bss->nl_preq,
                                    wpa_driver_nl80211_event_receive,
-                                   bss->nl_cb);
+                                   bss->nl_cb, 0);
 
        return 0;
 
@@ -6951,7 +7428,7 @@ static int wpa_driver_nl80211_deinit_ap(void *priv)
        struct wpa_driver_nl80211_data *drv = bss->drv;
        if (!is_ap_interface(drv->nlmode))
                return -1;
-       wpa_driver_nl80211_del_beacon(drv);
+       wpa_driver_nl80211_del_beacon(bss);
        bss->beacon_set = 0;
 
        /*
@@ -6971,7 +7448,7 @@ static int wpa_driver_nl80211_stop_ap(void *priv)
        struct wpa_driver_nl80211_data *drv = bss->drv;
        if (!is_ap_interface(drv->nlmode))
                return -1;
-       wpa_driver_nl80211_del_beacon(drv);
+       wpa_driver_nl80211_del_beacon(bss);
        bss->beacon_set = 0;
        return 0;
 }
@@ -7200,7 +7677,7 @@ static void nl80211_global_deinit(void *priv)
        nl_destroy_handles(&global->nl);
 
        if (global->nl_event)
-               nl80211_destroy_eloop_handle(&global->nl_event);
+               nl80211_destroy_eloop_handle(&global->nl_event, 0);
 
        nl_cb_put(global->nl_cb);
 
@@ -7219,14 +7696,24 @@ static const char * nl80211_get_radio_name(void *priv)
 }
 
 
-static int nl80211_pmkid(struct i802_bss *bss, int cmd, const u8 *bssid,
-                        const u8 *pmkid)
+static int nl80211_pmkid(struct i802_bss *bss, int cmd,
+                        struct wpa_pmkid_params *params)
 {
        struct nl_msg *msg;
+       const size_t PMK_MAX_LEN = 48; /* current cfg80211 limit */
 
        if (!(msg = nl80211_bss_msg(bss, 0, cmd)) ||
-           (pmkid && nla_put(msg, NL80211_ATTR_PMKID, 16, pmkid)) ||
-           (bssid && nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, bssid))) {
+           (params->pmkid &&
+            nla_put(msg, NL80211_ATTR_PMKID, 16, params->pmkid)) ||
+           (params->bssid &&
+            nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, params->bssid)) ||
+           (params->ssid_len &&
+            nla_put(msg, NL80211_ATTR_SSID, params->ssid_len, params->ssid)) ||
+           (params->fils_cache_id &&
+            nla_put(msg, NL80211_ATTR_FILS_CACHE_ID, 2,
+                    params->fils_cache_id)) ||
+           (params->pmk_len && params->pmk_len <= PMK_MAX_LEN &&
+            nla_put(msg, NL80211_ATTR_PMK, params->pmk_len, params->pmk))) {
                nlmsg_free(msg);
                return -ENOBUFS;
        }
@@ -7235,28 +7722,68 @@ static int nl80211_pmkid(struct i802_bss *bss, int cmd, const u8 *bssid,
 }
 
 
-static int nl80211_add_pmkid(void *priv, const u8 *bssid, const u8 *pmkid)
+static int nl80211_add_pmkid(void *priv, struct wpa_pmkid_params *params)
 {
        struct i802_bss *bss = priv;
-       wpa_printf(MSG_DEBUG, "nl80211: Add PMKID for " MACSTR, MAC2STR(bssid));
-       return nl80211_pmkid(bss, NL80211_CMD_SET_PMKSA, bssid, pmkid);
+       int ret;
+
+       if (params->bssid)
+               wpa_printf(MSG_DEBUG, "nl80211: Add PMKID for " MACSTR,
+                          MAC2STR(params->bssid));
+       else if (params->fils_cache_id && params->ssid_len) {
+               wpa_printf(MSG_DEBUG,
+                          "nl80211: Add PMKSA for cache id %02x%02x SSID %s",
+                          params->fils_cache_id[0], params->fils_cache_id[1],
+                          wpa_ssid_txt(params->ssid, params->ssid_len));
+       }
+
+       ret = nl80211_pmkid(bss, NL80211_CMD_SET_PMKSA, params);
+       if (ret < 0) {
+               wpa_printf(MSG_DEBUG,
+                          "nl80211: NL80211_CMD_SET_PMKSA failed: %d (%s)",
+                          ret, strerror(-ret));
+       }
+
+       return ret;
 }
 
 
-static int nl80211_remove_pmkid(void *priv, const u8 *bssid, const u8 *pmkid)
+static int nl80211_remove_pmkid(void *priv, struct wpa_pmkid_params *params)
 {
        struct i802_bss *bss = priv;
-       wpa_printf(MSG_DEBUG, "nl80211: Delete PMKID for " MACSTR,
-                  MAC2STR(bssid));
-       return nl80211_pmkid(bss, NL80211_CMD_DEL_PMKSA, bssid, pmkid);
+       int ret;
+
+       if (params->bssid)
+               wpa_printf(MSG_DEBUG, "nl80211: Delete PMKID for " MACSTR,
+                          MAC2STR(params->bssid));
+       else if (params->fils_cache_id && params->ssid_len) {
+               wpa_printf(MSG_DEBUG,
+                          "nl80211: Delete PMKSA for cache id %02x%02x SSID %s",
+                          params->fils_cache_id[0], params->fils_cache_id[1],
+                          wpa_ssid_txt(params->ssid, params->ssid_len));
+       }
+
+       ret = nl80211_pmkid(bss, NL80211_CMD_DEL_PMKSA, params);
+       if (ret < 0) {
+               wpa_printf(MSG_DEBUG,
+                          "nl80211: NL80211_CMD_DEL_PMKSA failed: %d (%s)",
+                          ret, strerror(-ret));
+       }
+
+       return ret;
 }
 
 
 static int nl80211_flush_pmkid(void *priv)
 {
        struct i802_bss *bss = priv;
+       struct nl_msg *msg;
+
        wpa_printf(MSG_DEBUG, "nl80211: Flush PMKIDs");
-       return nl80211_pmkid(bss, NL80211_CMD_FLUSH_PMKSA, NULL, NULL);
+       msg = nl80211_bss_msg(bss, 0, NL80211_CMD_FLUSH_PMKSA);
+       if (!msg)
+               return -ENOBUFS;
+       return send_and_recv_msgs(bss->drv, msg, NULL, NULL);
 }
 
 
@@ -7451,7 +7978,7 @@ static void nl80211_set_rekey_info(void *priv, const u8 *kek, size_t kek_len,
        if (!(msg = nl80211_bss_msg(bss, 0, NL80211_CMD_SET_REKEY_OFFLOAD)) ||
            !(replay_nested = nla_nest_start(msg, NL80211_ATTR_REKEY_DATA)) ||
            nla_put(msg, NL80211_REKEY_DATA_KEK, kek_len, kek) ||
-           nla_put(msg, NL80211_REKEY_DATA_KCK, kck_len, kck) ||
+           (kck_len && nla_put(msg, NL80211_REKEY_DATA_KCK, kck_len, kck)) ||
            nla_put(msg, NL80211_REKEY_DATA_REPLAY_CTR, NL80211_REPLAY_CTR_LEN,
                    replay_ctr)) {
                nl80211_nlmsg_clear(msg);
@@ -7539,6 +8066,7 @@ static void nl80211_poll_client(void *priv, const u8 *own_addr, const u8 *addr,
 static int nl80211_set_power_save(struct i802_bss *bss, int enabled)
 {
        struct nl_msg *msg;
+       int ret;
 
        if (!(msg = nl80211_bss_msg(bss, 0, NL80211_CMD_SET_POWER_SAVE)) ||
            nla_put_u32(msg, NL80211_ATTR_PS_STATE,
@@ -7546,7 +8074,15 @@ static int nl80211_set_power_save(struct i802_bss *bss, int enabled)
                nlmsg_free(msg);
                return -ENOBUFS;
        }
-       return send_and_recv_msgs(bss->drv, msg, NULL, NULL);
+
+       ret = send_and_recv_msgs(bss->drv, msg, NULL, NULL);
+       if (ret < 0) {
+               wpa_printf(MSG_DEBUG,
+                          "nl80211: Setting PS state %s failed: %d (%s)",
+                          enabled ? "enabled" : "disabled",
+                          ret, strerror(-ret));
+       }
+       return ret;
 }
 
 
@@ -7660,6 +8196,7 @@ static int nl80211_tdls_oper(void *priv, enum tdls_oper oper, const u8 *peer)
        struct wpa_driver_nl80211_data *drv = bss->drv;
        struct nl_msg *msg;
        enum nl80211_tdls_operation nl80211_oper;
+       int res;
 
        if (!(drv->capa.flags & WPA_DRIVER_FLAGS_TDLS_SUPPORT))
                return -EOPNOTSUPP;
@@ -7695,7 +8232,11 @@ static int nl80211_tdls_oper(void *priv, enum tdls_oper oper, const u8 *peer)
                return -ENOBUFS;
        }
 
-       return send_and_recv_msgs(drv, msg, NULL, NULL);
+       res = send_and_recv_msgs(drv, msg, NULL, NULL);
+       wpa_printf(MSG_DEBUG, "nl80211: TDLS_OPER: oper=%d mac=" MACSTR
+                  " --> res=%d (%s)", nl80211_oper, MAC2STR(peer), res,
+                  strerror(-res));
+       return res;
 }
 
 
@@ -8505,6 +9046,95 @@ static int nl80211_roaming(void *priv, int allowed, const u8 *bssid)
 
        return send_and_recv_msgs(drv, msg, NULL, NULL);
 }
+
+
+static int nl80211_disable_fils(void *priv, int disable)
+{
+       struct i802_bss *bss = priv;
+       struct wpa_driver_nl80211_data *drv = bss->drv;
+       struct nl_msg *msg;
+       struct nlattr *params;
+
+       wpa_printf(MSG_DEBUG, "nl80211: Disable FILS=%d", disable);
+
+       if (!drv->set_wifi_conf_vendor_cmd_avail)
+               return -1;
+
+       if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR)) ||
+           nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_QCA) ||
+           nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD,
+                       QCA_NL80211_VENDOR_SUBCMD_SET_WIFI_CONFIGURATION) ||
+           !(params = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA)) ||
+           nla_put_u8(msg, QCA_WLAN_VENDOR_ATTR_CONFIG_DISABLE_FILS,
+                      disable)) {
+               nlmsg_free(msg);
+               return -1;
+       }
+       nla_nest_end(msg, params);
+
+       return send_and_recv_msgs(drv, msg, NULL, NULL);
+}
+
+
+/* Reserved QCA_WLAN_VENDOR_ATTR_ROAMING_REQ_ID value for wpa_supplicant */
+#define WPA_SUPPLICANT_CLIENT_ID 1
+
+static int nl80211_set_bssid_blacklist(void *priv, unsigned int num_bssid,
+                                      const u8 *bssid)
+{
+       struct i802_bss *bss = priv;
+       struct wpa_driver_nl80211_data *drv = bss->drv;
+       struct nl_msg *msg;
+       struct nlattr *params, *nlbssids, *attr;
+       unsigned int i;
+
+       wpa_printf(MSG_DEBUG, "nl80211: Set blacklist BSSID (num=%u)",
+                  num_bssid);
+
+       if (!drv->roam_vendor_cmd_avail)
+               return -1;
+
+       if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR)) ||
+           nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_QCA) ||
+           nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD,
+                       QCA_NL80211_VENDOR_SUBCMD_ROAM) ||
+           !(params = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA)) ||
+           nla_put_u32(msg, QCA_WLAN_VENDOR_ATTR_ROAMING_SUBCMD,
+                       QCA_WLAN_VENDOR_ATTR_ROAM_SUBCMD_SET_BLACKLIST_BSSID) ||
+           nla_put_u32(msg, QCA_WLAN_VENDOR_ATTR_ROAMING_REQ_ID,
+                       WPA_SUPPLICANT_CLIENT_ID) ||
+           nla_put_u32(msg,
+                       QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_SET_BSSID_PARAMS_NUM_BSSID,
+                       num_bssid))
+               goto fail;
+
+       nlbssids = nla_nest_start(
+               msg, QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_SET_BSSID_PARAMS);
+       if (!nlbssids)
+               goto fail;
+
+       for (i = 0; i < num_bssid; i++) {
+               attr = nla_nest_start(msg, i);
+               if (!attr)
+                       goto fail;
+               if (nla_put(msg,
+                           QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_SET_BSSID_PARAMS_BSSID,
+                           ETH_ALEN, &bssid[i * ETH_ALEN]))
+                       goto fail;
+               wpa_printf(MSG_DEBUG, "nl80211:   BSSID[%u]: " MACSTR, i,
+                          MAC2STR(&bssid[i * ETH_ALEN]));
+               nla_nest_end(msg, attr);
+       }
+       nla_nest_end(msg, nlbssids);
+       nla_nest_end(msg, params);
+
+       return send_and_recv_msgs(drv, msg, NULL, NULL);
+
+fail:
+       nlmsg_free(msg);
+       return -1;
+}
+
 #endif /* CONFIG_DRIVER_NL80211_QCA */
 
 
@@ -8587,11 +9217,14 @@ static int nl80211_put_mesh_config(struct nl_msg *msg,
                return -1;
 
        if (((params->flags & WPA_DRIVER_MESH_CONF_FLAG_AUTO_PLINKS) &&
-            nla_put_u32(msg, NL80211_MESHCONF_AUTO_OPEN_PLINKS,
-                        params->auto_plinks)) ||
+            nla_put_u8(msg, NL80211_MESHCONF_AUTO_OPEN_PLINKS,
+                       params->auto_plinks)) ||
            ((params->flags & WPA_DRIVER_MESH_CONF_FLAG_MAX_PEER_LINKS) &&
             nla_put_u16(msg, NL80211_MESHCONF_MAX_PEER_LINKS,
-                        params->max_peer_links)))
+                        params->max_peer_links)) ||
+           ((params->flags & WPA_DRIVER_MESH_CONF_FLAG_RSSI_THRESHOLD) &&
+            nla_put_u32(msg, NL80211_MESHCONF_RSSI_THRESHOLD,
+                        params->rssi_threshold)))
                return -1;
 
        /*
@@ -9474,6 +10107,254 @@ fail:
        return -1;
 }
 
+
+#ifdef CONFIG_MBO
+
+static enum mbo_transition_reject_reason
+nl80211_mbo_reject_reason_mapping(enum qca_wlan_btm_candidate_status status)
+{
+       switch (status) {
+       case QCA_STATUS_REJECT_EXCESSIVE_FRAME_LOSS_EXPECTED:
+               return MBO_TRANSITION_REJECT_REASON_FRAME_LOSS;
+       case QCA_STATUS_REJECT_EXCESSIVE_DELAY_EXPECTED:
+               return MBO_TRANSITION_REJECT_REASON_DELAY;
+       case QCA_STATUS_REJECT_INSUFFICIENT_QOS_CAPACITY:
+               return MBO_TRANSITION_REJECT_REASON_QOS_CAPACITY;
+       case QCA_STATUS_REJECT_LOW_RSSI:
+               return MBO_TRANSITION_REJECT_REASON_RSSI;
+       case QCA_STATUS_REJECT_HIGH_INTERFERENCE:
+               return MBO_TRANSITION_REJECT_REASON_INTERFERENCE;
+       case QCA_STATUS_REJECT_UNKNOWN:
+       default:
+               return MBO_TRANSITION_REJECT_REASON_UNSPECIFIED;
+       }
+}
+
+
+static void nl80211_parse_btm_candidate_info(struct candidate_list *candidate,
+                                            struct nlattr *tb[], int num)
+{
+       enum qca_wlan_btm_candidate_status status;
+       char buf[50];
+
+       os_memcpy(candidate->bssid,
+                 nla_data(tb[QCA_WLAN_VENDOR_ATTR_BTM_CANDIDATE_INFO_BSSID]),
+                 ETH_ALEN);
+
+       status = nla_get_u32(
+               tb[QCA_WLAN_VENDOR_ATTR_BTM_CANDIDATE_INFO_STATUS]);
+       candidate->is_accept = status == QCA_STATUS_ACCEPT;
+       candidate->reject_reason = nl80211_mbo_reject_reason_mapping(status);
+
+       if (candidate->is_accept)
+               os_snprintf(buf, sizeof(buf), "Accepted");
+       else
+               os_snprintf(buf, sizeof(buf),
+                           "Rejected, Reject_reason: %d",
+                           candidate->reject_reason);
+       wpa_printf(MSG_DEBUG, "nl80211:   BSSID[%d]: " MACSTR " %s",
+                  num, MAC2STR(candidate->bssid), buf);
+}
+
+
+static int
+nl80211_get_bss_transition_status_handler(struct nl_msg *msg, void *arg)
+{
+       struct wpa_bss_candidate_info *info = arg;
+       struct candidate_list *candidate = info->candidates;
+       struct nlattr *tb_msg[NL80211_ATTR_MAX + 1];
+       struct nlattr *tb_vendor[QCA_WLAN_VENDOR_ATTR_MAX + 1];
+       struct nlattr *tb[QCA_WLAN_VENDOR_ATTR_BTM_CANDIDATE_INFO_MAX + 1];
+       static struct nla_policy policy[
+               QCA_WLAN_VENDOR_ATTR_BTM_CANDIDATE_INFO_MAX + 1] = {
+               [QCA_WLAN_VENDOR_ATTR_BTM_CANDIDATE_INFO_BSSID] = {
+                       .minlen = ETH_ALEN
+               },
+               [QCA_WLAN_VENDOR_ATTR_BTM_CANDIDATE_INFO_STATUS] = {
+                       .type = NLA_U32,
+               },
+       };
+       struct nlattr *attr;
+       int rem;
+       struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
+       u8 num;
+
+       num = info->num; /* number of candidates sent to driver */
+       info->num = 0;
+       nla_parse(tb_msg, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
+                 genlmsg_attrlen(gnlh, 0), NULL);
+
+       if (!tb_msg[NL80211_ATTR_VENDOR_DATA] ||
+           nla_parse_nested(tb_vendor, QCA_WLAN_VENDOR_ATTR_MAX,
+                            tb_msg[NL80211_ATTR_VENDOR_DATA], NULL) ||
+           !tb_vendor[QCA_WLAN_VENDOR_ATTR_BTM_CANDIDATE_INFO])
+               return NL_SKIP;
+
+       wpa_printf(MSG_DEBUG,
+                  "nl80211: WNM Candidate list received from driver");
+       nla_for_each_nested(attr,
+                           tb_vendor[QCA_WLAN_VENDOR_ATTR_BTM_CANDIDATE_INFO],
+                           rem) {
+               if (info->num >= num ||
+                   nla_parse_nested(
+                           tb, QCA_WLAN_VENDOR_ATTR_BTM_CANDIDATE_INFO_MAX,
+                           attr, policy) ||
+                   !tb[QCA_WLAN_VENDOR_ATTR_BTM_CANDIDATE_INFO_BSSID] ||
+                   !tb[QCA_WLAN_VENDOR_ATTR_BTM_CANDIDATE_INFO_STATUS])
+                       break;
+
+               nl80211_parse_btm_candidate_info(candidate, tb, info->num);
+
+               candidate++;
+               info->num++;
+       }
+
+       return NL_SKIP;
+}
+
+
+static struct wpa_bss_candidate_info *
+nl80211_get_bss_transition_status(void *priv, struct wpa_bss_trans_info *params)
+{
+       struct i802_bss *bss = priv;
+       struct wpa_driver_nl80211_data *drv = bss->drv;
+       struct nl_msg *msg;
+       struct nlattr *attr, *attr1, *attr2;
+       struct wpa_bss_candidate_info *info;
+       u8 i;
+       int ret;
+       u8 *pos;
+
+       if (!drv->fetch_bss_trans_status)
+               return NULL;
+
+       info = os_zalloc(sizeof(*info));
+       if (!info)
+               return NULL;
+       /* Allocate memory for number of candidates sent to driver */
+       info->candidates = os_calloc(params->n_candidates,
+                                    sizeof(*info->candidates));
+       if (!info->candidates) {
+               os_free(info);
+               return NULL;
+       }
+
+       /* Copy the number of candidates being sent to driver. This is used in
+        * nl80211_get_bss_transition_status_handler() to limit the number of
+        * candidates that can be populated in info->candidates and will be
+        * later overwritten with the actual number of candidates received from
+        * the driver.
+        */
+       info->num = params->n_candidates;
+
+       if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR)) ||
+           nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_QCA) ||
+           nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD,
+                       QCA_NL80211_VENDOR_SUBCMD_FETCH_BSS_TRANSITION_STATUS))
+               goto fail;
+
+       attr = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA);
+       if (!attr)
+               goto fail;
+
+       if (nla_put_u8(msg, QCA_WLAN_VENDOR_ATTR_BTM_MBO_TRANSITION_REASON,
+                      params->mbo_transition_reason))
+               goto fail;
+
+       attr1 = nla_nest_start(msg, QCA_WLAN_VENDOR_ATTR_BTM_CANDIDATE_INFO);
+       if (!attr1)
+               goto fail;
+
+       wpa_printf(MSG_DEBUG,
+                  "nl80211: WNM Candidate list info sending to driver: mbo_transition_reason: %d n_candidates: %d",
+                  params->mbo_transition_reason, params->n_candidates);
+       pos = params->bssid;
+       for (i = 0; i < params->n_candidates; i++) {
+               wpa_printf(MSG_DEBUG, "nl80211:   BSSID[%d]: " MACSTR, i,
+                          MAC2STR(pos));
+               attr2 = nla_nest_start(msg, i);
+               if (!attr2 ||
+                   nla_put(msg, QCA_WLAN_VENDOR_ATTR_BTM_CANDIDATE_INFO_BSSID,
+                           ETH_ALEN, pos))
+                       goto fail;
+               pos += ETH_ALEN;
+               nla_nest_end(msg, attr2);
+       }
+
+       nla_nest_end(msg, attr1);
+       nla_nest_end(msg, attr);
+
+       ret = send_and_recv_msgs(drv, msg,
+                                nl80211_get_bss_transition_status_handler,
+                                info);
+       msg = NULL;
+       if (ret) {
+               wpa_printf(MSG_ERROR,
+                          "nl80211: WNM Get BSS transition status failed: ret=%d (%s)",
+                          ret, strerror(-ret));
+               goto fail;
+       }
+       return info;
+
+fail:
+       nlmsg_free(msg);
+       os_free(info->candidates);
+       os_free(info);
+       return NULL;
+}
+
+
+/**
+ * nl80211_ignore_assoc_disallow - Configure driver to ignore assoc_disallow
+ * @priv: Pointer to private driver data from wpa_driver_nl80211_init()
+ * @ignore_assoc_disallow: 0 to not ignore, 1 to ignore
+ * Returns: 0 on success, -1 on failure
+ */
+static int nl80211_ignore_assoc_disallow(void *priv, int ignore_disallow)
+{
+       struct i802_bss *bss = priv;
+       struct wpa_driver_nl80211_data *drv = bss->drv;
+       struct nl_msg *msg;
+       struct nlattr *attr;
+       int ret = -1;
+
+       if (!drv->set_wifi_conf_vendor_cmd_avail)
+               return -1;
+
+       if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR)) ||
+           nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_QCA) ||
+           nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD,
+                       QCA_NL80211_VENDOR_SUBCMD_SET_WIFI_CONFIGURATION))
+               goto fail;
+
+       attr = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA);
+       if (!attr)
+               goto fail;
+
+       wpa_printf(MSG_DEBUG, "nl80211: Set ignore_assoc_disallow %d",
+                  ignore_disallow);
+       if (nla_put_u8(msg, QCA_WLAN_VENDOR_ATTR_CONFIG_IGNORE_ASSOC_DISALLOWED,
+                      ignore_disallow))
+               goto fail;
+
+       nla_nest_end(msg, attr);
+
+       ret = send_and_recv_msgs(drv, msg, NULL, NULL);
+       msg = NULL;
+       if (ret) {
+               wpa_printf(MSG_ERROR,
+                          "nl80211: Set ignore_assoc_disallow failed: ret=%d (%s)",
+                          ret, strerror(-ret));
+               goto fail;
+       }
+
+fail:
+       nlmsg_free(msg);
+       return ret;
+}
+
+#endif /* CONFIG_MBO */
+
 #endif /* CONFIG_DRIVER_NL80211_QCA */
 
 
@@ -9601,6 +10482,88 @@ static int nl80211_get_ext_capab(void *priv, enum wpa_driver_if_type type,
 }
 
 
+static int nl80211_update_connection_params(
+       void *priv, struct wpa_driver_associate_params *params,
+       enum wpa_drv_update_connect_params_mask mask)
+{
+       struct i802_bss *bss = priv;
+       struct wpa_driver_nl80211_data *drv = bss->drv;
+       struct nl_msg *msg;
+       int ret = -1;
+       enum nl80211_auth_type type;
+
+       msg = nl80211_drv_msg(drv, 0, NL80211_CMD_UPDATE_CONNECT_PARAMS);
+       if (!msg)
+               goto fail;
+
+       wpa_printf(MSG_DEBUG, "nl80211: Update connection params (ifindex=%d)",
+                  drv->ifindex);
+
+       if ((mask & WPA_DRV_UPDATE_ASSOC_IES) && params->wpa_ie) {
+               if (nla_put(msg, NL80211_ATTR_IE, params->wpa_ie_len,
+                           params->wpa_ie))
+                       goto fail;
+               wpa_hexdump(MSG_DEBUG, "  * IEs", params->wpa_ie,
+                           params->wpa_ie_len);
+       }
+
+       if (mask & WPA_DRV_UPDATE_AUTH_TYPE) {
+               type = get_nl_auth_type(params->auth_alg);
+               if (type == NL80211_AUTHTYPE_MAX ||
+                   nla_put_u32(msg, NL80211_ATTR_AUTH_TYPE, type))
+                       goto fail;
+               wpa_printf(MSG_DEBUG, "  * Auth Type %d", type);
+       }
+
+       if ((mask & WPA_DRV_UPDATE_FILS_ERP_INFO) &&
+           nl80211_put_fils_connect_params(drv, params, msg))
+               goto fail;
+
+       ret = send_and_recv_msgs(drv, msg, NULL, NULL);
+       msg = NULL;
+       if (ret)
+               wpa_dbg(drv->ctx, MSG_DEBUG,
+                       "nl80211: Update connect params command failed: ret=%d (%s)",
+                       ret, strerror(-ret));
+
+fail:
+       nlmsg_free(msg);
+       return ret;
+}
+
+
+static int nl80211_send_external_auth_status(void *priv,
+                                            struct external_auth *params)
+{
+       struct i802_bss *bss = priv;
+       struct wpa_driver_nl80211_data *drv = bss->drv;
+       struct nl_msg *msg = NULL;
+       int ret = -1;
+
+       wpa_dbg(drv->ctx, MSG_DEBUG,
+               "nl80211: External auth status: %u", params->status);
+
+       msg = nl80211_drv_msg(drv, 0, NL80211_CMD_EXTERNAL_AUTH);
+       if (!msg ||
+           nla_put_u16(msg, NL80211_ATTR_STATUS_CODE, params->status) ||
+           nla_put(msg, NL80211_ATTR_SSID, params->ssid_len,
+                   params->ssid) ||
+           nla_put(msg, NL80211_ATTR_BSSID, ETH_ALEN, params->bssid))
+               goto fail;
+       ret = send_and_recv_msgs(drv, msg, NULL, NULL);
+       msg = NULL;
+       if (ret) {
+               wpa_printf(MSG_DEBUG,
+                          "nl80211: External Auth status update failed: ret=%d (%s)",
+                          ret, strerror(-ret));
+               goto fail;
+       }
+fail:
+       nlmsg_free(msg);
+       return ret;
+}
+
+
 const struct wpa_driver_ops wpa_driver_nl80211_ops = {
        .name = "nl80211",
        .desc = "Linux nl80211/cfg80211",
@@ -9710,6 +10673,7 @@ const struct wpa_driver_ops wpa_driver_nl80211_ops = {
        .get_ifindex = nl80211_get_ifindex,
 #ifdef CONFIG_DRIVER_NL80211_QCA
        .roaming = nl80211_roaming,
+       .disable_fils = nl80211_disable_fils,
        .do_acs = wpa_driver_do_acs,
        .set_band = nl80211_set_band,
        .get_pref_freq_list = nl80211_get_pref_freq_list,
@@ -9718,7 +10682,14 @@ const struct wpa_driver_ops wpa_driver_nl80211_ops = {
        .p2p_lo_stop = nl80211_p2p_lo_stop,
        .set_default_scan_ies = nl80211_set_default_scan_ies,
        .set_tdls_mode = nl80211_set_tdls_mode,
+#ifdef CONFIG_MBO
+       .get_bss_transition_status = nl80211_get_bss_transition_status,
+       .ignore_assoc_disallow = nl80211_ignore_assoc_disallow,
+#endif /* CONFIG_MBO */
+       .set_bssid_blacklist = nl80211_set_bssid_blacklist,
 #endif /* CONFIG_DRIVER_NL80211_QCA */
        .configure_data_frame_filters = nl80211_configure_data_frame_filters,
        .get_ext_capab = nl80211_get_ext_capab,
+       .update_connect_params = nl80211_update_connection_params,
+       .send_external_auth_status = nl80211_send_external_auth_status,
 };