wext: Fix scan result signal levels when driver reports in dBm
[libeap.git] / src / drivers / driver_wext.c
index a7f4ead..f0de6aa 100644 (file)
@@ -1,6 +1,6 @@
 /*
- * WPA Supplicant - driver interaction with generic Linux Wireless Extensions
- * Copyright (c) 2003-2007, Jouni Malinen <j@w1.fi>
+ * Driver interaction with generic Linux Wireless Extensions
+ * Copyright (c) 2003-2010, Jouni Malinen <j@w1.fi>
  *
  * 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 <sys/stat.h>
 #include <net/if_arp.h>
 
 #include "wireless_copy.h"
 #include "common.h"
-#include "driver.h"
 #include "eloop.h"
+#include "common/ieee802_11_defs.h"
+#include "common/wpa_common.h"
 #include "priv_netlink.h"
+#include "netlink.h"
+#include "linux_ioctl.h"
+#include "rfkill.h"
+#include "driver.h"
 #include "driver_wext.h"
-#include "ieee802_11_defs.h"
-#include "wpa_common.h"
-
-#ifdef CONFIG_CLIENT_MLME
-#include <netpacket/packet.h>
-/* old definitions from net/mac80211 */
-
-typedef u32 __bitwise __be32;
-typedef u64 __bitwise __be64;
-
-#define PRISM2_IOCTL_PRISM2_PARAM (SIOCIWFIRSTPRIV + 0)
-#define PRISM2_IOCTL_GET_PRISM2_PARAM (SIOCIWFIRSTPRIV + 1)
-#define PRISM2_IOCTL_HOSTAPD (SIOCIWFIRSTPRIV + 3)
-
-#define PRISM2_PARAM_USER_SPACE_MLME 1045
-#define PRISM2_PARAM_MGMT_IF           1046
-#define PRISM2_HOSTAPD_ADD_STA 2
-#define PRISM2_HOSTAPD_REMOVE_STA 3
-#define PRISM2_HOSTAPD_GET_HW_FEATURES 1002
-#define PRISM2_HOSTAPD_MAX_BUF_SIZE    2048
-
-#ifndef ALIGNED
-#define ALIGNED __attribute__ ((aligned))
-#endif
-
-struct prism2_hostapd_param {
-       u32 cmd;
-       u8 sta_addr[ETH_ALEN];
-       u8 pad[2];
-       union {
-               struct {
-                       u16 aid;
-                       u16 capability;
-                       u8 supp_rates[32];
-                       u8 wds_flags;
-#define IEEE80211_STA_DYNAMIC_ENC BIT(0)
-                       u8 enc_flags;
-                       u16 listen_interval;
-               } add_sta;
-               struct {
-                       u16 num_modes;
-                       u16 flags;
-                       u8 data[0] ALIGNED; /* num_modes * feature data */
-               } hw_features;
-               struct {
-                       u16 mode; /* MODE_* */
-                       u16 num_supported_rates;
-                       u16 num_basic_rates;
-                       u8 data[0] ALIGNED; /* num_supported_rates * u16 +
-                                            * num_basic_rates * u16 */
-               } set_rate_sets;
-               struct {
-                       u16 mode; /* MODE_* */
-                       u16 chan;
-                       u32 flag;
-                       u8 power_level; /* regulatory limit in dBm */
-                       u8 antenna_max;
-               } set_channel_flag;
-               struct {
-                       u32 rd;
-               } set_regulatory_domain;
-               struct {
-                       u32 queue;
-                       s32 aifs;
-                       u32 cw_min;
-                       u32 cw_max;
-                       u32 burst_time; /* maximum burst time in 0.1 ms, i.e.,
-                                        * 10 = 1 ms */
-               } tx_queue_params;
-       } u;
-};
-
-struct hostapd_ioctl_hw_modes_hdr {
-       int mode;
-       int num_channels;
-       int num_rates;
-};
-
-/*
- * frame format for the management interface that is slated
- * to be replaced by "cooked monitor" with radiotap
- */
-#define IEEE80211_FI_VERSION 0x80211001
-struct ieee80211_frame_info {
-       __be32 version;
-       __be32 length;
-       __be64 mactime;
-       __be64 hosttime;
-       __be32 phytype;
-       __be32 channel;
-       __be32 datarate;
-       __be32 antenna;
-       __be32 priority;
-       __be32 ssi_type;
-       __be32 ssi_signal;
-       __be32 ssi_noise;
-       __be32 preamble;
-       __be32 encoding;
-
-       /* Note: this structure is otherwise identical to capture format used
-        * in linux-wlan-ng, but this additional field is used to provide meta
-        * data about the frame to hostapd. This was the easiest method for
-        * providing this information, but this might change in the future. */
-       __be32 msg_type;
-} __attribute__ ((packed));
-
-/* old mode definitions */
-enum {
-       MODE_IEEE80211A = 0 /* IEEE 802.11a */,
-       MODE_IEEE80211B = 1 /* IEEE 802.11b only */,
-       MODE_ATHEROS_TURBO = 2 /* Atheros Turbo mode (2x.11a at 5 GHz) */,
-       MODE_IEEE80211G = 3 /* IEEE 802.11g (and 802.11b compatibility) */,
-       MODE_ATHEROS_TURBOG = 4 /* Atheros Turbo mode (2x.11g at 2.4 GHz) */,
-       NUM_IEEE80211_MODES = 5
-};
-
-#ifndef ETH_P_ALL
-#define ETH_P_ALL 0x0003
-#endif
-#endif /* CONFIG_CLIENT_MLME */
-
-
 
 
 static int wpa_driver_wext_flush_pmkid(void *priv);
 static int wpa_driver_wext_get_range(void *priv);
-
-static int wpa_driver_wext_send_oper_ifla(struct wpa_driver_wext_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 = (struct rtattr *)
-                       ((char *) &req + NLMSG_ALIGN(req.hdr.nlmsg_len));
-               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 = (struct rtattr *)
-                       ((char *) &req + NLMSG_ALIGN(req.hdr.nlmsg_len));
-               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, "WEXT: Operstate: linkmode=%d, operstate=%d",
-                  linkmode, operstate);
-
-       ret = send(drv->event_sock, &req, req.hdr.nlmsg_len, 0);
-       if (ret < 0) {
-               wpa_printf(MSG_DEBUG, "WEXT: Sending operstate IFLA failed: "
-                          "%s (assume operstate is not supported)",
-                          strerror(errno));
-       }
-
-       return ret < 0 ? -1 : 0;
-}
+static int wpa_driver_wext_finish_drv_init(struct wpa_driver_wext_data *drv);
+static void wpa_driver_wext_disconnect(struct wpa_driver_wext_data *drv);
+static int wpa_driver_wext_set_auth_alg(void *priv, int auth_alg);
 
 
 int wpa_driver_wext_set_auth_param(struct wpa_driver_wext_data *drv,
@@ -420,6 +249,7 @@ wpa_driver_wext_event_wireless_custom(void *ctx, char *custom)
        } else if (os_strncmp(custom, "ASSOCINFO(ReqIEs=", 17) == 0) {
                char *spos;
                int bytes;
+               u8 *req_ies = NULL, *resp_ies = NULL;
 
                spos = custom + 17;
 
@@ -428,12 +258,12 @@ wpa_driver_wext_event_wireless_custom(void *ctx, char *custom)
                        return;
                bytes /= 2;
 
-               data.assoc_info.req_ies = os_malloc(bytes);
-               if (data.assoc_info.req_ies == NULL)
-                       return;
-
+               req_ies = os_malloc(bytes);
+               if (req_ies == NULL ||
+                   hexstr2bin(spos, req_ies, bytes) < 0)
+                       goto done;
+               data.assoc_info.req_ies = req_ies;
                data.assoc_info.req_ies_len = bytes;
-               hexstr2bin(spos, data.assoc_info.req_ies, bytes);
 
                spos += bytes * 2;
 
@@ -448,19 +278,19 @@ wpa_driver_wext_event_wireless_custom(void *ctx, char *custom)
                                goto done;
                        bytes /= 2;
 
-                       data.assoc_info.resp_ies = os_malloc(bytes);
-                       if (data.assoc_info.resp_ies == NULL)
+                       resp_ies = os_malloc(bytes);
+                       if (resp_ies == NULL ||
+                           hexstr2bin(spos, resp_ies, bytes) < 0)
                                goto done;
-
+                       data.assoc_info.resp_ies = resp_ies;
                        data.assoc_info.resp_ies_len = bytes;
-                       hexstr2bin(spos, data.assoc_info.resp_ies, bytes);
                }
 
                wpa_supplicant_event(ctx, EVENT_ASSOCINFO, &data);
 
        done:
-               os_free(data.assoc_info.resp_ies);
-               os_free(data.assoc_info.req_ies);
+               os_free(resp_ies);
+               os_free(req_ies);
 #ifdef CONFIG_PEERKEY
        } else if (os_strncmp(custom, "STKSTART.request=", 17) == 0) {
                if (hwaddr_aton(custom + 17, data.stkstart.peer)) {
@@ -576,24 +406,24 @@ static void wpa_driver_wext_event_assoc_ies(struct wpa_driver_wext_data *drv)
        os_memset(&data, 0, sizeof(data));
        if (drv->assoc_req_ies) {
                data.assoc_info.req_ies = drv->assoc_req_ies;
-               drv->assoc_req_ies = NULL;
                data.assoc_info.req_ies_len = drv->assoc_req_ies_len;
        }
        if (drv->assoc_resp_ies) {
                data.assoc_info.resp_ies = drv->assoc_resp_ies;
-               drv->assoc_resp_ies = NULL;
                data.assoc_info.resp_ies_len = drv->assoc_resp_ies_len;
        }
 
        wpa_supplicant_event(drv->ctx, EVENT_ASSOCINFO, &data);
 
-       os_free(data.assoc_info.req_ies);
-       os_free(data.assoc_info.resp_ies);
+       os_free(drv->assoc_req_ies);
+       drv->assoc_req_ies = NULL;
+       os_free(drv->assoc_resp_ies);
+       drv->assoc_resp_ies = NULL;
 }
 
 
 static void wpa_driver_wext_event_wireless(struct wpa_driver_wext_data *drv,
-                                          void *ctx, char *data, int len)
+                                          char *data, int len)
 {
        struct iw_event iwe_buf, *iwe = &iwe_buf;
        char *pos, *end, *custom, *buf;
@@ -641,44 +471,69 @@ static void wpa_driver_wext_event_wireless(struct wpa_driver_wext_data *drv,
                                drv->assoc_req_ies = NULL;
                                os_free(drv->assoc_resp_ies);
                                drv->assoc_resp_ies = NULL;
-                               wpa_supplicant_event(ctx, EVENT_DISASSOC,
+                               wpa_supplicant_event(drv->ctx, EVENT_DISASSOC,
                                                     NULL);
                        
                        } else {
                                wpa_driver_wext_event_assoc_ies(drv);
-                               wpa_supplicant_event(ctx, EVENT_ASSOC, NULL);
+                               wpa_supplicant_event(drv->ctx, EVENT_ASSOC,
+                                                    NULL);
                        }
                        break;
                case IWEVMICHAELMICFAILURE:
+                       if (custom + iwe->u.data.length > end) {
+                               wpa_printf(MSG_DEBUG, "WEXT: Invalid "
+                                          "IWEVMICHAELMICFAILURE length");
+                               return;
+                       }
                        wpa_driver_wext_event_wireless_michaelmicfailure(
-                               ctx, custom, iwe->u.data.length);
+                               drv->ctx, custom, iwe->u.data.length);
                        break;
                case IWEVCUSTOM:
-                       if (custom + iwe->u.data.length > end)
+                       if (custom + iwe->u.data.length > end) {
+                               wpa_printf(MSG_DEBUG, "WEXT: Invalid "
+                                          "IWEVCUSTOM length");
                                return;
+                       }
                        buf = os_malloc(iwe->u.data.length + 1);
                        if (buf == NULL)
                                return;
                        os_memcpy(buf, custom, iwe->u.data.length);
                        buf[iwe->u.data.length] = '\0';
-                       wpa_driver_wext_event_wireless_custom(ctx, buf);
+                       wpa_driver_wext_event_wireless_custom(drv->ctx, buf);
                        os_free(buf);
                        break;
                case SIOCGIWSCAN:
                        drv->scan_complete_events = 1;
                        eloop_cancel_timeout(wpa_driver_wext_scan_timeout,
-                                            drv, ctx);
-                       wpa_supplicant_event(ctx, EVENT_SCAN_RESULTS, NULL);
+                                            drv, drv->ctx);
+                       wpa_supplicant_event(drv->ctx, EVENT_SCAN_RESULTS,
+                                            NULL);
                        break;
                case IWEVASSOCREQIE:
+                       if (custom + iwe->u.data.length > end) {
+                               wpa_printf(MSG_DEBUG, "WEXT: Invalid "
+                                          "IWEVASSOCREQIE length");
+                               return;
+                       }
                        wpa_driver_wext_event_wireless_assocreqie(
                                drv, custom, iwe->u.data.length);
                        break;
                case IWEVASSOCRESPIE:
+                       if (custom + iwe->u.data.length > end) {
+                               wpa_printf(MSG_DEBUG, "WEXT: Invalid "
+                                          "IWEVASSOCRESPIE length");
+                               return;
+                       }
                        wpa_driver_wext_event_wireless_assocrespie(
                                drv, custom, iwe->u.data.length);
                        break;
                case IWEVPMKIDCAND:
+                       if (custom + iwe->u.data.length > end) {
+                               wpa_printf(MSG_DEBUG, "WEXT: Invalid "
+                                          "IWEVPMKIDCAND length");
+                               return;
+                       }
                        wpa_driver_wext_event_wireless_pmkidcand(
                                drv, custom, iwe->u.data.length);
                        break;
@@ -689,8 +544,8 @@ static void wpa_driver_wext_event_wireless(struct wpa_driver_wext_data *drv,
 }
 
 
-static void wpa_driver_wext_event_link(void *ctx, char *buf, size_t len,
-                                      int del)
+static void wpa_driver_wext_event_link(struct wpa_driver_wext_data *drv,
+                                      char *buf, size_t len, int del)
 {
        union wpa_event_data event;
 
@@ -706,25 +561,68 @@ static void wpa_driver_wext_event_link(void *ctx, char *buf, size_t len,
                   event.interface_status.ifname,
                   del ? "removed" : "added");
 
-       wpa_supplicant_event(ctx, EVENT_INTERFACE_STATUS, &event);
+       if (os_strcmp(drv->ifname, event.interface_status.ifname) == 0) {
+               if (del)
+                       drv->if_removed = 1;
+               else
+                       drv->if_removed = 0;
+       }
+
+       wpa_supplicant_event(drv->ctx, EVENT_INTERFACE_STATUS, &event);
 }
 
 
-static void wpa_driver_wext_event_rtm_newlink(struct wpa_driver_wext_data *drv,
-                                             void *ctx, struct nlmsghdr *h,
-                                             size_t len)
+static int wpa_driver_wext_own_ifname(struct wpa_driver_wext_data *drv,
+                                     u8 *buf, size_t len)
 {
-       struct ifinfomsg *ifi;
-       int attrlen, nlmsg_len, rta_len;
-       struct rtattr * attr;
+       int attrlen, rta_len;
+       struct rtattr *attr;
 
-       if (len < sizeof(*ifi))
-               return;
+       attrlen = len;
+       attr = (struct rtattr *) buf;
 
-       ifi = NLMSG_DATA(h);
+       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)
+                           == 0)
+                               return 1;
+                       else
+                               break;
+               }
+               attr = RTA_NEXT(attr, attrlen);
+       }
 
-       if (drv->ifindex != ifi->ifi_index && drv->ifindex2 != ifi->ifi_index)
-       {
+       return 0;
+}
+
+
+static int wpa_driver_wext_own_ifindex(struct wpa_driver_wext_data *drv,
+                                      int ifindex, u8 *buf, size_t len)
+{
+       if (drv->ifindex == ifindex || drv->ifindex2 == ifindex)
+               return 1;
+
+       if (drv->if_removed && wpa_driver_wext_own_ifname(drv, buf, len)) {
+               drv->ifindex = if_nametoindex(drv->ifname);
+               wpa_printf(MSG_DEBUG, "WEXT: Update ifindex for a removed "
+                          "interface");
+               wpa_driver_wext_finish_drv_init(drv);
+               return 1;
+       }
+
+       return 0;
+}
+
+
+static void wpa_driver_wext_event_rtm_newlink(void *ctx, struct ifinfomsg *ifi,
+                                             u8 *buf, size_t len)
+{
+       struct wpa_driver_wext_data *drv = ctx;
+       int attrlen, rta_len;
+       struct rtattr *attr;
+
+       if (!wpa_driver_wext_own_ifindex(drv, ifi->ifi_index, buf, len)) {
                wpa_printf(MSG_DEBUG, "Ignore event for foreign ifindex %d",
                           ifi->ifi_index);
                return;
@@ -737,6 +635,19 @@ static void wpa_driver_wext_event_rtm_newlink(struct wpa_driver_wext_data *drv,
                   (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, "WEXT: 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, "WEXT: 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_wext_set_operstate()
@@ -746,24 +657,20 @@ static void wpa_driver_wext_event_rtm_newlink(struct wpa_driver_wext_data *drv,
        if (drv->operstate == 1 &&
            (ifi->ifi_flags & (IFF_LOWER_UP | IFF_DORMANT)) == IFF_LOWER_UP &&
            !(ifi->ifi_flags & IFF_RUNNING))
-               wpa_driver_wext_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;
+               netlink_send_oper_ifla(drv->netlink, drv->ifindex,
+                                      -1, IF_OPER_UP);
 
-       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_WIRELESS) {
                        wpa_driver_wext_event_wireless(
-                               drv, ctx, ((char *) attr) + rta_len,
+                               drv, ((char *) attr) + rta_len,
                                attr->rta_len - rta_len);
                } else if (attr->rta_type == IFLA_IFNAME) {
-                       wpa_driver_wext_event_link(ctx,
+                       wpa_driver_wext_event_link(drv,
                                                   ((char *) attr) + rta_len,
                                                   attr->rta_len - rta_len, 0);
                }
@@ -772,31 +679,20 @@ static void wpa_driver_wext_event_rtm_newlink(struct wpa_driver_wext_data *drv,
 }
 
 
-static void wpa_driver_wext_event_rtm_dellink(struct wpa_driver_wext_data *drv,
-                                             void *ctx, struct nlmsghdr *h,
-                                             size_t len)
+static void wpa_driver_wext_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_wext_data *drv = ctx;
+       int attrlen, rta_len;
+       struct rtattr *attr;
 
-       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_wext_event_link(ctx,
+                       wpa_driver_wext_event_link(drv,
                                                   ((char *) attr) + rta_len,
                                                   attr->rta_len - rta_len, 1);
                }
@@ -805,166 +701,26 @@ static void wpa_driver_wext_event_rtm_dellink(struct wpa_driver_wext_data *drv,
 }
 
 
-static void wpa_driver_wext_event_receive(int sock, void *eloop_ctx,
-                                         void *sock_ctx)
+static void wpa_driver_wext_rfkill_blocked(void *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_wext_event_rtm_newlink(eloop_ctx, sock_ctx,
-                                                         h, plen);
-                       break;
-               case RTM_DELLINK:
-                       wpa_driver_wext_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;
-       }
-}
-
-
-static int wpa_driver_wext_get_ifflags_ifname(struct wpa_driver_wext_data *drv,
-                                             const char *ifname, int *flags)
-{
-       struct ifreq ifr;
-
-       os_memset(&ifr, 0, sizeof(ifr));
-       os_strlcpy(ifr.ifr_name, ifname, IFNAMSIZ);
-       if (ioctl(drv->ioctl_sock, SIOCGIFFLAGS, (caddr_t) &ifr) < 0) {
-               perror("ioctl[SIOCGIFFLAGS]");
-               return -1;
-       }
-       *flags = ifr.ifr_flags & 0xffff;
-       return 0;
-}
-
-
-/**
- * wpa_driver_wext_get_ifflags - Get interface flags (SIOCGIFFLAGS)
- * @drv: driver_wext private data
- * @flags: Pointer to returned flags value
- * Returns: 0 on success, -1 on failure
- */
-int wpa_driver_wext_get_ifflags(struct wpa_driver_wext_data *drv, int *flags)
-{
-       return wpa_driver_wext_get_ifflags_ifname(drv, drv->ifname, flags);
-}
-
-
-static int wpa_driver_wext_set_ifflags_ifname(struct wpa_driver_wext_data *drv,
-                                             const char *ifname, int flags)
-{
-       struct ifreq ifr;
-
-       os_memset(&ifr, 0, sizeof(ifr));
-       os_strlcpy(ifr.ifr_name, ifname, IFNAMSIZ);
-       ifr.ifr_flags = flags & 0xffff;
-       if (ioctl(drv->ioctl_sock, SIOCSIFFLAGS, (caddr_t) &ifr) < 0) {
-               perror("SIOCSIFFLAGS");
-               return -1;
-       }
-       return 0;
-}
-
-
-#ifdef CONFIG_CLIENT_MLME
-
-static int wpa_driver_prism2_param_set(struct wpa_driver_wext_data *drv,
-                                      int param, int value)
-{
-       struct iwreq iwr;
-       int *i;
-
-       os_memset(&iwr, 0, sizeof(iwr));
-       os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ);
-       i = (int *) iwr.u.name;
-       *i++ = param;
-       *i++ = value;
-
-       return ioctl(drv->ioctl_sock, PRISM2_IOCTL_PRISM2_PARAM, &iwr);
+       wpa_printf(MSG_DEBUG, "WEXT: RFKILL blocked");
+       /*
+        * This may be for any interface; use ifdown event to disable
+        * interface.
+        */
 }
 
 
-static int wpa_driver_prism2_param_get(struct wpa_driver_wext_data *drv,
-                                      int param)
+static void wpa_driver_wext_rfkill_unblocked(void *ctx)
 {
-       struct iwreq iwr;
-       int *i;
-
-       os_memset(&iwr, 0, sizeof(iwr));
-       os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ);
-       i = (int *) iwr.u.name;
-       *i = param;
-
-       if (ioctl(drv->ioctl_sock, PRISM2_IOCTL_GET_PRISM2_PARAM, &iwr) < 0) {
-               perror("ioctl[PRISM2_IOCTL_GET_PRISM2_PARAM]");
-               return -1;
+       struct wpa_driver_wext_data *drv = ctx;
+       wpa_printf(MSG_DEBUG, "WEXT: RFKILL unblocked");
+       if (linux_set_iface_flags(drv->ioctl_sock, drv->ifname, 1)) {
+               wpa_printf(MSG_DEBUG, "WEXT: Could not set interface UP "
+                          "after rfkill unblock");
+               return;
        }
-
-       return *i;
-}
-
-#endif /* CONFIG_CLIENT_MLME */
-
-
-/**
- * wpa_driver_wext_set_ifflags - Set interface flags (SIOCSIFFLAGS)
- * @drv: driver_wext private data
- * @flags: New value for flags
- * Returns: 0 on success, -1 on failure
- */
-int wpa_driver_wext_set_ifflags(struct wpa_driver_wext_data *drv, int flags)
-{
-       return wpa_driver_wext_set_ifflags_ifname(drv, drv->ifname, flags);
+       /* rtnetlink ifup handler will report interface as enabled */
 }
 
 
@@ -977,9 +733,11 @@ int wpa_driver_wext_set_ifflags(struct wpa_driver_wext_data *drv, int flags)
  */
 void * wpa_driver_wext_init(void *ctx, const char *ifname)
 {
-       int s, flags;
-       struct sockaddr_nl local;
        struct wpa_driver_wext_data *drv;
+       struct netlink_config *cfg;
+       struct rfkill_config *rcfg;
+       char path[128];
+       struct stat buf;
 
        drv = os_zalloc(sizeof(*drv));
        if (drv == NULL)
@@ -987,54 +745,84 @@ void * wpa_driver_wext_init(void *ctx, const char *ifname)
        drv->ctx = ctx;
        os_strlcpy(drv->ifname, ifname, sizeof(drv->ifname));
 
+       os_snprintf(path, sizeof(path), "/sys/class/net/%s/phy80211", ifname);
+       if (stat(path, &buf) == 0) {
+               wpa_printf(MSG_DEBUG, "WEXT: cfg80211-based driver detected");
+               drv->cfg80211 = 1;
+       }
+
        drv->ioctl_sock = socket(PF_INET, SOCK_DGRAM, 0);
        if (drv->ioctl_sock < 0) {
                perror("socket(PF_INET,SOCK_DGRAM)");
-               os_free(drv);
-               return NULL;
+               goto err1;
+       }
+
+       cfg = os_zalloc(sizeof(*cfg));
+       if (cfg == NULL)
+               goto err1;
+       cfg->ctx = drv;
+       cfg->newlink_cb = wpa_driver_wext_event_rtm_newlink;
+       cfg->dellink_cb = wpa_driver_wext_event_rtm_dellink;
+       drv->netlink = netlink_init(cfg);
+       if (drv->netlink == NULL) {
+               os_free(cfg);
+               goto err2;
+       }
+
+       rcfg = os_zalloc(sizeof(*rcfg));
+       if (rcfg == NULL)
+               goto err3;
+       rcfg->ctx = drv;
+       os_strlcpy(rcfg->ifname, ifname, sizeof(rcfg->ifname));
+       rcfg->blocked_cb = wpa_driver_wext_rfkill_blocked;
+       rcfg->unblocked_cb = wpa_driver_wext_rfkill_unblocked;
+       drv->rfkill = rfkill_init(rcfg);
+       if (drv->rfkill == NULL) {
+               wpa_printf(MSG_DEBUG, "WEXT: RFKILL status not available");
+               os_free(rcfg);
        }
 
-       s = socket(PF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
-       if (s < 0) {
-               perror("socket(PF_NETLINK,SOCK_RAW,NETLINK_ROUTE)");
-               close(drv->ioctl_sock);
-               os_free(drv);
-               return NULL;
-       }
+       drv->mlme_sock = -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);
-               close(drv->ioctl_sock);
-               os_free(drv);
-               return NULL;
-       }
+       if (wpa_driver_wext_finish_drv_init(drv) < 0)
+               goto err3;
 
-       eloop_register_read_sock(s, wpa_driver_wext_event_receive, drv, ctx);
-       drv->event_sock = s;
+       wpa_driver_wext_set_auth_param(drv, IW_AUTH_WPA_ENABLED, 1);
 
-       drv->mlme_sock = -1;
+       return drv;
+
+err3:
+       rfkill_deinit(drv->rfkill);
+       netlink_deinit(drv->netlink);
+err2:
+       close(drv->ioctl_sock);
+err1:
+       os_free(drv);
+       return NULL;
+}
+
+
+static void wpa_driver_wext_send_rfkill(void *eloop_ctx, void *timeout_ctx)
+{
+       wpa_supplicant_event(timeout_ctx, EVENT_INTERFACE_DISABLED, NULL);
+}
 
-       if (wpa_driver_wext_get_ifflags(drv, &flags) != 0)
-               printf("Could not get interface '%s' flags\n", drv->ifname);
-       else if (!(flags & IFF_UP)) {
-               if (wpa_driver_wext_set_ifflags(drv, flags | IFF_UP) != 0) {
-                       printf("Could not set interface '%s' UP\n",
-                              drv->ifname);
+
+static int wpa_driver_wext_finish_drv_init(struct wpa_driver_wext_data *drv)
+{
+       int send_rfkill_event = 0;
+
+       if (linux_set_iface_flags(drv->ioctl_sock, drv->ifname, 1) < 0) {
+               if (rfkill_is_blocked(drv->rfkill)) {
+                       wpa_printf(MSG_DEBUG, "WEXT: Could not yet enable "
+                                  "interface '%s' due to rfkill",
+                                  drv->ifname);
+                       drv->if_disabled = 1;
+                       send_rfkill_event = 1;
                } else {
-                       /*
-                        * Wait some time to allow driver to initialize before
-                        * starting configuring the driver. This seems to be
-                        * needed at least some drivers that load firmware etc.
-                        * when the interface is set up.
-                        */
-                       wpa_printf(MSG_DEBUG, "Interface %s set UP - waiting "
-                                  "a second for the driver to complete "
-                                  "initialization", drv->ifname);
-                       sleep(1);
+                       wpa_printf(MSG_ERROR, "WEXT: Could not set "
+                                  "interface '%s' UP", drv->ifname);
+                       return -1;
                }
        }
 
@@ -1044,14 +832,23 @@ void * wpa_driver_wext_init(void *ctx, const char *ifname)
        wpa_driver_wext_flush_pmkid(drv);
 
        if (wpa_driver_wext_set_mode(drv, 0) < 0) {
-               printf("Could not configure driver to use managed mode\n");
+               wpa_printf(MSG_DEBUG, "Could not configure driver to use "
+                          "managed mode");
+               /* Try to use it anyway */
        }
 
        wpa_driver_wext_get_range(drv);
 
+       /*
+        * Unlock the driver's BSSID and force to a random SSID to clear any
+        * previous association the driver might have when the supplicant
+        * starts up.
+        */
+       wpa_driver_wext_disconnect(drv);
+
        drv->ifindex = if_nametoindex(drv->ifname);
 
-       if (os_strncmp(ifname, "wlan", 4) == 0) {
+       if (os_strncmp(drv->ifname, "wlan", 4) == 0) {
                /*
                 * Host AP driver may use both wlan# and wifi# interface in
                 * wireless events. Since some of the versions included WE-18
@@ -1061,14 +858,20 @@ void * wpa_driver_wext_init(void *ctx, const char *ifname)
                 * driver are not in use anymore.
                 */
                char ifname2[IFNAMSIZ + 1];
-               os_strlcpy(ifname2, ifname, sizeof(ifname2));
+               os_strlcpy(ifname2, drv->ifname, sizeof(ifname2));
                os_memcpy(ifname2, "wifi", 4);
                wpa_driver_wext_alternative_ifindex(drv, ifname2);
        }
 
-       wpa_driver_wext_send_oper_ifla(drv, 1, IF_OPER_DORMANT);
+       netlink_send_oper_ifla(drv->netlink, drv->ifindex,
+                              1, IF_OPER_DORMANT);
 
-       return drv;
+       if (send_rfkill_event) {
+               eloop_register_timeout(0, 0, wpa_driver_wext_send_rfkill,
+                                      drv, drv->ctx);
+       }
+
+       return 0;
 }
 
 
@@ -1082,7 +885,8 @@ void * wpa_driver_wext_init(void *ctx, const char *ifname)
 void wpa_driver_wext_deinit(void *priv)
 {
        struct wpa_driver_wext_data *drv = priv;
-       int flags;
+
+       wpa_driver_wext_set_auth_param(drv, IW_AUTH_WPA_ENABLED, 0);
 
        eloop_cancel_timeout(wpa_driver_wext_scan_timeout, drv, drv->ctx);
 
@@ -1090,31 +894,17 @@ void wpa_driver_wext_deinit(void *priv)
         * Clear possibly configured driver parameters in order to make it
         * easier to use the driver after wpa_supplicant has been terminated.
         */
-       (void) wpa_driver_wext_set_bssid(drv,
-                                        (u8 *) "\x00\x00\x00\x00\x00\x00");
+       wpa_driver_wext_disconnect(drv);
 
-       wpa_driver_wext_send_oper_ifla(priv, 0, IF_OPER_UP);
+       netlink_send_oper_ifla(drv->netlink, drv->ifindex, 0, IF_OPER_UP);
+       netlink_deinit(drv->netlink);
+       rfkill_deinit(drv->rfkill);
 
-       eloop_unregister_read_sock(drv->event_sock);
        if (drv->mlme_sock >= 0)
                eloop_unregister_read_sock(drv->mlme_sock);
 
-       if (wpa_driver_wext_get_ifflags(drv, &flags) == 0)
-               (void) wpa_driver_wext_set_ifflags(drv, flags & ~IFF_UP);
+       (void) linux_set_iface_flags(drv->ioctl_sock, drv->ifname, 0);
 
-#ifdef CONFIG_CLIENT_MLME
-       if (drv->mlmedev[0]) {
-               if (wpa_driver_wext_get_ifflags_ifname(drv, drv->mlmedev,
-                                                      &flags) == 0)
-                       (void) wpa_driver_wext_set_ifflags_ifname(
-                               drv, drv->mlmedev, flags & ~IFF_UP);
-               wpa_driver_prism2_param_set(drv, PRISM2_PARAM_MGMT_IF, 0);
-               wpa_driver_prism2_param_set(drv, PRISM2_PARAM_USER_SPACE_MLME,
-                                           0);
-       }
-#endif /* CONFIG_CLIENT_MLME */
-
-       close(drv->event_sock);
        close(drv->ioctl_sock);
        if (drv->mlme_sock >= 0)
                close(drv->mlme_sock);
@@ -1142,18 +932,17 @@ void wpa_driver_wext_scan_timeout(void *eloop_ctx, void *timeout_ctx)
 /**
  * wpa_driver_wext_scan - Request the driver to initiate scan
  * @priv: Pointer to private wext data from wpa_driver_wext_init()
- * @ssid: Specific SSID to scan for (ProbeReq) or %NULL to scan for
- *     all SSIDs (either active scan with broadcast SSID or passive
- *     scan
- * @ssid_len: Length of the SSID
+ * @param: Scan parameters (specific SSID to scan for (ProbeReq), etc.)
  * Returns: 0 on success, -1 on failure
  */
-int wpa_driver_wext_scan(void *priv, const u8 *ssid, size_t ssid_len)
+int wpa_driver_wext_scan(void *priv, struct wpa_driver_scan_params *params)
 {
        struct wpa_driver_wext_data *drv = priv;
        struct iwreq iwr;
        int ret = 0, timeout;
        struct iw_scan_req req;
+       const u8 *ssid = params->ssids[0].ssid;
+       size_t ssid_len = params->ssids[0].ssid_len;
 
        if (ssid_len > IW_ESSID_MAX_SIZE) {
                wpa_printf(MSG_DEBUG, "%s: too long SSID (%lu)",
@@ -1221,10 +1010,12 @@ static u8 * wpa_driver_wext_giwscan(struct wpa_driver_wext_data *drv,
                if (ioctl(drv->ioctl_sock, SIOCGIWSCAN, &iwr) == 0)
                        break;
 
-               if (errno == E2BIG && res_buf_len < 100000) {
+               if (errno == E2BIG && res_buf_len < 65535) {
                        os_free(res_buf);
                        res_buf = NULL;
                        res_buf_len *= 2;
+                       if (res_buf_len > 65535)
+                               res_buf_len = 65535; /* 16-bit length field */
                        wpa_printf(MSG_DEBUG, "Scan results did not fit - "
                                   "trying larger buffer (%lu bytes)",
                                   (unsigned long) res_buf_len);
@@ -1326,12 +1117,29 @@ static void wext_get_scan_freq(struct iw_event *iwe,
 }
 
 
-static void wext_get_scan_qual(struct iw_event *iwe,
+static void wext_get_scan_qual(struct wpa_driver_wext_data *drv,
+                              struct iw_event *iwe,
                               struct wext_scan_data *res)
 {
        res->res.qual = iwe->u.qual.qual;
        res->res.noise = iwe->u.qual.noise;
        res->res.level = iwe->u.qual.level;
+       if (iwe->u.qual.updated & IW_QUAL_QUAL_INVALID)
+               res->res.flags |= WPA_SCAN_QUAL_INVALID;
+       if (iwe->u.qual.updated & IW_QUAL_LEVEL_INVALID)
+               res->res.flags |= WPA_SCAN_LEVEL_INVALID;
+       if (iwe->u.qual.updated & IW_QUAL_NOISE_INVALID)
+               res->res.flags |= WPA_SCAN_NOISE_INVALID;
+       if (iwe->u.qual.updated & IW_QUAL_DBM)
+               res->res.flags |= WPA_SCAN_LEVEL_DBM;
+       if ((iwe->u.qual.updated & IW_QUAL_DBM) ||
+           ((iwe->u.qual.level != 0) &&
+            (iwe->u.qual.level > drv->max_level))) {
+               if (iwe->u.qual.level >= 64)
+                       res->res.level -= 0x100;
+               if (iwe->u.qual.noise >= 64)
+                       res->res.noise -= 0x100;
+       }
 }
 
 
@@ -1379,6 +1187,9 @@ static void wext_get_scan_iwevgenie(struct iw_event *iwe,
        char *genie, *gpos, *gend;
        u8 *tmp;
 
+       if (iwe->u.data.length == 0)
+               return;
+
        gpos = genie = custom;
        gend = genie + iwe->u.data.length;
        if (gend > end) {
@@ -1411,28 +1222,30 @@ static void wext_get_scan_custom(struct iw_event *iwe,
                int bytes;
                spos = custom + 7;
                bytes = custom + clen - spos;
-               if (bytes & 1)
+               if (bytes & 1 || bytes == 0)
                        return;
                bytes /= 2;
                tmp = os_realloc(res->ie, res->ie_len + bytes);
                if (tmp == NULL)
                        return;
-               hexstr2bin(spos, tmp + res->ie_len, bytes);
                res->ie = tmp;
+               if (hexstr2bin(spos, tmp + res->ie_len, bytes) < 0)
+                       return;
                res->ie_len += bytes;
        } else if (clen > 7 && os_strncmp(custom, "rsn_ie=", 7) == 0) {
                char *spos;
                int bytes;
                spos = custom + 7;
                bytes = custom + clen - spos;
-               if (bytes & 1)
+               if (bytes & 1 || bytes == 0)
                        return;
                bytes /= 2;
                tmp = os_realloc(res->ie, res->ie_len + bytes);
                if (tmp == NULL)
                        return;
-               hexstr2bin(spos, tmp + res->ie_len, bytes);
                res->ie = tmp;
+               if (hexstr2bin(spos, tmp + res->ie_len, bytes) < 0)
+                       return;
                res->ie_len += bytes;
        } else if (clen > 4 && os_strncmp(custom, "tsf=", 4) == 0) {
                char *spos;
@@ -1445,7 +1258,10 @@ static void wext_get_scan_custom(struct iw_event *iwe,
                        return;
                }
                bytes /= 2;
-               hexstr2bin(spos, bin, bytes);
+               if (hexstr2bin(spos, bin, bytes) < 0) {
+                       wpa_printf(MSG_DEBUG, "WEXT: Invalid TSF value");
+                       return;
+               }
                res->res.tsf += WPA_GET_BE64(bin);
        }
 }
@@ -1599,7 +1415,7 @@ struct wpa_scan_results * wpa_driver_wext_get_scan_results(void *priv)
                        wext_get_scan_freq(iwe, &data);
                        break;
                case IWEVQUAL:
-                       wext_get_scan_qual(iwe, &data);
+                       wext_get_scan_qual(drv, iwe, &data);
                        break;
                case SIOCGIWENCODE:
                        wext_get_scan_encode(iwe, &data);
@@ -1684,6 +1500,10 @@ static int wpa_driver_wext_get_range(void *priv)
                        drv->capa.enc |= WPA_DRIVER_CAPA_ENC_CCMP;
                if (range->enc_capa & IW_ENC_CAPA_4WAY_HANDSHAKE)
                        drv->capa.flags |= WPA_DRIVER_FLAGS_4WAY_HANDSHAKE;
+               drv->capa.auth = WPA_DRIVER_AUTH_OPEN |
+                       WPA_DRIVER_AUTH_SHARED |
+                       WPA_DRIVER_AUTH_LEAP;
+               drv->capa.max_scan_ssids = 1;
 
                wpa_printf(MSG_DEBUG, "  capabilities: key_mgmt 0x%x enc 0x%x "
                           "flags 0x%x",
@@ -1693,21 +1513,13 @@ static int wpa_driver_wext_get_range(void *priv)
                           "assuming WPA is not supported");
        }
 
+       drv->max_level = range->max_qual.level;
+
        os_free(range);
        return 0;
 }
 
 
-static int wpa_driver_wext_set_wpa(void *priv, int enabled)
-{
-       struct wpa_driver_wext_data *drv = priv;
-       wpa_printf(MSG_DEBUG, "%s", __FUNCTION__);
-
-       return wpa_driver_wext_set_auth_param(drv, IW_AUTH_WPA_ENABLED,
-                                             enabled);
-}
-
-
 static int wpa_driver_wext_set_psk(struct wpa_driver_wext_data *drv,
                                   const u8 *psk)
 {
@@ -1745,7 +1557,7 @@ static int wpa_driver_wext_set_psk(struct wpa_driver_wext_data *drv,
 }
 
 
-static int wpa_driver_wext_set_key_ext(void *priv, wpa_alg alg,
+static int wpa_driver_wext_set_key_ext(void *priv, enum wpa_alg alg,
                                       const u8 *addr, int key_idx,
                                       int set_tx, const u8 *seq,
                                       size_t seq_len,
@@ -1768,6 +1580,7 @@ static int wpa_driver_wext_set_key_ext(void *priv, wpa_alg alg,
        os_memset(&iwr, 0, sizeof(iwr));
        os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ);
        iwr.u.encoding.flags = key_idx + 1;
+       iwr.u.encoding.flags |= IW_ENCODE_TEMP;
        if (alg == WPA_ALG_NONE)
                iwr.u.encoding.flags |= IW_ENCODE_DISABLED;
        iwr.u.encoding.pointer = (caddr_t) ext;
@@ -1804,13 +1617,11 @@ static int wpa_driver_wext_set_key_ext(void *priv, wpa_alg alg,
        case WPA_ALG_PMK:
                ext->alg = IW_ENCODE_ALG_PMK;
                break;
-#ifdef WEXT_MFP_PENDING
 #ifdef CONFIG_IEEE80211W
        case WPA_ALG_IGTK:
                ext->alg = IW_ENCODE_ALG_AES_CMAC;
                break;
 #endif /* CONFIG_IEEE80211W */
-#endif /* WEXT_MFP_PENDING */
        default:
                wpa_printf(MSG_DEBUG, "%s: Unknown algorithm %d",
                           __FUNCTION__, alg);
@@ -1866,7 +1677,7 @@ static int wpa_driver_wext_set_key_ext(void *priv, wpa_alg alg,
  * This function uses SIOCSIWENCODEEXT by default, but tries to use
  * SIOCSIWENCODE if the extended ioctl fails when configuring a WEP key.
  */
-int wpa_driver_wext_set_key(void *priv, wpa_alg alg,
+int wpa_driver_wext_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)
@@ -1899,6 +1710,7 @@ int wpa_driver_wext_set_key(void *priv, wpa_alg alg,
        os_memset(&iwr, 0, sizeof(iwr));
        os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ);
        iwr.u.encoding.flags = key_idx + 1;
+       iwr.u.encoding.flags |= IW_ENCODE_TEMP;
        if (alg == WPA_ALG_NONE)
                iwr.u.encoding.flags |= IW_ENCODE_DISABLED;
        iwr.u.encoding.pointer = (caddr_t) key;
@@ -1913,6 +1725,7 @@ int wpa_driver_wext_set_key(void *priv, wpa_alg alg,
                os_memset(&iwr, 0, sizeof(iwr));
                os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ);
                iwr.u.encoding.flags = key_idx + 1;
+               iwr.u.encoding.flags |= IW_ENCODE_TEMP;
                iwr.u.encoding.pointer = (caddr_t) NULL;
                iwr.u.encoding.length = 0;
                if (ioctl(drv->ioctl_sock, SIOCSIWENCODE, &iwr) < 0) {
@@ -1973,12 +1786,65 @@ static int wpa_driver_wext_mlme(struct wpa_driver_wext_data *drv,
 }
 
 
+static void wpa_driver_wext_disconnect(struct wpa_driver_wext_data *drv)
+{
+       struct iwreq iwr;
+       const u8 null_bssid[ETH_ALEN] = { 0, 0, 0, 0, 0, 0 };
+       u8 ssid[32];
+       int i;
+
+       /*
+        * Only force-disconnect when the card is in infrastructure mode,
+        * otherwise the driver might interpret the cleared BSSID and random
+        * SSID as an attempt to create a new ad-hoc network.
+        */
+       os_memset(&iwr, 0, sizeof(iwr));
+       os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ);
+       if (ioctl(drv->ioctl_sock, SIOCGIWMODE, &iwr) < 0) {
+               perror("ioctl[SIOCGIWMODE]");
+               iwr.u.mode = IW_MODE_INFRA;
+       }
+
+       if (iwr.u.mode == IW_MODE_INFRA) {
+               if (drv->cfg80211) {
+                       /*
+                        * cfg80211 supports SIOCSIWMLME commands, so there is
+                        * no need for the random SSID hack, but clear the
+                        * BSSID and SSID.
+                        */
+                       if (wpa_driver_wext_set_bssid(drv, null_bssid) < 0 ||
+                           wpa_driver_wext_set_ssid(drv, (u8 *) "", 0) < 0) {
+                               wpa_printf(MSG_DEBUG, "WEXT: Failed to clear "
+                                          "to disconnect");
+                       }
+                       return;
+               }
+               /*
+                * Clear the BSSID selection and set a random SSID to make sure
+                * the driver will not be trying to associate with something
+                * even if it does not understand SIOCSIWMLME commands (or
+                * tries to associate automatically after deauth/disassoc).
+                */
+               for (i = 0; i < 32; i++)
+                       ssid[i] = rand() & 0xFF;
+               if (wpa_driver_wext_set_bssid(drv, null_bssid) < 0 ||
+                   wpa_driver_wext_set_ssid(drv, ssid, 32) < 0) {
+                       wpa_printf(MSG_DEBUG, "WEXT: Failed to set bogus "
+                                  "BSSID/SSID to disconnect");
+               }
+       }
+}
+
+
 static int wpa_driver_wext_deauthenticate(void *priv, const u8 *addr,
                                          int reason_code)
 {
        struct wpa_driver_wext_data *drv = priv;
+       int ret;
        wpa_printf(MSG_DEBUG, "%s", __FUNCTION__);
-       return wpa_driver_wext_mlme(drv, addr, IW_MLME_DEAUTH, reason_code);
+       ret = wpa_driver_wext_mlme(drv, addr, IW_MLME_DEAUTH, reason_code);
+       wpa_driver_wext_disconnect(drv);
+       return ret;
 }
 
 
@@ -1986,9 +1852,11 @@ static int wpa_driver_wext_disassociate(void *priv, const u8 *addr,
                                        int reason_code)
 {
        struct wpa_driver_wext_data *drv = priv;
+       int ret;
        wpa_printf(MSG_DEBUG, "%s", __FUNCTION__);
-       return wpa_driver_wext_mlme(drv, addr, IW_MLME_DISASSOC,
-                                   reason_code);
+       ret = wpa_driver_wext_mlme(drv, addr, IW_MLME_DISASSOC, reason_code);
+       wpa_driver_wext_disconnect(drv);
+       return ret;
 }
 
 
@@ -2075,9 +1943,9 @@ wpa_driver_wext_auth_alg_fallback(struct wpa_driver_wext_data *drv,
        if (!drv->use_crypt) {
                iwr.u.encoding.flags |= IW_ENCODE_DISABLED;
        } else {
-               if (params->auth_alg & AUTH_ALG_OPEN_SYSTEM)
+               if (params->auth_alg & WPA_AUTH_ALG_OPEN)
                        iwr.u.encoding.flags |= IW_ENCODE_OPEN;
-               if (params->auth_alg & AUTH_ALG_SHARED_KEY)
+               if (params->auth_alg & WPA_AUTH_ALG_SHARED)
                        iwr.u.encoding.flags |= IW_ENCODE_RESTRICTED;
        }
 
@@ -2100,6 +1968,22 @@ int wpa_driver_wext_associate(void *priv,
 
        wpa_printf(MSG_DEBUG, "%s", __FUNCTION__);
 
+       if (drv->cfg80211) {
+               /*
+                * Stop cfg80211 from trying to associate before we are done
+                * with all parameters.
+                */
+               wpa_driver_wext_set_ssid(drv, (u8 *) "", 0);
+       }
+
+       if (wpa_driver_wext_set_drop_unencrypted(drv, params->drop_unencrypted)
+           < 0)
+               ret = -1;
+       if (wpa_driver_wext_set_auth_alg(drv, params->auth_alg) < 0)
+               ret = -1;
+       if (wpa_driver_wext_set_mode(drv, params->mode) < 0)
+               ret = -1;
+
        /*
         * If the driver did not support SIOCSIWAUTH, fallback to
         * SIOCSIWENCODE here.
@@ -2112,8 +1996,6 @@ int wpa_driver_wext_associate(void *priv,
            wpa_driver_wext_set_bssid(drv, NULL) < 0)
                ret = -1;
 
-       if (wpa_driver_wext_set_mode(drv, params->mode) < 0)
-               ret = -1;
        /* TODO: should consider getting wpa version and cipher/key_mgmt suites
         * from configuration, not from here, where only the selected suite is
         * available */
@@ -2164,7 +2046,6 @@ int wpa_driver_wext_associate(void *priv,
                                           IW_AUTH_RX_UNENCRYPTED_EAPOL,
                                           allow_unencrypted_eapol) < 0)
                ret = -1;
-#ifdef WEXT_MFP_PENDING
 #ifdef CONFIG_IEEE80211W
        switch (params->mgmt_frame_protection) {
        case NO_MGMT_FRAME_PROTECTION:
@@ -2180,14 +2061,17 @@ int wpa_driver_wext_associate(void *priv,
        if (wpa_driver_wext_set_auth_param(drv, IW_AUTH_MFP, value) < 0)
                ret = -1;
 #endif /* CONFIG_IEEE80211W */
-#endif /* WEXT_MFP_PENDING */
        if (params->freq && wpa_driver_wext_set_freq(drv, params->freq) < 0)
                ret = -1;
-       if (wpa_driver_wext_set_ssid(drv, params->ssid, params->ssid_len) < 0)
+       if (!drv->cfg80211 &&
+           wpa_driver_wext_set_ssid(drv, params->ssid, params->ssid_len) < 0)
                ret = -1;
        if (params->bssid &&
            wpa_driver_wext_set_bssid(drv, params->bssid) < 0)
                ret = -1;
+       if (drv->cfg80211 &&
+           wpa_driver_wext_set_ssid(drv, params->ssid, params->ssid_len) < 0)
+               ret = -1;
 
        return ret;
 }
@@ -2198,11 +2082,11 @@ static int wpa_driver_wext_set_auth_alg(void *priv, int auth_alg)
        struct wpa_driver_wext_data *drv = priv;
        int algs = 0, res;
 
-       if (auth_alg & AUTH_ALG_OPEN_SYSTEM)
+       if (auth_alg & WPA_AUTH_ALG_OPEN)
                algs |= IW_AUTH_ALG_OPEN_SYSTEM;
-       if (auth_alg & AUTH_ALG_SHARED_KEY)
+       if (auth_alg & WPA_AUTH_ALG_SHARED)
                algs |= IW_AUTH_ALG_SHARED_KEY;
-       if (auth_alg & AUTH_ALG_LEAP)
+       if (auth_alg & WPA_AUTH_ALG_LEAP)
                algs |= IW_AUTH_ALG_LEAP;
        if (algs == 0) {
                /* at least one algorithm should be set */
@@ -2226,7 +2110,7 @@ int wpa_driver_wext_set_mode(void *priv, int mode)
 {
        struct wpa_driver_wext_data *drv = priv;
        struct iwreq iwr;
-       int ret = -1, flags;
+       int ret = -1;
        unsigned int new_mode = mode ? IW_MODE_ADHOC : IW_MODE_INFRA;
 
        os_memset(&iwr, 0, sizeof(iwr));
@@ -2256,9 +2140,7 @@ int wpa_driver_wext_set_mode(void *priv, int mode)
                goto done;
        }
 
-       if (wpa_driver_wext_get_ifflags(drv, &flags) == 0) {
-               (void) wpa_driver_wext_set_ifflags(drv, flags & ~IFF_UP);
-
+       if (linux_set_iface_flags(drv->ioctl_sock, drv->ifname, 0) == 0) {
                /* Try to set the mode again while the interface is down */
                iwr.u.mode = new_mode;
                if (ioctl(drv->ioctl_sock, SIOCSIWMODE, &iwr) < 0)
@@ -2266,11 +2148,7 @@ int wpa_driver_wext_set_mode(void *priv, int mode)
                else
                        ret = 0;
 
-               /* Ignore return value of get_ifflags to ensure that the device
-                * is always up like it was before this function was called.
-                */
-               (void) wpa_driver_wext_get_ifflags(drv, &flags);
-               (void) wpa_driver_wext_set_ifflags(drv, flags | IFF_UP);
+               (void) linux_set_iface_flags(drv->ioctl_sock, drv->ifname, 1);
        }
 
 done:
@@ -2366,306 +2244,8 @@ int wpa_driver_wext_set_operstate(void *priv, int state)
        wpa_printf(MSG_DEBUG, "%s: operstate %d->%d (%s)",
                   __func__, drv->operstate, state, state ? "UP" : "DORMANT");
        drv->operstate = state;
-       return wpa_driver_wext_send_oper_ifla(
-               drv, -1, state ? IF_OPER_UP : IF_OPER_DORMANT);
-}
-
-
-#ifdef CONFIG_CLIENT_MLME
-static int hostapd_ioctl(struct wpa_driver_wext_data *drv,
-                        struct prism2_hostapd_param *param, int len)
-{
-       struct iwreq iwr;
-
-       os_memset(&iwr, 0, sizeof(iwr));
-       os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ);
-       iwr.u.data.pointer = (caddr_t) param;
-       iwr.u.data.length = len;
-
-       if (ioctl(drv->ioctl_sock, PRISM2_IOCTL_HOSTAPD, &iwr) < 0) {
-               perror("ioctl[PRISM2_IOCTL_HOSTAPD]");
-               return -1;
-       }
-
-       return 0;
-}
-
-
-static struct wpa_hw_modes *
-wpa_driver_wext_get_hw_feature_data(void *priv, u16 *num_modes, u16 *flags)
-{
-       struct wpa_driver_wext_data *drv = priv;
-       struct prism2_hostapd_param *param;
-       u8 *pos, *end;
-       struct wpa_hw_modes *modes = NULL;
-       int i;
-
-       param = os_zalloc(PRISM2_HOSTAPD_MAX_BUF_SIZE);
-       if (param == NULL)
-               return NULL;
-       param->cmd = PRISM2_HOSTAPD_GET_HW_FEATURES;
-
-       if (hostapd_ioctl(drv, param, PRISM2_HOSTAPD_MAX_BUF_SIZE) < 0) {
-               perror("ioctl[PRISM2_IOCTL_HOSTAPD]");
-               goto out;
-       }
-
-       *num_modes = param->u.hw_features.num_modes;
-       *flags = param->u.hw_features.flags;
-
-       pos = param->u.hw_features.data;
-       end = pos + PRISM2_HOSTAPD_MAX_BUF_SIZE -
-               (param->u.hw_features.data - (u8 *) param);
-
-       modes = os_zalloc(*num_modes * sizeof(struct wpa_hw_modes));
-       if (modes == NULL)
-               goto out;
-
-       for (i = 0; i < *num_modes; i++) {
-               struct hostapd_ioctl_hw_modes_hdr *hdr;
-               struct wpa_hw_modes *feature;
-               int clen, rlen;
-
-               hdr = (struct hostapd_ioctl_hw_modes_hdr *) pos;
-               pos = (u8 *) (hdr + 1);
-               clen = hdr->num_channels * sizeof(struct wpa_channel_data);
-               rlen = hdr->num_rates * sizeof(struct wpa_rate_data);
-
-               feature = &modes[i];
-               switch (hdr->mode) {
-               case MODE_IEEE80211A:
-                       feature->mode = WPA_MODE_IEEE80211A;
-                       break;
-               case MODE_IEEE80211B:
-                       feature->mode = WPA_MODE_IEEE80211B;
-                       break;
-               case MODE_IEEE80211G:
-                       feature->mode = WPA_MODE_IEEE80211G;
-                       break;
-               case MODE_ATHEROS_TURBO:
-               case MODE_ATHEROS_TURBOG:
-                       wpa_printf(MSG_ERROR, "Skip unsupported hw_mode=%d in "
-                                  "get_hw_features data", hdr->mode);
-                       pos += clen + rlen;
-                       continue;
-               default:
-                       wpa_printf(MSG_ERROR, "Unknown hw_mode=%d in "
-                                  "get_hw_features data", hdr->mode);
-                       wpa_supplicant_sta_free_hw_features(modes, *num_modes);
-                       modes = NULL;
-                       break;
-               }
-               feature->num_channels = hdr->num_channels;
-               feature->num_rates = hdr->num_rates;
-
-               feature->channels = os_malloc(clen);
-               feature->rates = os_malloc(rlen);
-               if (!feature->channels || !feature->rates ||
-                   pos + clen + rlen > end) {
-                       wpa_supplicant_sta_free_hw_features(modes, *num_modes);
-                       modes = NULL;
-                       break;
-               }
-
-               os_memcpy(feature->channels, pos, clen);
-               pos += clen;
-               os_memcpy(feature->rates, pos, rlen);
-               pos += rlen;
-       }
-
-out:
-       os_free(param);
-       return modes;
-}
-
-
-int wpa_driver_wext_set_channel(void *priv, wpa_hw_mode phymode, int chan,
-                               int freq)
-{
-       return wpa_driver_wext_set_freq(priv, freq);
-}
-
-
-static void wpa_driver_wext_mlme_read(int sock, void *eloop_ctx,
-                                     void *sock_ctx)
-{
-       struct wpa_driver_wext_data *drv = eloop_ctx;
-       int len;
-       unsigned char buf[3000];
-       struct ieee80211_frame_info *fi;
-       struct ieee80211_rx_status rx_status;
-
-       len = recv(sock, buf, sizeof(buf), 0);
-       if (len < 0) {
-               perror("recv[MLME]");
-               return;
-       }
-
-       if (len < (int) sizeof(struct ieee80211_frame_info)) {
-               wpa_printf(MSG_DEBUG, "WEXT: Too short MLME frame (len=%d)",
-                          len);
-               return;
-       }
-
-       fi = (struct ieee80211_frame_info *) buf;
-       if (ntohl(fi->version) != IEEE80211_FI_VERSION) {
-               wpa_printf(MSG_DEBUG, "WEXT: Invalid MLME frame info version "
-                          "0x%x", ntohl(fi->version));
-               return;
-       }
-
-       os_memset(&rx_status, 0, sizeof(rx_status));
-       rx_status.ssi = ntohl(fi->ssi_signal);
-       rx_status.channel = ntohl(fi->channel);
-
-       wpa_supplicant_sta_rx(drv->ctx,
-                             buf + sizeof(struct ieee80211_frame_info),
-                             len - sizeof(struct ieee80211_frame_info),
-                             &rx_status);
-}
-
-
-static int wpa_driver_wext_open_mlme(struct wpa_driver_wext_data *drv)
-{
-       int flags, ifindex, s;
-       struct sockaddr_ll addr;
-       struct ifreq ifr;
-
-       if (wpa_driver_prism2_param_set(drv, PRISM2_PARAM_USER_SPACE_MLME, 1) <
-           0) {
-               wpa_printf(MSG_ERROR, "WEXT: Failed to configure driver to "
-                          "use user space MLME");
-               return -1;
-       }
-
-       if (wpa_driver_prism2_param_set(drv, PRISM2_PARAM_MGMT_IF, 1) < 0) {
-               wpa_printf(MSG_ERROR, "WEXT: Failed to add management "
-                          "interface for user space MLME");
-               return -1;
-       }
-
-       ifindex = wpa_driver_prism2_param_get(drv, PRISM2_PARAM_MGMT_IF);
-       if (ifindex <= 0) {
-               wpa_printf(MSG_ERROR, "WEXT: MLME management device not "
-                          "found");
-               return -1;
-       }
-
-       os_memset(&ifr, 0, sizeof(ifr));
-       ifr.ifr_ifindex = ifindex;
-       if (ioctl(drv->ioctl_sock, SIOCGIFNAME, &ifr) != 0) {
-               perror("ioctl(SIOCGIFNAME)");
-               return -1;
-       }
-       os_strlcpy(drv->mlmedev, ifr.ifr_name, sizeof(drv->mlmedev));
-       wpa_printf(MSG_DEBUG, "WEXT: MLME management device '%s'",
-                  drv->mlmedev);
-
-       if (wpa_driver_wext_get_ifflags_ifname(drv, drv->mlmedev, &flags) != 0
-           || wpa_driver_wext_set_ifflags_ifname(drv, drv->mlmedev,
-                                                 flags | IFF_UP) != 0) {
-               wpa_printf(MSG_ERROR, "WEXT: Could not set interface "
-                          "'%s' UP", drv->mlmedev);
-               return -1;
-       }
-
-       s = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL));
-       if (s < 0) {
-               perror("socket[PF_PACKET,SOCK_RAW]");
-               return -1;
-       }
-
-       os_memset(&addr, 0, sizeof(addr));
-       addr.sll_family = AF_PACKET;
-       addr.sll_ifindex = ifindex;
-
-       if (bind(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
-               perror("bind(MLME)");
-               return -1;
-       }
-
-       if (eloop_register_read_sock(s, wpa_driver_wext_mlme_read, drv, NULL))
-       {
-               wpa_printf(MSG_ERROR, "WEXT: Could not register MLME read "
-                          "socket");
-               close(s);
-               return -1;
-       }
-
-       return s;
-}
-
-
-static int wpa_driver_wext_send_mlme(void *priv, const u8 *data,
-                                    size_t data_len)
-{
-       struct wpa_driver_wext_data *drv = priv;
-       int ret;
-
-       ret = send(drv->mlme_sock, data, data_len, 0);
-       if (ret < 0) {
-               perror("send[MLME]");
-               return -1;
-       }
-
-       return 0;
-}
-
-
-static int wpa_driver_wext_mlme_add_sta(void *priv, const u8 *addr,
-                                       const u8 *supp_rates,
-                                       size_t supp_rates_len)
-{
-       struct wpa_driver_wext_data *drv = priv;
-       struct prism2_hostapd_param param;
-       size_t len;
-
-       os_memset(&param, 0, sizeof(param));
-       param.cmd = PRISM2_HOSTAPD_ADD_STA;
-       os_memcpy(param.sta_addr, addr, ETH_ALEN);
-       len = supp_rates_len;
-       if (len > sizeof(param.u.add_sta.supp_rates))
-               len = sizeof(param.u.add_sta.supp_rates);
-       os_memcpy(param.u.add_sta.supp_rates, supp_rates, len);
-       return hostapd_ioctl(drv, &param, sizeof(param));
-}
-
-
-static int wpa_driver_wext_mlme_remove_sta(void *priv, const u8 *addr)
-{
-       struct wpa_driver_wext_data *drv = priv;
-       struct prism2_hostapd_param param;
-
-       os_memset(&param, 0, sizeof(param));
-       param.cmd = PRISM2_HOSTAPD_REMOVE_STA;
-       os_memcpy(param.sta_addr, addr, ETH_ALEN);
-       return hostapd_ioctl(drv, &param, sizeof(param));
-}
-
-#endif /* CONFIG_CLIENT_MLME */
-
-
-static int wpa_driver_wext_set_param(void *priv, const char *param)
-{
-#ifdef CONFIG_CLIENT_MLME
-       struct wpa_driver_wext_data *drv = priv;
-
-       if (param == NULL)
-               return 0;
-
-       wpa_printf(MSG_DEBUG, "%s: param='%s'", __func__, param);
-
-       if (os_strstr(param, "use_mlme=1")) {
-               wpa_printf(MSG_DEBUG, "WEXT: Using user space MLME");
-               drv->capa.flags |= WPA_DRIVER_FLAGS_USER_SPACE_MLME;
-
-               drv->mlme_sock = wpa_driver_wext_open_mlme(drv);
-               if (drv->mlme_sock < 0)
-                       return -1;
-       }
-#endif /* CONFIG_CLIENT_MLME */
-
-       return 0;
+       return netlink_send_oper_ifla(drv->netlink, drv->ifindex, -1,
+                                     state ? IF_OPER_UP : IF_OPER_DORMANT);
 }
 
 
@@ -2680,31 +2260,18 @@ const struct wpa_driver_ops wpa_driver_wext_ops = {
        .desc = "Linux wireless extensions (generic)",
        .get_bssid = wpa_driver_wext_get_bssid,
        .get_ssid = wpa_driver_wext_get_ssid,
-       .set_wpa = wpa_driver_wext_set_wpa,
        .set_key = wpa_driver_wext_set_key,
        .set_countermeasures = wpa_driver_wext_set_countermeasures,
-       .set_drop_unencrypted = wpa_driver_wext_set_drop_unencrypted,
-       .scan = wpa_driver_wext_scan,
+       .scan2 = wpa_driver_wext_scan,
        .get_scan_results2 = wpa_driver_wext_get_scan_results,
        .deauthenticate = wpa_driver_wext_deauthenticate,
        .disassociate = wpa_driver_wext_disassociate,
        .associate = wpa_driver_wext_associate,
-       .set_auth_alg = wpa_driver_wext_set_auth_alg,
        .init = wpa_driver_wext_init,
        .deinit = wpa_driver_wext_deinit,
-       .set_param = wpa_driver_wext_set_param,
        .add_pmkid = wpa_driver_wext_add_pmkid,
        .remove_pmkid = wpa_driver_wext_remove_pmkid,
        .flush_pmkid = wpa_driver_wext_flush_pmkid,
        .get_capa = wpa_driver_wext_get_capa,
        .set_operstate = wpa_driver_wext_set_operstate,
-#ifdef CONFIG_CLIENT_MLME
-       .get_hw_feature_data = wpa_driver_wext_get_hw_feature_data,
-       .set_channel = wpa_driver_wext_set_channel,
-       .set_ssid = wpa_driver_wext_set_ssid,
-       .set_bssid = wpa_driver_wext_set_bssid,
-       .send_mlme = wpa_driver_wext_send_mlme,
-       .mlme_add_sta = wpa_driver_wext_mlme_add_sta,
-       .mlme_remove_sta = wpa_driver_wext_mlme_remove_sta,
-#endif /* CONFIG_CLIENT_MLME */
 };