Add driver op for disabling 802.11b rates
[libeap.git] / src / drivers / driver_nl80211.c
index 6b4b706..a394e29 100644 (file)
@@ -18,7 +18,6 @@
 
 #include "includes.h"
 #include <sys/ioctl.h>
-#include <net/if_arp.h>
 #include <net/if.h>
 #include <netlink/genl/genl.h>
 #include <netlink/genl/family.h>
@@ -31,6 +30,7 @@
 #include "eloop.h"
 #include "common/ieee802_11_defs.h"
 #include "netlink.h"
+#include "linux_ioctl.h"
 #include "radiotap.h"
 #include "radiotap_iter.h"
 #include "driver.h"
@@ -95,8 +95,13 @@ struct wpa_driver_nl80211_data {
 
        int monitor_sock;
        int monitor_ifidx;
+       int probe_req_report;
+       int disable_11b_rates;
 
        unsigned int beacon_set:1;
+       unsigned int pending_remain_on_chan:1;
+
+       u64 remain_on_chan_cookie;
 
 #ifdef HOSTAPD
        int eapol_sock; /* socket for EAPOL frames */
@@ -134,6 +139,11 @@ static int wpa_driver_nl80211_if_remove(void *priv,
                                        const char *ifname);
 #endif /* HOSTAPD */
 
+static void wpa_driver_nl80211_probe_req_report_timeout(void *eloop_ctx,
+                                                       void *timeout_ctx);
+static int nl80211_disable_11b_rates(struct wpa_driver_nl80211_data *drv,
+                                    int ifindex, int disabled);
+
 
 /* nl80211 code */
 static int ack_handler(struct nl_msg *msg, void *arg)
@@ -262,52 +272,6 @@ nla_put_failure:
 }
 
 
-#ifdef HOSTAPD
-static int get_ifhwaddr(struct wpa_driver_nl80211_data *drv,
-                       const char *ifname, u8 *addr)
-{
-       struct ifreq ifr;
-
-       os_memset(&ifr, 0, sizeof(ifr));
-       os_strlcpy(ifr.ifr_name, ifname, IFNAMSIZ);
-       if (ioctl(drv->ioctl_sock, SIOCGIFHWADDR, &ifr)) {
-               wpa_printf(MSG_ERROR, "%s: ioctl(SIOCGIFHWADDR): %d (%s)",
-                          ifname, errno, strerror(errno));
-               return -1;
-       }
-
-       if (ifr.ifr_hwaddr.sa_family != ARPHRD_ETHER) {
-               wpa_printf(MSG_ERROR, "%s: Invalid HW-addr family 0x%04x",
-                          ifname, ifr.ifr_hwaddr.sa_family);
-               return -1;
-       }
-       os_memcpy(addr, ifr.ifr_hwaddr.sa_data, ETH_ALEN);
-
-       return 0;
-}
-
-
-static int set_ifhwaddr(struct wpa_driver_nl80211_data *drv,
-                       const char *ifname, const u8 *addr)
-{
-       struct ifreq ifr;
-
-       os_memset(&ifr, 0, sizeof(ifr));
-       os_strlcpy(ifr.ifr_name, ifname, IFNAMSIZ);
-       os_memcpy(ifr.ifr_hwaddr.sa_data, addr, ETH_ALEN);
-       ifr.ifr_hwaddr.sa_family = ARPHRD_ETHER;
-
-       if (ioctl(drv->ioctl_sock, SIOCSIFHWADDR, &ifr)) {
-               wpa_printf(MSG_DEBUG, "%s: ioctl(SIOCSIFHWADDR): %d (%s)",
-                          ifname, errno, strerror(errno));
-               return -1;
-       }
-
-       return 0;
-}
-#endif /* HOSTAPD */
-
-
 static int wpa_driver_nl80211_get_bssid(void *priv, u8 *bssid)
 {
        struct wpa_driver_nl80211_data *drv = priv;
@@ -707,6 +671,53 @@ static void mlme_event_join_ibss(struct wpa_driver_nl80211_data *drv,
 }
 
 
+static void mlme_event_remain_on_channel(struct wpa_driver_nl80211_data *drv,
+                                        int cancel_event, struct nlattr *tb[])
+{
+       unsigned int freq, chan_type, duration;
+       union wpa_event_data data;
+       u64 cookie;
+
+       if (tb[NL80211_ATTR_WIPHY_FREQ])
+               freq = nla_get_u32(tb[NL80211_ATTR_WIPHY_FREQ]);
+       else
+               freq = 0;
+
+       if (tb[NL80211_ATTR_WIPHY_CHANNEL_TYPE])
+               chan_type = nla_get_u32(tb[NL80211_ATTR_WIPHY_CHANNEL_TYPE]);
+       else
+               chan_type = 0;
+
+       if (tb[NL80211_ATTR_DURATION])
+               duration = nla_get_u32(tb[NL80211_ATTR_DURATION]);
+       else
+               duration = 0;
+
+       if (tb[NL80211_ATTR_COOKIE])
+               cookie = nla_get_u64(tb[NL80211_ATTR_COOKIE]);
+       else
+               cookie = 0;
+
+       wpa_printf(MSG_DEBUG, "nl80211: Remain-on-channel event (cancel=%d "
+                  "freq=%u channel_type=%u duration=%u cookie=0x%llx (%s))",
+                  cancel_event, freq, chan_type, duration,
+                  (long long unsigned int) cookie,
+                  cookie == drv->remain_on_chan_cookie ? "match" : "unknown");
+
+       if (cookie != drv->remain_on_chan_cookie)
+               return; /* not for us */
+
+       drv->pending_remain_on_chan = !cancel_event;
+
+       os_memset(&data, 0, sizeof(data));
+       data.remain_on_channel.freq = freq;
+       data.remain_on_channel.duration = duration;
+       wpa_supplicant_event(drv->ctx, cancel_event ?
+                            EVENT_CANCEL_REMAIN_ON_CHANNEL :
+                            EVENT_REMAIN_ON_CHANNEL, &data);
+}
+
+
 static void send_scan_event(struct wpa_driver_nl80211_data *drv, int aborted,
                            struct nlattr *tb[])
 {
@@ -829,6 +840,12 @@ static int process_event(struct nl_msg *msg, void *arg)
        case NL80211_CMD_JOIN_IBSS:
                mlme_event_join_ibss(drv, tb);
                break;
+       case NL80211_CMD_REMAIN_ON_CHANNEL:
+               mlme_event_remain_on_channel(drv, 0, tb);
+               break;
+       case NL80211_CMD_CANCEL_REMAIN_ON_CHANNEL:
+               mlme_event_remain_on_channel(drv, 1, tb);
+               break;
        default:
                wpa_printf(MSG_DEBUG, "nl80211: Ignored unknown event "
                           "(cmd=%d)", gnlh->cmd);
@@ -857,43 +874,6 @@ static void wpa_driver_nl80211_event_receive(int sock, void *eloop_ctx,
 }
 
 
-static int hostapd_set_iface_flags(struct wpa_driver_nl80211_data *drv,
-                                  const char *ifname, int dev_up)
-{
-       struct ifreq ifr;
-
-       if (drv->ioctl_sock < 0)
-               return -1;
-
-       os_memset(&ifr, 0, sizeof(ifr));
-       os_strlcpy(ifr.ifr_name, ifname, IFNAMSIZ);
-
-       if (ioctl(drv->ioctl_sock, SIOCGIFFLAGS, &ifr) != 0) {
-               perror("ioctl[SIOCGIFFLAGS]");
-               wpa_printf(MSG_DEBUG, "Could not read interface flags (%s)",
-                          ifname);
-               return -1;
-       }
-
-       if (dev_up) {
-               if (ifr.ifr_flags & IFF_UP)
-                       return 0;
-               ifr.ifr_flags |= IFF_UP;
-       } else {
-               if (!(ifr.ifr_flags & IFF_UP))
-                       return 0;
-               ifr.ifr_flags &= ~IFF_UP;
-       }
-
-       if (ioctl(drv->ioctl_sock, SIOCSIFFLAGS, &ifr) != 0) {
-               perror("ioctl[SIOCSIFFLAGS]");
-               return -1;
-       }
-
-       return 0;
-}
-
-
 /**
  * wpa_driver_nl80211_set_country - ask nl80211 to set the regulatory domain
  * @priv: driver_nl80211 private data
@@ -1231,9 +1211,9 @@ wpa_driver_nl80211_finish_drv_init(struct wpa_driver_nl80211_data *drv)
                           "use managed mode");
        }
 
-       if (hostapd_set_iface_flags(drv, drv->ifname, 1)) {
-               wpa_printf(MSG_ERROR, "Could not set interface '%s' "
-                          "UP", drv->ifname);
+       if (linux_set_iface_flags(drv->ioctl_sock, drv->ifname, 1)) {
+               wpa_printf(MSG_ERROR, "Could not set interface '%s' UP",
+                          drv->ifname);
                return -1;
        }
 
@@ -1292,10 +1272,6 @@ static void wpa_driver_nl80211_deinit(void *priv)
        struct wpa_driver_nl80211_data *drv = priv;
 
        nl80211_remove_monitor_interface(drv);
-       if (drv->monitor_sock >= 0) {
-               eloop_unregister_read_sock(drv->monitor_sock);
-               close(drv->monitor_sock);
-       }
 
        if (drv->nlmode == NL80211_IFTYPE_AP)
                wpa_driver_nl80211_del_beacon(drv);
@@ -1320,12 +1296,15 @@ static void wpa_driver_nl80211_deinit(void *priv)
        wpa_driver_nl80211_free_bss(drv);
 #endif /* HOSTAPD */
 
+       if (drv->disable_11b_rates)
+               nl80211_disable_11b_rates(drv, drv->ifindex, 0);
+
        netlink_send_oper_ifla(drv->netlink, drv->ifindex, 0, IF_OPER_UP);
        netlink_deinit(drv->netlink);
 
        eloop_cancel_timeout(wpa_driver_nl80211_scan_timeout, drv, drv->ctx);
 
-       (void) hostapd_set_iface_flags(drv, drv->ifname, 0);
+       (void) linux_set_iface_flags(drv->ioctl_sock, drv->ifname, 0);
        wpa_driver_nl80211_set_mode(drv, IEEE80211_MODE_INFRA);
 
        if (drv->ioctl_sock >= 0)
@@ -1339,6 +1318,9 @@ static void wpa_driver_nl80211_deinit(void *priv)
        nl_handle_destroy(drv->nl_handle_event);
        nl_cb_put(drv->nl_cb);
 
+       eloop_cancel_timeout(wpa_driver_nl80211_probe_req_report_timeout,
+                            drv, NULL);
+
        os_free(drv);
 }
 
@@ -1625,6 +1607,20 @@ static void wpa_driver_nl80211_check_bss_status(
 }
 
 
+static void wpa_scan_results_free(struct wpa_scan_results *res)
+{
+       size_t i;
+
+       if (res == NULL)
+               return;
+
+       for (i = 0; i < res->num; i++)
+               os_free(res->res[i]);
+       os_free(res->res);
+       os_free(res);
+}
+
+
 /**
  * wpa_driver_nl80211_get_scan_results - Fetch the latest scan results
  * @priv: Pointer to private wext data from wpa_driver_nl80211_init()
@@ -2018,13 +2014,13 @@ retry:
         * TODO: if multiple auth_alg options enabled, try them one by one if
         * the AP rejects authentication due to unknown auth alg
         */
-       if (params->auth_alg & AUTH_ALG_OPEN_SYSTEM)
+       if (params->auth_alg & WPA_AUTH_ALG_OPEN)
                type = NL80211_AUTHTYPE_OPEN_SYSTEM;
-       else if (params->auth_alg & AUTH_ALG_SHARED_KEY)
+       else if (params->auth_alg & WPA_AUTH_ALG_SHARED)
                type = NL80211_AUTHTYPE_SHARED_KEY;
-       else if (params->auth_alg & AUTH_ALG_LEAP)
+       else if (params->auth_alg & WPA_AUTH_ALG_LEAP)
                type = NL80211_AUTHTYPE_NETWORK_EAP;
-       else if (params->auth_alg & AUTH_ALG_FT)
+       else if (params->auth_alg & WPA_AUTH_ALG_FT)
                type = NL80211_AUTHTYPE_FT;
        else
                goto nla_put_failure;
@@ -2658,13 +2654,13 @@ static int nl80211_create_iface_once(struct wpa_driver_nl80211_data *drv,
 #ifdef HOSTAPD
        /* start listening for EAPOL on this interface */
        add_ifidx(drv, ifidx);
+#endif /* HOSTAPD */
 
-       if (addr && iftype == NL80211_IFTYPE_AP &&
-           set_ifhwaddr(drv, ifname, addr)) {
+       if (addr && iftype != NL80211_IFTYPE_MONITOR &&
+           linux_set_ifhwaddr(drv->ioctl_sock, ifname, addr)) {
                nl80211_remove_iface(drv, ifidx);
                return -1;
        }
-#endif /* HOSTAPD */
 
        return ifidx;
 }
@@ -2690,6 +2686,9 @@ static int nl80211_create_iface(struct wpa_driver_nl80211_data *drv,
                                                wds);
        }
 
+       if (ret >= 0 && drv->disable_11b_rates)
+               nl80211_disable_11b_rates(drv, ret, 1);
+
        return ret;
 }
 
@@ -2726,8 +2725,7 @@ static void from_unknown_sta(struct wpa_driver_nl80211_data *drv,
 
 
 static void handle_frame(struct wpa_driver_nl80211_data *drv,
-                        u8 *buf, size_t len,
-                        struct hostapd_frame_info *hfi)
+                        u8 *buf, size_t len, int datarate, int ssi_signal)
 {
        struct ieee80211_hdr *hdr;
        u16 fc;
@@ -2741,7 +2739,8 @@ static void handle_frame(struct wpa_driver_nl80211_data *drv,
                os_memset(&event, 0, sizeof(event));
                event.rx_mgmt.frame = buf;
                event.rx_mgmt.frame_len = len;
-               event.rx_mgmt.fi = hfi;
+               event.rx_mgmt.datarate = datarate;
+               event.rx_mgmt.ssi_signal = ssi_signal;
                wpa_supplicant_event(drv->ctx, EVENT_RX_MGMT, &event);
                break;
        case WLAN_FC_TYPE_CTRL:
@@ -2763,7 +2762,7 @@ static void handle_monitor_read(int sock, void *eloop_ctx, void *sock_ctx)
        unsigned char buf[3000];
        struct ieee80211_radiotap_iterator iter;
        int ret;
-       struct hostapd_frame_info hfi;
+       int datarate = 0, ssi_signal = 0;
        int injected = 0, failed = 0, rxflags = 0;
 
        len = recv(sock, buf, sizeof(buf), 0);
@@ -2772,13 +2771,17 @@ static void handle_monitor_read(int sock, void *eloop_ctx, void *sock_ctx)
                return;
        }
 
+       if (drv->nlmode == NL80211_IFTYPE_STATION && !drv->probe_req_report) {
+               wpa_printf(MSG_DEBUG, "nl80211: Ignore monitor interface "
+                          "frame since Probe Request reporting is disabled");
+               return;
+       }
+
        if (ieee80211_radiotap_iterator_init(&iter, (void*)buf, len)) {
                printf("received invalid radiotap frame\n");
                return;
        }
 
-       memset(&hfi, 0, sizeof(hfi));
-
        while (1) {
                ret = ieee80211_radiotap_iterator_next(&iter);
                if (ret == -ENOENT)
@@ -2803,15 +2806,13 @@ static void handle_monitor_read(int sock, void *eloop_ctx, void *sock_ctx)
                case IEEE80211_RADIOTAP_DATA_RETRIES:
                        break;
                case IEEE80211_RADIOTAP_CHANNEL:
-                       /* TODO convert from freq/flags to channel number
-                       hfi.channel = XXX;
-                        */
+                       /* TODO: convert from freq/flags to channel number */
                        break;
                case IEEE80211_RADIOTAP_RATE:
-                       hfi.datarate = *iter.this_arg * 5;
+                       datarate = *iter.this_arg * 5;
                        break;
                case IEEE80211_RADIOTAP_DB_ANTSIGNAL:
-                       hfi.ssi_signal = *iter.this_arg;
+                       ssi_signal = *iter.this_arg;
                        break;
                }
        }
@@ -2821,7 +2822,7 @@ static void handle_monitor_read(int sock, void *eloop_ctx, void *sock_ctx)
 
        if (!injected)
                handle_frame(drv, buf + iter.max_length,
-                            len - iter.max_length, &hfi);
+                            len - iter.max_length, datarate, ssi_signal);
        else
                handle_tx_callback(drv->ctx, buf + iter.max_length,
                                   len - iter.max_length, !failed);
@@ -2979,6 +2980,11 @@ static void nl80211_remove_monitor_interface(
                nl80211_remove_iface(drv, drv->monitor_ifidx);
                drv->monitor_ifidx = -1;
        }
+       if (drv->monitor_sock >= 0) {
+               eloop_unregister_read_sock(drv->monitor_sock);
+               close(drv->monitor_sock);
+               drv->monitor_sock = -1;
+       }
 }
 
 
@@ -3000,7 +3006,7 @@ nl80211_create_monitor_interface(struct wpa_driver_nl80211_data *drv)
        if (drv->monitor_ifidx < 0)
                return -1;
 
-       if (hostapd_set_iface_flags(drv, buf, 1))
+       if (linux_set_iface_flags(drv->ioctl_sock, buf, 1))
                goto error;
 
        memset(&ll, 0, sizeof(ll));
@@ -3349,13 +3355,13 @@ static int wpa_driver_nl80211_connect(
                NLA_PUT(msg, NL80211_ATTR_IE, params->wpa_ie_len,
                        params->wpa_ie);
 
-       if (params->auth_alg & AUTH_ALG_OPEN_SYSTEM)
+       if (params->auth_alg & WPA_AUTH_ALG_OPEN)
                type = NL80211_AUTHTYPE_OPEN_SYSTEM;
-       else if (params->auth_alg & AUTH_ALG_SHARED_KEY)
+       else if (params->auth_alg & WPA_AUTH_ALG_SHARED)
                type = NL80211_AUTHTYPE_SHARED_KEY;
-       else if (params->auth_alg & AUTH_ALG_LEAP)
+       else if (params->auth_alg & WPA_AUTH_ALG_LEAP)
                type = NL80211_AUTHTYPE_NETWORK_EAP;
-       else if (params->auth_alg & AUTH_ALG_FT)
+       else if (params->auth_alg & WPA_AUTH_ALG_FT)
                type = NL80211_AUTHTYPE_FT;
        else
                goto nla_put_failure;
@@ -3604,10 +3610,10 @@ static int wpa_driver_nl80211_set_mode(void *priv, int mode)
         * take the device down, try to set the mode again, and bring the
         * device back up.
         */
-       if (hostapd_set_iface_flags(drv, drv->ifname, 0) == 0) {
+       if (linux_set_iface_flags(drv->ioctl_sock, drv->ifname, 0) == 0) {
                /* Try to set the mode again while the interface is down */
                ret = nl80211_set_mode(drv, drv->ifindex, nlmode);
-               if (hostapd_set_iface_flags(drv, drv->ifname, 1))
+               if (linux_set_iface_flags(drv->ioctl_sock, drv->ifname, 1))
                        ret = -1;
        }
 
@@ -4144,7 +4150,7 @@ static int i802_set_wds_sta(void *priv, const u8 *addr, int aid, int val)
                if (nl80211_create_iface(priv, name, NL80211_IFTYPE_AP_VLAN,
                                         NULL, 1) < 0)
                        return -1;
-               hostapd_set_iface_flags(drv, name, 1);
+               linux_set_iface_flags(drv->ioctl_sock, name, 1);
                return i802_set_sta_vlan(priv, addr, name, 0);
        } else {
                i802_set_sta_vlan(priv, addr, drv->ifname, 0);
@@ -4169,13 +4175,8 @@ static void handle_eapol(int sock, void *eloop_ctx, void *sock_ctx)
                return;
        }
 
-       if (have_ifidx(drv, lladdr.sll_ifindex)) {
-               void *ctx;
-               ctx = hostapd_sta_get_bss(drv->ctx, lladdr.sll_addr);
-               if (!ctx)
-                       return;
-               hostapd_eapol_receive(ctx, lladdr.sll_addr, buf, len);
-       }
+       if (have_ifidx(drv, lladdr.sll_ifindex))
+               drv_event_eapol_rx(drv->ctx, lladdr.sll_addr, buf, len);
 }
 
 
@@ -4261,11 +4262,12 @@ static void *i802_init(struct hostapd_data *hapd,
        /* start listening for EAPOL on the default AP interface */
        add_ifidx(drv, drv->ifindex);
 
-       if (hostapd_set_iface_flags(drv, drv->ifname, 0))
+       if (linux_set_iface_flags(drv->ioctl_sock, drv->ifname, 0))
                goto failed;
 
        if (params->bssid) {
-               if (set_ifhwaddr(drv, drv->ifname, params->bssid))
+               if (linux_set_ifhwaddr(drv->ioctl_sock, drv->ifname,
+                                      params->bssid))
                        goto failed;
        }
 
@@ -4275,7 +4277,7 @@ static void *i802_init(struct hostapd_data *hapd,
                goto failed;
        }
 
-       if (hostapd_set_iface_flags(drv, drv->ifname, 1))
+       if (linux_set_iface_flags(drv->ioctl_sock, drv->ifname, 1))
                goto failed;
 
        drv->eapol_sock = socket(PF_PACKET, SOCK_DGRAM, htons(ETH_P_PAE));
@@ -4290,7 +4292,7 @@ static void *i802_init(struct hostapd_data *hapd,
                goto failed;
        }
 
-       if (get_ifhwaddr(drv, drv->ifname, params->own_addr))
+       if (linux_get_ifhwaddr(drv->ioctl_sock, drv->ifname, params->own_addr))
                goto failed;
 
        return drv;
@@ -4362,8 +4364,8 @@ static int wpa_driver_nl80211_if_add(const char *iface, void *priv,
 
 #ifdef HOSTAPD
        if (type == WPA_IF_AP_BSS) {
-               if (hostapd_set_iface_flags(priv, ifname, 1)) {
-                       nl80211_remove_iface(priv, ifidx);
+               if (linux_set_iface_flags(drv->ioctl_sock, ifname, 1)) {
+                       nl80211_remove_iface(drv, ifidx);
                        os_free(bss);
                        return -1;
                }
@@ -4407,6 +4409,226 @@ static int wpa_driver_nl80211_if_remove(void *priv,
 }
 
 
+static int cookie_handler(struct nl_msg *msg, void *arg)
+{
+       struct nlattr *tb[NL80211_ATTR_MAX + 1];
+       struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
+       u64 *cookie = arg;
+       nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
+                 genlmsg_attrlen(gnlh, 0), NULL);
+       if (tb[NL80211_ATTR_COOKIE])
+               *cookie = nla_get_u64(tb[NL80211_ATTR_COOKIE]);
+       return NL_SKIP;
+}
+
+
+static int wpa_driver_nl80211_remain_on_channel(void *priv, unsigned int freq,
+                                               unsigned int duration)
+{
+       struct wpa_driver_nl80211_data *drv = priv;
+       struct nl_msg *msg;
+       int ret;
+       u64 cookie;
+
+       msg = nlmsg_alloc();
+       if (!msg)
+               return -1;
+
+       genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, 0,
+                   NL80211_CMD_REMAIN_ON_CHANNEL, 0);
+
+       NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex);
+       NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_FREQ, freq);
+       NLA_PUT_U32(msg, NL80211_ATTR_DURATION, duration);
+
+       cookie = 0;
+       ret = send_and_recv_msgs(drv, msg, cookie_handler, &cookie);
+       if (ret == 0) {
+               wpa_printf(MSG_DEBUG, "nl80211: Remain-on-channel cookie "
+                          "0x%llx for freq=%u MHz duration=%u",
+                          (long long unsigned int) cookie, freq, duration);
+               drv->remain_on_chan_cookie = cookie;
+               return 0;
+       }
+       wpa_printf(MSG_DEBUG, "nl80211: Failed to request remain-on-channel "
+                  "(freq=%d): %d (%s)", freq, ret, strerror(-ret));
+nla_put_failure:
+       return -1;
+}
+
+
+static int wpa_driver_nl80211_cancel_remain_on_channel(void *priv)
+{
+       struct wpa_driver_nl80211_data *drv = priv;
+       struct nl_msg *msg;
+       int ret;
+
+       if (!drv->pending_remain_on_chan) {
+               wpa_printf(MSG_DEBUG, "nl80211: No pending remain-on-channel "
+                          "to cancel");
+               return -1;
+       }
+
+       wpa_printf(MSG_DEBUG, "nl80211: Cancel remain-on-channel with cookie "
+                  "0x%llx",
+                  (long long unsigned int) drv->remain_on_chan_cookie);
+
+       msg = nlmsg_alloc();
+       if (!msg)
+               return -1;
+
+       genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, 0,
+                   NL80211_CMD_CANCEL_REMAIN_ON_CHANNEL, 0);
+
+       NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex);
+       NLA_PUT_U64(msg, NL80211_ATTR_COOKIE, drv->remain_on_chan_cookie);
+
+       ret = send_and_recv_msgs(drv, msg, NULL, NULL);
+       if (ret == 0)
+               return 0;
+       wpa_printf(MSG_DEBUG, "nl80211: Failed to cancel remain-on-channel: "
+                  "%d (%s)", ret, strerror(-ret));
+nla_put_failure:
+       return -1;
+}
+
+
+static void wpa_driver_nl80211_probe_req_report_timeout(void *eloop_ctx,
+                                                       void *timeout_ctx)
+{
+       struct wpa_driver_nl80211_data *drv = eloop_ctx;
+       if (drv->monitor_ifidx < 0)
+               return; /* monitor interface already removed */
+
+       if (drv->nlmode != NL80211_IFTYPE_STATION)
+               return; /* not in station mode anymore */
+
+       if (drv->probe_req_report)
+               return; /* reporting enabled */
+
+       wpa_printf(MSG_DEBUG, "nl80211: Remove monitor interface due to no "
+                  "Probe Request reporting needed anymore");
+       nl80211_remove_monitor_interface(drv);
+}
+
+
+static int wpa_driver_nl80211_probe_req_report(void *priv, int report)
+{
+       struct wpa_driver_nl80211_data *drv = priv;
+
+       if (drv->nlmode != NL80211_IFTYPE_STATION) {
+               wpa_printf(MSG_DEBUG, "nl80211: probe_req_report control only "
+                          "allowed in station mode (iftype=%d)",
+                          drv->nlmode);
+               return -1;
+       }
+       drv->probe_req_report = report;
+
+       if (report) {
+               eloop_cancel_timeout(
+                       wpa_driver_nl80211_probe_req_report_timeout,
+                       drv, NULL);
+               if (drv->monitor_ifidx < 0 &&
+                   nl80211_create_monitor_interface(drv))
+                       return -1;
+       } else {
+               /*
+                * It takes a while to remove the monitor interface, so try to
+                * avoid doing this if it is needed again shortly. Instead,
+                * schedule the interface to be removed later if no need for it
+                * is seen.
+                */
+               wpa_printf(MSG_DEBUG, "nl80211: Scheduling monitor interface "
+                          "to be removed after 10 seconds of no use");
+               eloop_register_timeout(
+                       10, 0, wpa_driver_nl80211_probe_req_report_timeout,
+                       drv, NULL);
+       }
+
+       return 0;
+}
+
+
+static int wpa_driver_nl80211_alloc_interface_addr(void *priv, u8 *addr)
+{
+       struct wpa_driver_nl80211_data *drv = priv;
+
+       if (linux_get_ifhwaddr(drv->ioctl_sock, drv->ifname, addr) < 0)
+               return -1;
+
+       if (addr[0] & 0x02) {
+               /* TODO: add support for generating multiple addresses */
+               addr[0] ^= 0x80;
+       } else
+               addr[0] = 0x02; /* locally administered */
+
+       return 0;
+}
+
+
+static void wpa_driver_nl80211_release_interface_addr(void *priv,
+                                                     const u8 *addr)
+{
+       /* TODO: keep list of allocated address and release them here */
+}
+
+
+static int nl80211_disable_11b_rates(struct wpa_driver_nl80211_data *drv,
+                                    int ifindex, int disabled)
+{
+       struct nl_msg *msg;
+       struct nlattr *bands, *band;
+       int ret;
+
+       msg = nlmsg_alloc();
+       if (!msg)
+               return -1;
+
+       genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, 0,
+                   NL80211_CMD_SET_TX_BITRATE_MASK, 0);
+       NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, ifindex);
+
+       bands = nla_nest_start(msg, NL80211_ATTR_TX_RATES);
+       if (!bands)
+               goto nla_put_failure;
+
+       /*
+        * Disable 2 GHz rates 1, 2, 5.5, 11 Mbps by masking out everything
+        * else apart from 6, 9, 12, 18, 24, 36, 48, 54 Mbps from non-MCS
+        * rates. All 5 GHz rates are left enabled.
+        */
+       band = nla_nest_start(msg, NL80211_BAND_2GHZ);
+       if (!band)
+               goto nla_put_failure;
+       NLA_PUT(msg, NL80211_TXRATE_LEGACY, 8,
+               "\x0c\x12\x18\x24\x30\x48\x60\x6c");
+       nla_nest_end(msg, band);
+
+       nla_nest_end(msg, bands);
+
+       ret = send_and_recv_msgs(drv, msg, NULL, NULL);
+       msg = NULL;
+       if (ret) {
+               wpa_printf(MSG_DEBUG, "nl80211: Set TX rates failed: ret=%d "
+                          "(%s)", ret, strerror(-ret));
+       }
+
+       return ret;
+
+nla_put_failure:
+       nlmsg_free(msg);
+       return -1;
+}
+
+
+static int wpa_driver_nl80211_disable_11b_rates(void *priv, int disabled)
+{
+       struct wpa_driver_nl80211_data *drv = priv;
+       drv->disable_11b_rates = disabled;
+       return nl80211_disable_11b_rates(drv, drv->ifindex, disabled);
+}
+
+
 const struct wpa_driver_ops wpa_driver_nl80211_ops = {
        .name = "nl80211",
        .desc = "Linux nl80211/cfg80211",
@@ -4455,4 +4677,11 @@ const struct wpa_driver_ops wpa_driver_nl80211_ops = {
        .set_sta_vlan = i802_set_sta_vlan,
        .set_wds_sta = i802_set_wds_sta,
 #endif /* HOSTAPD */
+       .remain_on_channel = wpa_driver_nl80211_remain_on_channel,
+       .cancel_remain_on_channel =
+       wpa_driver_nl80211_cancel_remain_on_channel,
+       .probe_req_report = wpa_driver_nl80211_probe_req_report,
+       .alloc_interface_addr = wpa_driver_nl80211_alloc_interface_addr,
+       .release_interface_addr = wpa_driver_nl80211_release_interface_addr,
+       .disable_11b_rates = wpa_driver_nl80211_disable_11b_rates,
 };