]> git.ipfire.org Git - thirdparty/hostap.git/blobdiff - src/drivers/driver_nl80211.c
Do not trigger fast reconnection on locally generated deauth/disassoc
[thirdparty/hostap.git] / src / drivers / driver_nl80211.c
index 13b54d7cd133e24780b4d7725b1c2e85636d200d..6af8cc9cb1a22791031920b6ca6ea82b360d593f 100644 (file)
@@ -1,6 +1,6 @@
 /*
  * Driver interaction with Linux nl80211/cfg80211
- * Copyright (c) 2002-2010, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2002-2012, Jouni Malinen <j@w1.fi>
  * Copyright (c) 2003-2004, Instant802 Networks, Inc.
  * Copyright (c) 2005-2006, Devicescape Software, Inc.
  * Copyright (c) 2007, Johannes Berg <johannes@sipsolutions.net>
@@ -115,43 +115,34 @@ static void nl80211_handle_destroy(struct nl_handle *handle)
 #endif /* CONFIG_LIBNL20 */
 
 
-struct nl80211_handles {
-       struct nl_handle *handle;
-};
-
-
-static int nl_create_handles(struct nl80211_handles *handles, struct nl_cb *cb,
-                            const char *dbg)
+static struct nl_handle * nl_create_handle(struct nl_cb *cb, const char *dbg)
 {
-       if (!handles)
-               return -1;
+       struct nl_handle *handle;
 
-       handles->handle = nl80211_handle_alloc(cb);
-       if (handles->handle == NULL) {
+       handle = nl80211_handle_alloc(cb);
+       if (handle == NULL) {
                wpa_printf(MSG_ERROR, "nl80211: Failed to allocate netlink "
                           "callbacks (%s)", dbg);
-               return -1;
+               return NULL;
        }
 
-       if (genl_connect(handles->handle)) {
+       if (genl_connect(handle)) {
                wpa_printf(MSG_ERROR, "nl80211: Failed to connect to generic "
                           "netlink (%s)", dbg);
-               goto err;
+               nl80211_handle_destroy(handle);
+               return NULL;
        }
 
-       return 0;
-err:
-       nl80211_handle_destroy(handles->handle);
-       return -1;
+       return handle;
 }
 
 
-static void nl_destroy_handles(struct nl80211_handles *handles)
+static void nl_destroy_handles(struct nl_handle **handle)
 {
-       if (handles->handle == NULL)
+       if (*handle == NULL)
                return;
-       nl80211_handle_destroy(handles->handle);
-       handles->handle = NULL;
+       nl80211_handle_destroy(*handle);
+       *handle = NULL;
 }
 
 
@@ -174,9 +165,22 @@ struct nl80211_global {
        int if_add_ifindex;
        struct netlink_data *netlink;
        struct nl_cb *nl_cb;
-       struct nl80211_handles nl;
+       struct nl_handle *nl;
        int nl80211_id;
        int ioctl_sock; /* socket for ioctl() use */
+
+       struct nl_handle *nl_event;
+};
+
+struct nl80211_wiphy_data {
+       struct dl_list list;
+       struct dl_list bsss;
+       struct dl_list drvs;
+
+       struct nl_handle *nl_beacons;
+       struct nl_cb *nl_cb;
+
+       int wiphy_idx;
 };
 
 static void nl80211_global_deinit(void *priv);
@@ -192,16 +196,21 @@ struct i802_bss {
        unsigned int added_if_into_bridge:1;
        unsigned int added_bridge:1;
 
+       u8 addr[ETH_ALEN];
+
        int freq;
 
-       struct nl80211_handles nl_preq, nl_mgmt;
+       struct nl_handle *nl_preq, *nl_mgmt;
        struct nl_cb *nl_cb;
+
+       struct nl80211_wiphy_data *wiphy_data;
+       struct dl_list wiphy_list;
 };
 
 struct wpa_driver_nl80211_data {
        struct nl80211_global *global;
        struct dl_list list;
-       u8 addr[ETH_ALEN];
+       struct dl_list wiphy_list;
        char phyname[32];
        void *ctx;
        int ifindex;
@@ -216,7 +225,6 @@ struct wpa_driver_nl80211_data {
 
        int scan_complete_events;
 
-       struct nl80211_handles nl_event;
        struct nl_cb *nl_cb;
 
        u8 auth_bssid[ETH_ALEN];
@@ -395,7 +403,7 @@ static int no_seq_check(struct nl_msg *msg, void *arg)
 }
 
 
-static int send_and_recv(struct wpa_driver_nl80211_data *drv,
+static int send_and_recv(struct nl80211_global *global,
                         struct nl_handle *nl_handle, struct nl_msg *msg,
                         int (*valid_handler)(struct nl_msg *, void *),
                         void *valid_data)
@@ -403,7 +411,7 @@ static int send_and_recv(struct wpa_driver_nl80211_data *drv,
        struct nl_cb *cb;
        int err = -ENOMEM;
 
-       cb = nl_cb_clone(drv->global->nl_cb);
+       cb = nl_cb_clone(global->nl_cb);
        if (!cb)
                goto out;
 
@@ -430,13 +438,23 @@ static int send_and_recv(struct wpa_driver_nl80211_data *drv,
 }
 
 
+static int send_and_recv_msgs_global(struct nl80211_global *global,
+                                    struct nl_msg *msg,
+                                    int (*valid_handler)(struct nl_msg *, void *),
+                                    void *valid_data)
+{
+       return send_and_recv(global, global->nl, msg, valid_handler,
+                            valid_data);
+}
+
+
 static 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)
 {
-       return send_and_recv(drv, drv->global->nl.handle, msg, valid_handler,
-                            valid_data);
+       return send_and_recv(drv->global, drv->global->nl, msg,
+                            valid_handler, valid_data);
 }
 
 
@@ -477,7 +495,7 @@ static int family_handler(struct nl_msg *msg, void *arg)
 }
 
 
-static int nl_get_multicast_id(struct wpa_driver_nl80211_data *drv,
+static int nl_get_multicast_id(struct nl80211_global *global,
                               const char *family, const char *group)
 {
        struct nl_msg *msg;
@@ -487,12 +505,11 @@ static int nl_get_multicast_id(struct wpa_driver_nl80211_data *drv,
        msg = nlmsg_alloc();
        if (!msg)
                return -ENOMEM;
-       genlmsg_put(msg, 0, 0,
-                   genl_ctrl_resolve(drv->global->nl.handle, "nlctrl"),
+       genlmsg_put(msg, 0, 0, genl_ctrl_resolve(global->nl, "nlctrl"),
                    0, 0, CTRL_CMD_GETFAMILY, 0);
        NLA_PUT_STRING(msg, CTRL_ATTR_FAMILY_NAME, family);
 
-       ret = send_and_recv_msgs(drv, msg, family_handler, &res);
+       ret = send_and_recv_msgs_global(global, msg, family_handler, &res);
        msg = NULL;
        if (ret == 0)
                ret = res.id;
@@ -511,6 +528,227 @@ static void * nl80211_cmd(struct wpa_driver_nl80211_data *drv,
 }
 
 
+struct wiphy_idx_data {
+       int wiphy_idx;
+};
+
+
+static int netdev_info_handler(struct nl_msg *msg, void *arg)
+{
+       struct nlattr *tb[NL80211_ATTR_MAX + 1];
+       struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
+       struct wiphy_idx_data *info = arg;
+
+       nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
+                 genlmsg_attrlen(gnlh, 0), NULL);
+
+       if (tb[NL80211_ATTR_WIPHY])
+               info->wiphy_idx = nla_get_u32(tb[NL80211_ATTR_WIPHY]);
+
+       return NL_SKIP;
+}
+
+
+static int nl80211_get_wiphy_index(struct i802_bss *bss)
+{
+       struct nl_msg *msg;
+       struct wiphy_idx_data data = {
+               .wiphy_idx = -1,
+       };
+
+       msg = nlmsg_alloc();
+       if (!msg)
+               return -1;
+
+       nl80211_cmd(bss->drv, msg, 0, NL80211_CMD_GET_INTERFACE);
+
+       NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, bss->ifindex);
+
+       if (send_and_recv_msgs(bss->drv, msg, netdev_info_handler, &data) == 0)
+               return data.wiphy_idx;
+       msg = NULL;
+nla_put_failure:
+       nlmsg_free(msg);
+       return -1;
+}
+
+
+static int nl80211_register_beacons(struct wpa_driver_nl80211_data *drv,
+                                   struct nl80211_wiphy_data *w)
+{
+       struct nl_msg *msg;
+       int ret = -1;
+
+       msg = nlmsg_alloc();
+       if (!msg)
+               return -1;
+
+       nl80211_cmd(drv, msg, 0, NL80211_CMD_REGISTER_BEACONS);
+
+       NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, w->wiphy_idx);
+
+       ret = send_and_recv(drv->global, w->nl_beacons, msg, NULL, NULL);
+       msg = NULL;
+       if (ret) {
+               wpa_printf(MSG_DEBUG, "nl80211: Register beacons command "
+                          "failed: ret=%d (%s)",
+                          ret, strerror(-ret));
+               goto nla_put_failure;
+       }
+       ret = 0;
+nla_put_failure:
+       nlmsg_free(msg);
+       return ret;
+}
+
+
+static void nl80211_recv_beacons(int sock, void *eloop_ctx, void *handle)
+{
+       struct nl80211_wiphy_data *w = eloop_ctx;
+
+       wpa_printf(MSG_EXCESSIVE, "nl80211: Beacon event message available");
+
+       nl_recvmsgs(handle, w->nl_cb);
+}
+
+
+static int process_beacon_event(struct nl_msg *msg, void *arg)
+{
+       struct nl80211_wiphy_data *w = arg;
+       struct wpa_driver_nl80211_data *drv;
+       struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
+       struct nlattr *tb[NL80211_ATTR_MAX + 1];
+       union wpa_event_data event;
+
+       nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
+                 genlmsg_attrlen(gnlh, 0), NULL);
+
+       if (gnlh->cmd != NL80211_CMD_FRAME) {
+               wpa_printf(MSG_DEBUG, "nl80211: Unexpected beacon event? (%d)",
+                          gnlh->cmd);
+               return NL_SKIP;
+       }
+
+       if (!tb[NL80211_ATTR_FRAME])
+               return NL_SKIP;
+
+       dl_list_for_each(drv, &w->drvs, struct wpa_driver_nl80211_data,
+                        wiphy_list) {
+               os_memset(&event, 0, sizeof(event));
+               event.rx_mgmt.frame = nla_data(tb[NL80211_ATTR_FRAME]);
+               event.rx_mgmt.frame_len = nla_len(tb[NL80211_ATTR_FRAME]);
+               wpa_supplicant_event(drv->ctx, EVENT_RX_MGMT, &event);
+       }
+
+       return NL_SKIP;
+}
+
+
+static struct nl80211_wiphy_data *
+nl80211_get_wiphy_data_ap(struct i802_bss *bss)
+{
+       static DEFINE_DL_LIST(nl80211_wiphys);
+       struct nl80211_wiphy_data *w;
+       int wiphy_idx, found = 0;
+       struct i802_bss *tmp_bss;
+
+       if (bss->wiphy_data != NULL)
+               return bss->wiphy_data;
+
+       wiphy_idx = nl80211_get_wiphy_index(bss);
+
+       dl_list_for_each(w, &nl80211_wiphys, struct nl80211_wiphy_data, list) {
+               if (w->wiphy_idx == wiphy_idx)
+                       goto add;
+       }
+
+       /* alloc new one */
+       w = os_zalloc(sizeof(*w));
+       if (w == NULL)
+               return NULL;
+       w->wiphy_idx = wiphy_idx;
+       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);
+
+       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;
+       }
+
+       eloop_register_read_sock(nl_socket_get_fd(w->nl_beacons),
+                                nl80211_recv_beacons, w, w->nl_beacons);
+
+       dl_list_add(&nl80211_wiphys, &w->list);
+
+add:
+       /* drv entry for this bss already there? */
+       dl_list_for_each(tmp_bss, &w->bsss, struct i802_bss, wiphy_list) {
+               if (tmp_bss->drv == bss->drv) {
+                       found = 1;
+                       break;
+               }
+       }
+       /* if not add it */
+       if (!found)
+               dl_list_add(&w->drvs, &bss->drv->wiphy_list);
+
+       dl_list_add(&w->bsss, &bss->wiphy_list);
+       bss->wiphy_data = w;
+       return w;
+}
+
+
+static void nl80211_put_wiphy_data_ap(struct i802_bss *bss)
+{
+       struct nl80211_wiphy_data *w = bss->wiphy_data;
+       struct i802_bss *tmp_bss;
+       int found = 0;
+
+       if (w == NULL)
+               return;
+       bss->wiphy_data = NULL;
+       dl_list_del(&bss->wiphy_list);
+
+       /* still any for this drv present? */
+       dl_list_for_each(tmp_bss, &w->bsss, struct i802_bss, wiphy_list) {
+               if (tmp_bss->drv == bss->drv) {
+                       found = 1;
+                       break;
+               }
+       }
+       /* if not remove it */
+       if (!found)
+               dl_list_del(&bss->drv->wiphy_list);
+
+       if (!dl_list_empty(&w->bsss))
+               return;
+
+       eloop_unregister_read_sock(nl_socket_get_fd(w->nl_beacons));
+
+       nl_cb_put(w->nl_cb);
+       nl_destroy_handles(&w->nl_beacons);
+       dl_list_del(&w->list);
+       os_free(w);
+}
+
+
 static int wpa_driver_nl80211_get_bssid(void *priv, u8 *bssid)
 {
        struct i802_bss *bss = priv;
@@ -913,7 +1151,8 @@ static void mlme_event_connect(struct wpa_driver_nl80211_data *drv,
 
 
 static void mlme_event_disconnect(struct wpa_driver_nl80211_data *drv,
-                                 struct nlattr *reason, struct nlattr *addr)
+                                 struct nlattr *reason, struct nlattr *addr,
+                                 struct nlattr *by_ap)
 {
        union wpa_event_data data;
 
@@ -931,6 +1170,7 @@ static void mlme_event_disconnect(struct wpa_driver_nl80211_data *drv,
        os_memset(&data, 0, sizeof(data));
        if (reason)
                data.disassoc_info.reason_code = nla_get_u16(reason);
+       data.disassoc_info.locally_generated = by_ap == NULL;
        wpa_supplicant_event(drv->ctx, EVENT_DISASSOC, &data);
 }
 
@@ -1071,6 +1311,8 @@ static void mlme_event_deauth_disassoc(struct wpa_driver_nl80211_data *drv,
                reason_code = le_to_host16(mgmt->u.deauth.reason_code);
 
        if (type == EVENT_DISASSOC) {
+               event.disassoc_info.locally_generated =
+                       !os_memcmp(mgmt->sa, drv->first_bss.addr, ETH_ALEN);
                event.disassoc_info.addr = bssid;
                event.disassoc_info.reason_code = reason_code;
                if (frame + len > mgmt->u.disassoc.variable) {
@@ -1079,6 +1321,8 @@ static void mlme_event_deauth_disassoc(struct wpa_driver_nl80211_data *drv,
                                mgmt->u.disassoc.variable;
                }
        } else {
+               event.deauth_info.locally_generated =
+                       !os_memcmp(mgmt->sa, drv->first_bss.addr, ETH_ALEN);
                event.deauth_info.addr = bssid;
                event.deauth_info.reason_code = reason_code;
                if (frame + len > mgmt->u.deauth.variable) {
@@ -1397,6 +1641,7 @@ static int nl80211_get_link_signal(struct wpa_driver_nl80211_data *drv,
 
        return send_and_recv_msgs(drv, msg, get_link_signal, sig);
  nla_put_failure:
+       nlmsg_free(msg);
        return -ENOBUFS;
 }
 
@@ -1463,6 +1708,7 @@ static int nl80211_get_link_noise(struct wpa_driver_nl80211_data *drv,
 
        return send_and_recv_msgs(drv, msg, get_link_noise, sig_change);
  nla_put_failure:
+       nlmsg_free(msg);
        return -ENOBUFS;
 }
 
@@ -1537,6 +1783,7 @@ static int nl80211_get_noise_for_scan_results(
        return send_and_recv_msgs(drv, msg, get_noise_for_scan_results,
                                  scan_res);
  nla_put_failure:
+       nlmsg_free(msg);
        return -ENOBUFS;
 }
 
@@ -1758,56 +2005,36 @@ static void nl80211_client_probe_event(struct wpa_driver_nl80211_data *drv,
 }
 
 
-static void nl80211_spurious_class3_frame(struct i802_bss *bss,
-                                         struct nlattr **tb)
+static void nl80211_spurious_frame(struct i802_bss *bss, struct nlattr **tb,
+                                  int wds)
 {
        struct wpa_driver_nl80211_data *drv = bss->drv;
        union wpa_event_data event;
-       u8 bssid[ETH_ALEN];
 
        if (!tb[NL80211_ATTR_MAC])
                return;
 
-       if (linux_get_ifhwaddr(drv->global->ioctl_sock, bss->ifname, bssid) <
-           0)
-               return;
-
        os_memset(&event, 0, sizeof(event));
-       event.rx_from_unknown.bssid = bssid;
+       event.rx_from_unknown.bssid = bss->addr;
        event.rx_from_unknown.addr = nla_data(tb[NL80211_ATTR_MAC]);
+       event.rx_from_unknown.wds = wds;
 
        wpa_supplicant_event(drv->ctx, EVENT_RX_FROM_UNKNOWN, &event);
 }
 
 
-static int process_drv_event(struct nl_msg *msg, void *arg)
+static void do_process_drv_event(struct wpa_driver_nl80211_data *drv,
+                                int cmd, struct nlattr **tb)
 {
-       struct wpa_driver_nl80211_data *drv = arg;
-       struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
-       struct nlattr *tb[NL80211_ATTR_MAX + 1];
-
-       nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
-                 genlmsg_attrlen(gnlh, 0), NULL);
-
-       if (tb[NL80211_ATTR_IFINDEX]) {
-               int ifindex = nla_get_u32(tb[NL80211_ATTR_IFINDEX]);
-               if (ifindex != drv->ifindex && !have_ifidx(drv, ifindex)) {
-                       wpa_printf(MSG_DEBUG, "nl80211: Ignored event (cmd=%d)"
-                                  " for foreign interface (ifindex %d)",
-                                  gnlh->cmd, ifindex);
-                       return NL_SKIP;
-               }
-       }
-
        if (drv->ap_scan_as_station != NL80211_IFTYPE_UNSPECIFIED &&
-           (gnlh->cmd == NL80211_CMD_NEW_SCAN_RESULTS ||
-            gnlh->cmd == NL80211_CMD_SCAN_ABORTED)) {
+           (cmd == NL80211_CMD_NEW_SCAN_RESULTS ||
+            cmd == NL80211_CMD_SCAN_ABORTED)) {
                wpa_driver_nl80211_set_mode(&drv->first_bss,
                                            drv->ap_scan_as_station);
                drv->ap_scan_as_station = NL80211_IFTYPE_UNSPECIFIED;
        }
 
-       switch (gnlh->cmd) {
+       switch (cmd) {
        case NL80211_CMD_TRIGGER_SCAN:
                wpa_printf(MSG_DEBUG, "nl80211: Scan trigger");
                break;
@@ -1847,14 +2074,14 @@ static int process_drv_event(struct nl_msg *msg, void *arg)
        case NL80211_CMD_FRAME_TX_STATUS:
        case NL80211_CMD_UNPROT_DEAUTHENTICATE:
        case NL80211_CMD_UNPROT_DISASSOCIATE:
-               mlme_event(drv, gnlh->cmd, tb[NL80211_ATTR_FRAME],
+               mlme_event(drv, cmd, tb[NL80211_ATTR_FRAME],
                           tb[NL80211_ATTR_MAC], tb[NL80211_ATTR_TIMED_OUT],
                           tb[NL80211_ATTR_WIPHY_FREQ], tb[NL80211_ATTR_ACK],
                           tb[NL80211_ATTR_COOKIE]);
                break;
        case NL80211_CMD_CONNECT:
        case NL80211_CMD_ROAM:
-               mlme_event_connect(drv, gnlh->cmd,
+               mlme_event_connect(drv, cmd,
                                   tb[NL80211_ATTR_STATUS_CODE],
                                   tb[NL80211_ATTR_MAC],
                                   tb[NL80211_ATTR_REQ_IE],
@@ -1862,7 +2089,8 @@ static int process_drv_event(struct nl_msg *msg, void *arg)
                break;
        case NL80211_CMD_DISCONNECT:
                mlme_event_disconnect(drv, tb[NL80211_ATTR_REASON_CODE],
-                                     tb[NL80211_ATTR_MAC]);
+                                     tb[NL80211_ATTR_MAC],
+                                     tb[NL80211_ATTR_DISCONNECTED_BY_AP]);
                break;
        case NL80211_CMD_MICHAEL_MIC_FAILURE:
                mlme_event_michael_mic_failure(drv, tb);
@@ -1906,9 +2134,56 @@ static int process_drv_event(struct nl_msg *msg, void *arg)
                break;
        default:
                wpa_printf(MSG_DEBUG, "nl80211: Ignored unknown event "
-                          "(cmd=%d)", gnlh->cmd);
+                          "(cmd=%d)", cmd);
                break;
        }
+}
+
+
+static int process_drv_event(struct nl_msg *msg, void *arg)
+{
+       struct wpa_driver_nl80211_data *drv = arg;
+       struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
+       struct nlattr *tb[NL80211_ATTR_MAX + 1];
+
+       nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
+                 genlmsg_attrlen(gnlh, 0), NULL);
+
+       if (tb[NL80211_ATTR_IFINDEX]) {
+               int ifindex = nla_get_u32(tb[NL80211_ATTR_IFINDEX]);
+               if (ifindex != drv->ifindex && !have_ifidx(drv, ifindex)) {
+                       wpa_printf(MSG_DEBUG, "nl80211: Ignored event (cmd=%d)"
+                                  " for foreign interface (ifindex %d)",
+                                  gnlh->cmd, ifindex);
+                       return NL_SKIP;
+               }
+       }
+
+       do_process_drv_event(drv, gnlh->cmd, tb);
+       return NL_SKIP;
+}
+
+
+static int process_global_event(struct nl_msg *msg, void *arg)
+{
+       struct nl80211_global *global = arg;
+       struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
+       struct nlattr *tb[NL80211_ATTR_MAX + 1];
+       struct wpa_driver_nl80211_data *drv;
+       int ifidx = -1;
+
+       nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
+                 genlmsg_attrlen(gnlh, 0), NULL);
+
+       if (tb[NL80211_ATTR_IFINDEX])
+               ifidx = nla_get_u32(tb[NL80211_ATTR_IFINDEX]);
+
+       dl_list_for_each(drv, &global->interfaces,
+                        struct wpa_driver_nl80211_data, list) {
+               if (ifidx == -1 || ifidx == drv->ifindex ||
+                   have_ifidx(drv, ifidx))
+                       do_process_drv_event(drv, gnlh->cmd, tb);
+       }
 
        return NL_SKIP;
 }
@@ -1932,7 +2207,10 @@ static int process_bss_event(struct nl_msg *msg, void *arg)
                           tb[NL80211_ATTR_COOKIE]);
                break;
        case NL80211_CMD_UNEXPECTED_FRAME:
-               nl80211_spurious_class3_frame(bss, tb);
+               nl80211_spurious_frame(bss, tb, 0);
+               break;
+       case NL80211_CMD_UNEXPECTED_4ADDR_FRAME:
+               nl80211_spurious_frame(bss, tb, 1);
                break;
        default:
                wpa_printf(MSG_DEBUG, "nl80211: Ignored unknown event "
@@ -1986,6 +2264,7 @@ static int wpa_driver_nl80211_set_country(void *priv, const char *alpha2_arg)
                return -EINVAL;
        return 0;
 nla_put_failure:
+       nlmsg_free(msg);
        return -EINVAL;
 }
 
@@ -1997,9 +2276,27 @@ struct wiphy_info_data {
        unsigned int device_ap_sme:1;
        unsigned int poll_command_supported:1;
        unsigned int data_tx_status:1;
+       unsigned int monitor_supported:1;
 };
 
 
+static unsigned int probe_resp_offload_support(int supp_protocols)
+{
+       unsigned int prot = 0;
+
+       if (supp_protocols & NL80211_PROBE_RESP_OFFLOAD_SUPPORT_WPS)
+               prot |= WPA_DRIVER_PROBE_RESP_OFFLOAD_WPS;
+       if (supp_protocols & NL80211_PROBE_RESP_OFFLOAD_SUPPORT_WPS2)
+               prot |= WPA_DRIVER_PROBE_RESP_OFFLOAD_WPS2;
+       if (supp_protocols & NL80211_PROBE_RESP_OFFLOAD_SUPPORT_P2P)
+               prot |= WPA_DRIVER_PROBE_RESP_OFFLOAD_P2P;
+       if (supp_protocols & NL80211_PROBE_RESP_OFFLOAD_SUPPORT_80211U)
+               prot |= WPA_DRIVER_PROBE_RESP_OFFLOAD_INTERWORKING;
+
+       return prot;
+}
+
+
 static int wiphy_info_handler(struct nl_msg *msg, void *arg)
 {
        struct nlattr *tb[NL80211_ATTR_MAX + 1];
@@ -2051,6 +2348,9 @@ static int wiphy_info_handler(struct nl_msg *msg, void *arg)
                        case NL80211_IFTYPE_P2P_CLIENT:
                                p2p_client_supported = 1;
                                break;
+                       case NL80211_IFTYPE_MONITOR:
+                               info->monitor_supported = 1;
+                               break;
                        }
                }
        }
@@ -2149,6 +2449,9 @@ broken_combination:
        /* default to 5000 since early versions of mac80211 don't set it */
        capa->max_remain_on_chan = 5000;
 
+       if (tb[NL80211_ATTR_SUPPORT_AP_UAPSD])
+               capa->flags |= WPA_DRIVER_FLAGS_AP_UAPSD;
+
        if (tb[NL80211_ATTR_MAX_REMAIN_ON_CHANNEL_DURATION])
                capa->max_remain_on_chan =
                        nla_get_u32(tb[NL80211_ATTR_MAX_REMAIN_ON_CHANNEL_DURATION]);
@@ -2191,6 +2494,16 @@ broken_combination:
                        info->data_tx_status = 1;
        }
 
+       if (tb[NL80211_ATTR_PROBE_RESP_OFFLOAD]) {
+               int protocols =
+                       nla_get_u32(tb[NL80211_ATTR_PROBE_RESP_OFFLOAD]);
+               wpa_printf(MSG_DEBUG, "nl80211: Supports Probe Response "
+                          "offload in AP mode");
+               capa->flags |= WPA_DRIVER_FLAGS_PROBE_RESP_OFFLOAD;
+               capa->probe_resp_offloads =
+                       probe_resp_offload_support(protocols);
+       }
+
        return NL_SKIP;
 }
 
@@ -2258,6 +2571,20 @@ static int wpa_driver_nl80211_capa(struct wpa_driver_nl80211_data *drv)
         */
        drv->use_monitor = !info.poll_command_supported;
 
+       if (drv->device_ap_sme && drv->use_monitor) {
+               /*
+                * Non-mac80211 drivers may not support monitor interface.
+                * Make sure we do not get stuck with incorrect capability here
+                * by explicitly testing this.
+                */
+               if (!info.monitor_supported) {
+                       wpa_printf(MSG_DEBUG, "nl80211: Disable use_monitor "
+                                  "with device_ap_sme since no monitor mode "
+                                  "support detected");
+                       drv->use_monitor = 0;
+               }
+       }
+
        /*
         * If we aren't going to use monitor interfaces, but the
         * driver doesn't support data TX status, we won't get TX
@@ -2270,8 +2597,46 @@ static int wpa_driver_nl80211_capa(struct wpa_driver_nl80211_data *drv)
 }
 
 
+#ifdef ANDROID
+static int android_genl_ctrl_resolve(struct nl_handle *handle,
+                                    const char *name)
+{
+       /*
+        * Android ICS has very minimal genl_ctrl_resolve() implementation, so
+        * need to work around that.
+        */
+       struct nl_cache *cache = NULL;
+       struct genl_family *nl80211 = NULL;
+       int id = -1;
+
+       if (genl_ctrl_alloc_cache(handle, &cache) < 0) {
+               wpa_printf(MSG_ERROR, "nl80211: Failed to allocate generic "
+                          "netlink cache");
+               goto fail;
+       }
+
+       nl80211 = genl_ctrl_search_by_name(cache, name);
+       if (nl80211 == NULL)
+               goto fail;
+
+       id = genl_family_get_id(nl80211);
+
+fail:
+       if (nl80211)
+               genl_family_put(nl80211);
+       if (cache)
+               nl_cache_free(cache);
+
+       return id;
+}
+#define genl_ctrl_resolve android_genl_ctrl_resolve
+#endif /* ANDROID */
+
+
 static int wpa_driver_nl80211_init_nl_global(struct nl80211_global *global)
 {
+       int ret;
+
        global->nl_cb = nl_cb_alloc(NL_CB_DEFAULT);
        if (global->nl_cb == NULL) {
                wpa_printf(MSG_ERROR, "nl80211: Failed to allocate netlink "
@@ -2279,53 +2644,44 @@ static int wpa_driver_nl80211_init_nl_global(struct nl80211_global *global)
                return -1;
        }
 
-       if (nl_create_handles(&global->nl, global->nl_cb, "nl"))
-               return -1;
+       global->nl = nl_create_handle(global->nl_cb, "nl");
+       if (global->nl == NULL)
+               goto err;
 
-       global->nl80211_id = genl_ctrl_resolve(global->nl.handle, "nl80211");
+       global->nl80211_id = genl_ctrl_resolve(global->nl, "nl80211");
        if (global->nl80211_id < 0) {
                wpa_printf(MSG_ERROR, "nl80211: 'nl80211' generic netlink not "
                           "found");
-               return -1;
+               goto err;
        }
 
-       return 0;
-}
-
-
-static int wpa_driver_nl80211_init_nl(struct wpa_driver_nl80211_data *drv)
-{
-       struct nl80211_global *global = drv->global;
-       int ret;
-
-       /* Initialize generic netlink and nl80211 */
-
-       if (nl_create_handles(&drv->nl_event, global->nl_cb, "event"))
-               goto err3;
+       global->nl_event = nl_create_handle(global->nl_cb, "event");
+       if (global->nl_event == NULL)
+               goto err;
 
-       ret = nl_get_multicast_id(drv, "nl80211", "scan");
+       ret = nl_get_multicast_id(global, "nl80211", "scan");
        if (ret >= 0)
-               ret = nl_socket_add_membership(drv->nl_event.handle, ret);
+               ret = nl_socket_add_membership(global->nl_event, ret);
        if (ret < 0) {
                wpa_printf(MSG_ERROR, "nl80211: Could not add multicast "
                           "membership for scan events: %d (%s)",
                           ret, strerror(-ret));
-               goto err4;
+               goto err;
        }
 
-       ret = nl_get_multicast_id(drv, "nl80211", "mlme");
+       ret = nl_get_multicast_id(global, "nl80211", "mlme");
        if (ret >= 0)
-               ret = nl_socket_add_membership(drv->nl_event.handle, ret);
+               ret = nl_socket_add_membership(global->nl_event, ret);
        if (ret < 0) {
                wpa_printf(MSG_ERROR, "nl80211: Could not add multicast "
                           "membership for mlme events: %d (%s)",
                           ret, strerror(-ret));
-               goto err4;
+               goto err;
        }
 
-       ret = nl_get_multicast_id(drv, "nl80211", "regulatory");
+       ret = nl_get_multicast_id(global, "nl80211", "regulatory");
        if (ret >= 0)
-               ret = nl_socket_add_membership(drv->nl_event.handle, ret);
+               ret = nl_socket_add_membership(global->nl_event, ret);
        if (ret < 0) {
                wpa_printf(MSG_DEBUG, "nl80211: Could not add multicast "
                           "membership for regulatory events: %d (%s)",
@@ -2333,10 +2689,32 @@ static int wpa_driver_nl80211_init_nl(struct wpa_driver_nl80211_data *drv)
                /* Continue without regulatory events */
        }
 
+       nl_cb_set(global->nl_cb, NL_CB_SEQ_CHECK, NL_CB_CUSTOM,
+                 no_seq_check, NULL);
+       nl_cb_set(global->nl_cb, NL_CB_VALID, NL_CB_CUSTOM,
+                 process_global_event, global);
+
+       eloop_register_read_sock(nl_socket_get_fd(global->nl_event),
+                                wpa_driver_nl80211_event_receive,
+                                global->nl_cb, global->nl_event);
+
+       return 0;
+
+err:
+       nl_destroy_handles(&global->nl_event);
+       nl_destroy_handles(&global->nl);
+       nl_cb_put(global->nl_cb);
+       global->nl_cb = NULL;
+       return -1;
+}
+
+
+static int wpa_driver_nl80211_init_nl(struct wpa_driver_nl80211_data *drv)
+{
        drv->nl_cb = nl_cb_alloc(NL_CB_DEFAULT);
        if (!drv->nl_cb) {
                wpa_printf(MSG_ERROR, "nl80211: Failed to alloc cb struct");
-               goto err4;
+               return -1;
        }
 
        nl_cb_set(drv->nl_cb, NL_CB_SEQ_CHECK, NL_CB_CUSTOM,
@@ -2344,16 +2722,7 @@ static int wpa_driver_nl80211_init_nl(struct wpa_driver_nl80211_data *drv)
        nl_cb_set(drv->nl_cb, NL_CB_VALID, NL_CB_CUSTOM,
                  process_drv_event, drv);
 
-       eloop_register_read_sock(nl_socket_get_fd(drv->nl_event.handle),
-                                wpa_driver_nl80211_event_receive, drv->nl_cb,
-                                drv->nl_event.handle);
-
        return 0;
-
-err4:
-       nl_destroy_handles(&drv->nl_event);
-err3:
-       return -1;
 }
 
 
@@ -2601,13 +2970,18 @@ static int nl80211_register_frame(struct i802_bss *bss,
        if (!msg)
                return -1;
 
+       wpa_printf(MSG_DEBUG, "nl80211: Register frame type=0x%x nl_handle=%p",
+                  type, nl_handle);
+       wpa_hexdump(MSG_DEBUG, "nl80211: Register frame match",
+                   match, match_len);
+
        nl80211_cmd(drv, msg, 0, NL80211_CMD_REGISTER_ACTION);
 
        NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, bss->ifindex);
        NLA_PUT_U16(msg, NL80211_ATTR_FRAME_TYPE, type);
        NLA_PUT(msg, NL80211_ATTR_FRAME_MATCH, match_len, match);
 
-       ret = send_and_recv(drv, nl_handle, msg, NULL, NULL);
+       ret = send_and_recv(drv->global, nl_handle, msg, NULL, NULL);
        msg = NULL;
        if (ret) {
                wpa_printf(MSG_DEBUG, "nl80211: Register frame command "
@@ -2628,18 +3002,19 @@ static int nl80211_alloc_mgmt_handle(struct i802_bss *bss)
 {
        struct wpa_driver_nl80211_data *drv = bss->drv;
 
-       if (bss->nl_mgmt.handle) {
+       if (bss->nl_mgmt) {
                wpa_printf(MSG_DEBUG, "nl80211: Mgmt reporting "
-                          "already on!");
+                          "already on! (nl_mgmt=%p)", bss->nl_mgmt);
                return -1;
        }
 
-       if (nl_create_handles(&bss->nl_mgmt, drv->nl_cb, "mgmt"))
+       bss->nl_mgmt = nl_create_handle(drv->nl_cb, "mgmt");
+       if (bss->nl_mgmt == NULL)
                return -1;
 
-       eloop_register_read_sock(nl_socket_get_fd(bss->nl_mgmt.handle),
+       eloop_register_read_sock(nl_socket_get_fd(bss->nl_mgmt),
                                 wpa_driver_nl80211_event_receive, bss->nl_cb,
-                                bss->nl_mgmt.handle);
+                                bss->nl_mgmt);
 
        return 0;
 }
@@ -2649,7 +3024,7 @@ static int nl80211_register_action_frame(struct i802_bss *bss,
                                         const u8 *match, size_t match_len)
 {
        u16 type = (WLAN_FC_TYPE_MGMT << 2) | (WLAN_FC_STYPE_ACTION << 4);
-       return nl80211_register_frame(bss, bss->nl_mgmt.handle,
+       return nl80211_register_frame(bss, bss->nl_mgmt,
                                      type, match, match_len);
 }
 
@@ -2660,6 +3035,8 @@ static int nl80211_mgmt_subscribe_non_ap(struct i802_bss *bss)
 
        if (nl80211_alloc_mgmt_handle(bss))
                return -1;
+       wpa_printf(MSG_DEBUG, "nl80211: Subscribe to mgmt frames with non-AP "
+                  "handle %p", bss->nl_mgmt);
 
 #if defined(CONFIG_P2P) || defined(CONFIG_INTERWORKING)
        /* GAS Initial Request */
@@ -2695,7 +3072,7 @@ static int nl80211_mgmt_subscribe_non_ap(struct i802_bss *bss)
 #ifdef CONFIG_TDLS
        if ((drv->capa.flags & WPA_DRIVER_FLAGS_TDLS_SUPPORT)) {
                /* TDLS Discovery Response */
-               if (nl80211_register_action_frame(drv, (u8 *) "\x04\x0e", 2) <
+               if (nl80211_register_action_frame(bss, (u8 *) "\x04\x0e", 2) <
                    0)
                        return -1;
        }
@@ -2730,7 +3107,7 @@ static int nl80211_register_spurious_class3(struct i802_bss *bss)
 
        NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, bss->ifindex);
 
-       ret = send_and_recv(drv, bss->nl_mgmt.handle, msg, NULL, NULL);
+       ret = send_and_recv(drv->global, bss->nl_mgmt, msg, NULL, NULL);
        msg = NULL;
        if (ret) {
                wpa_printf(MSG_DEBUG, "nl80211: Register spurious class3 "
@@ -2766,9 +3143,11 @@ static int nl80211_mgmt_subscribe_ap(struct i802_bss *bss)
 
        if (nl80211_alloc_mgmt_handle(bss))
                return -1;
+       wpa_printf(MSG_DEBUG, "nl80211: Subscribe to mgmt frames with AP "
+                  "handle %p", bss->nl_mgmt);
 
        for (i = 0; i < sizeof(stypes) / sizeof(stypes[0]); i++) {
-               if (nl80211_register_frame(bss, bss->nl_mgmt.handle,
+               if (nl80211_register_frame(bss, bss->nl_mgmt,
                                           (WLAN_FC_TYPE_MGMT << 2) |
                                           (stypes[i] << 4),
                                           NULL, 0) < 0) {
@@ -2779,21 +3158,50 @@ 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;
+
+       return 0;
+
+out_err:
+       eloop_unregister_read_sock(nl_socket_get_fd(bss->nl_mgmt));
+       nl_destroy_handles(&bss->nl_mgmt);
+       return -1;
+}
+
+
+static int nl80211_mgmt_subscribe_ap_dev_sme(struct i802_bss *bss)
+{
+       if (nl80211_alloc_mgmt_handle(bss))
+               return -1;
+       wpa_printf(MSG_DEBUG, "nl80211: Subscribe to mgmt frames with AP "
+                  "handle %p (device SME)", bss->nl_mgmt);
+
+       if (nl80211_register_frame(bss, bss->nl_mgmt,
+                                  (WLAN_FC_TYPE_MGMT << 2) |
+                                  (WLAN_FC_STYPE_ACTION << 4),
+                                  NULL, 0) < 0)
+               goto out_err;
+
        return 0;
 
 out_err:
-       eloop_unregister_read_sock(nl_socket_get_fd(bss->nl_mgmt.handle));
+       eloop_unregister_read_sock(nl_socket_get_fd(bss->nl_mgmt));
        nl_destroy_handles(&bss->nl_mgmt);
        return -1;
 }
 
 
-static void nl80211_mgmt_unsubscribe(struct i802_bss *bss)
+static void nl80211_mgmt_unsubscribe(struct i802_bss *bss, const char *reason)
 {
-       if (bss->nl_mgmt.handle == NULL)
+       if (bss->nl_mgmt == NULL)
                return;
-       eloop_unregister_read_sock(nl_socket_get_fd(bss->nl_mgmt.handle));
+       wpa_printf(MSG_DEBUG, "nl80211: Unsubscribe mgmt frames handle %p "
+                  "(%s)", bss->nl_mgmt, reason);
+       eloop_unregister_read_sock(nl_socket_get_fd(bss->nl_mgmt));
        nl_destroy_handles(&bss->nl_mgmt);
+
+       nl80211_put_wiphy_data_ap(bss);
 }
 
 
@@ -2847,7 +3255,7 @@ wpa_driver_nl80211_finish_drv_init(struct wpa_driver_nl80211_data *drv)
                return -1;
 
        if (linux_get_ifhwaddr(drv->global->ioctl_sock, bss->ifname,
-                              drv->addr))
+                              bss->addr))
                return -1;
 
        if (send_rfkill_event) {
@@ -2872,6 +3280,7 @@ static int wpa_driver_nl80211_del_beacon(struct wpa_driver_nl80211_data *drv)
 
        return send_and_recv_msgs(drv, msg, NULL, NULL);
  nla_put_failure:
+       nlmsg_free(msg);
        return -ENOBUFS;
 }
 
@@ -2893,7 +3302,7 @@ static void wpa_driver_nl80211_deinit(void *priv)
        if (drv->eapol_tx_sock >= 0)
                close(drv->eapol_tx_sock);
 
-       if (bss->nl_preq.handle)
+       if (bss->nl_preq)
                wpa_driver_nl80211_probe_req_report(bss, 0);
        if (bss->added_if_into_bridge) {
                if (linux_br_del_if(drv->global->ioctl_sock, bss->brname,
@@ -2943,10 +3352,8 @@ static void wpa_driver_nl80211_deinit(void *priv)
 
        (void) linux_set_iface_flags(drv->global->ioctl_sock, bss->ifname, 0);
        wpa_driver_nl80211_set_mode(bss, NL80211_IFTYPE_STATION);
-       nl80211_mgmt_unsubscribe(bss);
+       nl80211_mgmt_unsubscribe(bss, "deinit");
 
-       eloop_unregister_read_sock(nl_socket_get_fd(drv->nl_event.handle));
-       nl_destroy_handles(&drv->nl_event);
        nl_cb_put(drv->nl_cb);
 
        nl80211_destroy_bss(&drv->first_bss);
@@ -3785,6 +4192,7 @@ static int wpa_driver_nl80211_set_key(const char *ifname, void *priv,
        return ret;
 
 nla_put_failure:
+       nlmsg_free(msg);
        return -ENOBUFS;
 }
 
@@ -4653,7 +5061,9 @@ wpa_driver_nl80211_get_hw_feature_data(void *priv, u16 *num_modes, u16 *flags)
                nl80211_set_ht40_flags(drv, &result);
                return wpa_driver_nl80211_add_11b(result.modes, num_modes);
        }
+       msg = NULL;
  nla_put_failure:
+       nlmsg_free(msg);
        return NULL;
 }
 
@@ -4821,6 +5231,7 @@ static int nl80211_set_bss(struct i802_bss *bss, int cts, int preamble,
 
        return send_and_recv_msgs(drv, msg, NULL, NULL);
  nla_put_failure:
+       nlmsg_free(msg);
        return -ENOBUFS;
 }
 
@@ -4858,6 +5269,9 @@ static int wpa_driver_nl80211_set_ap(void *priv,
        NLA_PUT_U32(msg, NL80211_ATTR_DTIM_PERIOD, params->dtim_period);
        NLA_PUT(msg, NL80211_ATTR_SSID, params->ssid_len,
                params->ssid);
+       if (params->proberesp && params->proberesp_len)
+               NLA_PUT(msg, NL80211_ATTR_PROBE_RESP, params->proberesp_len,
+                       params->proberesp);
        switch (params->hide_ssid) {
        case NO_SSID_HIDING:
                NLA_PUT_U32(msg, NL80211_ATTR_HIDDEN_SSID,
@@ -4966,6 +5380,7 @@ static int wpa_driver_nl80211_set_ap(void *priv,
        }
        return ret;
  nla_put_failure:
+       nlmsg_free(msg);
        return -ENOBUFS;
 }
 
@@ -5007,6 +5422,7 @@ static int wpa_driver_nl80211_set_freq(struct i802_bss *bss,
        }
 
        ret = send_and_recv_msgs(drv, msg, NULL, NULL);
+       msg = NULL;
        if (ret == 0) {
                bss->freq = freq;
                return 0;
@@ -5014,6 +5430,7 @@ static int wpa_driver_nl80211_set_freq(struct i802_bss *bss,
        wpa_printf(MSG_DEBUG, "nl80211: Failed to set channel (freq=%d): "
                   "%d (%s)", freq, ret, strerror(-ret));
 nla_put_failure:
+       nlmsg_free(msg);
        return -1;
 }
 
@@ -5042,7 +5459,7 @@ static int wpa_driver_nl80211_sta_add(void *priv,
 {
        struct i802_bss *bss = priv;
        struct wpa_driver_nl80211_data *drv = bss->drv;
-       struct nl_msg *msg;
+       struct nl_msg *msg, *wme = NULL;
        struct nl80211_sta_flag_update upd;
        int ret = -ENOBUFS;
 
@@ -5077,7 +5494,21 @@ static int wpa_driver_nl80211_sta_add(void *priv,
        upd.set = upd.mask;
        NLA_PUT(msg, NL80211_ATTR_STA_FLAGS2, sizeof(upd), &upd);
 
+       if (params->flags & WPA_STA_WMM) {
+               wme = nlmsg_alloc();
+               if (!wme)
+                       goto nla_put_failure;
+
+               NLA_PUT_U8(wme, NL80211_STA_WME_UAPSD_QUEUES,
+                               params->qosinfo & WMM_QOSINFO_STA_AC_MASK);
+               NLA_PUT_U8(wme, NL80211_STA_WME_MAX_SP,
+                               (params->qosinfo > WMM_QOSINFO_STA_SP_SHIFT) &
+                               WMM_QOSINFO_STA_SP_MASK);
+               nla_put_nested(msg, NL80211_ATTR_STA_WME, wme);
+       }
+
        ret = send_and_recv_msgs(drv, msg, NULL, NULL);
+       msg = NULL;
        if (ret)
                wpa_printf(MSG_DEBUG, "nl80211: NL80211_CMD_%s_STATION "
                           "result: %d (%s)", params->set ? "SET" : "NEW", ret,
@@ -5085,6 +5516,8 @@ static int wpa_driver_nl80211_sta_add(void *priv,
        if (ret == -EEXIST)
                ret = 0;
  nla_put_failure:
+       nlmsg_free(wme);
+       nlmsg_free(msg);
        return ret;
 }
 
@@ -5111,6 +5544,7 @@ static int wpa_driver_nl80211_sta_remove(void *priv, const u8 *addr)
                return 0;
        return ret;
  nla_put_failure:
+       nlmsg_free(msg);
        return -ENOBUFS;
 }
 
@@ -5134,7 +5568,9 @@ static void nl80211_remove_iface(struct wpa_driver_nl80211_data *drv,
 
        if (send_and_recv_msgs(drv, msg, NULL, NULL) == 0)
                return;
+       msg = NULL;
  nla_put_failure:
+       nlmsg_free(msg);
        wpa_printf(MSG_ERROR, "Failed to remove interface (ifidx=%d)", ifidx);
 }
 
@@ -5201,8 +5637,10 @@ static int nl80211_create_iface_once(struct wpa_driver_nl80211_data *drv,
        }
 
        ret = send_and_recv_msgs(drv, msg, NULL, NULL);
+       msg = NULL;
        if (ret) {
  nla_put_failure:
+               nlmsg_free(msg);
                wpa_printf(MSG_ERROR, "Failed to create interface %s: %d (%s)",
                           ifname, ret, strerror(-ret));
                return ret;
@@ -5660,6 +6098,9 @@ static int nl80211_setup_ap(struct i802_bss *bss)
 {
        struct wpa_driver_nl80211_data *drv = bss->drv;
 
+       wpa_printf(MSG_DEBUG, "nl80211: Setup AP - device_ap_sme=%d "
+                  "use_monitor=%d", drv->device_ap_sme, drv->use_monitor);
+
        /*
         * Disable Probe Request reporting unless we need it in this way for
         * devices that include the AP SME, in the other case (unless using
@@ -5672,6 +6113,10 @@ static int nl80211_setup_ap(struct i802_bss *bss)
                if (nl80211_mgmt_subscribe_ap(bss))
                        return -1;
 
+       if (drv->device_ap_sme && !drv->use_monitor)
+               if (nl80211_mgmt_subscribe_ap_dev_sme(bss))
+                       return -1;
+
        if (!drv->device_ap_sme && drv->use_monitor &&
            nl80211_create_monitor_interface(drv) &&
            !drv->device_ap_sme)
@@ -5692,12 +6137,14 @@ static void nl80211_teardown_ap(struct i802_bss *bss)
 {
        struct wpa_driver_nl80211_data *drv = bss->drv;
 
-       if (drv->device_ap_sme)
+       if (drv->device_ap_sme) {
                wpa_driver_nl80211_probe_req_report(bss, 0);
-       else if (drv->use_monitor)
+               if (!drv->use_monitor)
+                       nl80211_mgmt_unsubscribe(bss, "AP teardown (dev SME)");
+       } else if (drv->use_monitor)
                nl80211_remove_monitor_interface(drv);
        else
-               nl80211_mgmt_unsubscribe(bss);
+               nl80211_mgmt_unsubscribe(bss, "AP teardown");
 
        bss->beacon_set = 0;
 }
@@ -5853,6 +6300,7 @@ static int wpa_driver_nl80211_sta_set_flags(void *priv, const u8 *addr,
 
        return send_and_recv_msgs(drv, msg, NULL, NULL);
  nla_put_failure:
+       nlmsg_free(msg);
        nlmsg_free(flags);
        return -ENOBUFS;
 }
@@ -6357,9 +6805,11 @@ static int nl80211_set_mode(struct wpa_driver_nl80211_data *drv,
        NLA_PUT_U32(msg, NL80211_ATTR_IFTYPE, mode);
 
        ret = send_and_recv_msgs(drv, msg, NULL, NULL);
+       msg = NULL;
        if (!ret)
                return 0;
 nla_put_failure:
+       nlmsg_free(msg);
        wpa_printf(MSG_DEBUG, "nl80211: Failed to set interface %d to mode %d:"
                   " %d (%s)", ifindex, mode, ret, strerror(-ret));
        return ret;
@@ -6373,13 +6823,18 @@ static int wpa_driver_nl80211_set_mode(struct i802_bss *bss,
        int ret = -1;
        int i;
        int was_ap = is_ap_interface(drv->nlmode);
+       int res;
 
-       if (nl80211_set_mode(drv, drv->ifindex, nlmode) == 0) {
+       res = nl80211_set_mode(drv, drv->ifindex, nlmode);
+       if (res == 0) {
                drv->nlmode = nlmode;
                ret = 0;
                goto done;
        }
 
+       if (res == -ENODEV)
+               return -1;
+
        if (nlmode == drv->nlmode) {
                wpa_printf(MSG_DEBUG, "nl80211: Interface already in "
                           "requested mode - ignore error");
@@ -6394,7 +6849,6 @@ static int wpa_driver_nl80211_set_mode(struct i802_bss *bss,
        wpa_printf(MSG_DEBUG, "nl80211: Try mode change after setting "
                   "interface down");
        for (i = 0; i < 10; i++) {
-               int res;
                res = linux_set_iface_flags(drv->global->ioctl_sock,
                                            bss->ifname, 0);
                if (res == -EACCES || res == -ENODEV)
@@ -6432,7 +6886,7 @@ done:
        }
 
        if (is_ap_interface(nlmode)) {
-               nl80211_mgmt_unsubscribe(bss);
+               nl80211_mgmt_unsubscribe(bss, "start AP");
                /* Setup additional AP mode functionality if needed */
                if (nl80211_setup_ap(bss))
                        return -1;
@@ -6440,7 +6894,7 @@ done:
                /* Remove additional AP mode functionality */
                nl80211_teardown_ap(bss);
        } else {
-               nl80211_mgmt_unsubscribe(bss);
+               nl80211_mgmt_unsubscribe(bss, "mode change");
        }
 
        if (!is_ap_interface(nlmode) &&
@@ -6502,6 +6956,7 @@ static int wpa_driver_nl80211_set_supp_port(void *priv, int authorized)
 
        return send_and_recv_msgs(drv, msg, NULL, NULL);
  nla_put_failure:
+       nlmsg_free(msg);
        return -ENOBUFS;
 }
 
@@ -6568,6 +7023,7 @@ static int i802_get_seqnum(const char *iface, void *priv, const u8 *addr,
 
        return send_and_recv_msgs(drv, msg, get_key_handler, seq);
  nla_put_failure:
+       nlmsg_free(msg);
        return -ENOBUFS;
 }
 
@@ -6594,9 +7050,11 @@ static int i802_set_rts(void *priv, int rts)
        NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_RTS_THRESHOLD, val);
 
        ret = send_and_recv_msgs(drv, msg, NULL, NULL);
+       msg = NULL;
        if (!ret)
                return 0;
 nla_put_failure:
+       nlmsg_free(msg);
        wpa_printf(MSG_DEBUG, "nl80211: Failed to set RTS threshold %d: "
                   "%d (%s)", rts, ret, strerror(-ret));
        return ret;
@@ -6625,9 +7083,11 @@ static int i802_set_frag(void *priv, int frag)
        NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_FRAG_THRESHOLD, val);
 
        ret = send_and_recv_msgs(drv, msg, NULL, NULL);
+       msg = NULL;
        if (!ret)
                return 0;
 nla_put_failure:
+       nlmsg_free(msg);
        wpa_printf(MSG_DEBUG, "nl80211: Failed to set fragmentation threshold "
                   "%d: %d (%s)", frag, ret, strerror(-ret));
        return ret;
@@ -6639,6 +7099,7 @@ static int i802_flush(void *priv)
        struct i802_bss *bss = priv;
        struct wpa_driver_nl80211_data *drv = bss->drv;
        struct nl_msg *msg;
+       int res;
 
        msg = nlmsg_alloc();
        if (!msg)
@@ -6652,8 +7113,14 @@ static int i802_flush(void *priv)
        NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX,
                    if_nametoindex(bss->ifname));
 
-       return send_and_recv_msgs(drv, msg, NULL, NULL);
+       res = send_and_recv_msgs(drv, msg, NULL, NULL);
+       if (res) {
+               wpa_printf(MSG_DEBUG, "nl80211: Station flush failed: ret=%d "
+                          "(%s)", res, strerror(-res));
+       }
+       return res;
  nla_put_failure:
+       nlmsg_free(msg);
        return -ENOBUFS;
 }
 
@@ -6728,6 +7195,7 @@ static int i802_read_sta_data(void *priv, struct hostap_sta_driver_data *data,
 
        return send_and_recv_msgs(drv, msg, get_sta_handler, data);
  nla_put_failure:
+       nlmsg_free(msg);
        return -ENOBUFS;
 }
 
@@ -6784,7 +7252,9 @@ static int i802_set_tx_queue_params(void *priv, int queue, int aifs,
 
        if (send_and_recv_msgs(drv, msg, NULL, NULL) == 0)
                return 0;
+       msg = NULL;
  nla_put_failure:
+       nlmsg_free(msg);
        return -1;
 }
 
@@ -6810,6 +7280,7 @@ static int i802_set_sta_vlan(void *priv, const u8 *addr,
                    if_nametoindex(ifname));
 
        ret = send_and_recv_msgs(drv, msg, NULL, NULL);
+       msg = NULL;
        if (ret < 0) {
                wpa_printf(MSG_ERROR, "nl80211: NL80211_ATTR_STA_VLAN (addr="
                           MACSTR " ifname=%s vlan_id=%d) failed: %d (%s)",
@@ -6817,6 +7288,7 @@ static int i802_set_sta_vlan(void *priv, const u8 *addr,
                           strerror(-ret));
        }
  nla_put_failure:
+       nlmsg_free(msg);
        return ret;
 }
 
@@ -7136,6 +7608,8 @@ static void *i802_init(struct hostapd_data *hapd,
                               params->own_addr))
                goto failed;
 
+       memcpy(bss->addr, params->own_addr, ETH_ALEN);
+
        return bss;
 
 failed:
@@ -7179,7 +7653,7 @@ static int nl80211_addr_in_use(struct nl80211_global *global, const u8 *addr)
        struct wpa_driver_nl80211_data *drv;
        dl_list_for_each(drv, &global->interfaces,
                         struct wpa_driver_nl80211_data, list) {
-               if (os_memcmp(addr, drv->addr, ETH_ALEN) == 0)
+               if (os_memcmp(addr, drv->first_bss.addr, ETH_ALEN) == 0)
                        return 1;
        }
        return 0;
@@ -7194,9 +7668,9 @@ static int nl80211_p2p_interface_addr(struct wpa_driver_nl80211_data *drv,
        if (!drv->global)
                return -1;
 
-       os_memcpy(new_addr, drv->addr, ETH_ALEN);
+       os_memcpy(new_addr, drv->first_bss.addr, ETH_ALEN);
        for (idx = 0; idx < 64; idx++) {
-               new_addr[0] = drv->addr[0] | 0x02;
+               new_addr[0] = drv->first_bss.addr[0] | 0x02;
                new_addr[0] ^= idx << 2;
                if (!nl80211_addr_in_use(drv->global, new_addr))
                        break;
@@ -7300,6 +7774,7 @@ static int wpa_driver_nl80211_if_add(void *priv, enum wpa_driver_if_type type,
                        return -1;
                }
                os_strlcpy(new_bss->ifname, ifname, IFNAMSIZ);
+               os_memcpy(new_bss->addr, if_addr, ETH_ALEN);
                new_bss->ifindex = ifidx;
                new_bss->drv = drv;
                new_bss->next = drv->first_bss.next;
@@ -7408,7 +7883,7 @@ static int nl80211_send_frame_cmd(struct i802_bss *bss,
        NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_FREQ, freq);
        if (wait)
                NLA_PUT_U32(msg, NL80211_ATTR_DURATION, wait);
-       if (offchanok)
+       if (offchanok && (drv->capa.flags & WPA_DRIVER_FLAGS_OFFCHANNEL_TX))
                NLA_PUT_FLAG(msg, NL80211_ATTR_OFFCHANNEL_TX_OK);
        if (no_cck)
                NLA_PUT_FLAG(msg, NL80211_ATTR_TX_NO_CCK_RATE);
@@ -7528,6 +8003,7 @@ static int wpa_driver_nl80211_remain_on_channel(void *priv, unsigned int freq,
 
        cookie = 0;
        ret = send_and_recv_msgs(drv, msg, cookie_handler, &cookie);
+       msg = NULL;
        if (ret == 0) {
                wpa_printf(MSG_DEBUG, "nl80211: Remain-on-channel cookie "
                           "0x%llx for freq=%u MHz duration=%u",
@@ -7540,6 +8016,7 @@ static int wpa_driver_nl80211_remain_on_channel(void *priv, unsigned int freq,
                   "(freq=%d duration=%u): %d (%s)",
                   freq, duration, ret, strerror(-ret));
 nla_put_failure:
+       nlmsg_free(msg);
        return -1;
 }
 
@@ -7571,11 +8048,13 @@ static int wpa_driver_nl80211_cancel_remain_on_channel(void *priv)
        NLA_PUT_U64(msg, NL80211_ATTR_COOKIE, drv->remain_on_chan_cookie);
 
        ret = send_and_recv_msgs(drv, msg, NULL, NULL);
+       msg = NULL;
        if (ret == 0)
                return 0;
        wpa_printf(MSG_DEBUG, "nl80211: Failed to cancel remain-on-channel: "
                   "%d (%s)", ret, strerror(-ret));
 nla_put_failure:
+       nlmsg_free(msg);
        return -1;
 }
 
@@ -7586,32 +8065,46 @@ static int wpa_driver_nl80211_probe_req_report(void *priv, int report)
        struct wpa_driver_nl80211_data *drv = bss->drv;
 
        if (!report) {
-               if (bss->nl_preq.handle) {
+               if (bss->nl_preq && drv->device_ap_sme &&
+                   is_ap_interface(drv->nlmode)) {
+                       /*
+                        * Do not disable Probe Request reporting that was
+                        * enabled in nl80211_setup_ap().
+                        */
+                       wpa_printf(MSG_DEBUG, "nl80211: Skip disabling of "
+                                  "Probe Request reporting nl_preq=%p while "
+                                  "in AP mode", bss->nl_preq);
+               } else if (bss->nl_preq) {
+                       wpa_printf(MSG_DEBUG, "nl80211: Disable Probe Request "
+                                  "reporting nl_preq=%p", bss->nl_preq);
                        eloop_unregister_read_sock(
-                               nl_socket_get_fd(bss->nl_preq.handle));
+                               nl_socket_get_fd(bss->nl_preq));
                        nl_destroy_handles(&bss->nl_preq);
                }
                return 0;
        }
 
-       if (bss->nl_preq.handle) {
+       if (bss->nl_preq) {
                wpa_printf(MSG_DEBUG, "nl80211: Probe Request reporting "
-                          "already on!");
+                          "already on! nl_preq=%p", bss->nl_preq);
                return 0;
        }
 
-       if (nl_create_handles(&bss->nl_preq, drv->global->nl_cb, "preq"))
+       bss->nl_preq = nl_create_handle(drv->global->nl_cb, "preq");
+       if (bss->nl_preq == NULL)
                return -1;
+       wpa_printf(MSG_DEBUG, "nl80211: Enable Probe Request "
+                  "reporting nl_preq=%p", bss->nl_preq);
 
-       if (nl80211_register_frame(bss, bss->nl_preq.handle,
+       if (nl80211_register_frame(bss, bss->nl_preq,
                                   (WLAN_FC_TYPE_MGMT << 2) |
                                   (WLAN_FC_STYPE_PROBE_REQ << 4),
                                   NULL, 0) < 0)
                goto out_err;
 
-       eloop_register_read_sock(nl_socket_get_fd(bss->nl_preq.handle),
+       eloop_register_read_sock(nl_socket_get_fd(bss->nl_preq),
                                 wpa_driver_nl80211_event_receive, bss->nl_cb,
-                                bss->nl_preq.handle);
+                                bss->nl_preq);
 
        return 0;
 
@@ -7700,11 +8193,7 @@ static int nl80211_send_ft_action(void *priv, u8 action, const u8 *target_ap,
        int ret;
        u8 *data, *pos;
        size_t data_len;
-       u8 own_addr[ETH_ALEN];
-
-       if (linux_get_ifhwaddr(drv->global->ioctl_sock, bss->ifname,
-                              own_addr) < 0)
-               return -1;
+       const u8 *own_addr = bss->addr;
 
        if (action != 1) {
                wpa_printf(MSG_ERROR, "nl80211: Unsupported send_ft_action "
@@ -7773,8 +8262,7 @@ static int nl80211_signal_monitor(void *priv, int threshold, int hysteresis)
        msg = NULL;
 
 nla_put_failure:
-       if (cqm)
-               nlmsg_free(cqm);
+       nlmsg_free(cqm);
        nlmsg_free(msg);
        return -1;
 }
@@ -7819,7 +8307,7 @@ static int wpa_driver_nl80211_shared_freq(void *priv)
                wpa_printf(MSG_DEBUG, "nl80211: Found a match for PHY %s - %s "
                           MACSTR,
                           driver->phyname, driver->first_bss.ifname,
-                          MAC2STR(driver->addr));
+                          MAC2STR(driver->first_bss.addr));
                freq = nl80211_get_assoc_freq(driver);
                wpa_printf(MSG_DEBUG, "nl80211: Shared freq for PHY %s: %d",
                           drv->phyname, freq);
@@ -7921,8 +8409,13 @@ static void nl80211_global_deinit(void *priv)
 
        nl_destroy_handles(&global->nl);
 
-       if (global->nl_cb)
-               nl_cb_put(global->nl_cb);
+       if (global->nl_event) {
+               eloop_unregister_read_sock(
+                       nl_socket_get_fd(global->nl_event));
+               nl_destroy_handles(&global->nl_event);
+       }
+
+       nl_cb_put(global->nl_cb);
 
        if (global->ioctl_sock >= 0)
                close(global->ioctl_sock);
@@ -7958,6 +8451,7 @@ static int nl80211_pmkid(struct i802_bss *bss, int cmd, const u8 *bssid,
 
        return send_and_recv_msgs(bss->drv, msg, NULL, NULL);
  nla_put_failure:
+       nlmsg_free(msg);
        return -ENOBUFS;
 }
 
@@ -8290,7 +8784,8 @@ static int android_pno_start(struct i802_bss *bss,
                     WEXT_PNO_NONSSID_SECTIONS_SIZE + 1) >= (int) sizeof(buf))
                        break;
                wpa_hexdump_ascii(MSG_DEBUG, "For PNO Scan",
-                                 ssid[i].ssid, ssid[i].ssid_len);
+                                 params->ssids[i].ssid,
+                                 params->ssids[i].ssid_len);
                buf[bp++] = WEXT_PNO_SSID_SECTION;
                buf[bp++] = params->ssids[i].ssid_len;
                os_memcpy(&buf[bp], params->ssids[i].ssid,