Remove src/drivers/scan_helpers.c
[mech_eap.git] / src / drivers / driver_nl80211.c
index 2359a67..89acdae 100644 (file)
@@ -1,6 +1,6 @@
 /*
  * Driver interaction with Linux nl80211/cfg80211
- * Copyright (c) 2002-2008, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2002-2010, Jouni Malinen <j@w1.fi>
  * Copyright (c) 2003-2004, Instant802 Networks, Inc.
  * Copyright (c) 2005-2006, Devicescape Software, Inc.
  * Copyright (c) 2007, Johannes Berg <johannes@sipsolutions.net>
@@ -91,11 +91,16 @@ struct wpa_driver_nl80211_data {
        size_t ssid_len;
        int nlmode;
        int ap_scan_as_station;
+       unsigned int assoc_freq;
 
        int monitor_sock;
        int monitor_ifidx;
+       int probe_req_report;
 
        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 */
@@ -128,8 +133,14 @@ static void del_ifidx(struct wpa_driver_nl80211_data *drv, int ifidx);
 static struct i802_bss * get_bss(struct wpa_driver_nl80211_data *drv,
                                 int ifindex);
 static int i802_set_freq(void *priv, struct hostapd_freq_params *freq);
+static int wpa_driver_nl80211_if_remove(void *priv,
+                                       enum wpa_driver_if_type type,
+                                       const char *ifname);
 #endif /* HOSTAPD */
 
+static void wpa_driver_nl80211_probe_req_report_timeout(void *eloop_ctx,
+                                                       void *timeout_ctx);
+
 
 /* nl80211 code */
 static int ack_handler(struct nl_msg *msg, void *arg)
@@ -258,7 +269,6 @@ nla_put_failure:
 }
 
 
-#ifdef HOSTAPD
 static int get_ifhwaddr(struct wpa_driver_nl80211_data *drv,
                        const char *ifname, u8 *addr)
 {
@@ -301,7 +311,6 @@ static int set_ifhwaddr(struct wpa_driver_nl80211_data *drv,
 
        return 0;
 }
-#endif /* HOSTAPD */
 
 
 static int wpa_driver_nl80211_get_bssid(void *priv, u8 *bssid)
@@ -533,6 +542,8 @@ static void mlme_event_assoc(struct wpa_driver_nl80211_data *drv,
                        len - 24 - sizeof(mgmt->u.assoc_resp);
        }
 
+       event.assoc_info.freq = drv->assoc_freq;
+
        wpa_supplicant_event(drv->ctx, EVENT_ASSOC, &event);
 }
 
@@ -701,6 +712,94 @@ 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[])
+{
+       union wpa_event_data event;
+       struct nlattr *nl;
+       int rem;
+       struct scan_info *info;
+#define MAX_REPORT_FREQS 50
+       int freqs[MAX_REPORT_FREQS];
+       int num_freqs = 0;
+
+       os_memset(&event, 0, sizeof(event));
+       info = &event.scan_info;
+       info->aborted = aborted;
+
+       if (tb[NL80211_ATTR_SCAN_SSIDS]) {
+               nla_for_each_nested(nl, tb[NL80211_ATTR_SCAN_SSIDS], rem) {
+                       struct wpa_driver_scan_ssid *s =
+                               &info->ssids[info->num_ssids];
+                       s->ssid = nla_data(nl);
+                       s->ssid_len = nla_len(nl);
+                       info->num_ssids++;
+                       if (info->num_ssids == WPAS_MAX_SCAN_SSIDS)
+                               break;
+               }
+       }
+       if (tb[NL80211_ATTR_SCAN_FREQUENCIES]) {
+               nla_for_each_nested(nl, tb[NL80211_ATTR_SCAN_FREQUENCIES], rem)
+               {
+                       freqs[num_freqs] = nla_get_u32(nl);
+                       num_freqs++;
+                       if (num_freqs == MAX_REPORT_FREQS - 1)
+                               break;
+               }
+               info->freqs = freqs;
+               info->num_freqs = num_freqs;
+       }
+       wpa_supplicant_event(drv->ctx, EVENT_SCAN_RESULTS, &event);
+}
+
+
 static int process_event(struct nl_msg *msg, void *arg)
 {
        struct wpa_driver_nl80211_data *drv = arg;
@@ -736,7 +835,7 @@ static int process_event(struct nl_msg *msg, void *arg)
                drv->scan_complete_events = 1;
                eloop_cancel_timeout(wpa_driver_nl80211_scan_timeout, drv,
                                     drv->ctx);
-               wpa_supplicant_event(drv->ctx, EVENT_SCAN_RESULTS, NULL);
+               send_scan_event(drv, 0, tb);
                break;
        case NL80211_CMD_SCAN_ABORTED:
                wpa_printf(MSG_DEBUG, "nl80211: Scan aborted");
@@ -746,7 +845,7 @@ static int process_event(struct nl_msg *msg, void *arg)
                 */
                eloop_cancel_timeout(wpa_driver_nl80211_scan_timeout, drv,
                                     drv->ctx);
-               wpa_supplicant_event(drv->ctx, EVENT_SCAN_RESULTS, NULL);
+               send_scan_event(drv, 1, tb);
                break;
        case NL80211_CMD_AUTHENTICATE:
        case NL80211_CMD_ASSOCIATE:
@@ -782,6 +881,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);
@@ -973,6 +1078,9 @@ static int wpa_driver_nl80211_capa(struct wpa_driver_nl80211_data *drv)
                WPA_DRIVER_CAPA_ENC_WEP104 |
                WPA_DRIVER_CAPA_ENC_TKIP |
                WPA_DRIVER_CAPA_ENC_CCMP;
+       drv->capa.auth = WPA_DRIVER_AUTH_OPEN |
+               WPA_DRIVER_AUTH_SHARED |
+               WPA_DRIVER_AUTH_LEAP;
 
        drv->capa.max_scan_ssids = info.max_scan_ssids;
        if (info.ap_supported)
@@ -1242,10 +1350,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);
@@ -1289,6 +1393,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);
 }
 
@@ -1575,6 +1682,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()
@@ -1617,8 +1738,8 @@ nla_put_failure:
 
 
 static int wpa_driver_nl80211_set_key(const char *ifname, void *priv,
-                                     wpa_alg alg, const u8 *addr, int key_idx,
-                                     int set_tx,
+                                     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)
 {
@@ -1727,7 +1848,7 @@ nla_put_failure:
 }
 
 
-static int nl_add_key(struct nl_msg *msg, wpa_alg alg,
+static int nl_add_key(struct nl_msg *msg, enum wpa_alg alg,
                      int key_idx, int defkey,
                      const u8 *seq, size_t seq_len,
                      const u8 *key, size_t key_len)
@@ -2056,7 +2177,7 @@ static int phy_info_handler(struct nl_msg *msg, void *arg)
                return NL_SKIP;
 
        nla_for_each_nested(nl_band, tb_msg[NL80211_ATTR_WIPHY_BANDS], rem_band) {
-               mode = realloc(phy_info->modes, (*phy_info->num_modes + 1) * sizeof(*mode));
+               mode = os_realloc(phy_info->modes, (*phy_info->num_modes + 1) * sizeof(*mode));
                if (!mode)
                        return NL_SKIP;
                phy_info->modes = mode;
@@ -2102,7 +2223,7 @@ static int phy_info_handler(struct nl_msg *msg, void *arg)
                        mode->num_channels++;
                }
 
-               mode->channels = calloc(mode->num_channels, sizeof(struct hostapd_channel_data));
+               mode->channels = os_zalloc(mode->num_channels * sizeof(struct hostapd_channel_data));
                if (!mode->channels)
                        return NL_SKIP;
 
@@ -2164,7 +2285,7 @@ static int phy_info_handler(struct nl_msg *msg, void *arg)
                        mode->num_rates++;
                }
 
-               mode->rates = calloc(mode->num_rates, sizeof(int));
+               mode->rates = os_zalloc(mode->num_rates * sizeof(int));
                if (!mode->rates)
                        return NL_SKIP;
 
@@ -2529,6 +2650,8 @@ static void nl80211_remove_iface(struct wpa_driver_nl80211_data *drv,
 {
        struct nl_msg *msg;
 
+       wpa_printf(MSG_DEBUG, "nl80211: Remove interface ifindex=%d", ifidx);
+
 #ifdef HOSTAPD
        /* stop listening for EAPOL on this interface */
        del_ifidx(drv, ifidx);
@@ -2553,7 +2676,7 @@ static void nl80211_remove_iface(struct wpa_driver_nl80211_data *drv,
 static int nl80211_create_iface_once(struct wpa_driver_nl80211_data *drv,
                                     const char *ifname,
                                     enum nl80211_iftype iftype,
-                                    const u8 *addr)
+                                    const u8 *addr, int wds)
 {
        struct nl_msg *msg, *flags = NULL;
        int ifidx;
@@ -2584,6 +2707,8 @@ static int nl80211_create_iface_once(struct wpa_driver_nl80211_data *drv,
 
                if (err)
                        goto nla_put_failure;
+       } else if (wds) {
+               NLA_PUT_U8(msg, NL80211_ATTR_4ADDR, wds);
        }
 
        ret = send_and_recv_msgs(drv, msg, NULL, NULL);
@@ -2595,6 +2720,8 @@ static int nl80211_create_iface_once(struct wpa_driver_nl80211_data *drv,
        }
 
        ifidx = if_nametoindex(ifname);
+       wpa_printf(MSG_DEBUG, "nl80211: New interface %s created: ifindex=%d",
+                  ifname, ifidx);
 
        if (ifidx <= 0)
                return -1;
@@ -2602,13 +2729,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 &&
+       if (addr && iftype != NL80211_IFTYPE_MONITOR &&
            set_ifhwaddr(drv, ifname, addr)) {
                nl80211_remove_iface(drv, ifidx);
                return -1;
        }
-#endif /* HOSTAPD */
 
        return ifidx;
 }
@@ -2616,11 +2743,11 @@ static int nl80211_create_iface_once(struct wpa_driver_nl80211_data *drv,
 
 static int nl80211_create_iface(struct wpa_driver_nl80211_data *drv,
                                const char *ifname, enum nl80211_iftype iftype,
-                               const u8 *addr)
+                               const u8 *addr, int wds)
 {
        int ret;
 
-       ret = nl80211_create_iface_once(drv, ifname, iftype, addr);
+       ret = nl80211_create_iface_once(drv, ifname, iftype, addr, wds);
 
        /* if error occured and interface exists already */
        if (ret == -ENFILE && if_nametoindex(ifname)) {
@@ -2630,7 +2757,8 @@ static int nl80211_create_iface(struct wpa_driver_nl80211_data *drv,
                nl80211_remove_iface(drv, if_nametoindex(ifname));
 
                /* Try to create the interface again */
-               ret = nl80211_create_iface_once(drv, ifname, iftype, addr);
+               ret = nl80211_create_iface_once(drv, ifname, iftype, addr,
+                                               wds);
        }
 
        return ret;
@@ -2658,19 +2786,18 @@ static void handle_tx_callback(void *ctx, u8 *buf, size_t len, int ok)
 
 
 static void from_unknown_sta(struct wpa_driver_nl80211_data *drv,
-                            struct ieee80211_hdr *hdr, size_t len)
+                            u8 *buf, size_t len)
 {
        union wpa_event_data event;
        os_memset(&event, 0, sizeof(event));
-       event.rx_from_unknown.hdr = hdr;
+       event.rx_from_unknown.frame = buf;
        event.rx_from_unknown.len = len;
        wpa_supplicant_event(drv->ctx, EVENT_RX_FROM_UNKNOWN, &event);
 }
 
 
 static void handle_frame(struct wpa_driver_nl80211_data *drv,
-                        u8 *buf, size_t len,
-                        struct hostapd_frame_info *hfi)
+                        u8 *buf, size_t len, int datarate, int ssi_signal)
 {
        struct ieee80211_hdr *hdr;
        u16 fc;
@@ -2684,16 +2811,17 @@ 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:
                /* can only get here with PS-Poll frames */
                wpa_printf(MSG_DEBUG, "CTRL");
-               from_unknown_sta(drv, hdr, len);
+               from_unknown_sta(drv, buf, len);
                break;
        case WLAN_FC_TYPE_DATA:
-               from_unknown_sta(drv, hdr, len);
+               from_unknown_sta(drv, buf, len);
                break;
        }
 }
@@ -2706,7 +2834,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);
@@ -2715,13 +2843,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)
@@ -2746,15 +2878,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;
                }
        }
@@ -2764,7 +2894,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);
@@ -2817,7 +2947,7 @@ static struct sock_filter msock_filter_insns[] = {
 
 #if 0
        /*
-        * drop non-data frames, WDS frames
+        * drop non-data frames
         */
        /* load the lower byte of the frame control field */
        BPF_STMT(BPF_LD   | BPF_B | BPF_IND, 0),
@@ -2825,13 +2955,13 @@ static struct sock_filter msock_filter_insns[] = {
        BPF_STMT(BPF_ALU  | BPF_AND | BPF_K, 0x0c),
        /* drop non-data frames */
        BPF_JUMP(BPF_JMP  | BPF_JEQ | BPF_K, 8, 0, FAIL),
+#endif
        /* load the upper byte of the frame control field */
-       BPF_STMT(BPF_LD   | BPF_B | BPF_IND, 0),
+       BPF_STMT(BPF_LD   | BPF_B | BPF_IND, 1),
        /* mask off toDS/fromDS */
        BPF_STMT(BPF_ALU  | BPF_AND | BPF_K, 0x03),
-       /* drop WDS frames */
-       BPF_JUMP(BPF_JMP  | BPF_JEQ | BPF_K, 3, FAIL, 0),
-#endif
+       /* accept WDS frames */
+       BPF_JUMP(BPF_JMP  | BPF_JEQ | BPF_K, 3, PASS, 0),
 
        /*
         * add header length to index
@@ -2922,6 +3052,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;
+       }
 }
 
 
@@ -2937,7 +3072,8 @@ nl80211_create_monitor_interface(struct wpa_driver_nl80211_data *drv)
        buf[IFNAMSIZ - 1] = '\0';
 
        drv->monitor_ifidx =
-               nl80211_create_iface(drv, buf, NL80211_IFTYPE_MONITOR, NULL);
+               nl80211_create_iface(drv, buf, NL80211_IFTYPE_MONITOR, NULL,
+                                    0);
 
        if (drv->monitor_ifidx < 0)
                return -1;
@@ -3050,7 +3186,7 @@ static int wpa_driver_nl80211_hapd_send_eapol(
                           "failed: %d (%s)",
                           (unsigned long) len, errno, strerror(errno));
        }
-       free(hdr);
+       os_free(hdr);
 
        return res;
 }
@@ -3435,7 +3571,9 @@ static int wpa_driver_nl80211_associate(
        if (params->freq) {
                wpa_printf(MSG_DEBUG, "  * freq=%d", params->freq);
                NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_FREQ, params->freq);
-       }
+               drv->assoc_freq = params->freq;
+       } else
+               drv->assoc_freq = 0;
        if (params->ssid) {
                wpa_hexdump_ascii(MSG_DEBUG, "  * SSID",
                                  params->ssid, params->ssid_len);
@@ -3534,6 +3672,8 @@ static int wpa_driver_nl80211_set_mode(void *priv, int mode)
        }
 
        if (nlmode == drv->nlmode) {
+               wpa_printf(MSG_DEBUG, "nl80211: Interface already in "
+                          "requested mode - ignore error");
                ret = 0;
                goto done; /* Already in the requested mode */
        }
@@ -3549,8 +3689,11 @@ static int wpa_driver_nl80211_set_mode(void *priv, int mode)
                        ret = -1;
        }
 
-       if (!ret)
+       if (!ret) {
+               wpa_printf(MSG_DEBUG, "nl80211: Mode change succeeded while "
+                          "interface is down");
                drv->nlmode = nlmode;
+       }
 
 done:
        if (!ret && nlmode == NL80211_IFTYPE_AP) {
@@ -3563,6 +3706,10 @@ done:
                nl80211_remove_monitor_interface(drv);
        }
 
+       if (ret)
+               wpa_printf(MSG_DEBUG, "nl80211: Interface mode change to %d "
+                          "from %d failed", nlmode, drv->nlmode);
+
        return ret;
 }
 
@@ -3654,8 +3801,8 @@ static void add_ifidx(struct wpa_driver_nl80211_data *drv, int ifidx)
        else
                old = NULL;
 
-       drv->if_indices = realloc(old,
-                                 sizeof(int) * (drv->num_if_indices + 1));
+       drv->if_indices = os_realloc(old,
+                                    sizeof(int) * (drv->num_if_indices + 1));
        if (!drv->if_indices) {
                if (!old)
                        drv->if_indices = drv->default_if_indices;
@@ -4065,6 +4212,26 @@ static int i802_set_sta_vlan(void *priv, const u8 *addr,
 }
 
 
+static int i802_set_wds_sta(void *priv, const u8 *addr, int aid, int val)
+{
+       struct wpa_driver_nl80211_data *drv = priv;
+       char name[16];
+
+       os_snprintf(name, sizeof(name), "%s.sta%d", drv->ifname, aid);
+       if (val) {
+               if (nl80211_create_iface(priv, name, NL80211_IFTYPE_AP_VLAN,
+                                        NULL, 1) < 0)
+                       return -1;
+               hostapd_set_iface_flags(drv, name, 1);
+               return i802_set_sta_vlan(priv, addr, name, 0);
+       } else {
+               i802_set_sta_vlan(priv, addr, drv->ifname, 0);
+               return wpa_driver_nl80211_if_remove(priv, WPA_IF_AP_VLAN,
+                                                   name);
+       }
+}
+
+
 static void handle_eapol(int sock, void *eloop_ctx, void *sock_ctx)
 {
        struct wpa_driver_nl80211_data *drv = eloop_ctx;
@@ -4080,13 +4247,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);
 }
 
 
@@ -4262,7 +4424,8 @@ static int wpa_driver_nl80211_if_add(const char *iface, void *priv,
 #endif /* HOSTAPD */
 
        ifidx = nl80211_create_iface(drv, ifname,
-                                    wpa_driver_nl80211_if_type(type), addr);
+                                    wpa_driver_nl80211_if_type(type), addr,
+                                    0);
        if (ifidx < 0) {
 #ifdef HOSTAPD
                os_free(bss);
@@ -4317,6 +4480,170 @@ 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 (get_ifhwaddr(drv, 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 */
+}
+
+
 const struct wpa_driver_ops wpa_driver_nl80211_ops = {
        .name = "nl80211",
        .desc = "Linux nl80211/cfg80211",
@@ -4363,5 +4690,12 @@ const struct wpa_driver_ops wpa_driver_nl80211_ops = {
        .set_short_slot_time = i802_set_short_slot_time,
        .set_tx_queue_params = i802_set_tx_queue_params,
        .set_sta_vlan = i802_set_sta_vlan,
+       .set_wds_sta = i802_set_wds_sta,
 #endif /* HOSTAPD */
+       .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,
 };