wext: Fix scan result signal levels when driver reports in dBm
[libeap.git] / src / drivers / driver_wext.c
index ade8904..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"
 
 
 static int wpa_driver_wext_flush_pmkid(void *priv);
 static int wpa_driver_wext_get_range(void *priv);
 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_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_set_auth_alg(void *priv, int auth_alg);
 
 
 int wpa_driver_wext_set_auth_param(struct wpa_driver_wext_data *drv,
@@ -303,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;
 
@@ -311,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;
 
@@ -331,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)) {
@@ -459,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;
@@ -524,12 +471,13 @@ 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:
@@ -539,7 +487,7 @@ static void wpa_driver_wext_event_wireless(struct wpa_driver_wext_data *drv,
                                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) {
@@ -552,14 +500,15 @@ static void wpa_driver_wext_event_wireless(struct wpa_driver_wext_data *drv,
                                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) {
@@ -596,8 +545,7 @@ static void wpa_driver_wext_event_wireless(struct wpa_driver_wext_data *drv,
 
 
 static void wpa_driver_wext_event_link(struct wpa_driver_wext_data *drv,
-                                      void *ctx, char *buf, size_t len,
-                                      int del)
+                                      char *buf, size_t len, int del)
 {
        union wpa_event_data event;
 
@@ -620,26 +568,18 @@ static void wpa_driver_wext_event_link(struct wpa_driver_wext_data *drv,
                        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_wext_own_ifname(struct wpa_driver_wext_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)) {
@@ -658,12 +598,12 @@ static int wpa_driver_wext_own_ifname(struct wpa_driver_wext_data *drv,
 
 
 static int wpa_driver_wext_own_ifindex(struct wpa_driver_wext_data *drv,
-                                      int ifindex, struct nlmsghdr *h)
+                                      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, h)) {
+       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");
@@ -675,20 +615,14 @@ static int wpa_driver_wext_own_ifindex(struct wpa_driver_wext_data *drv,
 }
 
 
-static void wpa_driver_wext_event_rtm_newlink(struct wpa_driver_wext_data *drv,
-                                             void *ctx, struct nlmsghdr *h,
-                                             size_t len)
+static void wpa_driver_wext_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_wext_data *drv = ctx;
+       int attrlen, rta_len;
+       struct rtattr *attr;
 
-       if (!wpa_driver_wext_own_ifindex(drv, ifi->ifi_index, h)) {
+       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;
@@ -701,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()
@@ -710,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);
+               netlink_send_oper_ifla(drv->netlink, drv->ifindex,
+                                      -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);
+       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(drv, ctx,
+                       wpa_driver_wext_event_link(drv,
                                                   ((char *) attr) + rta_len,
                                                   attr->rta_len - rta_len, 0);
                }
@@ -736,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(drv,  ctx,
+                       wpa_driver_wext_event_link(drv,
                                                   ((char *) attr) + rta_len,
                                                   attr->rta_len - rta_len, 1);
                }
@@ -769,126 +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)
-{
-       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)
+static void wpa_driver_wext_rfkill_blocked(void *ctx)
 {
-       return wpa_driver_wext_get_ifflags_ifname(drv, drv->ifname, flags);
+       wpa_printf(MSG_DEBUG, "WEXT: RFKILL blocked");
+       /*
+        * This may be for any interface; use ifdown event to disable
+        * interface.
+        */
 }
 
 
-static int wpa_driver_wext_set_ifflags_ifname(struct wpa_driver_wext_data *drv,
-                                             const char *ifname, int flags)
+static void wpa_driver_wext_rfkill_unblocked(void *ctx)
 {
-       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;
+       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 0;
-}
-
-
-/**
- * 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 */
 }
 
 
@@ -901,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;
-       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)
@@ -911,40 +745,55 @@ 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)");
                goto err1;
        }
 
-       s = socket(PF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
-       if (s < 0) {
-               perror("socket(PF_NETLINK,SOCK_RAW,NETLINK_ROUTE)");
+       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;
        }
-       drv->event_sock = s;
 
-       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)");
+       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);
        }
 
-       eloop_register_read_sock(s, wpa_driver_wext_event_receive, drv, ctx);
-
        drv->mlme_sock = -1;
 
        if (wpa_driver_wext_finish_drv_init(drv) < 0)
-               goto err4;
+               goto err3;
+
+       wpa_driver_wext_set_auth_param(drv, IW_AUTH_WPA_ENABLED, 1);
 
        return drv;
 
-err4:
-       eloop_unregister_read_sock(drv->event_sock);
 err3:
-       close(drv->event_sock);
+       rfkill_deinit(drv->rfkill);
+       netlink_deinit(drv->netlink);
 err2:
        close(drv->ioctl_sock);
 err1:
@@ -953,32 +802,27 @@ err1:
 }
 
 
-static int wpa_driver_wext_finish_drv_init(struct wpa_driver_wext_data *drv)
+static void wpa_driver_wext_send_rfkill(void *eloop_ctx, void *timeout_ctx)
 {
-       int flags;
+       wpa_supplicant_event(timeout_ctx, EVENT_INTERFACE_DISABLED, NULL);
+}
 
-       if (wpa_driver_wext_get_ifflags(drv, &flags) != 0) {
-               wpa_printf(MSG_ERROR, "Could not get interface '%s' flags",
-                          drv->ifname);
-               return -1;
-       }
 
-       if (!(flags & IFF_UP)) {
-               if (wpa_driver_wext_set_ifflags(drv, flags | IFF_UP) != 0) {
-                       wpa_printf(MSG_ERROR, "Could not set interface '%s' "
-                                  "UP", drv->ifname);
-                       return -1;
+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;
                }
        }
 
@@ -1019,7 +863,13 @@ static int wpa_driver_wext_finish_drv_init(struct wpa_driver_wext_data *drv)
                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);
+
+       if (send_rfkill_event) {
+               eloop_register_timeout(0, 0, wpa_driver_wext_send_rfkill,
+                                      drv, drv->ctx);
+       }
 
        return 0;
 }
@@ -1035,7 +885,8 @@ static int wpa_driver_wext_finish_drv_init(struct wpa_driver_wext_data *drv)
 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);
 
@@ -1045,16 +896,15 @@ void wpa_driver_wext_deinit(void *priv)
         */
        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);
 
-       close(drv->event_sock);
        close(drv->ioctl_sock);
        if (drv->mlme_sock >= 0)
                close(drv->mlme_sock);
@@ -1082,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 wildcard 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)",
@@ -1268,7 +1117,8 @@ 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;
@@ -1282,6 +1132,14 @@ static void wext_get_scan_qual(struct iw_event *iwe,
                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;
+       }
 }
 
 
@@ -1370,8 +1228,9 @@ static void wext_get_scan_custom(struct iw_event *iwe,
                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;
@@ -1384,8 +1243,9 @@ static void wext_get_scan_custom(struct iw_event *iwe,
                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;
@@ -1398,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);
        }
 }
@@ -1552,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);
@@ -1637,6 +1500,9 @@ 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 "
@@ -1647,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)
 {
@@ -1699,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,
@@ -1819,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)
@@ -1948,17 +1806,32 @@ static void wpa_driver_wext_disconnect(struct wpa_driver_wext_data *drv)
        }
 
        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).
                 */
-               wpa_driver_wext_set_bssid(drv, null_bssid);
-
                for (i = 0; i < 32; i++)
                        ssid[i] = rand() & 0xFF;
-               wpa_driver_wext_set_ssid(drv, ssid, 32);
+               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");
+               }
        }
 }
 
@@ -2070,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;
        }
 
@@ -2095,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.
@@ -2174,11 +2063,15 @@ int wpa_driver_wext_associate(void *priv,
 #endif /* CONFIG_IEEE80211W */
        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;
 }
@@ -2189,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 */
@@ -2217,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));
@@ -2247,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)
@@ -2257,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:
@@ -2357,8 +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);
+       return netlink_send_oper_ifla(drv->netlink, drv->ifindex, -1,
+                                     state ? IF_OPER_UP : IF_OPER_DORMANT);
 }
 
 
@@ -2373,17 +2260,13 @@ 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,
-       .set_mode = wpa_driver_wext_set_mode,
        .associate = wpa_driver_wext_associate,
-       .set_auth_alg = wpa_driver_wext_set_auth_alg,
        .init = wpa_driver_wext_init,
        .deinit = wpa_driver_wext_deinit,
        .add_pmkid = wpa_driver_wext_add_pmkid,