nl80211: Include linux/rtnetlink.h explicitly
[libeap.git] / src / drivers / driver_nl80211.c
index 91757b1..3f91be4 100644 (file)
@@ -1,10 +1,10 @@
 /*
  * Driver interaction with Linux nl80211/cfg80211
- * Copyright (c) 2002-2008, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2002-2010, 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>
- * Copyright (c) 2009, Atheros Communications
+ * Copyright (c) 2009-2010, Atheros Communications
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 as
 
 #include "includes.h"
 #include <sys/ioctl.h>
-#include <net/if_arp.h>
 #include <net/if.h>
 #include <netlink/genl/genl.h>
 #include <netlink/genl/family.h>
 #include <netlink/genl/ctrl.h>
+#include <linux/rtnetlink.h>
+#include <netpacket/packet.h>
+#include <linux/filter.h>
 #include "nl80211_copy.h"
 
 #include "common.h"
-#include "driver.h"
 #include "eloop.h"
 #include "common/ieee802_11_defs.h"
-
-#if defined(CONFIG_AP) || defined(HOSTAPD)
-#include <netpacket/packet.h>
-#include <linux/filter.h>
+#include "netlink.h"
+#include "linux_ioctl.h"
 #include "radiotap.h"
 #include "radiotap_iter.h"
-
-#include "../../hostapd/sta_flags.h"
-#endif /* CONFIG_AP || HOSTAPD */
+#include "rfkill.h"
+#include "driver.h"
 
 #ifdef CONFIG_LIBNL20
 /* libnl 2.0 compatibility code */
 #endif
 
 struct i802_bss {
+       struct wpa_driver_nl80211_data *drv;
        struct i802_bss *next;
        int ifindex;
+       char ifname[IFNAMSIZ + 1];
        unsigned int beacon_set:1;
 };
 
 struct wpa_driver_nl80211_data {
        void *ctx;
-       int link_event_sock;
+       struct netlink_data *netlink;
        int ioctl_sock; /* socket for ioctl() use */
-       char ifname[IFNAMSIZ + 1];
+       char brname[IFNAMSIZ];
        int ifindex;
        int if_removed;
+       int if_disabled;
+       struct rfkill_data *rfkill;
        struct wpa_driver_capa capa;
        int has_capability;
 
@@ -95,11 +97,24 @@ struct wpa_driver_nl80211_data {
        size_t ssid_len;
        int nlmode;
        int ap_scan_as_station;
+       unsigned int assoc_freq;
 
        int monitor_sock;
        int monitor_ifidx;
+       int probe_req_report;
+       int disable_11b_rates;
 
-       unsigned int beacon_set:1;
+       unsigned int pending_remain_on_chan:1;
+       unsigned int added_bridge:1;
+       unsigned int added_if_into_bridge:1;
+
+       u64 remain_on_chan_cookie;
+       u64 send_action_cookie;
+
+       struct wpa_driver_scan_filter *filter_ssids;
+       size_t num_filter_ssids;
+
+       struct i802_bss first_bss;
 
 #ifdef HOSTAPD
        int eapol_sock; /* socket for EAPOL frames */
@@ -108,8 +123,6 @@ struct wpa_driver_nl80211_data {
        int *if_indices;
        int num_if_indices;
 
-       struct i802_bss bss;
-
        int last_freq;
        int last_freq_ht;
 #endif /* HOSTAPD */
@@ -122,28 +135,31 @@ static int wpa_driver_nl80211_set_mode(void *priv, int mode);
 static int
 wpa_driver_nl80211_finish_drv_init(struct wpa_driver_nl80211_data *drv);
 static int wpa_driver_nl80211_mlme(struct wpa_driver_nl80211_data *drv,
-                                  const u8 *addr, int cmd, u16 reason_code);
-
-#if defined(CONFIG_AP) || defined(HOSTAPD)
+                                  const u8 *addr, int cmd, u16 reason_code,
+                                  int local_state_change);
 static void nl80211_remove_monitor_interface(
        struct wpa_driver_nl80211_data *drv);
-#endif /* CONFIG_AP || HOSTAPD */
-
-#ifdef CONFIG_AP
-static void nl80211_remove_iface(struct wpa_driver_nl80211_data *drv,
-                                int ifidx);
-#endif /* CONFIG_AP */
 
 #ifdef HOSTAPD
 static void add_ifidx(struct wpa_driver_nl80211_data *drv, int ifidx);
 static void del_ifidx(struct wpa_driver_nl80211_data *drv, int ifidx);
-static struct i802_bss * get_bss(struct wpa_driver_nl80211_data *drv,
-                                int ifindex);
-static void nl80211_remove_iface(struct wpa_driver_nl80211_data *drv,
-                                int ifidx);
-static int i802_set_freq(void *priv, struct hostapd_freq_params *freq);
+static int have_ifidx(struct wpa_driver_nl80211_data *drv, int ifidx);
+static int wpa_driver_nl80211_if_remove(void *priv,
+                                       enum wpa_driver_if_type type,
+                                       const char *ifname);
+#else /* HOSTAPD */
+static int have_ifidx(struct wpa_driver_nl80211_data *drv, int ifidx)
+{
+       return 0;
+}
 #endif /* HOSTAPD */
 
+static int i802_set_freq(void *priv, struct hostapd_freq_params *freq);
+static void wpa_driver_nl80211_probe_req_report_timeout(void *eloop_ctx,
+                                                       void *timeout_ctx);
+static int nl80211_disable_11b_rates(struct wpa_driver_nl80211_data *drv,
+                                    int ifindex, int disabled);
+
 
 /* nl80211 code */
 static int ack_handler(struct nl_msg *msg, void *arg)
@@ -175,10 +191,10 @@ static int no_seq_check(struct nl_msg *msg, void *arg)
 }
 
 
-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)
+static int send_and_recv(struct wpa_driver_nl80211_data *drv,
+                        struct nl_handle *nl_handle, struct nl_msg *msg,
+                        int (*valid_handler)(struct nl_msg *, void *),
+                        void *valid_data)
 {
        struct nl_cb *cb;
        int err = -ENOMEM;
@@ -187,7 +203,7 @@ static int send_and_recv_msgs(struct wpa_driver_nl80211_data *drv,
        if (!cb)
                goto out;
 
-       err = nl_send_auto_complete(drv->nl_handle, msg);
+       err = nl_send_auto_complete(nl_handle, msg);
        if (err < 0)
                goto out;
 
@@ -202,7 +218,7 @@ static int send_and_recv_msgs(struct wpa_driver_nl80211_data *drv,
                          valid_handler, valid_data);
 
        while (err > 0)
-               nl_recvmsgs(drv->nl_handle, cb);
+               nl_recvmsgs(nl_handle, cb);
  out:
        nl_cb_put(cb);
        nlmsg_free(msg);
@@ -210,6 +226,16 @@ static int send_and_recv_msgs(struct wpa_driver_nl80211_data *drv,
 }
 
 
+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->nl_handle, msg, valid_handler,
+                            valid_data);
+}
+
+
 struct family_data {
        const char *group;
        int id;
@@ -272,117 +298,10 @@ nla_put_failure:
 }
 
 
-#ifdef HOSTAPD
-static int get_ifhwaddr(struct wpa_driver_nl80211_data *drv,
-                       const char *ifname, u8 *addr)
-{
-       struct ifreq ifr;
-
-       os_memset(&ifr, 0, sizeof(ifr));
-       os_strlcpy(ifr.ifr_name, ifname, IFNAMSIZ);
-       if (ioctl(drv->ioctl_sock, SIOCGIFHWADDR, &ifr)) {
-               wpa_printf(MSG_ERROR, "%s: ioctl(SIOCGIFHWADDR): %d (%s)",
-                          ifname, errno, strerror(errno));
-               return -1;
-       }
-
-       if (ifr.ifr_hwaddr.sa_family != ARPHRD_ETHER) {
-               wpa_printf(MSG_ERROR, "%s: Invalid HW-addr family 0x%04x",
-                          ifname, ifr.ifr_hwaddr.sa_family);
-               return -1;
-       }
-       os_memcpy(addr, ifr.ifr_hwaddr.sa_data, ETH_ALEN);
-
-       return 0;
-}
-
-
-static int set_ifhwaddr(struct wpa_driver_nl80211_data *drv,
-                       const char *ifname, const u8 *addr)
-{
-       struct ifreq ifr;
-
-       os_memset(&ifr, 0, sizeof(ifr));
-       os_strlcpy(ifr.ifr_name, ifname, IFNAMSIZ);
-       os_memcpy(ifr.ifr_hwaddr.sa_data, addr, ETH_ALEN);
-       ifr.ifr_hwaddr.sa_family = ARPHRD_ETHER;
-
-       if (ioctl(drv->ioctl_sock, SIOCSIFHWADDR, &ifr)) {
-               wpa_printf(MSG_DEBUG, "%s: ioctl(SIOCSIFHWADDR): %d (%s)",
-                          ifname, errno, strerror(errno));
-               return -1;
-       }
-
-       return 0;
-}
-#endif /* HOSTAPD */
-
-
-static int wpa_driver_nl80211_send_oper_ifla(
-       struct wpa_driver_nl80211_data *drv,
-       int linkmode, int operstate)
-{
-       struct {
-               struct nlmsghdr hdr;
-               struct ifinfomsg ifinfo;
-               char opts[16];
-       } req;
-       struct rtattr *rta;
-       static int nl_seq;
-       ssize_t ret;
-
-       os_memset(&req, 0, sizeof(req));
-
-       req.hdr.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg));
-       req.hdr.nlmsg_type = RTM_SETLINK;
-       req.hdr.nlmsg_flags = NLM_F_REQUEST;
-       req.hdr.nlmsg_seq = ++nl_seq;
-       req.hdr.nlmsg_pid = 0;
-
-       req.ifinfo.ifi_family = AF_UNSPEC;
-       req.ifinfo.ifi_type = 0;
-       req.ifinfo.ifi_index = drv->ifindex;
-       req.ifinfo.ifi_flags = 0;
-       req.ifinfo.ifi_change = 0;
-
-       if (linkmode != -1) {
-               rta = aliasing_hide_typecast(
-                       ((char *) &req + NLMSG_ALIGN(req.hdr.nlmsg_len)),
-                       struct rtattr);
-               rta->rta_type = IFLA_LINKMODE;
-               rta->rta_len = RTA_LENGTH(sizeof(char));
-               *((char *) RTA_DATA(rta)) = linkmode;
-               req.hdr.nlmsg_len = NLMSG_ALIGN(req.hdr.nlmsg_len) +
-                       RTA_LENGTH(sizeof(char));
-       }
-       if (operstate != -1) {
-               rta = aliasing_hide_typecast(
-                       ((char *) &req + NLMSG_ALIGN(req.hdr.nlmsg_len)),
-                       struct rtattr);
-               rta->rta_type = IFLA_OPERSTATE;
-               rta->rta_len = RTA_LENGTH(sizeof(char));
-               *((char *) RTA_DATA(rta)) = operstate;
-               req.hdr.nlmsg_len = NLMSG_ALIGN(req.hdr.nlmsg_len) +
-                       RTA_LENGTH(sizeof(char));
-       }
-
-       wpa_printf(MSG_DEBUG, "nl80211: Operstate: linkmode=%d, operstate=%d",
-                  linkmode, operstate);
-
-       ret = send(drv->link_event_sock, &req, req.hdr.nlmsg_len, 0);
-       if (ret < 0) {
-               wpa_printf(MSG_DEBUG, "nl80211: Sending operstate IFLA failed:"
-                          " %s (assume operstate is not supported)",
-                          strerror(errno));
-       }
-
-       return ret < 0 ? -1 : 0;
-}
-
-
 static int wpa_driver_nl80211_get_bssid(void *priv, u8 *bssid)
 {
-       struct wpa_driver_nl80211_data *drv = priv;
+       struct i802_bss *bss = priv;
+       struct wpa_driver_nl80211_data *drv = bss->drv;
        if (!drv->associated)
                return -1;
        os_memcpy(bssid, drv->bssid, ETH_ALEN);
@@ -392,7 +311,8 @@ static int wpa_driver_nl80211_get_bssid(void *priv, u8 *bssid)
 
 static int wpa_driver_nl80211_get_ssid(void *priv, u8 *ssid)
 {
-       struct wpa_driver_nl80211_data *drv = priv;
+       struct i802_bss *bss = priv;
+       struct wpa_driver_nl80211_data *drv = bss->drv;
        if (!drv->associated)
                return -1;
        os_memcpy(ssid, drv->ssid, drv->ssid_len);
@@ -400,11 +320,8 @@ static int wpa_driver_nl80211_get_ssid(void *priv, u8 *ssid)
 }
 
 
-#ifndef HOSTAPD
-
 static void wpa_driver_nl80211_event_link(struct wpa_driver_nl80211_data *drv,
-                                         void *ctx, char *buf, size_t len,
-                                         int del)
+                                         char *buf, size_t len, int del)
 {
        union wpa_event_data event;
 
@@ -420,38 +337,30 @@ static void wpa_driver_nl80211_event_link(struct wpa_driver_nl80211_data *drv,
                   event.interface_status.ifname,
                   del ? "removed" : "added");
 
-       if (os_strcmp(drv->ifname, event.interface_status.ifname) == 0) {
+       if (os_strcmp(drv->first_bss.ifname, event.interface_status.ifname) == 0) {
                if (del)
                        drv->if_removed = 1;
                else
                        drv->if_removed = 0;
        }
 
-       wpa_supplicant_event(ctx, EVENT_INTERFACE_STATUS, &event);
+       wpa_supplicant_event(drv->ctx, EVENT_INTERFACE_STATUS, &event);
 }
 
 
 static int wpa_driver_nl80211_own_ifname(struct wpa_driver_nl80211_data *drv,
-                                        struct nlmsghdr *h)
+                                        u8 *buf, size_t len)
 {
-       struct ifinfomsg *ifi;
-       int attrlen, _nlmsg_len, rta_len;
+       int attrlen, rta_len;
        struct rtattr *attr;
 
-       ifi = NLMSG_DATA(h);
-
-       _nlmsg_len = NLMSG_ALIGN(sizeof(struct ifinfomsg));
-
-       attrlen = h->nlmsg_len - _nlmsg_len;
-       if (attrlen < 0)
-               return 0;
-
-       attr = (struct rtattr *) (((char *) ifi) + _nlmsg_len);
+       attrlen = len;
+       attr = (struct rtattr *) buf;
 
        rta_len = RTA_ALIGN(sizeof(struct rtattr));
        while (RTA_OK(attr, attrlen)) {
                if (attr->rta_type == IFLA_IFNAME) {
-                       if (os_strcmp(((char *) attr) + rta_len, drv->ifname)
+                       if (os_strcmp(((char *) attr) + rta_len, drv->first_bss.ifname)
                            == 0)
                                return 1;
                        else
@@ -465,13 +374,13 @@ static int wpa_driver_nl80211_own_ifname(struct wpa_driver_nl80211_data *drv,
 
 
 static int wpa_driver_nl80211_own_ifindex(struct wpa_driver_nl80211_data *drv,
-                                         int ifindex, struct nlmsghdr *h)
+                                         int ifindex, u8 *buf, size_t len)
 {
        if (drv->ifindex == ifindex)
                return 1;
 
-       if (drv->if_removed && wpa_driver_nl80211_own_ifname(drv, h)) {
-               drv->ifindex = if_nametoindex(drv->ifname);
+       if (drv->if_removed && wpa_driver_nl80211_own_ifname(drv, buf, len)) {
+               drv->first_bss.ifindex = if_nametoindex(drv->first_bss.ifname);
                wpa_printf(MSG_DEBUG, "nl80211: Update ifindex for a removed "
                           "interface");
                wpa_driver_nl80211_finish_drv_init(drv);
@@ -482,22 +391,19 @@ static int wpa_driver_nl80211_own_ifindex(struct wpa_driver_nl80211_data *drv,
 }
 
 
-static void wpa_driver_nl80211_event_rtm_newlink(struct wpa_driver_nl80211_data *drv,
-                                             void *ctx, struct nlmsghdr *h,
-                                             size_t len)
+static void wpa_driver_nl80211_event_rtm_newlink(void *ctx,
+                                                struct ifinfomsg *ifi,
+                                                u8 *buf, size_t len)
 {
-       struct ifinfomsg *ifi;
-       int attrlen, _nlmsg_len, rta_len;
-       struct rtattr * attr;
-
-       if (len < sizeof(*ifi))
-               return;
-
-       ifi = NLMSG_DATA(h);
+       struct wpa_driver_nl80211_data *drv = ctx;
+       int attrlen, rta_len;
+       struct rtattr *attr;
+       u32 brid = 0;
 
-       if (!wpa_driver_nl80211_own_ifindex(drv, ifi->ifi_index, h)) {
-               wpa_printf(MSG_DEBUG, "Ignore event for foreign ifindex %d",
-                          ifi->ifi_index);
+       if (!wpa_driver_nl80211_own_ifindex(drv, ifi->ifi_index, buf, len) &&
+           !have_ifidx(drv, ifi->ifi_index)) {
+               wpa_printf(MSG_DEBUG, "nl80211: Ignore event for foreign "
+                          "ifindex %d", ifi->ifi_index);
                return;
        }
 
@@ -508,6 +414,19 @@ static void wpa_driver_nl80211_event_rtm_newlink(struct wpa_driver_nl80211_data
                   (ifi->ifi_flags & IFF_RUNNING) ? "[RUNNING]" : "",
                   (ifi->ifi_flags & IFF_LOWER_UP) ? "[LOWER_UP]" : "",
                   (ifi->ifi_flags & IFF_DORMANT) ? "[DORMANT]" : "");
+
+       if (!drv->if_disabled && !(ifi->ifi_flags & IFF_UP)) {
+               wpa_printf(MSG_DEBUG, "nl80211: Interface down");
+               drv->if_disabled = 1;
+               wpa_supplicant_event(drv->ctx, EVENT_INTERFACE_DISABLED, NULL);
+       }
+
+       if (drv->if_disabled && (ifi->ifi_flags & IFF_UP)) {
+               wpa_printf(MSG_DEBUG, "nl80211: Interface up");
+               drv->if_disabled = 0;
+               wpa_supplicant_event(drv->ctx, EVENT_INTERFACE_ENABLED, NULL);
+       }
+
        /*
         * Some drivers send the association event before the operup event--in
         * this case, lifting operstate in wpa_driver_nl80211_set_operstate()
@@ -517,130 +436,71 @@ static void wpa_driver_nl80211_event_rtm_newlink(struct wpa_driver_nl80211_data
        if (drv->operstate == 1 &&
            (ifi->ifi_flags & (IFF_LOWER_UP | IFF_DORMANT)) == IFF_LOWER_UP &&
            !(ifi->ifi_flags & IFF_RUNNING))
-               wpa_driver_nl80211_send_oper_ifla(drv, -1, IF_OPER_UP);
-
-       _nlmsg_len = NLMSG_ALIGN(sizeof(struct ifinfomsg));
-
-       attrlen = h->nlmsg_len - _nlmsg_len;
-       if (attrlen < 0)
-               return;
-
-       attr = (struct rtattr *) (((char *) ifi) + _nlmsg_len);
+               netlink_send_oper_ifla(drv->netlink, drv->ifindex,
+                                      -1, IF_OPER_UP);
 
+       attrlen = len;
+       attr = (struct rtattr *) buf;
        rta_len = RTA_ALIGN(sizeof(struct rtattr));
        while (RTA_OK(attr, attrlen)) {
                if (attr->rta_type == IFLA_IFNAME) {
                        wpa_driver_nl80211_event_link(
-                               drv, ctx,
+                               drv,
                                ((char *) attr) + rta_len,
                                attr->rta_len - rta_len, 0);
-               }
+               } else if (attr->rta_type == IFLA_MASTER)
+                       brid = nla_get_u32((struct nlattr *) attr);
                attr = RTA_NEXT(attr, attrlen);
        }
+
+#ifdef HOSTAPD
+       if (ifi->ifi_family == AF_BRIDGE && brid) {
+               /* device has been added to bridge */
+               char namebuf[IFNAMSIZ];
+               if_indextoname(brid, namebuf);
+               wpa_printf(MSG_DEBUG, "nl80211: Add ifindex %u for bridge %s",
+                          brid, namebuf);
+               add_ifidx(drv, brid);
+       }
+#endif /* HOSTAPD */
 }
 
 
-static void wpa_driver_nl80211_event_rtm_dellink(struct wpa_driver_nl80211_data *drv,
-                                             void *ctx, struct nlmsghdr *h,
-                                             size_t len)
+static void wpa_driver_nl80211_event_rtm_dellink(void *ctx,
+                                                struct ifinfomsg *ifi,
+                                                u8 *buf, size_t len)
 {
-       struct ifinfomsg *ifi;
-       int attrlen, _nlmsg_len, rta_len;
-       struct rtattr * attr;
-
-       if (len < sizeof(*ifi))
-               return;
-
-       ifi = NLMSG_DATA(h);
-
-       _nlmsg_len = NLMSG_ALIGN(sizeof(struct ifinfomsg));
-
-       attrlen = h->nlmsg_len - _nlmsg_len;
-       if (attrlen < 0)
-               return;
+       struct wpa_driver_nl80211_data *drv = ctx;
+       int attrlen, rta_len;
+       struct rtattr *attr;
+       u32 brid = 0;
 
-       attr = (struct rtattr *) (((char *) ifi) + _nlmsg_len);
+       attrlen = len;
+       attr = (struct rtattr *) buf;
 
        rta_len = RTA_ALIGN(sizeof(struct rtattr));
        while (RTA_OK(attr, attrlen)) {
                if (attr->rta_type == IFLA_IFNAME) {
                        wpa_driver_nl80211_event_link(
-                               drv, ctx,
+                               drv,
                                ((char *) attr) + rta_len,
                                attr->rta_len - rta_len, 1);
-               }
+               } else if (attr->rta_type == IFLA_MASTER)
+                       brid = nla_get_u32((struct nlattr *) attr);
                attr = RTA_NEXT(attr, attrlen);
        }
-}
-
-
-static void wpa_driver_nl80211_event_receive_link(int sock, void *eloop_ctx,
-                                                 void *sock_ctx)
-{
-       char buf[8192];
-       int left;
-       struct sockaddr_nl from;
-       socklen_t fromlen;
-       struct nlmsghdr *h;
-       int max_events = 10;
-
-try_again:
-       fromlen = sizeof(from);
-       left = recvfrom(sock, buf, sizeof(buf), MSG_DONTWAIT,
-                       (struct sockaddr *) &from, &fromlen);
-       if (left < 0) {
-               if (errno != EINTR && errno != EAGAIN)
-                       perror("recvfrom(netlink)");
-               return;
-       }
 
-       h = (struct nlmsghdr *) buf;
-       while (left >= (int) sizeof(*h)) {
-               int len, plen;
-
-               len = h->nlmsg_len;
-               plen = len - sizeof(*h);
-               if (len > left || plen < 0) {
-                       wpa_printf(MSG_DEBUG, "Malformed netlink message: "
-                                  "len=%d left=%d plen=%d",
-                                  len, left, plen);
-                       break;
-               }
-
-               switch (h->nlmsg_type) {
-               case RTM_NEWLINK:
-                       wpa_driver_nl80211_event_rtm_newlink(eloop_ctx, sock_ctx,
-                                                         h, plen);
-                       break;
-               case RTM_DELLINK:
-                       wpa_driver_nl80211_event_rtm_dellink(eloop_ctx, sock_ctx,
-                                                         h, plen);
-                       break;
-               }
-
-               len = NLMSG_ALIGN(len);
-               left -= len;
-               h = (struct nlmsghdr *) ((char *) h + len);
-       }
-
-       if (left > 0) {
-               wpa_printf(MSG_DEBUG, "%d extra bytes in the end of netlink "
-                          "message", left);
-       }
-
-       if (--max_events > 0) {
-               /*
-                * Try to receive all events in one eloop call in order to
-                * limit race condition on cases where AssocInfo event, Assoc
-                * event, and EAPOL frames are received more or less at the
-                * same time. We want to process the event messages first
-                * before starting EAPOL processing.
-                */
-               goto try_again;
+#ifdef HOSTAPD
+       if (ifi->ifi_family == AF_BRIDGE && brid) {
+               /* device has been removed from bridge */
+               char namebuf[IFNAMSIZ];
+               if_indextoname(brid, namebuf);
+               wpa_printf(MSG_DEBUG, "nl80211: Remove ifindex %u for bridge "
+                          "%s", brid, namebuf);
+               del_ifidx(drv, brid);
        }
-}
-
 #endif /* HOSTAPD */
+}
 
 
 static void mlme_event_auth(struct wpa_driver_nl80211_data *drv,
@@ -709,6 +569,8 @@ static void mlme_event_assoc(struct wpa_driver_nl80211_data *drv,
                        len - 24 - sizeof(mgmt->u.assoc_resp);
        }
 
+       event.assoc_info.freq = drv->assoc_freq;
+
        wpa_supplicant_event(drv->ctx, EVENT_ASSOC, &event);
 }
 
@@ -784,9 +646,132 @@ static void mlme_timeout_event(struct wpa_driver_nl80211_data *drv,
 }
 
 
+static void mlme_event_action(struct wpa_driver_nl80211_data *drv,
+                             struct nlattr *freq, const u8 *frame, size_t len)
+{
+       const struct ieee80211_mgmt *mgmt;
+       union wpa_event_data event;
+       u16 fc, stype;
+
+       mgmt = (const struct ieee80211_mgmt *) frame;
+       if (len < 24) {
+               wpa_printf(MSG_DEBUG, "nl80211: Too short action frame");
+               return;
+       }
+
+       fc = le_to_host16(mgmt->frame_control);
+       stype = WLAN_FC_GET_STYPE(fc);
+
+       os_memset(&event, 0, sizeof(event));
+       event.rx_action.da = mgmt->da;
+       event.rx_action.sa = mgmt->sa;
+       event.rx_action.bssid = mgmt->bssid;
+       event.rx_action.category = mgmt->u.action.category;
+       event.rx_action.data = &mgmt->u.action.category + 1;
+       event.rx_action.len = frame + len - event.rx_action.data;
+       if (freq)
+               event.rx_action.freq = nla_get_u32(freq);
+       wpa_supplicant_event(drv->ctx, EVENT_RX_ACTION, &event);
+}
+
+
+static void mlme_event_action_tx_status(struct wpa_driver_nl80211_data *drv,
+                                       struct nlattr *cookie, const u8 *frame,
+                                       size_t len, struct nlattr *ack)
+{
+       union wpa_event_data event;
+       const struct ieee80211_hdr *hdr;
+       u16 fc;
+       u64 cookie_val;
+
+       if (!cookie)
+               return;
+
+       cookie_val = nla_get_u64(cookie);
+       wpa_printf(MSG_DEBUG, "nl80211: Action TX status: cookie=0%llx%s "
+                  "(ack=%d)",
+                  (long long unsigned int) cookie_val,
+                  cookie_val == drv->send_action_cookie ?
+                  " (match)" : " (unknown)", ack != NULL);
+       if (cookie_val != drv->send_action_cookie)
+               return;
+
+       hdr = (const struct ieee80211_hdr *) frame;
+       fc = le_to_host16(hdr->frame_control);
+
+       os_memset(&event, 0, sizeof(event));
+       event.tx_status.type = WLAN_FC_GET_TYPE(fc);
+       event.tx_status.stype = WLAN_FC_GET_STYPE(fc);
+       event.tx_status.dst = hdr->addr1;
+       event.tx_status.data = frame;
+       event.tx_status.data_len = len;
+       event.tx_status.ack = ack != NULL;
+       wpa_supplicant_event(drv->ctx, EVENT_TX_STATUS, &event);
+}
+
+
+static void mlme_event_deauth_disassoc(struct wpa_driver_nl80211_data *drv,
+                                      enum wpa_event_type type,
+                                      const u8 *frame, size_t len)
+{
+       const struct ieee80211_mgmt *mgmt;
+       union wpa_event_data event;
+       const u8 *bssid = NULL;
+       u16 reason_code = 0;
+
+       mgmt = (const struct ieee80211_mgmt *) frame;
+       if (len >= 24) {
+               bssid = mgmt->bssid;
+
+               if (drv->associated != 0 &&
+                   os_memcmp(bssid, drv->bssid, ETH_ALEN) != 0 &&
+                   os_memcmp(bssid, drv->auth_bssid, ETH_ALEN) != 0) {
+                       /*
+                        * We have presumably received this deauth as a
+                        * response to a clear_state_mismatch() outgoing
+                        * deauth.  Don't let it take us offline!
+                        */
+                       wpa_printf(MSG_DEBUG, "nl80211: Deauth received "
+                                  "from Unknown BSSID " MACSTR " -- ignoring",
+                                  MAC2STR(bssid));
+                       return;
+               }
+       }
+
+       drv->associated = 0;
+       os_memset(&event, 0, sizeof(event));
+
+       /* Note: Same offset for Reason Code in both frame subtypes */
+       if (len >= 24 + sizeof(mgmt->u.deauth))
+               reason_code = le_to_host16(mgmt->u.deauth.reason_code);
+
+       if (type == EVENT_DISASSOC) {
+               event.disassoc_info.addr = bssid;
+               event.disassoc_info.reason_code = reason_code;
+               if (frame + len > mgmt->u.disassoc.variable) {
+                       event.disassoc_info.ie = mgmt->u.disassoc.variable;
+                       event.disassoc_info.ie_len = frame + len -
+                               mgmt->u.disassoc.variable;
+               }
+       } else {
+               event.deauth_info.addr = bssid;
+               event.deauth_info.reason_code = reason_code;
+               if (frame + len > mgmt->u.deauth.variable) {
+                       event.deauth_info.ie = mgmt->u.deauth.variable;
+                       event.deauth_info.ie_len = frame + len -
+                               mgmt->u.deauth.variable;
+               }
+       }
+
+       wpa_supplicant_event(drv->ctx, type, &event);
+}
+
+
 static void mlme_event(struct wpa_driver_nl80211_data *drv,
                       enum nl80211_commands cmd, struct nlattr *frame,
-                      struct nlattr *addr, struct nlattr *timed_out)
+                      struct nlattr *addr, struct nlattr *timed_out,
+                      struct nlattr *freq, struct nlattr *ack,
+                      struct nlattr *cookie)
 {
        if (timed_out && addr) {
                mlme_timeout_event(drv, cmd, addr);
@@ -811,12 +796,19 @@ static void mlme_event(struct wpa_driver_nl80211_data *drv,
                mlme_event_assoc(drv, nla_data(frame), nla_len(frame));
                break;
        case NL80211_CMD_DEAUTHENTICATE:
-               drv->associated = 0;
-               wpa_supplicant_event(drv->ctx, EVENT_DEAUTH, NULL);
+               mlme_event_deauth_disassoc(drv, EVENT_DEAUTH,
+                                          nla_data(frame), nla_len(frame));
                break;
        case NL80211_CMD_DISASSOCIATE:
-               drv->associated = 0;
-               wpa_supplicant_event(drv->ctx, EVENT_DISASSOC, NULL);
+               mlme_event_deauth_disassoc(drv, EVENT_DISASSOC,
+                                          nla_data(frame), nla_len(frame));
+               break;
+       case NL80211_CMD_ACTION:
+               mlme_event_action(drv, freq, nla_data(frame), nla_len(frame));
+               break;
+       case NL80211_CMD_ACTION_TX_STATUS:
+               mlme_event_action_tx_status(drv, cookie, nla_data(frame),
+                                           nla_len(frame), ack);
                break;
        default:
                break;
@@ -877,59 +869,247 @@ static void mlme_event_join_ibss(struct wpa_driver_nl80211_data *drv,
 }
 
 
-static int process_event(struct nl_msg *msg, void *arg)
+static void mlme_event_remain_on_channel(struct wpa_driver_nl80211_data *drv,
+                                        int cancel_event, struct nlattr *tb[])
+{
+       unsigned int freq, chan_type, duration;
+       union wpa_event_data data;
+       u64 cookie;
+
+       if (tb[NL80211_ATTR_WIPHY_FREQ])
+               freq = nla_get_u32(tb[NL80211_ATTR_WIPHY_FREQ]);
+       else
+               freq = 0;
+
+       if (tb[NL80211_ATTR_WIPHY_CHANNEL_TYPE])
+               chan_type = nla_get_u32(tb[NL80211_ATTR_WIPHY_CHANNEL_TYPE]);
+       else
+               chan_type = 0;
+
+       if (tb[NL80211_ATTR_DURATION])
+               duration = nla_get_u32(tb[NL80211_ATTR_DURATION]);
+       else
+               duration = 0;
+
+       if (tb[NL80211_ATTR_COOKIE])
+               cookie = nla_get_u64(tb[NL80211_ATTR_COOKIE]);
+       else
+               cookie = 0;
+
+       wpa_printf(MSG_DEBUG, "nl80211: Remain-on-channel event (cancel=%d "
+                  "freq=%u channel_type=%u duration=%u cookie=0x%llx (%s))",
+                  cancel_event, freq, chan_type, duration,
+                  (long long unsigned int) cookie,
+                  cookie == drv->remain_on_chan_cookie ? "match" : "unknown");
+
+       if (cookie != drv->remain_on_chan_cookie)
+               return; /* not for us */
+
+       drv->pending_remain_on_chan = !cancel_event;
+
+       os_memset(&data, 0, sizeof(data));
+       data.remain_on_channel.freq = freq;
+       data.remain_on_channel.duration = duration;
+       wpa_supplicant_event(drv->ctx, cancel_event ?
+                            EVENT_CANCEL_REMAIN_ON_CHANNEL :
+                            EVENT_REMAIN_ON_CHANNEL, &data);
+}
+
+
+static void send_scan_event(struct wpa_driver_nl80211_data *drv, int aborted,
+                           struct nlattr *tb[])
+{
+       union wpa_event_data event;
+       struct nlattr *nl;
+       int rem;
+       struct scan_info *info;
+#define MAX_REPORT_FREQS 50
+       int freqs[MAX_REPORT_FREQS];
+       int num_freqs = 0;
+
+       os_memset(&event, 0, sizeof(event));
+       info = &event.scan_info;
+       info->aborted = aborted;
+
+       if (tb[NL80211_ATTR_SCAN_SSIDS]) {
+               nla_for_each_nested(nl, tb[NL80211_ATTR_SCAN_SSIDS], rem) {
+                       struct wpa_driver_scan_ssid *s =
+                               &info->ssids[info->num_ssids];
+                       s->ssid = nla_data(nl);
+                       s->ssid_len = nla_len(nl);
+                       info->num_ssids++;
+                       if (info->num_ssids == WPAS_MAX_SCAN_SSIDS)
+                               break;
+               }
+       }
+       if (tb[NL80211_ATTR_SCAN_FREQUENCIES]) {
+               nla_for_each_nested(nl, tb[NL80211_ATTR_SCAN_FREQUENCIES], rem)
+               {
+                       freqs[num_freqs] = nla_get_u32(nl);
+                       num_freqs++;
+                       if (num_freqs == MAX_REPORT_FREQS - 1)
+                               break;
+               }
+               info->freqs = freqs;
+               info->num_freqs = num_freqs;
+       }
+       wpa_supplicant_event(drv->ctx, EVENT_SCAN_RESULTS, &event);
+}
+
+
+static int get_link_signal(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];
+       struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
+       struct nlattr *sinfo[NL80211_STA_INFO_MAX + 1];
+       static struct nla_policy policy[NL80211_STA_INFO_MAX + 1] = {
+               [NL80211_STA_INFO_SIGNAL] = { .type = NLA_U8 },
+       };
+       int *sig = arg;
 
        nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
                  genlmsg_attrlen(gnlh, 0), NULL);
+       if (!tb[NL80211_ATTR_STA_INFO] ||
+           nla_parse_nested(sinfo, NL80211_STA_INFO_MAX,
+                            tb[NL80211_ATTR_STA_INFO], policy))
+               return NL_SKIP;
+       if (!sinfo[NL80211_STA_INFO_SIGNAL])
+               return NL_SKIP;
 
-       if (tb[NL80211_ATTR_IFINDEX]) {
-               int ifindex = nla_get_u32(tb[NL80211_ATTR_IFINDEX]);
-               if (ifindex != drv->ifindex) {
-                       wpa_printf(MSG_DEBUG, "nl80211: Ignored event (cmd=%d)"
-                                  " for foreign interface (ifindex %d)",
-                                  gnlh->cmd, ifindex);
-                       return NL_SKIP;
-               }
-       }
+       *sig = (s8) nla_get_u8(sinfo[NL80211_STA_INFO_SIGNAL]);
+       return NL_SKIP;
+}
 
-       if (drv->ap_scan_as_station &&
-           (gnlh->cmd == NL80211_CMD_NEW_SCAN_RESULTS ||
-            gnlh->cmd == NL80211_CMD_SCAN_ABORTED)) {
-               wpa_driver_nl80211_set_mode(drv, IEEE80211_MODE_AP);
-               drv->ap_scan_as_station = 0;
-       }
 
-       switch (gnlh->cmd) {
-       case NL80211_CMD_TRIGGER_SCAN:
-               wpa_printf(MSG_DEBUG, "nl80211: Scan trigger");
-               break;
-       case NL80211_CMD_NEW_SCAN_RESULTS:
-               wpa_printf(MSG_DEBUG, "nl80211: New scan results available");
-               drv->scan_complete_events = 1;
-               eloop_cancel_timeout(wpa_driver_nl80211_scan_timeout, drv,
-                                    drv->ctx);
-               wpa_supplicant_event(drv->ctx, EVENT_SCAN_RESULTS, NULL);
-               break;
-       case NL80211_CMD_SCAN_ABORTED:
-               wpa_printf(MSG_DEBUG, "nl80211: Scan aborted");
-               /*
-                * Need to indicate that scan results are available in order
+static int nl80211_get_link_signal(struct wpa_driver_nl80211_data *drv,
+                                  int *sig)
+{
+       struct nl_msg *msg;
+
+       *sig = -9999;
+
+       msg = nlmsg_alloc();
+       if (!msg)
+               return -ENOMEM;
+
+       genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0,
+                   0, NL80211_CMD_GET_STATION, 0);
+
+       NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex);
+       NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, drv->bssid);
+
+       return send_and_recv_msgs(drv, msg, get_link_signal, sig);
+ nla_put_failure:
+       return -ENOBUFS;
+}
+
+
+static void nl80211_cqm_event(struct wpa_driver_nl80211_data *drv,
+                             struct nlattr *tb[])
+{
+       static struct nla_policy cqm_policy[NL80211_ATTR_CQM_MAX + 1] = {
+               [NL80211_ATTR_CQM_RSSI_THOLD] = { .type = NLA_U32 },
+               [NL80211_ATTR_CQM_RSSI_HYST] = { .type = NLA_U8 },
+               [NL80211_ATTR_CQM_RSSI_THRESHOLD_EVENT] = { .type = NLA_U32 },
+       };
+       struct nlattr *cqm[NL80211_ATTR_CQM_MAX + 1];
+       enum nl80211_cqm_rssi_threshold_event event;
+       union wpa_event_data ed;
+       int sig, res;
+
+       if (tb[NL80211_ATTR_CQM] == NULL ||
+           nla_parse_nested(cqm, NL80211_ATTR_CQM_MAX, tb[NL80211_ATTR_CQM],
+                            cqm_policy)) {
+               wpa_printf(MSG_DEBUG, "nl80211: Ignore invalid CQM event");
+               return;
+       }
+
+       if (cqm[NL80211_ATTR_CQM_RSSI_THRESHOLD_EVENT] == NULL)
+               return;
+       event = nla_get_u32(cqm[NL80211_ATTR_CQM_RSSI_THRESHOLD_EVENT]);
+
+       os_memset(&ed, 0, sizeof(ed));
+
+       if (event == NL80211_CQM_RSSI_THRESHOLD_EVENT_HIGH) {
+               wpa_printf(MSG_DEBUG, "nl80211: Connection quality monitor "
+                          "event: RSSI high");
+               ed.signal_change.above_threshold = 1;
+       } else if (event == NL80211_CQM_RSSI_THRESHOLD_EVENT_LOW) {
+               wpa_printf(MSG_DEBUG, "nl80211: Connection quality monitor "
+                          "event: RSSI low");
+               ed.signal_change.above_threshold = 0;
+       } else
+               return;
+
+       res = nl80211_get_link_signal(drv, &sig);
+       if (res == 0) {
+               ed.signal_change.current_signal = sig;
+               wpa_printf(MSG_DEBUG, "nl80211: Signal: %d dBm", sig);
+       }
+
+       wpa_supplicant_event(drv->ctx, EVENT_SIGNAL_CHANGE, &ed);
+}
+
+
+static int process_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];
+       union wpa_event_data data;
+
+       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 &&
+           (gnlh->cmd == NL80211_CMD_NEW_SCAN_RESULTS ||
+            gnlh->cmd == NL80211_CMD_SCAN_ABORTED)) {
+               wpa_driver_nl80211_set_mode(&drv->first_bss,
+                                           IEEE80211_MODE_AP);
+               drv->ap_scan_as_station = 0;
+       }
+
+       switch (gnlh->cmd) {
+       case NL80211_CMD_TRIGGER_SCAN:
+               wpa_printf(MSG_DEBUG, "nl80211: Scan trigger");
+               break;
+       case NL80211_CMD_NEW_SCAN_RESULTS:
+               wpa_printf(MSG_DEBUG, "nl80211: New scan results available");
+               drv->scan_complete_events = 1;
+               eloop_cancel_timeout(wpa_driver_nl80211_scan_timeout, drv,
+                                    drv->ctx);
+               send_scan_event(drv, 0, tb);
+               break;
+       case NL80211_CMD_SCAN_ABORTED:
+               wpa_printf(MSG_DEBUG, "nl80211: Scan aborted");
+               /*
+                * Need to indicate that scan results are available in order
                 * not to make wpa_supplicant stop its scanning.
                 */
                eloop_cancel_timeout(wpa_driver_nl80211_scan_timeout, drv,
                                     drv->ctx);
-               wpa_supplicant_event(drv->ctx, EVENT_SCAN_RESULTS, NULL);
+               send_scan_event(drv, 1, tb);
                break;
        case NL80211_CMD_AUTHENTICATE:
        case NL80211_CMD_ASSOCIATE:
        case NL80211_CMD_DEAUTHENTICATE:
        case NL80211_CMD_DISASSOCIATE:
+       case NL80211_CMD_ACTION:
+       case NL80211_CMD_ACTION_TX_STATUS:
                mlme_event(drv, gnlh->cmd, tb[NL80211_ATTR_FRAME],
-                          tb[NL80211_ATTR_MAC], tb[NL80211_ATTR_TIMED_OUT]);
+                          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:
@@ -950,7 +1130,11 @@ static int process_event(struct nl_msg *msg, void *arg)
                        break;
                }
                drv->associated = 0;
-               wpa_supplicant_event(drv->ctx, EVENT_DISASSOC, NULL);
+               os_memset(&data, 0, sizeof(data));
+               if (tb[NL80211_ATTR_REASON_CODE])
+                       data.disassoc_info.reason_code =
+                               nla_get_u16(tb[NL80211_ATTR_REASON_CODE]);
+               wpa_supplicant_event(drv->ctx, EVENT_DISASSOC, &data);
                break;
        case NL80211_CMD_MICHAEL_MIC_FAILURE:
                mlme_event_michael_mic_failure(drv, tb);
@@ -958,6 +1142,15 @@ static int process_event(struct nl_msg *msg, void *arg)
        case NL80211_CMD_JOIN_IBSS:
                mlme_event_join_ibss(drv, tb);
                break;
+       case NL80211_CMD_REMAIN_ON_CHANNEL:
+               mlme_event_remain_on_channel(drv, 0, tb);
+               break;
+       case NL80211_CMD_CANCEL_REMAIN_ON_CHANNEL:
+               mlme_event_remain_on_channel(drv, 1, tb);
+               break;
+       case NL80211_CMD_NOTIFY_CQM:
+               nl80211_cqm_event(drv, tb);
+               break;
        default:
                wpa_printf(MSG_DEBUG, "nl80211: Ignored unknown event "
                           "(cmd=%d)", gnlh->cmd);
@@ -986,43 +1179,6 @@ static void wpa_driver_nl80211_event_receive(int sock, void *eloop_ctx,
 }
 
 
-static int hostapd_set_iface_flags(struct wpa_driver_nl80211_data *drv,
-                                  const char *ifname, int dev_up)
-{
-       struct ifreq ifr;
-
-       if (drv->ioctl_sock < 0)
-               return -1;
-
-       os_memset(&ifr, 0, sizeof(ifr));
-       os_strlcpy(ifr.ifr_name, ifname, IFNAMSIZ);
-
-       if (ioctl(drv->ioctl_sock, SIOCGIFFLAGS, &ifr) != 0) {
-               perror("ioctl[SIOCGIFFLAGS]");
-               wpa_printf(MSG_DEBUG, "Could not read interface flags (%s)",
-                          ifname);
-               return -1;
-       }
-
-       if (dev_up) {
-               if (ifr.ifr_flags & IFF_UP)
-                       return 0;
-               ifr.ifr_flags |= IFF_UP;
-       } else {
-               if (!(ifr.ifr_flags & IFF_UP))
-                       return 0;
-               ifr.ifr_flags &= ~IFF_UP;
-       }
-
-       if (ioctl(drv->ioctl_sock, SIOCSIFFLAGS, &ifr) != 0) {
-               perror("ioctl[SIOCSIFFLAGS]");
-               return -1;
-       }
-
-       return 0;
-}
-
-
 /**
  * wpa_driver_nl80211_set_country - ask nl80211 to set the regulatory domain
  * @priv: driver_nl80211 private data
@@ -1034,7 +1190,8 @@ static int hostapd_set_iface_flags(struct wpa_driver_nl80211_data *drv,
  */
 static int wpa_driver_nl80211_set_country(void *priv, const char *alpha2_arg)
 {
-       struct wpa_driver_nl80211_data *drv = priv;
+       struct i802_bss *bss = priv;
+       struct wpa_driver_nl80211_data *drv = bss->drv;
        char alpha2[3];
        struct nl_msg *msg;
 
@@ -1123,7 +1280,7 @@ static int wpa_driver_nl80211_get_info(struct wpa_driver_nl80211_data *drv,
        genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0,
                    0, NL80211_CMD_GET_WIPHY, 0);
 
-       NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex);
+       NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->first_bss.ifindex);
 
        if (send_and_recv_msgs(drv, msg, wiphy_info_handler, info) == 0)
                return 0;
@@ -1149,6 +1306,9 @@ static int wpa_driver_nl80211_capa(struct wpa_driver_nl80211_data *drv)
                WPA_DRIVER_CAPA_ENC_WEP104 |
                WPA_DRIVER_CAPA_ENC_TKIP |
                WPA_DRIVER_CAPA_ENC_CCMP;
+       drv->capa.auth = WPA_DRIVER_AUTH_OPEN |
+               WPA_DRIVER_AUTH_SHARED |
+               WPA_DRIVER_AUTH_LEAP;
 
        drv->capa.max_scan_ssids = info.max_scan_ssids;
        if (info.ap_supported)
@@ -1163,14 +1323,15 @@ static int wpa_driver_nl80211_capa(struct wpa_driver_nl80211_data *drv)
        }
 
        drv->capa.flags |= WPA_DRIVER_FLAGS_SET_KEYS_AFTER_ASSOC_DONE;
+       drv->capa.flags |= WPA_DRIVER_FLAGS_P2P_CAPABLE;
+       drv->capa.max_remain_on_chan = 5000;
 
        return 0;
 }
 #endif /* HOSTAPD */
 
 
-static int wpa_driver_nl80211_init_nl(struct wpa_driver_nl80211_data *drv,
-                                     void *ctx)
+static int wpa_driver_nl80211_init_nl(struct wpa_driver_nl80211_data *drv)
 {
        int ret;
 
@@ -1264,7 +1425,7 @@ static int wpa_driver_nl80211_init_nl(struct wpa_driver_nl80211_data *drv,
        }
 
        eloop_register_read_sock(nl_socket_get_fd(drv->nl_handle_event),
-                                wpa_driver_nl80211_event_receive, drv, ctx);
+                                wpa_driver_nl80211_event_receive, drv, NULL);
 
        return 0;
 
@@ -1283,36 +1444,26 @@ err1:
 }
 
 
-static int wpa_driver_nl80211_init_link_events(
-       struct wpa_driver_nl80211_data *drv)
+static void wpa_driver_nl80211_rfkill_blocked(void *ctx)
 {
-#ifdef HOSTAPD
-       return 0;
-#else /* HOSTAPD */
-       int s;
-       struct sockaddr_nl local;
+       wpa_printf(MSG_DEBUG, "nl80211: RFKILL blocked");
+       /*
+        * This may be for any interface; use ifdown event to disable
+        * interface.
+        */
+}
 
-       s = socket(PF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
-       if (s < 0) {
-               perror("socket(PF_NETLINK,SOCK_RAW,NETLINK_ROUTE)");
-               return -1;
-       }
 
-       os_memset(&local, 0, sizeof(local));
-       local.nl_family = AF_NETLINK;
-       local.nl_groups = RTMGRP_LINK;
-       if (bind(s, (struct sockaddr *) &local, sizeof(local)) < 0) {
-               perror("bind(netlink)");
-               close(s);
-               return -1;
+static void wpa_driver_nl80211_rfkill_unblocked(void *ctx)
+{
+       struct wpa_driver_nl80211_data *drv = ctx;
+       wpa_printf(MSG_DEBUG, "nl80211: RFKILL unblocked");
+       if (linux_set_iface_flags(drv->ioctl_sock, drv->first_bss.ifname, 1)) {
+               wpa_printf(MSG_DEBUG, "nl80211: Could not set interface UP "
+                          "after rfkill unblock");
+               return;
        }
-
-       eloop_register_read_sock(s, wpa_driver_nl80211_event_receive_link, drv,
-                                drv->ctx);
-       drv->link_event_sock = s;
-
-       return 0;
-#endif /* HOSTAPD */
+       /* rtnetlink ifup handler will report interface as enabled */
 }
 
 
@@ -1326,18 +1477,22 @@ static int wpa_driver_nl80211_init_link_events(
 static void * wpa_driver_nl80211_init(void *ctx, const char *ifname)
 {
        struct wpa_driver_nl80211_data *drv;
+       struct netlink_config *cfg;
+       struct rfkill_config *rcfg;
+       struct i802_bss *bss;
 
        drv = os_zalloc(sizeof(*drv));
        if (drv == NULL)
                return NULL;
        drv->ctx = ctx;
-       os_strlcpy(drv->ifname, ifname, sizeof(drv->ifname));
+       bss = &drv->first_bss;
+       bss->drv = drv;
+       os_strlcpy(bss->ifname, ifname, sizeof(bss->ifname));
        drv->monitor_ifidx = -1;
        drv->monitor_sock = -1;
-       drv->link_event_sock = -1;
        drv->ioctl_sock = -1;
 
-       if (wpa_driver_nl80211_init_nl(drv, ctx)) {
+       if (wpa_driver_nl80211_init_nl(drv)) {
                os_free(drv);
                return NULL;
        }
@@ -1348,17 +1503,39 @@ static void * wpa_driver_nl80211_init(void *ctx, const char *ifname)
                goto failed;
        }
 
-       if (wpa_driver_nl80211_init_link_events(drv) ||
-           wpa_driver_nl80211_finish_drv_init(drv))
+       cfg = os_zalloc(sizeof(*cfg));
+       if (cfg == NULL)
+               goto failed;
+       cfg->ctx = drv;
+       cfg->newlink_cb = wpa_driver_nl80211_event_rtm_newlink;
+       cfg->dellink_cb = wpa_driver_nl80211_event_rtm_dellink;
+       drv->netlink = netlink_init(cfg);
+       if (drv->netlink == NULL) {
+               os_free(cfg);
+               goto failed;
+       }
+
+       rcfg = os_zalloc(sizeof(*rcfg));
+       if (rcfg == NULL)
+               goto failed;
+       rcfg->ctx = drv;
+       os_strlcpy(rcfg->ifname, ifname, sizeof(rcfg->ifname));
+       rcfg->blocked_cb = wpa_driver_nl80211_rfkill_blocked;
+       rcfg->unblocked_cb = wpa_driver_nl80211_rfkill_unblocked;
+       drv->rfkill = rfkill_init(rcfg);
+       if (drv->rfkill == NULL) {
+               wpa_printf(MSG_DEBUG, "nl80211: RFKILL status not available");
+               os_free(rcfg);
+       }
+
+       if (wpa_driver_nl80211_finish_drv_init(drv))
                goto failed;
 
-       return drv;
+       return bss;
 
 failed:
-       if (drv->link_event_sock >= 0) {
-               eloop_unregister_read_sock(drv->link_event_sock);
-               close(drv->link_event_sock);
-       }
+       rfkill_deinit(drv->rfkill);
+       netlink_deinit(drv->netlink);
        if (drv->ioctl_sock >= 0)
                close(drv->ioctl_sock);
 
@@ -1366,54 +1543,144 @@ failed:
        nl_cache_free(drv->nl_cache);
        nl_handle_destroy(drv->nl_handle);
        nl_cb_put(drv->nl_cb);
+       eloop_unregister_read_sock(nl_socket_get_fd(drv->nl_handle_event));
 
        os_free(drv);
        return NULL;
 }
 
 
+static int nl80211_register_action_frame(struct wpa_driver_nl80211_data *drv,
+                                        const u8 *match, size_t match_len)
+{
+       struct nl_msg *msg;
+       int ret = -1;
+
+       msg = nlmsg_alloc();
+       if (!msg)
+               return -1;
+
+       genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, 0,
+                   NL80211_CMD_REGISTER_ACTION, 0);
+
+       NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex);
+       NLA_PUT(msg, NL80211_ATTR_FRAME_MATCH, match_len, match);
+
+       ret = send_and_recv(drv, drv->nl_handle_event, msg, NULL, NULL);
+       msg = NULL;
+       if (ret) {
+               wpa_printf(MSG_DEBUG, "nl80211: Register Action command "
+                          "failed: ret=%d (%s)", ret, strerror(-ret));
+               wpa_hexdump(MSG_DEBUG, "nl80211: Register Action match",
+                           match, match_len);
+               goto nla_put_failure;
+       }
+       ret = 0;
+nla_put_failure:
+       nlmsg_free(msg);
+       return ret;
+}
+
+
+static int nl80211_register_action_frames(struct wpa_driver_nl80211_data *drv)
+{
+#ifdef CONFIG_P2P
+       /* GAS Initial Request */
+       if (nl80211_register_action_frame(drv, (u8 *) "\x04\x0a", 2) < 0)
+               return -1;
+       /* GAS Initial Response */
+       if (nl80211_register_action_frame(drv, (u8 *) "\x04\x0b", 2) < 0)
+               return -1;
+       /* GAS Comeback Request */
+       if (nl80211_register_action_frame(drv, (u8 *) "\x04\x0c", 2) < 0)
+               return -1;
+       /* GAS Comeback Response */
+       if (nl80211_register_action_frame(drv, (u8 *) "\x04\x0d", 2) < 0)
+               return -1;
+       /* P2P Public Action */
+       if (nl80211_register_action_frame(drv,
+                                         (u8 *) "\x04\x09\x50\x6f\x9a\x09",
+                                         6) < 0)
+               return -1;
+       /* P2P Action */
+       if (nl80211_register_action_frame(drv,
+                                         (u8 *) "\x7f\x50\x6f\x9a\x09",
+                                         5) < 0)
+               return -1;
+#endif /* CONFIG_P2P */
+
+       /* FT Action frames */
+       if (nl80211_register_action_frame(drv, (u8 *) "\x06", 1) < 0)
+               return -1;
+       else
+               drv->capa.key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_FT |
+                       WPA_DRIVER_CAPA_KEY_MGMT_FT_PSK;
+
+       return 0;
+}
+
+
+static void wpa_driver_nl80211_send_rfkill(void *eloop_ctx, void *timeout_ctx)
+{
+       wpa_supplicant_event(timeout_ctx, EVENT_INTERFACE_DISABLED, NULL);
+}
+
+
 static int
 wpa_driver_nl80211_finish_drv_init(struct wpa_driver_nl80211_data *drv)
 {
-       drv->ifindex = if_nametoindex(drv->ifname);
+       struct i802_bss *bss = &drv->first_bss;
+       int send_rfkill_event = 0;
+
+       drv->ifindex = if_nametoindex(bss->ifname);
+       drv->first_bss.ifindex = drv->ifindex;
 
 #ifndef HOSTAPD
-       if (wpa_driver_nl80211_set_mode(drv, IEEE80211_MODE_INFRA) < 0) {
+       if (wpa_driver_nl80211_set_mode(bss, IEEE80211_MODE_INFRA) < 0) {
                wpa_printf(MSG_DEBUG, "nl80211: Could not configure driver to "
                           "use managed mode");
        }
 
-       if (hostapd_set_iface_flags(drv, drv->ifname, 1)) {
-               wpa_printf(MSG_ERROR, "Could not set interface '%s' "
-                          "UP", drv->ifname);
-               return -1;
+       if (linux_set_iface_flags(drv->ioctl_sock, bss->ifname, 1)) {
+               if (rfkill_is_blocked(drv->rfkill)) {
+                       wpa_printf(MSG_DEBUG, "nl80211: Could not yet enable "
+                                  "interface '%s' due to rfkill",
+                                  bss->ifname);
+                       drv->if_disabled = 1;
+                       send_rfkill_event = 1;
+               } else {
+                       wpa_printf(MSG_ERROR, "nl80211: Could not set "
+                                  "interface '%s' UP", bss->ifname);
+                       return -1;
+               }
        }
 
        if (wpa_driver_nl80211_capa(drv))
                return -1;
 
-       wpa_driver_nl80211_send_oper_ifla(drv, 1, IF_OPER_DORMANT);
+       netlink_send_oper_ifla(drv->netlink, drv->ifindex,
+                              1, IF_OPER_DORMANT);
 #endif /* HOSTAPD */
 
-       return 0;
-}
-
+       if (nl80211_register_action_frames(drv) < 0) {
+               wpa_printf(MSG_DEBUG, "nl80211: Failed to register Action "
+                          "frame processing - ignore for now");
+               /*
+                * Older kernel versions did not support this, so ignore the
+                * error for now. Some functionality may not be available
+                * because of this.
+                */
+       }
 
-#ifdef HOSTAPD
-static void wpa_driver_nl80211_free_bss(struct wpa_driver_nl80211_data *drv)
-{
-       struct i802_bss *bss, *prev;
-       bss = drv->bss.next;
-       while (bss) {
-               prev = bss;
-               bss = bss->next;
-               os_free(bss);
+       if (send_rfkill_event) {
+               eloop_register_timeout(0, 0, wpa_driver_nl80211_send_rfkill,
+                                      drv, drv->ctx);
        }
+
+       return 0;
 }
-#endif /* HOSTAPD */
 
 
-#if defined(CONFIG_AP) || defined(HOSTAPD)
 static int wpa_driver_nl80211_del_beacon(struct wpa_driver_nl80211_data *drv)
 {
        struct nl_msg *msg;
@@ -1430,7 +1697,6 @@ static int wpa_driver_nl80211_del_beacon(struct wpa_driver_nl80211_data *drv)
  nla_put_failure:
        return -ENOBUFS;
 }
-#endif /* CONFIG_AP || HOSTAPD */
 
 
 /**
@@ -1442,18 +1708,27 @@ static int wpa_driver_nl80211_del_beacon(struct wpa_driver_nl80211_data *drv)
  */
 static void wpa_driver_nl80211_deinit(void *priv)
 {
-       struct wpa_driver_nl80211_data *drv = priv;
+       struct i802_bss *bss = priv;
+       struct wpa_driver_nl80211_data *drv = bss->drv;
 
-#if defined(CONFIG_AP) || defined(HOSTAPD)
-       nl80211_remove_monitor_interface(drv);
-       if (drv->monitor_sock >= 0) {
-               eloop_unregister_read_sock(drv->monitor_sock);
-               close(drv->monitor_sock);
+       if (drv->added_if_into_bridge) {
+               if (linux_br_del_if(drv->ioctl_sock, drv->brname, bss->ifname)
+                   < 0)
+                       wpa_printf(MSG_INFO, "nl80211: Failed to remove "
+                                  "interface %s from bridge %s: %s",
+                                  bss->ifname, drv->brname, strerror(errno));
+       }
+       if (drv->added_bridge) {
+               if (linux_br_del(drv->ioctl_sock, drv->brname) < 0)
+                       wpa_printf(MSG_INFO, "nl80211: Failed to remove "
+                                  "bridge %s: %s",
+                                  drv->brname, strerror(errno));
        }
 
+       nl80211_remove_monitor_interface(drv);
+
        if (drv->nlmode == NL80211_IFTYPE_AP)
                wpa_driver_nl80211_del_beacon(drv);
-#endif /* CONFIG_AP || HOSTAPD */
 
 #ifdef HOSTAPD
        if (drv->last_freq_ht) {
@@ -1471,22 +1746,19 @@ static void wpa_driver_nl80211_deinit(void *priv)
 
        if (drv->if_indices != drv->default_if_indices)
                os_free(drv->if_indices);
+#endif /* HOSTAPD */
 
-       wpa_driver_nl80211_free_bss(drv);
-#else /* HOSTAPD */
-
-       wpa_driver_nl80211_send_oper_ifla(priv, 0, IF_OPER_UP);
+       if (drv->disable_11b_rates)
+               nl80211_disable_11b_rates(drv, drv->ifindex, 0);
 
-       if (drv->link_event_sock >= 0) {
-               eloop_unregister_read_sock(drv->link_event_sock);
-               close(drv->link_event_sock);
-       }
-#endif /* HOSTAPD */
+       netlink_send_oper_ifla(drv->netlink, drv->ifindex, 0, IF_OPER_UP);
+       netlink_deinit(drv->netlink);
+       rfkill_deinit(drv->rfkill);
 
        eloop_cancel_timeout(wpa_driver_nl80211_scan_timeout, drv, drv->ctx);
 
-       (void) hostapd_set_iface_flags(drv, drv->ifname, 0);
-       wpa_driver_nl80211_set_mode(drv, IEEE80211_MODE_INFRA);
+       (void) linux_set_iface_flags(drv->ioctl_sock, bss->ifname, 0);
+       wpa_driver_nl80211_set_mode(bss, IEEE80211_MODE_INFRA);
 
        if (drv->ioctl_sock >= 0)
                close(drv->ioctl_sock);
@@ -1499,6 +1771,11 @@ static void wpa_driver_nl80211_deinit(void *priv)
        nl_handle_destroy(drv->nl_handle_event);
        nl_cb_put(drv->nl_cb);
 
+       eloop_cancel_timeout(wpa_driver_nl80211_probe_req_report_timeout,
+                            drv, NULL);
+
+       os_free(drv->filter_ssids);
+
        os_free(drv);
 }
 
@@ -1515,7 +1792,8 @@ static void wpa_driver_nl80211_scan_timeout(void *eloop_ctx, void *timeout_ctx)
 {
        struct wpa_driver_nl80211_data *drv = eloop_ctx;
        if (drv->ap_scan_as_station) {
-               wpa_driver_nl80211_set_mode(drv, IEEE80211_MODE_AP);
+               wpa_driver_nl80211_set_mode(&drv->first_bss,
+                                           IEEE80211_MODE_AP);
                drv->ap_scan_as_station = 0;
        }
        wpa_printf(MSG_DEBUG, "Scan timeout - try to get results");
@@ -1532,7 +1810,8 @@ static void wpa_driver_nl80211_scan_timeout(void *eloop_ctx, void *timeout_ctx)
 static int wpa_driver_nl80211_scan(void *priv,
                                   struct wpa_driver_scan_params *params)
 {
-       struct wpa_driver_nl80211_data *drv = priv;
+       struct i802_bss *bss = priv;
+       struct wpa_driver_nl80211_data *drv = bss->drv;
        int ret = 0, timeout;
        struct nl_msg *msg, *ssids, *freqs;
        size_t i;
@@ -1547,12 +1826,20 @@ static int wpa_driver_nl80211_scan(void *priv,
                return -1;
        }
 
+       os_free(drv->filter_ssids);
+       drv->filter_ssids = params->filter_ssids;
+       params->filter_ssids = NULL;
+       drv->num_filter_ssids = params->num_filter_ssids;
+
        genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, 0,
                    NL80211_CMD_TRIGGER_SCAN, 0);
 
        NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex);
 
        for (i = 0; i < params->num_ssids; i++) {
+               wpa_hexdump_ascii(MSG_MSGDUMP, "nl80211: Scan SSID",
+                                 params->ssids[i].ssid,
+                                 params->ssids[i].ssid_len);
                NLA_PUT(ssids, i + 1, params->ssids[i].ssid_len,
                        params->ssids[i].ssid);
        }
@@ -1560,13 +1847,18 @@ static int wpa_driver_nl80211_scan(void *priv,
                nla_put_nested(msg, NL80211_ATTR_SCAN_SSIDS, ssids);
 
        if (params->extra_ies) {
+               wpa_hexdump_ascii(MSG_MSGDUMP, "nl80211: Scan extra IEs",
+                                 params->extra_ies, params->extra_ies_len);
                NLA_PUT(msg, NL80211_ATTR_IE, params->extra_ies_len,
                        params->extra_ies);
        }
 
        if (params->freqs) {
-               for (i = 0; params->freqs[i]; i++)
+               for (i = 0; params->freqs[i]; i++) {
+                       wpa_printf(MSG_MSGDUMP, "nl80211: Scan frequency %u "
+                                  "MHz", params->freqs[i]);
                        NLA_PUT_U32(freqs, i + 1, params->freqs[i]);
+               }
                nla_put_nested(msg, NL80211_ATTR_SCAN_FREQUENCIES, freqs);
        }
 
@@ -1581,12 +1873,12 @@ static int wpa_driver_nl80211_scan(void *priv,
                         * mac80211 does not allow scan requests in AP mode, so
                         * try to do this in station mode.
                         */
-                       if (wpa_driver_nl80211_set_mode(drv,
+                       if (wpa_driver_nl80211_set_mode(bss,
                                                        IEEE80211_MODE_INFRA))
                                goto nla_put_failure;
 
                        if (wpa_driver_nl80211_scan(drv, params)) {
-                               wpa_driver_nl80211_set_mode(drv,
+                               wpa_driver_nl80211_set_mode(bss,
                                                            IEEE80211_MODE_AP);
                                goto nla_put_failure;
                        }
@@ -1626,6 +1918,57 @@ nla_put_failure:
 }
 
 
+static const u8 * nl80211_get_ie(const u8 *ies, size_t ies_len, u8 ie)
+{
+       const u8 *end, *pos;
+
+       if (ies == NULL)
+               return NULL;
+
+       pos = ies;
+       end = ies + ies_len;
+
+       while (pos + 1 < end) {
+               if (pos + 2 + pos[1] > end)
+                       break;
+               if (pos[0] == ie)
+                       return pos;
+               pos += 2 + pos[1];
+       }
+
+       return NULL;
+}
+
+
+static int nl80211_scan_filtered(struct wpa_driver_nl80211_data *drv,
+                                const u8 *ie, size_t ie_len)
+{
+       const u8 *ssid;
+       size_t i;
+
+       if (drv->filter_ssids == NULL)
+               return 0;
+
+       ssid = nl80211_get_ie(ie, ie_len, WLAN_EID_SSID);
+       if (ssid == NULL)
+               return 1;
+
+       for (i = 0; i < drv->num_filter_ssids; i++) {
+               if (ssid[1] == drv->filter_ssids[i].ssid_len &&
+                   os_memcmp(ssid + 2, drv->filter_ssids[i].ssid, ssid[1]) ==
+                   0)
+                       return 0;
+       }
+
+       return 1;
+}
+
+
+struct nl80211_bss_info_arg {
+       struct wpa_driver_nl80211_data *drv;
+       struct wpa_scan_results *res;
+};
+
 static int bss_info_handler(struct nl_msg *msg, void *arg)
 {
        struct nlattr *tb[NL80211_ATTR_MAX + 1];
@@ -1642,12 +1985,15 @@ static int bss_info_handler(struct nl_msg *msg, void *arg)
                [NL80211_BSS_SIGNAL_UNSPEC] = { .type = NLA_U8 },
                [NL80211_BSS_STATUS] = { .type = NLA_U32 },
                [NL80211_BSS_SEEN_MS_AGO] = { .type = NLA_U32 },
+               [NL80211_BSS_BEACON_IES] = { .type = NLA_UNSPEC },
        };
-       struct wpa_scan_results *res = arg;
+       struct nl80211_bss_info_arg *_arg = arg;
+       struct wpa_scan_results *res = _arg->res;
        struct wpa_scan_res **tmp;
        struct wpa_scan_res *r;
-       const u8 *ie;
-       size_t ie_len;
+       const u8 *ie, *beacon_ie;
+       size_t ie_len, beacon_ie_len;
+       u8 *pos;
 
        nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
                  genlmsg_attrlen(gnlh, 0), NULL);
@@ -1663,8 +2009,19 @@ static int bss_info_handler(struct nl_msg *msg, void *arg)
                ie = NULL;
                ie_len = 0;
        }
+       if (bss[NL80211_BSS_BEACON_IES]) {
+               beacon_ie = nla_data(bss[NL80211_BSS_BEACON_IES]);
+               beacon_ie_len = nla_len(bss[NL80211_BSS_BEACON_IES]);
+       } else {
+               beacon_ie = NULL;
+               beacon_ie_len = 0;
+       }
+
+       if (nl80211_scan_filtered(_arg->drv, ie ? ie : beacon_ie,
+                                 ie ? ie_len : beacon_ie_len))
+               return NL_SKIP;
 
-       r = os_zalloc(sizeof(*r) + ie_len);
+       r = os_zalloc(sizeof(*r) + ie_len + beacon_ie_len);
        if (r == NULL)
                return NL_SKIP;
        if (bss[NL80211_BSS_BSSID])
@@ -1691,8 +2048,14 @@ static int bss_info_handler(struct nl_msg *msg, void *arg)
        if (bss[NL80211_BSS_SEEN_MS_AGO])
                r->age = nla_get_u32(bss[NL80211_BSS_SEEN_MS_AGO]);
        r->ie_len = ie_len;
-       if (ie)
-               os_memcpy(r + 1, ie, ie_len);
+       pos = (u8 *) (r + 1);
+       if (ie) {
+               os_memcpy(pos, ie, ie_len);
+               pos += ie_len;
+       }
+       r->beacon_ie_len = beacon_ie_len;
+       if (beacon_ie)
+               os_memcpy(pos, beacon_ie, beacon_ie_len);
 
        if (bss[NL80211_BSS_STATUS]) {
                enum nl80211_bss_status status;
@@ -1727,10 +2090,10 @@ static void clear_state_mismatch(struct wpa_driver_nl80211_data *drv,
 {
        if (drv->capa.flags & WPA_DRIVER_FLAGS_SME) {
                wpa_printf(MSG_DEBUG, "nl80211: Clear possible state "
-                          "mismatch");
+                          "mismatch (" MACSTR ")", MAC2STR(addr));
                wpa_driver_nl80211_mlme(drv, addr,
                                        NL80211_CMD_DEAUTHENTICATE,
-                                       WLAN_REASON_PREV_AUTH_NOT_VALID);
+                                       WLAN_REASON_PREV_AUTH_NOT_VALID, 1);
        }
 }
 
@@ -1756,6 +2119,7 @@ static void wpa_driver_nl80211_check_bss_status(
                                           " assoc=" MACSTR ")",
                                           MAC2STR(drv->auth_bssid),
                                           MAC2STR(drv->bssid));
+                               clear_state_mismatch(drv, r->bssid);
                        }
                }
 
@@ -1785,18 +2149,27 @@ static void wpa_driver_nl80211_check_bss_status(
 }
 
 
-/**
- * wpa_driver_nl80211_get_scan_results - Fetch the latest scan results
- * @priv: Pointer to private wext data from wpa_driver_nl80211_init()
- * Returns: Scan results on success, -1 on failure
- */
+static void wpa_scan_results_free(struct wpa_scan_results *res)
+{
+       size_t i;
+
+       if (res == NULL)
+               return;
+
+       for (i = 0; i < res->num; i++)
+               os_free(res->res[i]);
+       os_free(res->res);
+       os_free(res);
+}
+
+
 static struct wpa_scan_results *
-wpa_driver_nl80211_get_scan_results(void *priv)
+nl80211_get_scan_results(struct wpa_driver_nl80211_data *drv)
 {
-       struct wpa_driver_nl80211_data *drv = priv;
        struct nl_msg *msg;
        struct wpa_scan_results *res;
        int ret;
+       struct nl80211_bss_info_arg arg;
 
        res = os_zalloc(sizeof(*res));
        if (res == NULL)
@@ -1809,12 +2182,13 @@ wpa_driver_nl80211_get_scan_results(void *priv)
                    NL80211_CMD_GET_SCAN, 0);
        NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex);
 
-       ret = send_and_recv_msgs(drv, msg, bss_info_handler, res);
+       arg.drv = drv;
+       arg.res = res;
+       ret = send_and_recv_msgs(drv, msg, bss_info_handler, &arg);
        msg = NULL;
        if (ret == 0) {
                wpa_printf(MSG_DEBUG, "Received scan results (%lu BSSes)",
                           (unsigned long) res->num);
-               wpa_driver_nl80211_check_bss_status(drv, res);
                return res;
        }
        wpa_printf(MSG_DEBUG, "nl80211: Scan result fetch failed: ret=%d "
@@ -1826,18 +2200,62 @@ nla_put_failure:
 }
 
 
-static int wpa_driver_nl80211_set_key(const char *ifname, void *priv,
-                                     wpa_alg alg, const u8 *addr, int key_idx,
-                                     int set_tx,
-                                     const u8 *seq, size_t seq_len,
-                                     const u8 *key, size_t key_len)
-{
-       struct wpa_driver_nl80211_data *drv = priv;
-       int ifindex = if_nametoindex(ifname);
-       struct nl_msg *msg;
-       int ret;
-
-       wpa_printf(MSG_DEBUG, "%s: ifindex=%d alg=%d addr=%p key_idx=%d "
+/**
+ * wpa_driver_nl80211_get_scan_results - Fetch the latest scan results
+ * @priv: Pointer to private wext data from wpa_driver_nl80211_init()
+ * Returns: Scan results on success, -1 on failure
+ */
+static struct wpa_scan_results *
+wpa_driver_nl80211_get_scan_results(void *priv)
+{
+       struct i802_bss *bss = priv;
+       struct wpa_driver_nl80211_data *drv = bss->drv;
+       struct wpa_scan_results *res;
+
+       res = nl80211_get_scan_results(drv);
+       if (res)
+               wpa_driver_nl80211_check_bss_status(drv, res);
+       return res;
+}
+
+
+static void nl80211_dump_scan(struct wpa_driver_nl80211_data *drv)
+{
+       struct wpa_scan_results *res;
+       size_t i;
+
+       res = nl80211_get_scan_results(drv);
+       if (res == NULL) {
+               wpa_printf(MSG_DEBUG, "nl80211: Failed to get scan results");
+               return;
+       }
+
+       wpa_printf(MSG_DEBUG, "nl80211: Scan result dump");
+       for (i = 0; i < res->num; i++) {
+               struct wpa_scan_res *r = res->res[i];
+               wpa_printf(MSG_DEBUG, "nl80211: %d/%d " MACSTR "%s%s",
+                          (int) i, (int) res->num, MAC2STR(r->bssid),
+                          r->flags & WPA_SCAN_AUTHENTICATED ? " [auth]" : "",
+                          r->flags & WPA_SCAN_ASSOCIATED ? " [assoc]" : "");
+       }
+
+       wpa_scan_results_free(res);
+}
+
+
+static int wpa_driver_nl80211_set_key(const char *ifname, void *priv,
+                                     enum wpa_alg alg, const u8 *addr,
+                                     int key_idx, int set_tx,
+                                     const u8 *seq, size_t seq_len,
+                                     const u8 *key, size_t key_len)
+{
+       struct i802_bss *bss = priv;
+       struct wpa_driver_nl80211_data *drv = bss->drv;
+       int ifindex = if_nametoindex(ifname);
+       struct nl_msg *msg;
+       int ret;
+
+       wpa_printf(MSG_DEBUG, "%s: ifindex=%d alg=%d addr=%p key_idx=%d "
                   "set_tx=%d seq_len=%lu key_len=%lu",
                   __func__, ifindex, alg, addr, key_idx, set_tx,
                   (unsigned long) seq_len, (unsigned long) key_len);
@@ -1894,7 +2312,7 @@ static int wpa_driver_nl80211_set_key(const char *ifname, void *priv,
        NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, ifindex);
 
        ret = send_and_recv_msgs(drv, msg, NULL, NULL);
-       if (ret == -ENOENT && alg == WPA_ALG_NONE)
+       if ((ret == -ENOENT || ret == -ENOLINK) && alg == WPA_ALG_NONE)
                ret = 0;
        if (ret)
                wpa_printf(MSG_DEBUG, "nl80211: set_key failed; err=%d %s)",
@@ -1906,9 +2324,12 @@ static int wpa_driver_nl80211_set_key(const char *ifname, void *priv,
         */
        if (ret || !set_tx || alg == WPA_ALG_NONE)
                return ret;
-#ifdef HOSTAPD /* FIX: is this needed? */
+#ifdef HOSTAPD
        if (addr)
                return ret;
+#else /* HOSTAPD */
+       if (drv->nlmode == NL80211_IFTYPE_AP && addr)
+               return ret;
 #endif /* HOSTAPD */
 
        msg = nlmsg_alloc();
@@ -1937,7 +2358,7 @@ nla_put_failure:
 }
 
 
-static int nl_add_key(struct nl_msg *msg, wpa_alg alg,
+static int nl_add_key(struct nl_msg *msg, enum wpa_alg alg,
                      int key_idx, int defkey,
                      const u8 *seq, size_t seq_len,
                      const u8 *key, size_t key_len)
@@ -2046,7 +2467,8 @@ nla_put_failure:
 
 
 static int wpa_driver_nl80211_mlme(struct wpa_driver_nl80211_data *drv,
-                                  const u8 *addr, int cmd, u16 reason_code)
+                                  const u8 *addr, int cmd, u16 reason_code,
+                                  int local_state_change)
 {
        int ret = -1;
        struct nl_msg *msg;
@@ -2060,6 +2482,8 @@ static int wpa_driver_nl80211_mlme(struct wpa_driver_nl80211_data *drv,
        NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex);
        NLA_PUT_U16(msg, NL80211_ATTR_REASON_CODE, reason_code);
        NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, addr);
+       if (local_state_change)
+               NLA_PUT_FLAG(msg, NL80211_ATTR_LOCAL_STATE_CHANGE);
 
        ret = send_and_recv_msgs(drv, msg, NULL, NULL);
        msg = NULL;
@@ -2079,43 +2503,48 @@ nla_put_failure:
 static int wpa_driver_nl80211_disconnect(struct wpa_driver_nl80211_data *drv,
                                         const u8 *addr, int reason_code)
 {
-       wpa_printf(MSG_DEBUG, "%s", __func__);
+       wpa_printf(MSG_DEBUG, "%s(addr=" MACSTR " reason_code=%d)",
+                  __func__, MAC2STR(addr), reason_code);
        drv->associated = 0;
        return wpa_driver_nl80211_mlme(drv, addr, NL80211_CMD_DISCONNECT,
-                                      reason_code);
+                                      reason_code, 0);
 }
 
 
 static int wpa_driver_nl80211_deauthenticate(void *priv, const u8 *addr,
                                             int reason_code)
 {
-       struct wpa_driver_nl80211_data *drv = priv;
+       struct i802_bss *bss = priv;
+       struct wpa_driver_nl80211_data *drv = bss->drv;
        if (!(drv->capa.flags & WPA_DRIVER_FLAGS_SME))
                return wpa_driver_nl80211_disconnect(drv, addr, reason_code);
-       wpa_printf(MSG_DEBUG, "%s", __func__);
+       wpa_printf(MSG_DEBUG, "%s(addr=" MACSTR " reason_code=%d)",
+                  __func__, MAC2STR(addr), reason_code);
        drv->associated = 0;
        return wpa_driver_nl80211_mlme(drv, addr, NL80211_CMD_DEAUTHENTICATE,
-                                      reason_code);
+                                      reason_code, 0);
 }
 
 
 static int wpa_driver_nl80211_disassociate(void *priv, const u8 *addr,
                                           int reason_code)
 {
-       struct wpa_driver_nl80211_data *drv = priv;
+       struct i802_bss *bss = priv;
+       struct wpa_driver_nl80211_data *drv = bss->drv;
        if (!(drv->capa.flags & WPA_DRIVER_FLAGS_SME))
                return wpa_driver_nl80211_disconnect(drv, addr, reason_code);
        wpa_printf(MSG_DEBUG, "%s", __func__);
        drv->associated = 0;
        return wpa_driver_nl80211_mlme(drv, addr, NL80211_CMD_DISASSOCIATE,
-                                      reason_code);
+                                      reason_code, 0);
 }
 
 
 static int wpa_driver_nl80211_authenticate(
        void *priv, struct wpa_driver_auth_params *params)
 {
-       struct wpa_driver_nl80211_data *drv = priv;
+       struct i802_bss *bss = priv;
+       struct wpa_driver_nl80211_data *drv = bss->drv;
        int ret = -1, i;
        struct nl_msg *msg;
        enum nl80211_auth_type type;
@@ -2123,8 +2552,11 @@ static int wpa_driver_nl80211_authenticate(
 
        drv->associated = 0;
        os_memset(drv->auth_bssid, 0, ETH_ALEN);
+       /* FIX: IBSS mode */
+       if (drv->nlmode != NL80211_IFTYPE_STATION)
+               wpa_driver_nl80211_set_mode(priv, IEEE80211_MODE_INFRA);
 
-       if (wpa_driver_nl80211_set_mode(drv, IEEE80211_MODE_INFRA) < 0)
+       if (wpa_driver_nl80211_set_mode(priv, IEEE80211_MODE_INFRA) < 0)
                return -1;
 
 retry:
@@ -2141,8 +2573,8 @@ retry:
        for (i = 0; i < 4; i++) {
                if (!params->wep_key[i])
                        continue;
-               wpa_driver_nl80211_set_key(drv->ifname, drv, WPA_ALG_WEP, NULL,
-                                          i,
+               wpa_driver_nl80211_set_key(bss->ifname, priv, WPA_ALG_WEP,
+                                          NULL, i,
                                           i == params->wep_tx_keyidx, NULL, 0,
                                           params->wep_key[i],
                                           params->wep_key_len[i]);
@@ -2174,22 +2606,22 @@ retry:
        wpa_hexdump(MSG_DEBUG, "  * IEs", params->ie, params->ie_len);
        if (params->ie)
                NLA_PUT(msg, NL80211_ATTR_IE, params->ie_len, params->ie);
-       /*
-        * TODO: if multiple auth_alg options enabled, try them one by one if
-        * the AP rejects authentication due to unknown auth alg
-        */
-       if (params->auth_alg & AUTH_ALG_OPEN_SYSTEM)
+       if (params->auth_alg & WPA_AUTH_ALG_OPEN)
                type = NL80211_AUTHTYPE_OPEN_SYSTEM;
-       else if (params->auth_alg & AUTH_ALG_SHARED_KEY)
+       else if (params->auth_alg & WPA_AUTH_ALG_SHARED)
                type = NL80211_AUTHTYPE_SHARED_KEY;
-       else if (params->auth_alg & AUTH_ALG_LEAP)
+       else if (params->auth_alg & WPA_AUTH_ALG_LEAP)
                type = NL80211_AUTHTYPE_NETWORK_EAP;
-       else if (params->auth_alg & AUTH_ALG_FT)
+       else if (params->auth_alg & WPA_AUTH_ALG_FT)
                type = NL80211_AUTHTYPE_FT;
        else
                goto nla_put_failure;
        wpa_printf(MSG_DEBUG, "  * Auth Type %d", type);
        NLA_PUT_U32(msg, NL80211_ATTR_AUTH_TYPE, type);
+       if (params->local_state_change) {
+               wpa_printf(MSG_DEBUG, "  * Local state change only");
+               NLA_PUT_FLAG(msg, NL80211_ATTR_LOCAL_STATE_CHANGE);
+       }
 
        ret = send_and_recv_msgs(drv, msg, NULL, NULL);
        msg = NULL;
@@ -2197,7 +2629,8 @@ retry:
                wpa_printf(MSG_DEBUG, "nl80211: MLME command failed: ret=%d "
                           "(%s)", ret, strerror(-ret));
                count++;
-               if (ret == -EALREADY && count == 1 && params->bssid) {
+               if (ret == -EALREADY && count == 1 && params->bssid &&
+                   !params->local_state_change) {
                        /*
                         * mac80211 does not currently accept new
                         * authentication if we are already authenticated. As a
@@ -2206,7 +2639,7 @@ retry:
                        wpa_printf(MSG_DEBUG, "nl80211: Retry authentication "
                                   "after forced deauthentication");
                        wpa_driver_nl80211_deauthenticate(
-                               drv, params->bssid,
+                               bss, params->bssid,
                                WLAN_REASON_PREV_AUTH_NOT_VALID);
                        nlmsg_free(msg);
                        goto retry;
@@ -2223,8 +2656,6 @@ nla_put_failure:
 }
 
 
-#if defined(CONFIG_AP) || defined(HOSTAPD)
-
 struct phy_info_arg {
        u16 *num_modes;
        struct hostapd_hw_modes *modes;
@@ -2268,7 +2699,7 @@ static int phy_info_handler(struct nl_msg *msg, void *arg)
                return NL_SKIP;
 
        nla_for_each_nested(nl_band, tb_msg[NL80211_ATTR_WIPHY_BANDS], rem_band) {
-               mode = realloc(phy_info->modes, (*phy_info->num_modes + 1) * sizeof(*mode));
+               mode = os_realloc(phy_info->modes, (*phy_info->num_modes + 1) * sizeof(*mode));
                if (!mode)
                        return NL_SKIP;
                phy_info->modes = mode;
@@ -2314,7 +2745,7 @@ static int phy_info_handler(struct nl_msg *msg, void *arg)
                        mode->num_channels++;
                }
 
-               mode->channels = calloc(mode->num_channels, sizeof(struct hostapd_channel_data));
+               mode->channels = os_zalloc(mode->num_channels * sizeof(struct hostapd_channel_data));
                if (!mode->channels)
                        return NL_SKIP;
 
@@ -2376,7 +2807,7 @@ static int phy_info_handler(struct nl_msg *msg, void *arg)
                        mode->num_rates++;
                }
 
-               mode->rates = calloc(mode->num_rates, sizeof(int));
+               mode->rates = os_zalloc(mode->num_rates * sizeof(int));
                if (!mode->rates)
                        return NL_SKIP;
 
@@ -2478,7 +2909,8 @@ wpa_driver_nl80211_add_11b(struct hostapd_hw_modes *modes, u16 *num_modes)
 static struct hostapd_hw_modes *
 wpa_driver_nl80211_get_hw_feature_data(void *priv, u16 *num_modes, u16 *flags)
 {
-       struct wpa_driver_nl80211_data *drv = priv;
+       struct i802_bss *bss = priv;
+       struct wpa_driver_nl80211_data *drv = bss->drv;
        struct nl_msg *msg;
        struct phy_info_arg result = {
                .num_modes = num_modes,
@@ -2547,7 +2979,8 @@ static int wpa_driver_nl80211_send_frame(struct wpa_driver_nl80211_data *drv,
 static int wpa_driver_nl80211_send_mlme(void *priv, const u8 *data,
                                        size_t data_len)
 {
-       struct wpa_driver_nl80211_data *drv = priv;
+       struct i802_bss *bss = priv;
+       struct wpa_driver_nl80211_data *drv = bss->drv;
        struct ieee80211_mgmt *mgmt;
        int encrypt = 1;
        u16 fc;
@@ -2572,30 +3005,21 @@ static int wpa_driver_nl80211_send_mlme(void *priv, const u8 *data,
        return wpa_driver_nl80211_send_frame(drv, data, data_len, encrypt);
 }
 
-#endif /* CONFIG_AP || HOSTAPD */
 
-
-static int wpa_driver_nl80211_set_beacon(const char *ifname, void *priv,
+static int wpa_driver_nl80211_set_beacon(void *priv,
                                         const u8 *head, size_t head_len,
                                         const u8 *tail, size_t tail_len,
                                         int dtim_period, int beacon_int)
 {
-       struct wpa_driver_nl80211_data *drv = priv;
+       struct i802_bss *bss = priv;
+       struct wpa_driver_nl80211_data *drv = bss->drv;
        struct nl_msg *msg;
        u8 cmd = NL80211_CMD_NEW_BEACON;
        int ret;
        int beacon_set;
-       int ifindex = if_nametoindex(ifname);
-#ifdef HOSTAPD
-       struct i802_bss *bss;
+       int ifindex = if_nametoindex(bss->ifname);
 
-       bss = get_bss(drv, ifindex);
-       if (bss == NULL)
-               return -ENOENT;
        beacon_set = bss->beacon_set;
-#else /* HOSTAPD */
-       beacon_set = drv->beacon_set;
-#endif /* HOSTAPD */
 
        msg = nlmsg_alloc();
        if (!msg)
@@ -2619,11 +3043,7 @@ static int wpa_driver_nl80211_set_beacon(const char *ifname, void *priv,
                wpa_printf(MSG_DEBUG, "nl80211: Beacon set failed: %d (%s)",
                           ret, strerror(-ret));
        } else {
-#ifdef HOSTAPD
                bss->beacon_set = 1;
-#else /* HOSTAPD */
-               drv->beacon_set = 1;
-#endif /* HOSTAPD */
        }
        return ret;
  nla_put_failure:
@@ -2631,8 +3051,6 @@ static int wpa_driver_nl80211_set_beacon(const char *ifname, void *priv,
 }
 
 
-#if defined(CONFIG_AP) || defined(HOSTAPD)
-
 static int wpa_driver_nl80211_set_freq(struct wpa_driver_nl80211_data *drv,
                                       int freq, int ht_enabled,
                                       int sec_channel_offset)
@@ -2676,10 +3094,11 @@ nla_put_failure:
 }
 
 
-static int wpa_driver_nl80211_sta_add(const char *ifname, void *priv,
+static int wpa_driver_nl80211_sta_add(void *priv,
                                      struct hostapd_sta_add_params *params)
 {
-       struct wpa_driver_nl80211_data *drv = priv;
+       struct i802_bss *bss = priv;
+       struct wpa_driver_nl80211_data *drv = bss->drv;
        struct nl_msg *msg;
        int ret = -ENOBUFS;
 
@@ -2690,7 +3109,7 @@ static int wpa_driver_nl80211_sta_add(const char *ifname, void *priv,
        genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0,
                    0, NL80211_CMD_NEW_STATION, 0);
 
-       NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, if_nametoindex(ifname));
+       NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, if_nametoindex(bss->ifname));
        NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, params->addr);
        NLA_PUT_U16(msg, NL80211_ATTR_STA_AID, params->aid);
        NLA_PUT(msg, NL80211_ATTR_STA_SUPPORTED_RATES, params->supp_rates_len,
@@ -2716,7 +3135,8 @@ static int wpa_driver_nl80211_sta_add(const char *ifname, void *priv,
 
 static int wpa_driver_nl80211_sta_remove(void *priv, const u8 *addr)
 {
-       struct wpa_driver_nl80211_data *drv = priv;
+       struct i802_bss *bss = priv;
+       struct wpa_driver_nl80211_data *drv = bss->drv;
        struct nl_msg *msg;
        int ret;
 
@@ -2728,7 +3148,7 @@ static int wpa_driver_nl80211_sta_remove(void *priv, const u8 *addr)
                    0, NL80211_CMD_DEL_STATION, 0);
 
        NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX,
-                   if_nametoindex(drv->ifname));
+                   if_nametoindex(bss->ifname));
        NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, addr);
 
        ret = send_and_recv_msgs(drv, msg, NULL, NULL);
@@ -2739,14 +3159,14 @@ static int wpa_driver_nl80211_sta_remove(void *priv, const u8 *addr)
        return -ENOBUFS;
 }
 
-#endif /* CONFIG_AP || HOSTAPD */
-
 
 static void nl80211_remove_iface(struct wpa_driver_nl80211_data *drv,
                                 int ifidx)
 {
        struct nl_msg *msg;
 
+       wpa_printf(MSG_DEBUG, "nl80211: Remove interface ifindex=%d", ifidx);
+
 #ifdef HOSTAPD
        /* stop listening for EAPOL on this interface */
        del_ifidx(drv, ifidx);
@@ -2763,15 +3183,14 @@ static void nl80211_remove_iface(struct wpa_driver_nl80211_data *drv,
        if (send_and_recv_msgs(drv, msg, NULL, NULL) == 0)
                return;
  nla_put_failure:
-       wpa_printf(MSG_ERROR, "Failed to remove interface (ifidx=%d).\n",
-                  ifidx);
+       wpa_printf(MSG_ERROR, "Failed to remove interface (ifidx=%d)", ifidx);
 }
 
 
 static int nl80211_create_iface_once(struct wpa_driver_nl80211_data *drv,
                                     const char *ifname,
                                     enum nl80211_iftype iftype,
-                                    const u8 *addr)
+                                    const u8 *addr, int wds)
 {
        struct nl_msg *msg, *flags = NULL;
        int ifidx;
@@ -2802,6 +3221,8 @@ static int nl80211_create_iface_once(struct wpa_driver_nl80211_data *drv,
 
                if (err)
                        goto nla_put_failure;
+       } else if (wds) {
+               NLA_PUT_U8(msg, NL80211_ATTR_4ADDR, wds);
        }
 
        ret = send_and_recv_msgs(drv, msg, NULL, NULL);
@@ -2813,6 +3234,8 @@ static int nl80211_create_iface_once(struct wpa_driver_nl80211_data *drv,
        }
 
        ifidx = if_nametoindex(ifname);
+       wpa_printf(MSG_DEBUG, "nl80211: New interface %s created: ifindex=%d",
+                  ifname, ifidx);
 
        if (ifidx <= 0)
                return -1;
@@ -2820,13 +3243,13 @@ static int nl80211_create_iface_once(struct wpa_driver_nl80211_data *drv,
 #ifdef HOSTAPD
        /* start listening for EAPOL on this interface */
        add_ifidx(drv, ifidx);
+#endif /* HOSTAPD */
 
-       if (addr && iftype == NL80211_IFTYPE_AP &&
-           set_ifhwaddr(drv, ifname, addr)) {
+       if (addr && iftype != NL80211_IFTYPE_MONITOR &&
+           linux_set_ifhwaddr(drv->ioctl_sock, ifname, addr)) {
                nl80211_remove_iface(drv, ifidx);
                return -1;
        }
-#endif /* HOSTAPD */
 
        return ifidx;
 }
@@ -2834,11 +3257,11 @@ static int nl80211_create_iface_once(struct wpa_driver_nl80211_data *drv,
 
 static int nl80211_create_iface(struct wpa_driver_nl80211_data *drv,
                                const char *ifname, enum nl80211_iftype iftype,
-                               const u8 *addr)
+                               const u8 *addr, int wds)
 {
        int ret;
 
-       ret = nl80211_create_iface_once(drv, ifname, iftype, addr);
+       ret = nl80211_create_iface_once(drv, ifname, iftype, addr, wds);
 
        /* if error occured and interface exists already */
        if (ret == -ENFILE && if_nametoindex(ifname)) {
@@ -2848,106 +3271,74 @@ static int nl80211_create_iface(struct wpa_driver_nl80211_data *drv,
                nl80211_remove_iface(drv, if_nametoindex(ifname));
 
                /* Try to create the interface again */
-               ret = nl80211_create_iface_once(drv, ifname, iftype, addr);
+               ret = nl80211_create_iface_once(drv, ifname, iftype, addr,
+                                               wds);
        }
 
+       if (ret >= 0 && drv->disable_11b_rates)
+               nl80211_disable_11b_rates(drv, ret, 1);
+
        return ret;
 }
 
 
-#ifdef CONFIG_AP
-
-void ap_tx_status(void *ctx, const u8 *addr,
-                 const u8 *buf, size_t len, int ack);
-void ap_rx_from_unknown_sta(void *ctx, struct ieee80211_hdr *hdr, size_t len);
-void ap_mgmt_rx(void *ctx, u8 *buf, size_t len, u16 stype,
-               struct hostapd_frame_info *fi);
-void ap_mgmt_tx_cb(void *ctx, u8 *buf, size_t len, u16 stype, int ok);
-
-#endif /* CONFIG_AP */
-
-#if defined(CONFIG_AP) || defined(HOSTAPD)
-
 static void handle_tx_callback(void *ctx, u8 *buf, size_t len, int ok)
 {
        struct ieee80211_hdr *hdr;
-       u16 fc, type, stype;
+       u16 fc;
+       union wpa_event_data event;
 
        hdr = (struct ieee80211_hdr *) buf;
        fc = le_to_host16(hdr->frame_control);
 
-       type = WLAN_FC_GET_TYPE(fc);
-       stype = WLAN_FC_GET_STYPE(fc);
-
-       switch (type) {
-       case WLAN_FC_TYPE_MGMT:
-               wpa_printf(MSG_DEBUG, "MGMT (TX callback) %s",
-                          ok ? "ACK" : "fail");
-#ifdef HOSTAPD
-               hostapd_mgmt_tx_cb(ctx, buf, len, stype, ok);
-#else /* HOSTAPD */
-               ap_mgmt_tx_cb(ctx, buf, len, stype, ok);
-#endif /* HOSTAPD */
-               break;
-       case WLAN_FC_TYPE_CTRL:
-               wpa_printf(MSG_DEBUG, "CTRL (TX callback) %s",
-                          ok ? "ACK" : "fail");
-               break;
-       case WLAN_FC_TYPE_DATA:
-#ifdef HOSTAPD
-               hostapd_tx_status(ctx, hdr->addr1, buf, len, ok);
-#else /* HOSTAPD */
-               ap_tx_status(ctx, hdr->addr1, buf, len, ok);
-#endif /* HOSTAPD */
-               break;
-       default:
-               wpa_printf(MSG_DEBUG, "unknown TX callback frame type %d",
-                          type);
-               break;
-       }
+       os_memset(&event, 0, sizeof(event));
+       event.tx_status.type = WLAN_FC_GET_TYPE(fc);
+       event.tx_status.stype = WLAN_FC_GET_STYPE(fc);
+       event.tx_status.dst = hdr->addr1;
+       event.tx_status.data = buf;
+       event.tx_status.data_len = len;
+       event.tx_status.ack = ok;
+       wpa_supplicant_event(ctx, EVENT_TX_STATUS, &event);
 }
 
 
 static void from_unknown_sta(struct wpa_driver_nl80211_data *drv,
-                            struct ieee80211_hdr *hdr, size_t len)
+                            u8 *buf, size_t len)
 {
-#ifdef HOSTAPD
-       hostapd_rx_from_unknown_sta(drv->ctx, hdr, len);
-#else /* HOSTAPD */
-       ap_rx_from_unknown_sta(drv->ctx, hdr, len);
-#endif /* HOSTAPD */
+       union wpa_event_data event;
+       os_memset(&event, 0, sizeof(event));
+       event.rx_from_unknown.frame = buf;
+       event.rx_from_unknown.len = len;
+       wpa_supplicant_event(drv->ctx, EVENT_RX_FROM_UNKNOWN, &event);
 }
 
 
 static void handle_frame(struct wpa_driver_nl80211_data *drv,
-                        u8 *buf, size_t len,
-                        struct hostapd_frame_info *hfi)
+                        u8 *buf, size_t len, int datarate, int ssi_signal)
 {
        struct ieee80211_hdr *hdr;
-       u16 fc, stype;
+       u16 fc;
+       union wpa_event_data event;
 
        hdr = (struct ieee80211_hdr *) buf;
        fc = le_to_host16(hdr->frame_control);
-       stype = WLAN_FC_GET_STYPE(fc);
 
        switch (WLAN_FC_GET_TYPE(fc)) {
        case WLAN_FC_TYPE_MGMT:
-               if (stype != WLAN_FC_STYPE_BEACON &&
-                   stype != WLAN_FC_STYPE_PROBE_REQ)
-                       wpa_printf(MSG_MSGDUMP, "MGMT");
-#ifdef HOSTAPD
-               hostapd_mgmt_rx(drv->ctx, buf, len, stype, hfi);
-#else /* HOSTAPD */
-               ap_mgmt_rx(drv->ctx, buf, len, stype, hfi);
-#endif /* HOSTAPD */
+               os_memset(&event, 0, sizeof(event));
+               event.rx_mgmt.frame = buf;
+               event.rx_mgmt.frame_len = len;
+               event.rx_mgmt.datarate = datarate;
+               event.rx_mgmt.ssi_signal = ssi_signal;
+               wpa_supplicant_event(drv->ctx, EVENT_RX_MGMT, &event);
                break;
        case WLAN_FC_TYPE_CTRL:
                /* can only get here with PS-Poll frames */
                wpa_printf(MSG_DEBUG, "CTRL");
-               from_unknown_sta(drv, hdr, len);
+               from_unknown_sta(drv, buf, len);
                break;
        case WLAN_FC_TYPE_DATA:
-               from_unknown_sta(drv, hdr, len);
+               from_unknown_sta(drv, buf, len);
                break;
        }
 }
@@ -2960,7 +3351,7 @@ static void handle_monitor_read(int sock, void *eloop_ctx, void *sock_ctx)
        unsigned char buf[3000];
        struct ieee80211_radiotap_iterator iter;
        int ret;
-       struct hostapd_frame_info hfi;
+       int datarate = 0, ssi_signal = 0;
        int injected = 0, failed = 0, rxflags = 0;
 
        len = recv(sock, buf, sizeof(buf), 0);
@@ -2969,13 +3360,17 @@ static void handle_monitor_read(int sock, void *eloop_ctx, void *sock_ctx)
                return;
        }
 
+       if (drv->nlmode == NL80211_IFTYPE_STATION && !drv->probe_req_report) {
+               wpa_printf(MSG_DEBUG, "nl80211: Ignore monitor interface "
+                          "frame since Probe Request reporting is disabled");
+               return;
+       }
+
        if (ieee80211_radiotap_iterator_init(&iter, (void*)buf, len)) {
                printf("received invalid radiotap frame\n");
                return;
        }
 
-       memset(&hfi, 0, sizeof(hfi));
-
        while (1) {
                ret = ieee80211_radiotap_iterator_next(&iter);
                if (ret == -ENOENT)
@@ -3000,15 +3395,13 @@ static void handle_monitor_read(int sock, void *eloop_ctx, void *sock_ctx)
                case IEEE80211_RADIOTAP_DATA_RETRIES:
                        break;
                case IEEE80211_RADIOTAP_CHANNEL:
-                       /* TODO convert from freq/flags to channel number
-                       hfi.channel = XXX;
-                        */
+                       /* TODO: convert from freq/flags to channel number */
                        break;
                case IEEE80211_RADIOTAP_RATE:
-                       hfi.datarate = *iter.this_arg * 5;
+                       datarate = *iter.this_arg * 5;
                        break;
                case IEEE80211_RADIOTAP_DB_ANTSIGNAL:
-                       hfi.ssi_signal = *iter.this_arg;
+                       ssi_signal = *iter.this_arg;
                        break;
                }
        }
@@ -3018,7 +3411,7 @@ static void handle_monitor_read(int sock, void *eloop_ctx, void *sock_ctx)
 
        if (!injected)
                handle_frame(drv, buf + iter.max_length,
-                            len - iter.max_length, &hfi);
+                            len - iter.max_length, datarate, ssi_signal);
        else
                handle_tx_callback(drv->ctx, buf + iter.max_length,
                                   len - iter.max_length, !failed);
@@ -3071,7 +3464,7 @@ static struct sock_filter msock_filter_insns[] = {
 
 #if 0
        /*
-        * drop non-data frames, WDS frames
+        * drop non-data frames
         */
        /* load the lower byte of the frame control field */
        BPF_STMT(BPF_LD   | BPF_B | BPF_IND, 0),
@@ -3079,13 +3472,13 @@ static struct sock_filter msock_filter_insns[] = {
        BPF_STMT(BPF_ALU  | BPF_AND | BPF_K, 0x0c),
        /* drop non-data frames */
        BPF_JUMP(BPF_JMP  | BPF_JEQ | BPF_K, 8, 0, FAIL),
+#endif
        /* load the upper byte of the frame control field */
-       BPF_STMT(BPF_LD   | BPF_B | BPF_IND, 0),
+       BPF_STMT(BPF_LD   | BPF_B | BPF_IND, 1),
        /* mask off toDS/fromDS */
        BPF_STMT(BPF_ALU  | BPF_AND | BPF_K, 0x03),
-       /* drop WDS frames */
-       BPF_JUMP(BPF_JMP  | BPF_JEQ | BPF_K, 3, FAIL, 0),
-#endif
+       /* accept WDS frames */
+       BPF_JUMP(BPF_JMP  | BPF_JEQ | BPF_K, 3, PASS, 0),
 
        /*
         * add header length to index
@@ -3176,6 +3569,11 @@ static void nl80211_remove_monitor_interface(
                nl80211_remove_iface(drv, drv->monitor_ifidx);
                drv->monitor_ifidx = -1;
        }
+       if (drv->monitor_sock >= 0) {
+               eloop_unregister_read_sock(drv->monitor_sock);
+               close(drv->monitor_sock);
+               drv->monitor_sock = -1;
+       }
 }
 
 
@@ -3187,16 +3585,17 @@ nl80211_create_monitor_interface(struct wpa_driver_nl80211_data *drv)
        int optval;
        socklen_t optlen;
 
-       snprintf(buf, IFNAMSIZ, "mon.%s", drv->ifname);
+       snprintf(buf, IFNAMSIZ, "mon.%s", drv->first_bss.ifname);
        buf[IFNAMSIZ - 1] = '\0';
 
        drv->monitor_ifidx =
-               nl80211_create_iface(drv, buf, NL80211_IFTYPE_MONITOR, NULL);
+               nl80211_create_iface(drv, buf, NL80211_IFTYPE_MONITOR, NULL,
+                                    0);
 
        if (drv->monitor_ifidx < 0)
                return -1;
 
-       if (hostapd_set_iface_flags(drv, buf, 1))
+       if (linux_set_iface_flags(drv->ioctl_sock, buf, 1))
                goto error;
 
        memset(&ll, 0, sizeof(ll));
@@ -3246,13 +3645,14 @@ static int wpa_driver_nl80211_hapd_send_eapol(
        void *priv, const u8 *addr, const u8 *data,
        size_t data_len, int encrypt, const u8 *own_addr)
 {
-       struct wpa_driver_nl80211_data *drv = priv;
+       struct i802_bss *bss = priv;
+       struct wpa_driver_nl80211_data *drv = bss->drv;
        struct ieee80211_hdr *hdr;
        size_t len;
        u8 *pos;
        int res;
 #if 0 /* FIX */
-       int qos = sta->flags & WLAN_STA_WME;
+       int qos = sta->flags & WPA_STA_WMM;
 #else
        int qos = 0;
 #endif
@@ -3304,7 +3704,7 @@ static int wpa_driver_nl80211_hapd_send_eapol(
                           "failed: %d (%s)",
                           (unsigned long) len, errno, strerror(errno));
        }
-       free(hdr);
+       os_free(hdr);
 
        return res;
 }
@@ -3314,13 +3714,13 @@ static u32 sta_flags_nl80211(int flags)
 {
        u32 f = 0;
 
-       if (flags & WLAN_STA_AUTHORIZED)
+       if (flags & WPA_STA_AUTHORIZED)
                f |= BIT(NL80211_STA_FLAG_AUTHORIZED);
-       if (flags & WLAN_STA_WMM)
+       if (flags & WPA_STA_WMM)
                f |= BIT(NL80211_STA_FLAG_WME);
-       if (flags & WLAN_STA_SHORT_PREAMBLE)
+       if (flags & WPA_STA_SHORT_PREAMBLE)
                f |= BIT(NL80211_STA_FLAG_SHORT_PREAMBLE);
-       if (flags & WLAN_STA_MFP)
+       if (flags & WPA_STA_MFP)
                f |= BIT(NL80211_STA_FLAG_MFP);
 
        return f;
@@ -3328,10 +3728,11 @@ static u32 sta_flags_nl80211(int flags)
 
 
 static int wpa_driver_nl80211_sta_set_flags(void *priv, const u8 *addr,
-                                           int total_flags, int flags_or,
-                                           int flags_and)
+                                           int total_flags,
+                                           int flags_or, int flags_and)
 {
-       struct wpa_driver_nl80211_data *drv = priv;
+       struct i802_bss *bss = priv;
+       struct wpa_driver_nl80211_data *drv = bss->drv;
        struct nl_msg *msg, *flags = NULL;
        struct nl80211_sta_flag_update upd;
 
@@ -3349,23 +3750,23 @@ static int wpa_driver_nl80211_sta_set_flags(void *priv, const u8 *addr,
                    0, NL80211_CMD_SET_STATION, 0);
 
        NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX,
-                   if_nametoindex(drv->ifname));
+                   if_nametoindex(bss->ifname));
        NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, addr);
 
        /*
         * Backwards compatibility version using NL80211_ATTR_STA_FLAGS. This
         * can be removed eventually.
         */
-       if (total_flags & WLAN_STA_AUTHORIZED)
+       if (total_flags & WPA_STA_AUTHORIZED)
                NLA_PUT_FLAG(flags, NL80211_STA_FLAG_AUTHORIZED);
 
-       if (total_flags & WLAN_STA_WMM)
+       if (total_flags & WPA_STA_WMM)
                NLA_PUT_FLAG(flags, NL80211_STA_FLAG_WME);
 
-       if (total_flags & WLAN_STA_SHORT_PREAMBLE)
+       if (total_flags & WPA_STA_SHORT_PREAMBLE)
                NLA_PUT_FLAG(flags, NL80211_STA_FLAG_SHORT_PREAMBLE);
 
-       if (total_flags & WLAN_STA_MFP)
+       if (total_flags & WPA_STA_MFP)
                NLA_PUT_FLAG(flags, NL80211_STA_FLAG_MFP);
 
        if (nla_put_nested(msg, NL80211_ATTR_STA_FLAGS, flags))
@@ -3384,14 +3785,14 @@ static int wpa_driver_nl80211_sta_set_flags(void *priv, const u8 *addr,
        return -ENOBUFS;
 }
 
-#endif /* CONFIG_AP || HOSTAPD */
-
-#ifdef CONFIG_AP
 
 static int wpa_driver_nl80211_ap(struct wpa_driver_nl80211_data *drv,
                                 struct wpa_driver_associate_params *params)
 {
-       if (wpa_driver_nl80211_set_mode(drv, params->mode) ||
+       if (params->p2p)
+               wpa_printf(MSG_DEBUG, "nl80211: Setup AP operations for P2P "
+                          "group (GO)");
+       if (wpa_driver_nl80211_set_mode(&drv->first_bss, params->mode) ||
            wpa_driver_nl80211_set_freq(drv, params->freq, 0, 0)) {
                nl80211_remove_monitor_interface(drv);
                return -1;
@@ -3402,7 +3803,6 @@ static int wpa_driver_nl80211_ap(struct wpa_driver_nl80211_data *drv,
 
        return 0;
 }
-#endif /* CONFIG_AP */
 
 
 static int nl80211_leave_ibss(struct wpa_driver_nl80211_data *drv)
@@ -3443,7 +3843,7 @@ static int wpa_driver_nl80211_ibss(struct wpa_driver_nl80211_data *drv,
 
        wpa_printf(MSG_DEBUG, "nl80211: Join IBSS (ifindex=%d)", drv->ifindex);
 
-       if (wpa_driver_nl80211_set_mode(drv, params->mode)) {
+       if (wpa_driver_nl80211_set_mode(&drv->first_bss, params->mode)) {
                wpa_printf(MSG_INFO, "nl80211: Failed to set interface into "
                           "IBSS mode");
                return -1;
@@ -3549,13 +3949,13 @@ static int wpa_driver_nl80211_connect(
                NLA_PUT(msg, NL80211_ATTR_IE, params->wpa_ie_len,
                        params->wpa_ie);
 
-       if (params->auth_alg & AUTH_ALG_OPEN_SYSTEM)
+       if (params->auth_alg & WPA_AUTH_ALG_OPEN)
                type = NL80211_AUTHTYPE_OPEN_SYSTEM;
-       else if (params->auth_alg & AUTH_ALG_SHARED_KEY)
+       else if (params->auth_alg & WPA_AUTH_ALG_SHARED)
                type = NL80211_AUTHTYPE_SHARED_KEY;
-       else if (params->auth_alg & AUTH_ALG_LEAP)
+       else if (params->auth_alg & WPA_AUTH_ALG_LEAP)
                type = NL80211_AUTHTYPE_NETWORK_EAP;
-       else if (params->auth_alg & AUTH_ALG_FT)
+       else if (params->auth_alg & WPA_AUTH_ALG_FT)
                type = NL80211_AUTHTYPE_FT;
        else
                goto nla_put_failure;
@@ -3657,20 +4057,19 @@ nla_put_failure:
 static int wpa_driver_nl80211_associate(
        void *priv, struct wpa_driver_associate_params *params)
 {
-       struct wpa_driver_nl80211_data *drv = priv;
+       struct i802_bss *bss = priv;
+       struct wpa_driver_nl80211_data *drv = bss->drv;
        int ret = -1;
        struct nl_msg *msg;
 
-#ifdef CONFIG_AP
        if (params->mode == IEEE80211_MODE_AP)
                return wpa_driver_nl80211_ap(drv, params);
-#endif /* CONFIG_AP */
 
        if (params->mode == IEEE80211_MODE_IBSS)
                return wpa_driver_nl80211_ibss(drv, params);
 
        if (!(drv->capa.flags & WPA_DRIVER_FLAGS_SME)) {
-               if (wpa_driver_nl80211_set_mode(drv, params->mode) < 0)
+               if (wpa_driver_nl80211_set_mode(priv, params->mode) < 0)
                        return -1;
                return wpa_driver_nl80211_connect(drv, params);
        }
@@ -3695,7 +4094,9 @@ static int wpa_driver_nl80211_associate(
        if (params->freq) {
                wpa_printf(MSG_DEBUG, "  * freq=%d", params->freq);
                NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_FREQ, params->freq);
-       }
+               drv->assoc_freq = params->freq;
+       } else
+               drv->assoc_freq = 0;
        if (params->ssid) {
                wpa_hexdump_ascii(MSG_DEBUG, "  * SSID",
                                  params->ssid, params->ssid_len);
@@ -3725,11 +4126,15 @@ static int wpa_driver_nl80211_associate(
                        params->prev_bssid);
        }
 
+       if (params->p2p)
+               wpa_printf(MSG_DEBUG, "  * P2P group");
+
        ret = send_and_recv_msgs(drv, msg, NULL, NULL);
        msg = NULL;
        if (ret) {
                wpa_printf(MSG_DEBUG, "nl80211: MLME command failed: ret=%d "
                           "(%s)", ret, strerror(-ret));
+               nl80211_dump_scan(drv);
                goto nla_put_failure;
        }
        ret = 0;
@@ -3769,9 +4174,11 @@ nla_put_failure:
 
 static int wpa_driver_nl80211_set_mode(void *priv, int mode)
 {
-       struct wpa_driver_nl80211_data *drv = priv;
+       struct i802_bss *bss = priv;
+       struct wpa_driver_nl80211_data *drv = bss->drv;
        int ret = -1;
        int nlmode;
+       int i;
 
        switch (mode) {
        case 0:
@@ -3794,6 +4201,8 @@ static int wpa_driver_nl80211_set_mode(void *priv, int mode)
        }
 
        if (nlmode == drv->nlmode) {
+               wpa_printf(MSG_DEBUG, "nl80211: Interface already in "
+                          "requested mode - ignore error");
                ret = 0;
                goto done; /* Already in the requested mode */
        }
@@ -3802,18 +4211,32 @@ static int wpa_driver_nl80211_set_mode(void *priv, int mode)
         * take the device down, try to set the mode again, and bring the
         * device back up.
         */
-       if (hostapd_set_iface_flags(drv, drv->ifname, 0) == 0) {
-               /* Try to set the mode again while the interface is down */
-               ret = nl80211_set_mode(drv, drv->ifindex, nlmode);
-               if (hostapd_set_iface_flags(drv, drv->ifname, 1))
-                       ret = -1;
+       wpa_printf(MSG_DEBUG, "nl80211: Try mode change after setting "
+                  "interface down");
+       for (i = 0; i < 10; i++) {
+               if (linux_set_iface_flags(drv->ioctl_sock, bss->ifname, 0) ==
+                   0) {
+                       /* Try to set the mode again while the interface is
+                        * down */
+                       ret = nl80211_set_mode(drv, drv->ifindex, nlmode);
+                       if (linux_set_iface_flags(drv->ioctl_sock, bss->ifname,
+                                                 1))
+                               ret = -1;
+                       if (!ret)
+                               break;
+               } else
+                       wpa_printf(MSG_DEBUG, "nl80211: Failed to set "
+                                  "interface down");
+               os_sleep(0, 100000);
        }
 
-       if (!ret)
+       if (!ret) {
+               wpa_printf(MSG_DEBUG, "nl80211: Mode change succeeded while "
+                          "interface is down");
                drv->nlmode = nlmode;
+       }
 
 done:
-#if defined(CONFIG_AP) || defined(HOSTAPD)
        if (!ret && nlmode == NL80211_IFTYPE_AP) {
                /* Setup additional AP mode functionality if needed */
                if (drv->monitor_ifidx < 0 &&
@@ -3822,8 +4245,12 @@ done:
        } else if (!ret && nlmode != NL80211_IFTYPE_AP) {
                /* Remove additional AP mode functionality */
                nl80211_remove_monitor_interface(drv);
+               bss->beacon_set = 0;
        }
-#endif /* CONFIG_AP || HOSTAPD */
+
+       if (ret)
+               wpa_printf(MSG_DEBUG, "nl80211: Interface mode change to %d "
+                          "from %d failed", nlmode, drv->nlmode);
 
        return ret;
 }
@@ -3832,7 +4259,8 @@ done:
 static int wpa_driver_nl80211_get_capa(void *priv,
                                       struct wpa_driver_capa *capa)
 {
-       struct wpa_driver_nl80211_data *drv = priv;
+       struct i802_bss *bss = priv;
+       struct wpa_driver_nl80211_data *drv = bss->drv;
        if (!drv->has_capability)
                return -1;
        os_memcpy(capa, &drv->capa, sizeof(*capa));
@@ -3842,19 +4270,21 @@ static int wpa_driver_nl80211_get_capa(void *priv,
 
 static int wpa_driver_nl80211_set_operstate(void *priv, int state)
 {
-       struct wpa_driver_nl80211_data *drv = priv;
+       struct i802_bss *bss = priv;
+       struct wpa_driver_nl80211_data *drv = bss->drv;
 
        wpa_printf(MSG_DEBUG, "%s: operstate %d->%d (%s)",
                   __func__, drv->operstate, state, state ? "UP" : "DORMANT");
        drv->operstate = state;
-       return wpa_driver_nl80211_send_oper_ifla(
-               drv, -1, state ? IF_OPER_UP : IF_OPER_DORMANT);
+       return netlink_send_oper_ifla(drv->netlink, drv->ifindex, -1,
+                                     state ? IF_OPER_UP : IF_OPER_DORMANT);
 }
 
 
 static int wpa_driver_nl80211_set_supp_port(void *priv, int authorized)
 {
-       struct wpa_driver_nl80211_data *drv = priv;
+       struct i802_bss *bss = priv;
+       struct wpa_driver_nl80211_data *drv = bss->drv;
        struct nl_msg *msg;
        struct nl80211_sta_flag_update upd;
 
@@ -3866,7 +4296,7 @@ static int wpa_driver_nl80211_set_supp_port(void *priv, int authorized)
                    0, NL80211_CMD_SET_STATION, 0);
 
        NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX,
-                   if_nametoindex(drv->ifname));
+                   if_nametoindex(bss->ifname));
        NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, drv->bssid);
 
        os_memset(&upd, 0, sizeof(upd));
@@ -3883,20 +4313,6 @@ static int wpa_driver_nl80211_set_supp_port(void *priv, int authorized)
 
 #ifdef HOSTAPD
 
-static struct i802_bss * get_bss(struct wpa_driver_nl80211_data *drv,
-                                int ifindex)
-{
-       struct i802_bss *bss = &drv->bss;
-       while (bss) {
-               if (ifindex == bss->ifindex)
-                       return bss;
-               bss = bss->next;
-       }
-       wpa_printf(MSG_DEBUG, "nl80211: get_bss(%d) failed", ifindex);
-       return NULL;
-}
-
-
 static void add_ifidx(struct wpa_driver_nl80211_data *drv, int ifidx)
 {
        int i;
@@ -3916,8 +4332,8 @@ static void add_ifidx(struct wpa_driver_nl80211_data *drv, int ifidx)
        else
                old = NULL;
 
-       drv->if_indices = realloc(old,
-                                 sizeof(int) * (drv->num_if_indices + 1));
+       drv->if_indices = os_realloc(old,
+                                    sizeof(int) * (drv->num_if_indices + 1));
        if (!drv->if_indices) {
                if (!old)
                        drv->if_indices = drv->default_if_indices;
@@ -3927,7 +4343,9 @@ static void add_ifidx(struct wpa_driver_nl80211_data *drv, int ifidx)
                           "interfaces");
                wpa_printf(MSG_ERROR, "Ignoring EAPOL on interface %d", ifidx);
                return;
-       }
+       } else if (!old)
+               os_memcpy(drv->if_indices, drv->default_if_indices,
+                         sizeof(drv->default_if_indices));
        drv->if_indices[drv->num_if_indices] = ifidx;
        drv->num_if_indices++;
 }
@@ -3990,7 +4408,8 @@ static int get_key_handler(struct nl_msg *msg, void *arg)
 static int i802_get_seqnum(const char *iface, void *priv, const u8 *addr,
                           int idx, u8 *seq)
 {
-       struct wpa_driver_nl80211_data *drv = priv;
+       struct i802_bss *bss = priv;
+       struct wpa_driver_nl80211_data *drv = bss->drv;
        struct nl_msg *msg;
 
        msg = nlmsg_alloc();
@@ -4016,7 +4435,8 @@ static int i802_get_seqnum(const char *iface, void *priv, const u8 *addr,
 static int i802_set_rate_sets(void *priv, int *supp_rates, int *basic_rates,
                              int mode)
 {
-       struct wpa_driver_nl80211_data *drv = priv;
+       struct i802_bss *bss = priv;
+       struct wpa_driver_nl80211_data *drv = bss->drv;
        struct nl_msg *msg;
        u8 rates[NL80211_MAX_SUPP_RATES];
        u8 rates_len = 0;
@@ -4034,27 +4454,32 @@ static int i802_set_rate_sets(void *priv, int *supp_rates, int *basic_rates,
 
        NLA_PUT(msg, NL80211_ATTR_BSS_BASIC_RATES, rates_len, rates);
 
-       /* TODO: multi-BSS support */
-       NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, if_nametoindex(drv->ifname));
+       NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, if_nametoindex(bss->ifname));
 
        return send_and_recv_msgs(drv, msg, NULL, NULL);
  nla_put_failure:
        return -ENOBUFS;
 }
 
+#endif /* HOSTAPD */
+
 
 /* Set kernel driver on given frequency (MHz) */
 static int i802_set_freq(void *priv, struct hostapd_freq_params *freq)
 {
-       struct wpa_driver_nl80211_data *drv = priv;
+       struct i802_bss *bss = priv;
+       struct wpa_driver_nl80211_data *drv = bss->drv;
        return wpa_driver_nl80211_set_freq(drv, freq->freq, freq->ht_enabled,
                                           freq->sec_channel_offset);
 }
 
 
+#ifdef HOSTAPD
+
 static int i802_set_rts(void *priv, int rts)
 {
-       struct wpa_driver_nl80211_data *drv = priv;
+       struct i802_bss *bss = priv;
+       struct wpa_driver_nl80211_data *drv = bss->drv;
        struct nl_msg *msg;
        int ret = -ENOBUFS;
        u32 val;
@@ -4085,7 +4510,8 @@ nla_put_failure:
 
 static int i802_set_frag(void *priv, int frag)
 {
-       struct wpa_driver_nl80211_data *drv = priv;
+       struct i802_bss *bss = priv;
+       struct wpa_driver_nl80211_data *drv = bss->drv;
        struct nl_msg *msg;
        int ret = -ENOBUFS;
        u32 val;
@@ -4116,7 +4542,8 @@ nla_put_failure:
 
 static int i802_flush(void *priv)
 {
-       struct wpa_driver_nl80211_data *drv = priv;
+       struct i802_bss *bss = priv;
+       struct wpa_driver_nl80211_data *drv = bss->drv;
        struct nl_msg *msg;
 
        msg = nlmsg_alloc();
@@ -4130,7 +4557,7 @@ static int i802_flush(void *priv)
         * XXX: FIX! this needs to flush all VLANs too
         */
        NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX,
-                   if_nametoindex(drv->ifname));
+                   if_nametoindex(bss->ifname));
 
        return send_and_recv_msgs(drv, msg, NULL, NULL);
  nla_put_failure:
@@ -4192,7 +4619,8 @@ static int get_sta_handler(struct nl_msg *msg, void *arg)
 static int i802_read_sta_data(void *priv, struct hostap_sta_driver_data *data,
                              const u8 *addr)
 {
-       struct wpa_driver_nl80211_data *drv = priv;
+       struct i802_bss *bss = priv;
+       struct wpa_driver_nl80211_data *drv = bss->drv;
        struct nl_msg *msg;
 
        os_memset(data, 0, sizeof(*data));
@@ -4204,7 +4632,7 @@ static int i802_read_sta_data(void *priv, struct hostap_sta_driver_data *data,
                    0, NL80211_CMD_GET_STATION, 0);
 
        NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, addr);
-       NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, if_nametoindex(drv->ifname));
+       NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, if_nametoindex(bss->ifname));
 
        return send_and_recv_msgs(drv, msg, get_sta_handler, data);
  nla_put_failure:
@@ -4215,7 +4643,8 @@ static int i802_read_sta_data(void *priv, struct hostap_sta_driver_data *data,
 static int i802_set_tx_queue_params(void *priv, int queue, int aifs,
                                    int cw_min, int cw_max, int burst_time)
 {
-       struct wpa_driver_nl80211_data *drv = priv;
+       struct i802_bss *bss = priv;
+       struct wpa_driver_nl80211_data *drv = bss->drv;
        struct nl_msg *msg;
        struct nlattr *txq, *params;
 
@@ -4226,7 +4655,7 @@ static int i802_set_tx_queue_params(void *priv, int queue, int aifs,
        genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0,
                    0, NL80211_CMD_SET_WIPHY, 0);
 
-       NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, if_nametoindex(drv->ifname));
+       NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, if_nametoindex(bss->ifname));
 
        txq = nla_nest_start(msg, NL80211_ATTR_WIPHY_TXQ_PARAMS);
        if (!txq)
@@ -4258,7 +4687,8 @@ static int i802_set_tx_queue_params(void *priv, int queue, int aifs,
 
 static int i802_set_bss(void *priv, int cts, int preamble, int slot)
 {
-       struct wpa_driver_nl80211_data *drv = priv;
+       struct i802_bss *bss = priv;
+       struct wpa_driver_nl80211_data *drv = bss->drv;
        struct nl_msg *msg;
 
        msg = nlmsg_alloc();
@@ -4275,8 +4705,7 @@ static int i802_set_bss(void *priv, int cts, int preamble, int slot)
        if (slot >= 0)
                NLA_PUT_U8(msg, NL80211_ATTR_BSS_SHORT_SLOT_TIME, slot);
 
-       /* TODO: multi-BSS support */
-       NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, if_nametoindex(drv->ifname));
+       NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, if_nametoindex(bss->ifname));
 
        return send_and_recv_msgs(drv, msg, NULL, NULL);
  nla_put_failure:
@@ -4305,8 +4734,10 @@ static int i802_set_short_slot_time(void *priv, int value)
 static int i802_set_sta_vlan(void *priv, const u8 *addr,
                             const char *ifname, int vlan_id)
 {
-       struct wpa_driver_nl80211_data *drv = priv;
+       struct i802_bss *bss = priv;
+       struct wpa_driver_nl80211_data *drv = bss->drv;
        struct nl_msg *msg;
+       int ret = -ENOBUFS;
 
        msg = nlmsg_alloc();
        if (!msg)
@@ -4316,14 +4747,43 @@ static int i802_set_sta_vlan(void *priv, const u8 *addr,
                    0, NL80211_CMD_SET_STATION, 0);
 
        NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX,
-                   if_nametoindex(drv->ifname));
+                   if_nametoindex(bss->ifname));
        NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, addr);
        NLA_PUT_U32(msg, NL80211_ATTR_STA_VLAN,
                    if_nametoindex(ifname));
 
-       return send_and_recv_msgs(drv, msg, NULL, NULL);
+       ret = send_and_recv_msgs(drv, msg, NULL, NULL);
+       if (ret < 0) {
+               wpa_printf(MSG_ERROR, "nl80211: NL80211_ATTR_STA_VLAN (addr="
+                          MACSTR " ifname=%s vlan_id=%d) failed: %d (%s)",
+                          MAC2STR(addr), ifname, vlan_id, ret,
+                          strerror(-ret));
+       }
  nla_put_failure:
-       return -ENOBUFS;
+       return ret;
+}
+
+
+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];
+
+       os_snprintf(name, sizeof(name), "%s.sta%d", bss->ifname, aid);
+       wpa_printf(MSG_DEBUG, "nl80211: Set WDS STA addr=" MACSTR
+                  " aid=%d val=%d name=%s", MAC2STR(addr), aid, val, name);
+       if (val) {
+               if (nl80211_create_iface(drv, name, NL80211_IFTYPE_AP_VLAN,
+                                        NULL, 1) < 0)
+                       return -1;
+               linux_set_iface_flags(drv->ioctl_sock, name, 1);
+               return i802_set_sta_vlan(priv, addr, name, 0);
+       } else {
+               i802_set_sta_vlan(priv, addr, bss->ifname, 0);
+               return wpa_driver_nl80211_if_remove(priv, WPA_IF_AP_VLAN,
+                                                   name);
+       }
 }
 
 
@@ -4342,13 +4802,8 @@ static void handle_eapol(int sock, void *eloop_ctx, void *sock_ctx)
                return;
        }
 
-       if (have_ifidx(drv, lladdr.sll_ifindex)) {
-               void *ctx;
-               ctx = hostapd_sta_get_bss(drv->ctx, lladdr.sll_addr);
-               if (!ctx)
-                       return;
-               hostapd_eapol_receive(ctx, lladdr.sll_addr, buf, len);
-       }
+       if (have_ifidx(drv, lladdr.sll_ifindex))
+               drv_event_eapol_rx(drv->ctx, lladdr.sll_addr, buf, len);
 }
 
 
@@ -4377,7 +4832,7 @@ static int i802_sta_clear_stats(void *priv, const u8 *addr)
 static int i802_sta_deauth(void *priv, const u8 *own_addr, const u8 *addr,
                           int reason)
 {
-       struct wpa_driver_nl80211_data *drv = priv;
+       struct i802_bss *bss = priv;
        struct ieee80211_mgmt mgmt;
 
        memset(&mgmt, 0, sizeof(mgmt));
@@ -4387,7 +4842,7 @@ static int i802_sta_deauth(void *priv, const u8 *own_addr, const u8 *addr,
        memcpy(mgmt.sa, own_addr, ETH_ALEN);
        memcpy(mgmt.bssid, own_addr, ETH_ALEN);
        mgmt.u.deauth.reason_code = host_to_le16(reason);
-       return wpa_driver_nl80211_send_mlme(drv, (u8 *) &mgmt,
+       return wpa_driver_nl80211_send_mlme(bss, (u8 *) &mgmt,
                                            IEEE80211_HDRLEN +
                                            sizeof(mgmt.u.deauth));
 }
@@ -4396,7 +4851,7 @@ static int i802_sta_deauth(void *priv, const u8 *own_addr, const u8 *addr,
 static int i802_sta_disassoc(void *priv, const u8 *own_addr, const u8 *addr,
                             int reason)
 {
-       struct wpa_driver_nl80211_data *drv = priv;
+       struct i802_bss *bss = priv;
        struct ieee80211_mgmt mgmt;
 
        memset(&mgmt, 0, sizeof(mgmt));
@@ -4406,49 +4861,126 @@ static int i802_sta_disassoc(void *priv, const u8 *own_addr, const u8 *addr,
        memcpy(mgmt.sa, own_addr, ETH_ALEN);
        memcpy(mgmt.bssid, own_addr, ETH_ALEN);
        mgmt.u.disassoc.reason_code = host_to_le16(reason);
-       return wpa_driver_nl80211_send_mlme(drv, (u8 *) &mgmt,
+       return wpa_driver_nl80211_send_mlme(bss, (u8 *) &mgmt,
                                            IEEE80211_HDRLEN +
                                            sizeof(mgmt.u.disassoc));
 }
 
 
+static int i802_check_bridge(struct wpa_driver_nl80211_data *drv,
+                            const char *brname, const char *ifname)
+{
+       int ifindex;
+       char in_br[IFNAMSIZ];
+
+       os_strlcpy(drv->brname, brname, IFNAMSIZ);
+       ifindex = if_nametoindex(brname);
+       if (ifindex == 0) {
+               /*
+                * Bridge was configured, but the bridge device does
+                * not exist. Try to add it now.
+                */
+               if (linux_br_add(drv->ioctl_sock, brname) < 0) {
+                       wpa_printf(MSG_ERROR, "nl80211: Failed to add the "
+                                  "bridge interface %s: %s",
+                                  brname, strerror(errno));
+                       return -1;
+               }
+               drv->added_bridge = 1;
+               add_ifidx(drv, if_nametoindex(brname));
+       }
+
+       if (linux_br_get(in_br, ifname) == 0) {
+               if (os_strcmp(in_br, brname) == 0)
+                       return 0; /* already in the bridge */
+
+               wpa_printf(MSG_DEBUG, "nl80211: Removing interface %s from "
+                          "bridge %s", ifname, in_br);
+               if (linux_br_del_if(drv->ioctl_sock, in_br, ifname) < 0) {
+                       wpa_printf(MSG_ERROR, "nl80211: Failed to "
+                                  "remove interface %s from bridge "
+                                  "%s: %s",
+                                  ifname, brname, strerror(errno));
+                       return -1;
+               }
+       }
+
+       wpa_printf(MSG_DEBUG, "nl80211: Adding interface %s into bridge %s",
+                  ifname, brname);
+       if (linux_br_add_if(drv->ioctl_sock, brname, ifname) < 0) {
+               wpa_printf(MSG_ERROR, "nl80211: Failed to add interface %s "
+                          "into bridge %s: %s",
+                          ifname, brname, strerror(errno));
+               return -1;
+       }
+       drv->added_if_into_bridge = 1;
+
+       return 0;
+}
+
+
 static void *i802_init(struct hostapd_data *hapd,
                       struct wpa_init_params *params)
 {
        struct wpa_driver_nl80211_data *drv;
+       struct i802_bss *bss;
        size_t i;
+       char brname[IFNAMSIZ];
+       int ifindex, br_ifindex;
+       int br_added = 0;
 
-       drv = wpa_driver_nl80211_init(hapd, params->ifname);
-       if (drv == NULL)
+       bss = wpa_driver_nl80211_init(hapd, params->ifname);
+       if (bss == NULL)
                return NULL;
 
-       drv->bss.ifindex = drv->ifindex;
+       drv = bss->drv;
+       if (linux_br_get(brname, params->ifname) == 0) {
+               wpa_printf(MSG_DEBUG, "nl80211: Interface %s is in bridge %s",
+                          params->ifname, brname);
+               br_ifindex = if_nametoindex(brname);
+       } else {
+               brname[0] = '\0';
+               br_ifindex = 0;
+       }
 
        drv->num_if_indices = sizeof(drv->default_if_indices) / sizeof(int);
        drv->if_indices = drv->default_if_indices;
        for (i = 0; i < params->num_bridge; i++) {
-               if (params->bridge[i])
-                       add_ifidx(drv, if_nametoindex(params->bridge[i]));
+               if (params->bridge[i]) {
+                       ifindex = if_nametoindex(params->bridge[i]);
+                       if (ifindex)
+                               add_ifidx(drv, ifindex);
+                       if (ifindex == br_ifindex)
+                               br_added = 1;
+               }
        }
+       if (!br_added && br_ifindex &&
+           (params->num_bridge == 0 || !params->bridge[0]))
+               add_ifidx(drv, br_ifindex);
 
        /* start listening for EAPOL on the default AP interface */
        add_ifidx(drv, drv->ifindex);
 
-       if (hostapd_set_iface_flags(drv, drv->ifname, 0))
+       if (linux_set_iface_flags(drv->ioctl_sock, bss->ifname, 0))
                goto failed;
 
        if (params->bssid) {
-               if (set_ifhwaddr(drv, drv->ifname, params->bssid))
+               if (linux_set_ifhwaddr(drv->ioctl_sock, bss->ifname,
+                                      params->bssid))
                        goto failed;
        }
 
-       if (wpa_driver_nl80211_set_mode(drv, IEEE80211_MODE_AP)) {
+       if (wpa_driver_nl80211_set_mode(bss, IEEE80211_MODE_AP)) {
                wpa_printf(MSG_ERROR, "nl80211: Failed to set interface %s "
-                          "into AP mode", drv->ifname);
+                          "into AP mode", bss->ifname);
                goto failed;
        }
 
-       if (hostapd_set_iface_flags(drv, drv->ifname, 1))
+       if (params->num_bridge && params->bridge[0] &&
+           i802_check_bridge(drv, params->bridge[0], params->ifname) < 0)
+               goto failed;
+
+       if (linux_set_iface_flags(drv->ioctl_sock, bss->ifname, 1))
                goto failed;
 
        drv->eapol_sock = socket(PF_PACKET, SOCK_DGRAM, htons(ETH_P_PAE));
@@ -4463,10 +4995,10 @@ static void *i802_init(struct hostapd_data *hapd,
                goto failed;
        }
 
-       if (get_ifhwaddr(drv, drv->ifname, params->own_addr))
+       if (linux_get_ifhwaddr(drv->ioctl_sock, bss->ifname, params->own_addr))
                goto failed;
 
-       return drv;
+       return bss;
 
 failed:
        nl80211_remove_monitor_interface(drv);
@@ -4496,51 +5028,67 @@ static enum nl80211_iftype wpa_driver_nl80211_if_type(
 {
        switch (type) {
        case WPA_IF_STATION:
+       case WPA_IF_P2P_CLIENT:
+       case WPA_IF_P2P_GROUP:
                return NL80211_IFTYPE_STATION;
        case WPA_IF_AP_VLAN:
                return NL80211_IFTYPE_AP_VLAN;
        case WPA_IF_AP_BSS:
+       case WPA_IF_P2P_GO:
                return NL80211_IFTYPE_AP;
        }
        return -1;
 }
 
 
-static int wpa_driver_nl80211_if_add(const char *iface, void *priv,
-                                    enum wpa_driver_if_type type,
-                                    const char *ifname, const u8 *addr)
+static int wpa_driver_nl80211_if_add(void *priv, enum wpa_driver_if_type type,
+                                    const char *ifname, const u8 *addr,
+                                    void *bss_ctx, void **drv_priv,
+                                    char *force_ifname, u8 *if_addr)
 {
-       struct wpa_driver_nl80211_data *drv = priv;
+       struct i802_bss *bss = priv;
+       struct wpa_driver_nl80211_data *drv = bss->drv;
        int ifidx;
 #ifdef HOSTAPD
-       struct i802_bss *bss = NULL;
+       struct i802_bss *new_bss = NULL;
 
        if (type == WPA_IF_AP_BSS) {
-               bss = os_zalloc(sizeof(*bss));
-               if (bss == NULL)
+               new_bss = os_zalloc(sizeof(*new_bss));
+               if (new_bss == NULL)
                        return -1;
        }
 #endif /* HOSTAPD */
 
+       if (addr)
+               os_memcpy(if_addr, addr, ETH_ALEN);
        ifidx = nl80211_create_iface(drv, ifname,
-                                    wpa_driver_nl80211_if_type(type), addr);
+                                    wpa_driver_nl80211_if_type(type), addr,
+                                    0);
        if (ifidx < 0) {
 #ifdef HOSTAPD
-               os_free(bss);
+               os_free(new_bss);
 #endif /* HOSTAPD */
                return -1;
        }
 
+       if (!addr &&
+           linux_get_ifhwaddr(drv->ioctl_sock, bss->ifname, if_addr) < 0)
+               return -1;
+
 #ifdef HOSTAPD
        if (type == WPA_IF_AP_BSS) {
-               if (hostapd_set_iface_flags(priv, ifname, 1)) {
-                       nl80211_remove_iface(priv, ifidx);
-                       os_free(bss);
+               if (linux_set_iface_flags(drv->ioctl_sock, ifname, 1)) {
+                       nl80211_remove_iface(drv, ifidx);
+                       os_free(new_bss);
                        return -1;
                }
-               bss->ifindex = ifidx;
-               bss->next = drv->bss.next;
-               drv->bss.next = bss;
+               os_strlcpy(new_bss->ifname, ifname, IFNAMSIZ);
+               new_bss->ifindex = ifidx;
+               new_bss->drv = drv;
+               new_bss->next = drv->first_bss.next;
+               drv->first_bss.next = new_bss;
+               if (drv_priv)
+                       *drv_priv = new_bss;
        }
 #endif /* HOSTAPD */
 
@@ -4552,24 +5100,30 @@ static int wpa_driver_nl80211_if_remove(void *priv,
                                        enum wpa_driver_if_type type,
                                        const char *ifname)
 {
-       struct wpa_driver_nl80211_data *drv = priv;
+       struct i802_bss *bss = priv;
+       struct wpa_driver_nl80211_data *drv = bss->drv;
        int ifindex = if_nametoindex(ifname);
 
+       wpa_printf(MSG_DEBUG, "nl80211: %s(type=%d ifname=%s) ifindex=%d",
+                  __func__, type, ifname, ifindex);
+       if (ifindex <= 0)
+               return -1;
        nl80211_remove_iface(drv, ifindex);
 
 #ifdef HOSTAPD
-       if (type == WPA_IF_AP_BSS) {
-               struct i802_bss *bss, *prev;
-               prev = &drv->bss;
-               bss = drv->bss.next;
-               while (bss) {
-                       if (ifindex == bss->ifindex) {
-                               prev->next = bss->next;
-                               os_free(bss);
-                               break;
-                       }
-                       prev = bss;
-                       bss = bss->next;
+       if (type != WPA_IF_AP_BSS)
+               return 0;
+
+       if (bss != &drv->first_bss) {
+               struct i802_bss *tbss = &drv->first_bss;
+
+               while (tbss) {
+                       if (tbss->next != bss)
+                               continue;
+
+                       tbss->next = bss->next;
+                       os_free(bss);
+                       break;
                }
        }
 #endif /* HOSTAPD */
@@ -4578,6 +5132,394 @@ static int wpa_driver_nl80211_if_remove(void *priv,
 }
 
 
+static int cookie_handler(struct nl_msg *msg, void *arg)
+{
+       struct nlattr *tb[NL80211_ATTR_MAX + 1];
+       struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
+       u64 *cookie = arg;
+       nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
+                 genlmsg_attrlen(gnlh, 0), NULL);
+       if (tb[NL80211_ATTR_COOKIE])
+               *cookie = nla_get_u64(tb[NL80211_ATTR_COOKIE]);
+       return NL_SKIP;
+}
+
+
+static int wpa_driver_nl80211_send_action(void *priv, unsigned int freq,
+                                         const u8 *dst, const u8 *src,
+                                         const u8 *bssid,
+                                         const u8 *data, size_t data_len)
+{
+       struct i802_bss *bss = priv;
+       struct wpa_driver_nl80211_data *drv = bss->drv;
+       int ret = -1;
+       struct nl_msg *msg;
+       u8 *buf;
+       struct ieee80211_hdr *hdr;
+       u64 cookie;
+
+       wpa_printf(MSG_DEBUG, "nl80211: Send Action frame (ifindex=%d)",
+                  drv->ifindex);
+
+       buf = os_zalloc(24 + data_len);
+       if (buf == NULL)
+               return ret;
+       os_memcpy(buf + 24, data, data_len);
+       hdr = (struct ieee80211_hdr *) buf;
+       hdr->frame_control =
+               IEEE80211_FC(WLAN_FC_TYPE_MGMT, WLAN_FC_STYPE_ACTION);
+       os_memcpy(hdr->addr1, dst, ETH_ALEN);
+       os_memcpy(hdr->addr2, src, ETH_ALEN);
+       os_memcpy(hdr->addr3, bssid, ETH_ALEN);
+
+       if (drv->nlmode == NL80211_IFTYPE_AP) {
+               ret = wpa_driver_nl80211_send_mlme(priv, buf, 24 + data_len);
+               os_free(buf);
+               return ret;
+       }
+
+       msg = nlmsg_alloc();
+       if (!msg) {
+               os_free(buf);
+               return -1;
+       }
+
+       genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, 0,
+                   NL80211_CMD_ACTION, 0);
+
+       NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex);
+       NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_FREQ, freq);
+       NLA_PUT(msg, NL80211_ATTR_FRAME, 24 + data_len, buf);
+       os_free(buf);
+       buf = NULL;
+
+       cookie = 0;
+       ret = send_and_recv_msgs(drv, msg, cookie_handler, &cookie);
+       msg = NULL;
+       if (ret) {
+               wpa_printf(MSG_DEBUG, "nl80211: Action command failed: ret=%d "
+                          "(%s)", ret, strerror(-ret));
+               goto nla_put_failure;
+       }
+       wpa_printf(MSG_DEBUG, "nl80211: Action TX command accepted; "
+                  "cookie 0x%llx", (long long unsigned int) cookie);
+       drv->send_action_cookie = cookie;
+       ret = 0;
+
+nla_put_failure:
+       os_free(buf);
+       nlmsg_free(msg);
+       return ret;
+}
+
+
+static int wpa_driver_nl80211_remain_on_channel(void *priv, unsigned int freq,
+                                               unsigned int duration)
+{
+       struct i802_bss *bss = priv;
+       struct wpa_driver_nl80211_data *drv = bss->drv;
+       struct nl_msg *msg;
+       int ret;
+       u64 cookie;
+
+       msg = nlmsg_alloc();
+       if (!msg)
+               return -1;
+
+       genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, 0,
+                   NL80211_CMD_REMAIN_ON_CHANNEL, 0);
+
+       NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex);
+       NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_FREQ, freq);
+       NLA_PUT_U32(msg, NL80211_ATTR_DURATION, duration);
+
+       cookie = 0;
+       ret = send_and_recv_msgs(drv, msg, cookie_handler, &cookie);
+       if (ret == 0) {
+               wpa_printf(MSG_DEBUG, "nl80211: Remain-on-channel cookie "
+                          "0x%llx for freq=%u MHz duration=%u",
+                          (long long unsigned int) cookie, freq, duration);
+               drv->remain_on_chan_cookie = cookie;
+               return 0;
+       }
+       wpa_printf(MSG_DEBUG, "nl80211: Failed to request remain-on-channel "
+                  "(freq=%d duration=%u): %d (%s)",
+                  freq, duration, ret, strerror(-ret));
+nla_put_failure:
+       return -1;
+}
+
+
+static int wpa_driver_nl80211_cancel_remain_on_channel(void *priv)
+{
+       struct i802_bss *bss = priv;
+       struct wpa_driver_nl80211_data *drv = bss->drv;
+       struct nl_msg *msg;
+       int ret;
+
+       if (!drv->pending_remain_on_chan) {
+               wpa_printf(MSG_DEBUG, "nl80211: No pending remain-on-channel "
+                          "to cancel");
+               return -1;
+       }
+
+       wpa_printf(MSG_DEBUG, "nl80211: Cancel remain-on-channel with cookie "
+                  "0x%llx",
+                  (long long unsigned int) drv->remain_on_chan_cookie);
+
+       msg = nlmsg_alloc();
+       if (!msg)
+               return -1;
+
+       genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, 0,
+                   NL80211_CMD_CANCEL_REMAIN_ON_CHANNEL, 0);
+
+       NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex);
+       NLA_PUT_U64(msg, NL80211_ATTR_COOKIE, drv->remain_on_chan_cookie);
+
+       ret = send_and_recv_msgs(drv, msg, NULL, 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:
+       return -1;
+}
+
+
+static void wpa_driver_nl80211_probe_req_report_timeout(void *eloop_ctx,
+                                                       void *timeout_ctx)
+{
+       struct wpa_driver_nl80211_data *drv = eloop_ctx;
+       if (drv->monitor_ifidx < 0)
+               return; /* monitor interface already removed */
+
+       if (drv->nlmode != NL80211_IFTYPE_STATION)
+               return; /* not in station mode anymore */
+
+       if (drv->probe_req_report)
+               return; /* reporting enabled */
+
+       wpa_printf(MSG_DEBUG, "nl80211: Remove monitor interface due to no "
+                  "Probe Request reporting needed anymore");
+       nl80211_remove_monitor_interface(drv);
+}
+
+
+static int wpa_driver_nl80211_probe_req_report(void *priv, int report)
+{
+       struct i802_bss *bss = priv;
+       struct wpa_driver_nl80211_data *drv = bss->drv;
+
+       if (drv->nlmode != NL80211_IFTYPE_STATION) {
+               wpa_printf(MSG_DEBUG, "nl80211: probe_req_report control only "
+                          "allowed in station mode (iftype=%d)",
+                          drv->nlmode);
+               return -1;
+       }
+       drv->probe_req_report = report;
+
+       if (report) {
+               eloop_cancel_timeout(
+                       wpa_driver_nl80211_probe_req_report_timeout,
+                       drv, NULL);
+               if (drv->monitor_ifidx < 0 &&
+                   nl80211_create_monitor_interface(drv))
+                       return -1;
+       } else {
+               /*
+                * It takes a while to remove the monitor interface, so try to
+                * avoid doing this if it is needed again shortly. Instead,
+                * schedule the interface to be removed later if no need for it
+                * is seen.
+                */
+               wpa_printf(MSG_DEBUG, "nl80211: Scheduling monitor interface "
+                          "to be removed after 10 seconds of no use");
+               eloop_register_timeout(
+                       10, 0, wpa_driver_nl80211_probe_req_report_timeout,
+                       drv, NULL);
+       }
+
+       return 0;
+}
+
+
+static int nl80211_disable_11b_rates(struct wpa_driver_nl80211_data *drv,
+                                    int ifindex, int disabled)
+{
+       struct nl_msg *msg;
+       struct nlattr *bands, *band;
+       int ret;
+
+       msg = nlmsg_alloc();
+       if (!msg)
+               return -1;
+
+       genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, 0,
+                   NL80211_CMD_SET_TX_BITRATE_MASK, 0);
+       NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, ifindex);
+
+       bands = nla_nest_start(msg, NL80211_ATTR_TX_RATES);
+       if (!bands)
+               goto nla_put_failure;
+
+       /*
+        * Disable 2 GHz rates 1, 2, 5.5, 11 Mbps by masking out everything
+        * else apart from 6, 9, 12, 18, 24, 36, 48, 54 Mbps from non-MCS
+        * rates. All 5 GHz rates are left enabled.
+        */
+       band = nla_nest_start(msg, NL80211_BAND_2GHZ);
+       if (!band)
+               goto nla_put_failure;
+       NLA_PUT(msg, NL80211_TXRATE_LEGACY, 8,
+               "\x0c\x12\x18\x24\x30\x48\x60\x6c");
+       nla_nest_end(msg, band);
+
+       nla_nest_end(msg, bands);
+
+       ret = send_and_recv_msgs(drv, msg, NULL, NULL);
+       msg = NULL;
+       if (ret) {
+               wpa_printf(MSG_DEBUG, "nl80211: Set TX rates failed: ret=%d "
+                          "(%s)", ret, strerror(-ret));
+       }
+
+       return ret;
+
+nla_put_failure:
+       nlmsg_free(msg);
+       return -1;
+}
+
+
+static int wpa_driver_nl80211_disable_11b_rates(void *priv, int disabled)
+{
+       struct i802_bss *bss = priv;
+       struct wpa_driver_nl80211_data *drv = bss->drv;
+       drv->disable_11b_rates = disabled;
+       return nl80211_disable_11b_rates(drv, drv->ifindex, disabled);
+}
+
+
+static int wpa_driver_nl80211_deinit_ap(void *priv)
+{
+       struct i802_bss *bss = priv;
+       struct wpa_driver_nl80211_data *drv = bss->drv;
+       if (drv->nlmode != NL80211_IFTYPE_AP)
+               return -1;
+       wpa_driver_nl80211_del_beacon(drv);
+       return wpa_driver_nl80211_set_mode(priv, IEEE80211_MODE_INFRA);
+}
+
+
+static void wpa_driver_nl80211_resume(void *priv)
+{
+       struct i802_bss *bss = priv;
+       struct wpa_driver_nl80211_data *drv = bss->drv;
+       if (linux_set_iface_flags(drv->ioctl_sock, bss->ifname, 1)) {
+               wpa_printf(MSG_DEBUG, "nl80211: Failed to set interface up on "
+                          "resume event");
+       }
+}
+
+
+static int nl80211_send_ft_action(void *priv, u8 action, const u8 *target_ap,
+                                 const u8 *ies, size_t ies_len)
+{
+       struct i802_bss *bss = priv;
+       struct wpa_driver_nl80211_data *drv = bss->drv;
+       int ret;
+       u8 *data, *pos;
+       size_t data_len;
+       u8 own_addr[ETH_ALEN];
+
+       if (linux_get_ifhwaddr(drv->ioctl_sock, bss->ifname, own_addr) < 0)
+               return -1;
+
+       if (action != 1) {
+               wpa_printf(MSG_ERROR, "nl80211: Unsupported send_ft_action "
+                          "action %d", action);
+               return -1;
+       }
+
+       /*
+        * Action frame payload:
+        * Category[1] = 6 (Fast BSS Transition)
+        * Action[1] = 1 (Fast BSS Transition Request)
+        * STA Address
+        * Target AP Address
+        * FT IEs
+        */
+
+       data_len = 2 + 2 * ETH_ALEN + ies_len;
+       data = os_malloc(data_len);
+       if (data == NULL)
+               return -1;
+       pos = data;
+       *pos++ = 0x06; /* FT Action category */
+       *pos++ = action;
+       os_memcpy(pos, own_addr, ETH_ALEN);
+       pos += ETH_ALEN;
+       os_memcpy(pos, target_ap, ETH_ALEN);
+       pos += ETH_ALEN;
+       os_memcpy(pos, ies, ies_len);
+
+       ret = wpa_driver_nl80211_send_action(bss, drv->assoc_freq, drv->bssid,
+                                            own_addr, drv->bssid,
+                                            data, data_len);
+       os_free(data);
+
+       return ret;
+}
+
+
+static int nl80211_signal_monitor(void *priv, int threshold, int hysteresis)
+{
+       struct i802_bss *bss = priv;
+       struct wpa_driver_nl80211_data *drv = bss->drv;
+       struct nl_msg *msg, *cqm = NULL;
+
+       wpa_printf(MSG_DEBUG, "nl80211: Signal monitor threshold=%d "
+                  "hysteresis=%d", threshold, hysteresis);
+
+       msg = nlmsg_alloc();
+       if (!msg)
+               return -1;
+
+       genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0,
+                   0, NL80211_CMD_SET_CQM, 0);
+
+       NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, bss->ifindex);
+
+       cqm = nlmsg_alloc();
+       if (cqm == NULL)
+               return -1;
+
+       NLA_PUT_U32(cqm, NL80211_ATTR_CQM_RSSI_THOLD, threshold);
+       NLA_PUT_U32(cqm, NL80211_ATTR_CQM_RSSI_HYST, hysteresis);
+       nla_put_nested(msg, NL80211_ATTR_CQM, cqm);
+
+       if (send_and_recv_msgs(drv, msg, NULL, NULL) == 0)
+               return 0;
+       msg = NULL;
+
+nla_put_failure:
+       if (cqm)
+               nlmsg_free(cqm);
+       nlmsg_free(msg);
+       return -1;
+}
+
+
+static int nl80211_send_frame(void *priv, const u8 *data, size_t data_len,
+                             int encrypt)
+{
+       struct i802_bss *bss = priv;
+       struct wpa_driver_nl80211_data *drv = bss->drv;
+       return wpa_driver_nl80211_send_frame(drv, data, data_len, encrypt);
+}
+
+
 const struct wpa_driver_ops wpa_driver_nl80211_ops = {
        .name = "nl80211",
        .desc = "Linux nl80211/cfg80211",
@@ -4599,14 +5541,12 @@ const struct wpa_driver_ops wpa_driver_nl80211_ops = {
        .set_beacon = wpa_driver_nl80211_set_beacon,
        .if_add = wpa_driver_nl80211_if_add,
        .if_remove = wpa_driver_nl80211_if_remove,
-#if defined(CONFIG_AP) || defined(HOSTAPD)
        .send_mlme = wpa_driver_nl80211_send_mlme,
        .get_hw_feature_data = wpa_driver_nl80211_get_hw_feature_data,
        .sta_add = wpa_driver_nl80211_sta_add,
        .sta_remove = wpa_driver_nl80211_sta_remove,
        .hapd_send_eapol = wpa_driver_nl80211_hapd_send_eapol,
        .sta_set_flags = wpa_driver_nl80211_sta_set_flags,
-#endif /* CONFIG_AP || HOSTAPD */
 #ifdef HOSTAPD
        .hapd_init = i802_init,
        .hapd_deinit = i802_deinit,
@@ -4617,7 +5557,6 @@ const struct wpa_driver_ops wpa_driver_nl80211_ops = {
        .sta_disassoc = i802_sta_disassoc,
        .get_inact_sec = i802_get_inact_sec,
        .sta_clear_stats = i802_sta_clear_stats,
-       .set_freq = i802_set_freq,
        .set_rts = i802_set_rts,
        .set_frag = i802_set_frag,
        .set_rate_sets = i802_set_rate_sets,
@@ -4626,5 +5565,18 @@ const struct wpa_driver_ops wpa_driver_nl80211_ops = {
        .set_short_slot_time = i802_set_short_slot_time,
        .set_tx_queue_params = i802_set_tx_queue_params,
        .set_sta_vlan = i802_set_sta_vlan,
+       .set_wds_sta = i802_set_wds_sta,
 #endif /* HOSTAPD */
+       .set_freq = i802_set_freq,
+       .send_action = wpa_driver_nl80211_send_action,
+       .remain_on_channel = wpa_driver_nl80211_remain_on_channel,
+       .cancel_remain_on_channel =
+       wpa_driver_nl80211_cancel_remain_on_channel,
+       .probe_req_report = wpa_driver_nl80211_probe_req_report,
+       .disable_11b_rates = wpa_driver_nl80211_disable_11b_rates,
+       .deinit_ap = wpa_driver_nl80211_deinit_ap,
+       .resume = wpa_driver_nl80211_resume,
+       .send_ft_action = nl80211_send_ft_action,
+       .signal_monitor = nl80211_signal_monitor,
+       .send_frame = nl80211_send_frame,
 };