nl80211: Send STA flags to kernel on station addition
[mech_eap.git] / src / drivers / driver_nl80211.c
index a385224..1da5092 100644 (file)
@@ -112,7 +112,10 @@ struct i802_bss {
        struct i802_bss *next;
        int ifindex;
        char ifname[IFNAMSIZ + 1];
+       char brname[IFNAMSIZ];
        unsigned int beacon_set:1;
+       unsigned int added_if_into_bridge:1;
+       unsigned int added_bridge:1;
 };
 
 struct wpa_driver_nl80211_data {
@@ -123,7 +126,6 @@ struct wpa_driver_nl80211_data {
        void *ctx;
        struct netlink_data *netlink;
        int ioctl_sock; /* socket for ioctl() use */
-       char brname[IFNAMSIZ];
        int ifindex;
        int if_removed;
        int if_disabled;
@@ -155,11 +157,10 @@ struct wpa_driver_nl80211_data {
 
        int monitor_sock;
        int monitor_ifidx;
+       int no_monitor_iface_capab;
        int disable_11b_rates;
 
        unsigned int pending_remain_on_chan:1;
-       unsigned int added_bridge:1;
-       unsigned int added_if_into_bridge:1;
 
        u64 remain_on_chan_cookie;
        u64 send_action_cookie;
@@ -195,8 +196,8 @@ static int wpa_driver_nl80211_mlme(struct wpa_driver_nl80211_data *drv,
 static void nl80211_remove_monitor_interface(
        struct wpa_driver_nl80211_data *drv);
 static int nl80211_send_frame_cmd(struct wpa_driver_nl80211_data *drv,
-                                 unsigned int freq, const u8 *buf,
-                                 size_t buf_len, u64 *cookie);
+                                 unsigned int freq, unsigned int wait,
+                                 const u8 *buf, size_t buf_len, u64 *cookie);
 static int wpa_driver_nl80211_probe_req_report(void *priv, int report);
 
 #ifdef HOSTAPD
@@ -217,6 +218,17 @@ static int i802_set_freq(void *priv, struct hostapd_freq_params *freq);
 static int nl80211_disable_11b_rates(struct wpa_driver_nl80211_data *drv,
                                     int ifindex, int disabled);
 
+static int nl80211_leave_ibss(struct wpa_driver_nl80211_data *drv);
+
+
+struct nl80211_bss_info_arg {
+       struct wpa_driver_nl80211_data *drv;
+       struct wpa_scan_results *res;
+       unsigned int assoc_freq;
+};
+
+static int bss_info_handler(struct nl_msg *msg, void *arg);
+
 
 /* nl80211 code */
 static int ack_handler(struct nl_msg *msg, void *arg)
@@ -587,6 +599,38 @@ static void mlme_event_auth(struct wpa_driver_nl80211_data *drv,
 }
 
 
+static unsigned int nl80211_get_assoc_freq(struct wpa_driver_nl80211_data *drv)
+{
+       struct nl_msg *msg;
+       int ret;
+       struct nl80211_bss_info_arg arg;
+
+       os_memset(&arg, 0, sizeof(arg));
+       msg = nlmsg_alloc();
+       if (!msg)
+               goto nla_put_failure;
+
+       genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, NLM_F_DUMP,
+                   NL80211_CMD_GET_SCAN, 0);
+       NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex);
+
+       arg.drv = drv;
+       ret = send_and_recv_msgs(drv, msg, bss_info_handler, &arg);
+       msg = NULL;
+       if (ret == 0) {
+               wpa_printf(MSG_DEBUG, "nl80211: Operating frequency for the "
+                          "associated BSS from scan results: %u MHz",
+                          arg.assoc_freq);
+               return arg.assoc_freq ? arg.assoc_freq : drv->assoc_freq;
+       }
+       wpa_printf(MSG_DEBUG, "nl80211: Scan result fetch failed: ret=%d "
+                  "(%s)", ret, strerror(-ret));
+nla_put_failure:
+       nlmsg_free(msg);
+       return drv->assoc_freq;
+}
+
+
 static void mlme_event_assoc(struct wpa_driver_nl80211_data *drv,
                            const u8 *frame, size_t len)
 {
@@ -677,6 +721,8 @@ static void mlme_event_connect(struct wpa_driver_nl80211_data *drv,
                event.assoc_info.resp_ies_len = nla_len(resp_ie);
        }
 
+       event.assoc_info.freq = nl80211_get_assoc_freq(drv);
+
        wpa_supplicant_event(drv->ctx, EVENT_ASSOC, &event);
 }
 
@@ -835,6 +881,38 @@ static void mlme_event_deauth_disassoc(struct wpa_driver_nl80211_data *drv,
 }
 
 
+static void mlme_event_unprot_disconnect(struct wpa_driver_nl80211_data *drv,
+                                        enum wpa_event_type type,
+                                        const u8 *frame, size_t len)
+{
+       const struct ieee80211_mgmt *mgmt;
+       union wpa_event_data event;
+       u16 reason_code = 0;
+
+       if (len < 24)
+               return;
+
+       mgmt = (const struct ieee80211_mgmt *) frame;
+
+       os_memset(&event, 0, sizeof(event));
+       /* Note: Same offset for Reason Code in both frame subtypes */
+       if (len >= 24 + sizeof(mgmt->u.deauth))
+               reason_code = le_to_host16(mgmt->u.deauth.reason_code);
+
+       if (type == EVENT_UNPROT_DISASSOC) {
+               event.unprot_disassoc.sa = mgmt->sa;
+               event.unprot_disassoc.da = mgmt->da;
+               event.unprot_disassoc.reason_code = reason_code;
+       } else {
+               event.unprot_deauth.sa = mgmt->sa;
+               event.unprot_deauth.da = mgmt->da;
+               event.unprot_deauth.reason_code = reason_code;
+       }
+
+       wpa_supplicant_event(drv->ctx, type, &event);
+}
+
+
 static void mlme_event(struct wpa_driver_nl80211_data *drv,
                       enum nl80211_commands cmd, struct nlattr *frame,
                       struct nlattr *addr, struct nlattr *timed_out,
@@ -878,6 +956,14 @@ static void mlme_event(struct wpa_driver_nl80211_data *drv,
                mlme_event_action_tx_status(drv, cookie, nla_data(frame),
                                            nla_len(frame), ack);
                break;
+       case NL80211_CMD_UNPROT_DEAUTHENTICATE:
+               mlme_event_unprot_disconnect(drv, EVENT_UNPROT_DEAUTH,
+                                            nla_data(frame), nla_len(frame));
+               break;
+       case NL80211_CMD_UNPROT_DISASSOCIATE:
+               mlme_event_unprot_disconnect(drv, EVENT_UNPROT_DISASSOC,
+                                            nla_data(frame), nla_len(frame));
+               break;
        default:
                break;
        }
@@ -1040,7 +1126,7 @@ static int get_link_signal(struct nl_msg *msg, void *arg)
                [NL80211_RATE_INFO_40_MHZ_WIDTH] = { .type = NLA_FLAG },
                [NL80211_RATE_INFO_SHORT_GI] = { .type = NLA_FLAG },
        };
-       struct signal_change *sig_change = arg;
+       struct wpa_signal_info *sig_change = arg;
 
        nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
                  genlmsg_attrlen(gnlh, 0), NULL);
@@ -1073,7 +1159,7 @@ static int get_link_signal(struct nl_msg *msg, void *arg)
 
 
 static int nl80211_get_link_signal(struct wpa_driver_nl80211_data *drv,
-                                  struct signal_change *sig)
+                                  struct wpa_signal_info *sig)
 {
        struct nl_msg *msg;
 
@@ -1105,7 +1191,7 @@ static int get_link_noise(struct nl_msg *msg, void *arg)
                [NL80211_SURVEY_INFO_FREQUENCY] = { .type = NLA_U32 },
                [NL80211_SURVEY_INFO_NOISE] = { .type = NLA_U8 },
        };
-       struct signal_change *sig_change = arg;
+       struct wpa_signal_info *sig_change = arg;
 
        nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
                  genlmsg_attrlen(gnlh, 0), NULL);
@@ -1141,7 +1227,7 @@ static int get_link_noise(struct nl_msg *msg, void *arg)
 
 
 static int nl80211_get_link_noise(struct wpa_driver_nl80211_data *drv,
-                                 struct signal_change *sig_change)
+                                 struct wpa_signal_info *sig_change)
 {
        struct nl_msg *msg;
 
@@ -1170,11 +1256,12 @@ static void nl80211_cqm_event(struct wpa_driver_nl80211_data *drv,
                [NL80211_ATTR_CQM_RSSI_THOLD] = { .type = NLA_U32 },
                [NL80211_ATTR_CQM_RSSI_HYST] = { .type = NLA_U8 },
                [NL80211_ATTR_CQM_RSSI_THRESHOLD_EVENT] = { .type = NLA_U32 },
+               [NL80211_ATTR_CQM_PKT_LOSS_EVENT] = { .type = NLA_U32 },
        };
        struct nlattr *cqm[NL80211_ATTR_CQM_MAX + 1];
        enum nl80211_cqm_rssi_threshold_event event;
        union wpa_event_data ed;
-       struct signal_change sig;
+       struct wpa_signal_info sig;
        int res;
 
        if (tb[NL80211_ATTR_CQM] == NULL ||
@@ -1184,12 +1271,21 @@ static void nl80211_cqm_event(struct wpa_driver_nl80211_data *drv,
                return;
        }
 
+       os_memset(&ed, 0, sizeof(ed));
+
+       if (cqm[NL80211_ATTR_CQM_PKT_LOSS_EVENT]) {
+               if (!tb[NL80211_ATTR_MAC])
+                       return;
+               os_memcpy(ed.low_ack.addr, nla_data(tb[NL80211_ATTR_MAC]),
+                         ETH_ALEN);
+               wpa_supplicant_event(drv->ctx, EVENT_STATION_LOW_ACK, &ed);
+               return;
+       }
+
        if (cqm[NL80211_ATTR_CQM_RSSI_THRESHOLD_EVENT] == NULL)
                return;
        event = nla_get_u32(cqm[NL80211_ATTR_CQM_RSSI_THRESHOLD_EVENT]);
 
-       os_memset(&ed, 0, sizeof(ed));
-
        if (event == NL80211_CQM_RSSI_THRESHOLD_EVENT_HIGH) {
                wpa_printf(MSG_DEBUG, "nl80211: Connection quality monitor "
                           "event: RSSI high");
@@ -1230,6 +1326,20 @@ static void nl80211_new_station_event(struct wpa_driver_nl80211_data *drv,
                return;
        addr = nla_data(tb[NL80211_ATTR_MAC]);
        wpa_printf(MSG_DEBUG, "nl80211: New station " MACSTR, MAC2STR(addr));
+
+       if (drv->nlmode == NL80211_IFTYPE_AP &&
+           drv->no_monitor_iface_capab) {
+               u8 *ies = NULL;
+               size_t ies_len = 0;
+               if (tb[NL80211_ATTR_IE]) {
+                       ies = nla_data(tb[NL80211_ATTR_IE]);
+                       ies_len = nla_len(tb[NL80211_ATTR_IE]);
+               }
+               wpa_hexdump(MSG_DEBUG, "nl80211: Assoc Req IEs", ies, ies_len);
+               drv_event_assoc(drv->ctx, addr, ies, ies_len, 0);
+               return;
+       }
+
        if (drv->nlmode != NL80211_IFTYPE_ADHOC)
                return;
 
@@ -1239,6 +1349,33 @@ static void nl80211_new_station_event(struct wpa_driver_nl80211_data *drv,
 }
 
 
+static void nl80211_del_station_event(struct wpa_driver_nl80211_data *drv,
+                                     struct nlattr **tb)
+{
+       u8 *addr;
+       union wpa_event_data data;
+
+       if (tb[NL80211_ATTR_MAC] == NULL)
+               return;
+       addr = nla_data(tb[NL80211_ATTR_MAC]);
+       wpa_printf(MSG_DEBUG, "nl80211: Delete station " MACSTR,
+                  MAC2STR(addr));
+
+       if (drv->nlmode == NL80211_IFTYPE_AP &&
+           drv->no_monitor_iface_capab) {
+               drv_event_disassoc(drv->ctx, addr);
+               return;
+       }
+
+       if (drv->nlmode != NL80211_IFTYPE_ADHOC)
+               return;
+
+       os_memset(&data, 0, sizeof(data));
+       os_memcpy(data.ibss_peer_lost.peer, addr, ETH_ALEN);
+       wpa_supplicant_event(drv->ctx, EVENT_IBSS_PEER_LOST, &data);
+}
+
+
 static int process_event(struct nl_msg *msg, void *arg)
 {
        struct wpa_driver_nl80211_data *drv = arg;
@@ -1294,6 +1431,8 @@ static int process_event(struct nl_msg *msg, void *arg)
        case NL80211_CMD_DISASSOCIATE:
        case NL80211_CMD_FRAME:
        case NL80211_CMD_FRAME_TX_STATUS:
+       case NL80211_CMD_UNPROT_DEAUTHENTICATE:
+       case NL80211_CMD_UNPROT_DISASSOCIATE:
                mlme_event(drv, gnlh->cmd, tb[NL80211_ATTR_FRAME],
                           tb[NL80211_ATTR_MAC], tb[NL80211_ATTR_TIMED_OUT],
                           tb[NL80211_ATTR_WIPHY_FREQ], tb[NL80211_ATTR_ACK],
@@ -1352,6 +1491,9 @@ static int process_event(struct nl_msg *msg, void *arg)
        case NL80211_CMD_NEW_STATION:
                nl80211_new_station_event(drv, tb);
                break;
+       case NL80211_CMD_DEL_STATION:
+               nl80211_del_station_event(drv, tb);
+               break;
        default:
                wpa_printf(MSG_DEBUG, "nl80211: Ignored unknown event "
                           "(cmd=%d)", gnlh->cmd);
@@ -1416,12 +1558,14 @@ nla_put_failure:
 }
 
 
-#ifndef HOSTAPD
 struct wiphy_info_data {
        int max_scan_ssids;
        int ap_supported;
+       int p2p_supported;
        int auth_supported;
        int connect_supported;
+       int offchan_tx_supported;
+       int max_remain_on_chan;
 };
 
 
@@ -1430,6 +1574,7 @@ static int wiphy_info_handler(struct nl_msg *msg, void *arg)
        struct nlattr *tb[NL80211_ATTR_MAX + 1];
        struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
        struct wiphy_info_data *info = arg;
+       int p2p_go_supported = 0, p2p_client_supported = 0;
 
        nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
                  genlmsg_attrlen(gnlh, 0), NULL);
@@ -1443,13 +1588,22 @@ static int wiphy_info_handler(struct nl_msg *msg, void *arg)
                int i;
                nla_for_each_nested(nl_mode,
                                    tb[NL80211_ATTR_SUPPORTED_IFTYPES], i) {
-                       if (nl_mode->nla_type == NL80211_IFTYPE_AP) {
+                       switch (nla_type(nl_mode)) {
+                       case NL80211_IFTYPE_AP:
                                info->ap_supported = 1;
                                break;
+                       case NL80211_IFTYPE_P2P_GO:
+                               p2p_go_supported = 1;
+                               break;
+                       case NL80211_IFTYPE_P2P_CLIENT:
+                               p2p_client_supported = 1;
+                               break;
                        }
                }
        }
 
+       info->p2p_supported = p2p_go_supported && p2p_client_supported;
+
        if (tb[NL80211_ATTR_SUPPORTED_COMMANDS]) {
                struct nlattr *nl_cmd;
                int i;
@@ -1464,6 +1618,13 @@ static int wiphy_info_handler(struct nl_msg *msg, void *arg)
                }
        }
 
+       if (tb[NL80211_ATTR_OFFCHANNEL_TX_OK])
+               info->offchan_tx_supported = 1;
+
+       if (tb[NL80211_ATTR_MAX_REMAIN_ON_CHANNEL_DURATION])
+               info->max_remain_on_chan =
+                       nla_get_u32(tb[NL80211_ATTR_MAX_REMAIN_ON_CHANNEL_DURATION]);
+
        return NL_SKIP;
 }
 
@@ -1474,6 +1635,10 @@ static int wpa_driver_nl80211_get_info(struct wpa_driver_nl80211_data *drv,
        struct nl_msg *msg;
 
        os_memset(info, 0, sizeof(*info));
+
+       /* default to 5000 since early versions of mac80211 don't set it */
+       info->max_remain_on_chan = 5000;
+
        msg = nlmsg_alloc();
        if (!msg)
                return -1;
@@ -1523,14 +1688,21 @@ static int wpa_driver_nl80211_capa(struct wpa_driver_nl80211_data *drv)
                return -1;
        }
 
+       if (info.offchan_tx_supported) {
+               wpa_printf(MSG_DEBUG, "nl80211: Using driver-based "
+                          "off-channel TX");
+               drv->capa.flags |= WPA_DRIVER_FLAGS_OFFCHANNEL_TX;
+       }
+
        drv->capa.flags |= WPA_DRIVER_FLAGS_SANE_ERROR_CODES;
        drv->capa.flags |= WPA_DRIVER_FLAGS_SET_KEYS_AFTER_ASSOC_DONE;
-       drv->capa.flags |= WPA_DRIVER_FLAGS_P2P_CAPABLE;
-       drv->capa.max_remain_on_chan = 5000;
+       if (info.p2p_supported)
+               drv->capa.flags |= WPA_DRIVER_FLAGS_P2P_CAPABLE;
+       drv->capa.flags |= WPA_DRIVER_FLAGS_EAPOL_TX_STATUS;
+       drv->capa.max_remain_on_chan = info.max_remain_on_chan;
 
        return 0;
 }
-#endif /* HOSTAPD */
 
 
 static int wpa_driver_nl80211_init_nl(struct wpa_driver_nl80211_data *drv)
@@ -1733,8 +1905,6 @@ static void * wpa_driver_nl80211_init(void *ctx, const char *ifname,
        if (drv == NULL)
                return NULL;
        drv->global = global_priv;
-       if (drv->global)
-               dl_list_add(&drv->global->interfaces, &drv->list);
        drv->ctx = ctx;
        bss = &drv->first_bss;
        bss->drv = drv;
@@ -1784,6 +1954,9 @@ static void * wpa_driver_nl80211_init(void *ctx, const char *ifname,
        if (wpa_driver_nl80211_finish_drv_init(drv))
                goto failed;
 
+       if (drv->global)
+               dl_list_add(&drv->global->interfaces, &drv->list);
+
        return bss;
 
 failed:
@@ -1873,6 +2046,11 @@ static int nl80211_register_action_frames(struct wpa_driver_nl80211_data *drv)
                                          5) < 0)
                return -1;
 #endif /* CONFIG_P2P */
+#ifdef CONFIG_IEEE80211W
+       /* SA Query Response */
+       if (nl80211_register_action_frame(drv, (u8 *) "\x08\x01", 2) < 0)
+               return -1;
+#endif /* CONFIG_IEEE80211W */
 
        /* FT Action frames */
        if (nl80211_register_action_frame(drv, (u8 *) "\x06", 1) < 0)
@@ -1920,13 +2098,13 @@ wpa_driver_nl80211_finish_drv_init(struct wpa_driver_nl80211_data *drv)
                }
        }
 
-       if (wpa_driver_nl80211_capa(drv))
-               return -1;
-
        netlink_send_oper_ifla(drv->netlink, drv->ifindex,
                               1, IF_OPER_DORMANT);
 #endif /* HOSTAPD */
 
+       if (wpa_driver_nl80211_capa(drv))
+               return -1;
+
        if (linux_get_ifhwaddr(drv->ioctl_sock, bss->ifname, drv->addr))
                return -1;
 
@@ -1981,18 +2159,18 @@ static void wpa_driver_nl80211_deinit(void *priv)
 
        if (drv->nl_handle_preq)
                wpa_driver_nl80211_probe_req_report(bss, 0);
-       if (drv->added_if_into_bridge) {
-               if (linux_br_del_if(drv->ioctl_sock, drv->brname, bss->ifname)
+       if (bss->added_if_into_bridge) {
+               if (linux_br_del_if(drv->ioctl_sock, bss->brname, bss->ifname)
                    < 0)
                        wpa_printf(MSG_INFO, "nl80211: Failed to remove "
                                   "interface %s from bridge %s: %s",
-                                  bss->ifname, drv->brname, strerror(errno));
+                                  bss->ifname, bss->brname, strerror(errno));
        }
-       if (drv->added_bridge) {
-               if (linux_br_del(drv->ioctl_sock, drv->brname) < 0)
+       if (bss->added_bridge) {
+               if (linux_br_del(drv->ioctl_sock, bss->brname) < 0)
                        wpa_printf(MSG_INFO, "nl80211: Failed to remove "
                                   "bridge %s: %s",
-                                  drv->brname, strerror(errno));
+                                  bss->brname, strerror(errno));
        }
 
        nl80211_remove_monitor_interface(drv);
@@ -2234,11 +2412,6 @@ static int nl80211_scan_filtered(struct wpa_driver_nl80211_data *drv,
 }
 
 
-struct nl80211_bss_info_arg {
-       struct wpa_driver_nl80211_data *drv;
-       struct wpa_scan_results *res;
-};
-
 static int bss_info_handler(struct nl_msg *msg, void *arg)
 {
        struct nlattr *tb[NL80211_ATTR_MAX + 1];
@@ -2264,6 +2437,7 @@ static int bss_info_handler(struct nl_msg *msg, void *arg)
        const u8 *ie, *beacon_ie;
        size_t ie_len, beacon_ie_len;
        u8 *pos;
+       size_t i;
 
        nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
                  genlmsg_attrlen(gnlh, 0), NULL);
@@ -2272,6 +2446,19 @@ static int bss_info_handler(struct nl_msg *msg, void *arg)
        if (nla_parse_nested(bss, NL80211_BSS_MAX, tb[NL80211_ATTR_BSS],
                             bss_policy))
                return NL_SKIP;
+       if (bss[NL80211_BSS_STATUS]) {
+               enum nl80211_bss_status status;
+               status = nla_get_u32(bss[NL80211_BSS_STATUS]);
+               if (status == NL80211_BSS_STATUS_ASSOCIATED &&
+                   bss[NL80211_BSS_FREQUENCY]) {
+                       _arg->assoc_freq =
+                               nla_get_u32(bss[NL80211_BSS_FREQUENCY]);
+                       wpa_printf(MSG_DEBUG, "nl80211: Associated on %u MHz",
+                                  _arg->assoc_freq);
+               }
+       }
+       if (!res)
+               return NL_SKIP;
        if (bss[NL80211_BSS_INFORMATION_ELEMENTS]) {
                ie = nla_data(bss[NL80211_BSS_INFORMATION_ELEMENTS]);
                ie_len = nla_len(bss[NL80211_BSS_INFORMATION_ELEMENTS]);
@@ -2342,6 +2529,38 @@ static int bss_info_handler(struct nl_msg *msg, void *arg)
                }
        }
 
+       /*
+        * cfg80211 maintains separate BSS table entries for APs if the same
+        * BSSID,SSID pair is seen on multiple channels. wpa_supplicant does
+        * not use frequency as a separate key in the BSS table, so filter out
+        * duplicated entries. Prefer associated BSS entry in such a case in
+        * order to get the correct frequency into the BSS table.
+        */
+       for (i = 0; i < res->num; i++) {
+               const u8 *s1, *s2;
+               if (os_memcmp(res->res[i]->bssid, r->bssid, ETH_ALEN) != 0)
+                       continue;
+
+               s1 = nl80211_get_ie((u8 *) (res->res[i] + 1),
+                                   res->res[i]->ie_len, WLAN_EID_SSID);
+               s2 = nl80211_get_ie((u8 *) (r + 1), r->ie_len, WLAN_EID_SSID);
+               if (s1 == NULL || s2 == NULL || s1[1] != s2[1] ||
+                   os_memcmp(s1, s2, 2 + s1[1]) != 0)
+                       continue;
+
+               /* Same BSSID,SSID was already included in scan results */
+               wpa_printf(MSG_DEBUG, "nl80211: Remove duplicated scan result "
+                          "for " MACSTR, MAC2STR(r->bssid));
+
+               if ((r->flags & WPA_SCAN_ASSOCIATED) &&
+                   !(res->res[i]->flags & WPA_SCAN_ASSOCIATED)) {
+                       os_free(res->res[i]);
+                       res->res[i] = r;
+               } else
+                       os_free(r);
+               return NL_SKIP;
+       }
+
        tmp = os_realloc(res->res,
                         (res->num + 1) * sizeof(struct wpa_scan_res *));
        if (tmp == NULL) {
@@ -2573,8 +2792,7 @@ static int wpa_driver_nl80211_set_key(const char *ifname, void *priv,
        if (seq && seq_len)
                NLA_PUT(msg, NL80211_ATTR_KEY_SEQ, seq_len, seq);
 
-       if (addr && os_memcmp(addr, "\xff\xff\xff\xff\xff\xff", ETH_ALEN) != 0)
-       {
+       if (addr && !is_broadcast_ether_addr(addr)) {
                wpa_printf(MSG_DEBUG, "   addr=" MACSTR, MAC2STR(addr));
                NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, addr);
 
@@ -2583,6 +2801,19 @@ static int wpa_driver_nl80211_set_key(const char *ifname, void *priv,
                        NLA_PUT_U32(msg, NL80211_ATTR_KEY_TYPE,
                                    NL80211_KEYTYPE_GROUP);
                }
+       } else if (addr && is_broadcast_ether_addr(addr)) {
+               struct nl_msg *types;
+               int err;
+               wpa_printf(MSG_DEBUG, "   broadcast key");
+               types = nlmsg_alloc();
+               if (!types)
+                       goto nla_put_failure;
+               NLA_PUT_FLAG(types, NL80211_KEY_DEFAULT_TYPE_MULTICAST);
+               err = nla_put_nested(msg, NL80211_ATTR_KEY_DEFAULT_TYPES,
+                                    types);
+               nlmsg_free(types);
+               if (err)
+                       goto nla_put_failure;
        }
        NLA_PUT_U8(msg, NL80211_ATTR_KEY_IDX, key_idx);
        NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, ifindex);
@@ -2600,13 +2831,9 @@ static int wpa_driver_nl80211_set_key(const char *ifname, void *priv,
         */
        if (ret || !set_tx || alg == WPA_ALG_NONE)
                return ret;
-#ifdef HOSTAPD
-       if (addr)
-               return ret;
-#else /* HOSTAPD */
-       if (drv->nlmode == NL80211_IFTYPE_AP && addr)
+       if (drv->nlmode == NL80211_IFTYPE_AP && addr &&
+           !is_broadcast_ether_addr(addr))
                return ret;
-#endif /* HOSTAPD */
 
        msg = nlmsg_alloc();
        if (!msg)
@@ -2620,6 +2847,31 @@ static int wpa_driver_nl80211_set_key(const char *ifname, void *priv,
                NLA_PUT_FLAG(msg, NL80211_ATTR_KEY_DEFAULT_MGMT);
        else
                NLA_PUT_FLAG(msg, NL80211_ATTR_KEY_DEFAULT);
+       if (addr && is_broadcast_ether_addr(addr)) {
+               struct nl_msg *types;
+               int err;
+               types = nlmsg_alloc();
+               if (!types)
+                       goto nla_put_failure;
+               NLA_PUT_FLAG(types, NL80211_KEY_DEFAULT_TYPE_MULTICAST);
+               err = nla_put_nested(msg, NL80211_ATTR_KEY_DEFAULT_TYPES,
+                                    types);
+               nlmsg_free(types);
+               if (err)
+                       goto nla_put_failure;
+       } else if (addr) {
+               struct nl_msg *types;
+               int err;
+               types = nlmsg_alloc();
+               if (!types)
+                       goto nla_put_failure;
+               NLA_PUT_FLAG(types, NL80211_KEY_DEFAULT_TYPE_UNICAST);
+               err = nla_put_nested(msg, NL80211_ATTR_KEY_DEFAULT_TYPES,
+                                    types);
+               nlmsg_free(types);
+               if (err)
+                       goto nla_put_failure;
+       }
 
        ret = send_and_recv_msgs(drv, msg, NULL, NULL);
        if (ret == -ENOENT)
@@ -2803,6 +3055,8 @@ static int wpa_driver_nl80211_deauthenticate(void *priv, const u8 *addr,
        wpa_printf(MSG_DEBUG, "%s(addr=" MACSTR " reason_code=%d)",
                   __func__, MAC2STR(addr), reason_code);
        drv->associated = 0;
+       if (drv->nlmode == NL80211_IFTYPE_ADHOC)
+               return nl80211_leave_ibss(drv);
        return wpa_driver_nl80211_mlme(drv, addr, NL80211_CMD_DEAUTHENTICATE,
                                       reason_code, 0);
 }
@@ -2835,10 +3089,8 @@ static int wpa_driver_nl80211_authenticate(
        drv->associated = 0;
        os_memset(drv->auth_bssid, 0, ETH_ALEN);
        /* FIX: IBSS mode */
-       if (drv->nlmode != NL80211_IFTYPE_STATION)
-               wpa_driver_nl80211_set_mode(priv, IEEE80211_MODE_INFRA);
-
-       if (wpa_driver_nl80211_set_mode(priv, IEEE80211_MODE_INFRA) < 0)
+       if (drv->nlmode != NL80211_IFTYPE_STATION &&
+           wpa_driver_nl80211_set_mode(priv, IEEE80211_MODE_INFRA) < 0)
                return -1;
 
 retry:
@@ -3405,6 +3657,12 @@ static int wpa_driver_nl80211_send_frame(struct wpa_driver_nl80211_data *drv,
        if (encrypt)
                rtap_hdr[8] |= IEEE80211_RADIOTAP_F_WEP;
 
+       if (drv->monitor_sock < 0) {
+               wpa_printf(MSG_DEBUG, "nl80211: No monitor socket available "
+                          "for %s", __func__);
+               return -1;
+       }
+
        res = sendmsg(drv->monitor_sock, &msg, 0);
        if (res < 0) {
                wpa_printf(MSG_INFO, "nl80211: sendmsg: %s", strerror(errno));
@@ -3434,7 +3692,7 @@ static int wpa_driver_nl80211_send_mlme(void *priv, const u8 *data,
                 * but it works due to the single-threaded nature
                 * of wpa_supplicant.
                 */
-               return nl80211_send_frame_cmd(drv, drv->last_mgmt_freq,
+               return nl80211_send_frame_cmd(drv, drv->last_mgmt_freq, 0,
                                              data, data_len, NULL);
        }
 
@@ -3544,12 +3802,30 @@ nla_put_failure:
 }
 
 
+static u32 sta_flags_nl80211(int flags)
+{
+       u32 f = 0;
+
+       if (flags & WPA_STA_AUTHORIZED)
+               f |= BIT(NL80211_STA_FLAG_AUTHORIZED);
+       if (flags & WPA_STA_WMM)
+               f |= BIT(NL80211_STA_FLAG_WME);
+       if (flags & WPA_STA_SHORT_PREAMBLE)
+               f |= BIT(NL80211_STA_FLAG_SHORT_PREAMBLE);
+       if (flags & WPA_STA_MFP)
+               f |= BIT(NL80211_STA_FLAG_MFP);
+
+       return f;
+}
+
+
 static int wpa_driver_nl80211_sta_add(void *priv,
                                      struct hostapd_sta_add_params *params)
 {
        struct i802_bss *bss = priv;
        struct wpa_driver_nl80211_data *drv = bss->drv;
        struct nl_msg *msg;
+       struct nl80211_sta_flag_update upd;
        int ret = -ENOBUFS;
 
        msg = nlmsg_alloc();
@@ -3572,6 +3848,11 @@ static int wpa_driver_nl80211_sta_add(void *priv,
                        params->ht_capabilities);
        }
 
+       os_memset(&upd, 0, sizeof(upd));
+       upd.mask = sta_flags_nl80211(params->flags);
+       upd.set = upd.mask;
+       NLA_PUT(msg, NL80211_ATTR_STA_FLAGS2, sizeof(upd), &upd);
+
        ret = send_and_recv_msgs(drv, msg, NULL, NULL);
        if (ret)
                wpa_printf(MSG_DEBUG, "nl80211: NL80211_CMD_NEW_STATION "
@@ -4043,6 +4324,12 @@ nl80211_create_monitor_interface(struct wpa_driver_nl80211_data *drv)
                nl80211_create_iface(drv, buf, NL80211_IFTYPE_MONITOR, NULL,
                                     0);
 
+       if (drv->monitor_ifidx == -EOPNOTSUPP) {
+               wpa_printf(MSG_DEBUG, "nl80211: Driver does not support "
+                          "monitor interface type - try to run without it");
+               drv->no_monitor_iface_capab = 1;
+       }
+
        if (drv->monitor_ifidx < 0)
                return -1;
 
@@ -4094,7 +4381,7 @@ static const u8 rfc1042_header[6] = { 0xaa, 0xaa, 0x03, 0x00, 0x00, 0x00 };
 
 static int wpa_driver_nl80211_hapd_send_eapol(
        void *priv, const u8 *addr, const u8 *data,
-       size_t data_len, int encrypt, const u8 *own_addr)
+       size_t data_len, int encrypt, const u8 *own_addr, u32 flags)
 {
        struct i802_bss *bss = priv;
        struct wpa_driver_nl80211_data *drv = bss->drv;
@@ -4102,11 +4389,7 @@ static int wpa_driver_nl80211_hapd_send_eapol(
        size_t len;
        u8 *pos;
        int res;
-#if 0 /* FIX */
-       int qos = sta->flags & WPA_STA_WMM;
-#else
-       int qos = 0;
-#endif
+       int qos = flags & WPA_STA_WMM;
 
        len = sizeof(*hdr) + (qos ? 2 : 0) + sizeof(rfc1042_header) + 2 +
                data_len;
@@ -4122,26 +4405,22 @@ static int wpa_driver_nl80211_hapd_send_eapol(
        hdr->frame_control |= host_to_le16(WLAN_FC_FROMDS);
        if (encrypt)
                hdr->frame_control |= host_to_le16(WLAN_FC_ISWEP);
-#if 0 /* To be enabled if qos determination is added above */
        if (qos) {
                hdr->frame_control |=
                        host_to_le16(WLAN_FC_STYPE_QOS_DATA << 4);
        }
-#endif
 
        memcpy(hdr->IEEE80211_DA_FROMDS, addr, ETH_ALEN);
        memcpy(hdr->IEEE80211_BSSID_FROMDS, own_addr, ETH_ALEN);
        memcpy(hdr->IEEE80211_SA_FROMDS, own_addr, ETH_ALEN);
        pos = (u8 *) (hdr + 1);
 
-#if 0 /* To be enabled if qos determination is added above */
        if (qos) {
                /* add an empty QoS header if needed */
                pos[0] = 0;
                pos[1] = 0;
                pos += 2;
        }
-#endif
 
        memcpy(pos, rfc1042_header, sizeof(rfc1042_header));
        pos += sizeof(rfc1042_header);
@@ -4161,23 +4440,6 @@ static int wpa_driver_nl80211_hapd_send_eapol(
 }
 
 
-static u32 sta_flags_nl80211(int flags)
-{
-       u32 f = 0;
-
-       if (flags & WPA_STA_AUTHORIZED)
-               f |= BIT(NL80211_STA_FLAG_AUTHORIZED);
-       if (flags & WPA_STA_WMM)
-               f |= BIT(NL80211_STA_FLAG_WME);
-       if (flags & WPA_STA_SHORT_PREAMBLE)
-               f |= BIT(NL80211_STA_FLAG_SHORT_PREAMBLE);
-       if (flags & WPA_STA_MFP)
-               f |= BIT(NL80211_STA_FLAG_MFP);
-
-       return f;
-}
-
-
 static int wpa_driver_nl80211_sta_set_flags(void *priv, const u8 *addr,
                                            int total_flags,
                                            int flags_or, int flags_and)
@@ -4366,6 +4628,7 @@ static int wpa_driver_nl80211_connect(
        struct nl_msg *msg;
        enum nl80211_auth_type type;
        int ret = 0;
+       int algs;
 
        msg = nlmsg_alloc();
        if (!msg)
@@ -4400,6 +4663,19 @@ static int wpa_driver_nl80211_connect(
                NLA_PUT(msg, NL80211_ATTR_IE, params->wpa_ie_len,
                        params->wpa_ie);
 
+       algs = 0;
+       if (params->auth_alg & WPA_AUTH_ALG_OPEN)
+               algs++;
+       if (params->auth_alg & WPA_AUTH_ALG_SHARED)
+               algs++;
+       if (params->auth_alg & WPA_AUTH_ALG_LEAP)
+               algs++;
+       if (algs > 1) {
+               wpa_printf(MSG_DEBUG, "  * Leave out Auth Type for automatic "
+                          "selection");
+               goto skip_auth_type;
+       }
+
        if (params->auth_alg & WPA_AUTH_ALG_OPEN)
                type = NL80211_AUTHTYPE_OPEN_SYSTEM;
        else if (params->auth_alg & WPA_AUTH_ALG_SHARED)
@@ -4414,6 +4690,7 @@ static int wpa_driver_nl80211_connect(
        wpa_printf(MSG_DEBUG, "  * Auth Type %d", type);
        NLA_PUT_U32(msg, NL80211_ATTR_AUTH_TYPE, type);
 
+skip_auth_type:
        if (params->wpa_ie && params->wpa_ie_len) {
                enum nl80211_wpa_versions ver;
 
@@ -4581,7 +4858,7 @@ static int wpa_driver_nl80211_associate(
                        cipher = WLAN_CIPHER_SUITE_TKIP;
                        break;
                }
-               wpa_printf(MSG_DEBUG, "  * pairwise=0x%x\n", cipher);
+               wpa_printf(MSG_DEBUG, "  * pairwise=0x%x", cipher);
                NLA_PUT_U32(msg, NL80211_ATTR_CIPHER_SUITES_PAIRWISE, cipher);
        }
 
@@ -4603,7 +4880,7 @@ static int wpa_driver_nl80211_associate(
                        cipher = WLAN_CIPHER_SUITE_TKIP;
                        break;
                }
-               wpa_printf(MSG_DEBUG, "  * group=0x%x\n", cipher);
+               wpa_printf(MSG_DEBUG, "  * group=0x%x", cipher);
                NLA_PUT_U32(msg, NL80211_ATTR_CIPHER_SUITE_GROUP, cipher);
        }
 
@@ -4734,8 +5011,9 @@ static int wpa_driver_nl80211_set_mode(void *priv, int mode)
 done:
        if (!ret && nlmode == NL80211_IFTYPE_AP) {
                /* Setup additional AP mode functionality if needed */
-               if (drv->monitor_ifidx < 0 &&
-                   nl80211_create_monitor_interface(drv))
+               if (!drv->no_monitor_iface_capab && drv->monitor_ifidx < 0 &&
+                   nl80211_create_monitor_interface(drv) &&
+                   !drv->no_monitor_iface_capab)
                        return -1;
        } else if (!ret && nlmode != NL80211_IFTYPE_AP) {
                /* Remove additional AP mode functionality */
@@ -4806,83 +5084,30 @@ static int wpa_driver_nl80211_set_supp_port(void *priv, int authorized)
 }
 
 
-#ifdef HOSTAPD
+/* Set kernel driver on given frequency (MHz) */
+static int i802_set_freq(void *priv, struct hostapd_freq_params *freq)
+{
+       struct i802_bss *bss = priv;
+       struct wpa_driver_nl80211_data *drv = bss->drv;
+       return wpa_driver_nl80211_set_freq(drv, freq->freq, freq->ht_enabled,
+                                          freq->sec_channel_offset);
+}
 
-static void add_ifidx(struct wpa_driver_nl80211_data *drv, int ifidx)
+
+#if defined(HOSTAPD) || defined(CONFIG_AP)
+
+static inline int min_int(int a, int b)
 {
-       int i;
-       int *old;
+       if (a < b)
+               return a;
+       return b;
+}
 
-       wpa_printf(MSG_DEBUG, "nl80211: Add own interface ifindex %d",
-                  ifidx);
-       for (i = 0; i < drv->num_if_indices; i++) {
-               if (drv->if_indices[i] == 0) {
-                       drv->if_indices[i] = ifidx;
-                       return;
-               }
-       }
 
-       if (drv->if_indices != drv->default_if_indices)
-               old = drv->if_indices;
-       else
-               old = NULL;
-
-       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;
-               else
-                       drv->if_indices = old;
-               wpa_printf(MSG_ERROR, "Failed to reallocate memory for "
-                          "interfaces");
-               wpa_printf(MSG_ERROR, "Ignoring EAPOL on interface %d", ifidx);
-               return;
-       } else if (!old)
-               os_memcpy(drv->if_indices, drv->default_if_indices,
-                         sizeof(drv->default_if_indices));
-       drv->if_indices[drv->num_if_indices] = ifidx;
-       drv->num_if_indices++;
-}
-
-
-static void del_ifidx(struct wpa_driver_nl80211_data *drv, int ifidx)
-{
-       int i;
-
-       for (i = 0; i < drv->num_if_indices; i++) {
-               if (drv->if_indices[i] == ifidx) {
-                       drv->if_indices[i] = 0;
-                       break;
-               }
-       }
-}
-
-
-static int have_ifidx(struct wpa_driver_nl80211_data *drv, int ifidx)
-{
-       int i;
-
-       for (i = 0; i < drv->num_if_indices; i++)
-               if (drv->if_indices[i] == ifidx)
-                       return 1;
-
-       return 0;
-}
-
-
-static inline int min_int(int a, int b)
-{
-       if (a < b)
-               return a;
-       return b;
-}
-
-
-static int get_key_handler(struct nl_msg *msg, void *arg)
-{
-       struct nlattr *tb[NL80211_ATTR_MAX + 1];
-       struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
+static int get_key_handler(struct nl_msg *msg, void *arg)
+{
+       struct nlattr *tb[NL80211_ATTR_MAX + 1];
+       struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
 
        nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
                  genlmsg_attrlen(gnlh, 0), NULL);
@@ -4956,20 +5181,6 @@ static int i802_set_rate_sets(void *priv, int *supp_rates, int *basic_rates,
        return -ENOBUFS;
 }
 
-#endif /* HOSTAPD */
-
-
-/* Set kernel driver on given frequency (MHz) */
-static int i802_set_freq(void *priv, struct hostapd_freq_params *freq)
-{
-       struct i802_bss *bss = priv;
-       struct wpa_driver_nl80211_data *drv = bss->drv;
-       return wpa_driver_nl80211_set_freq(drv, freq->freq, freq->ht_enabled,
-                                          freq->sec_channel_offset);
-}
-
-
-#ifdef HOSTAPD
 
 static int i802_set_rts(void *priv, int rts)
 {
@@ -5193,7 +5404,8 @@ static int i802_set_tx_queue_params(void *priv, int queue, int aifs,
 }
 
 
-static int i802_set_bss(void *priv, int cts, int preamble, int slot)
+static int i802_set_bss(void *priv, int cts, int preamble, int slot,
+                       int ht_opmode)
 {
        struct i802_bss *bss = priv;
        struct wpa_driver_nl80211_data *drv = bss->drv;
@@ -5212,7 +5424,8 @@ static int i802_set_bss(void *priv, int cts, int preamble, int slot)
                NLA_PUT_U8(msg, NL80211_ATTR_BSS_SHORT_PREAMBLE, preamble);
        if (slot >= 0)
                NLA_PUT_U8(msg, NL80211_ATTR_BSS_SHORT_SLOT_TIME, slot);
-
+       if (ht_opmode >= 0)
+               NLA_PUT_U16(msg, NL80211_ATTR_BSS_HT_OPMODE, ht_opmode);
        NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, if_nametoindex(bss->ifname));
 
        return send_and_recv_msgs(drv, msg, NULL, NULL);
@@ -5223,19 +5436,19 @@ static int i802_set_bss(void *priv, int cts, int preamble, int slot)
 
 static int i802_set_cts_protect(void *priv, int value)
 {
-       return i802_set_bss(priv, value, -1, -1);
+       return i802_set_bss(priv, value, -1, -1, -1);
 }
 
 
 static int i802_set_preamble(void *priv, int value)
 {
-       return i802_set_bss(priv, -1, value, -1);
+       return i802_set_bss(priv, -1, value, -1, -1);
 }
 
 
 static int i802_set_short_slot_time(void *priv, int value)
 {
-       return i802_set_bss(priv, -1, -1, value);
+       return i802_set_bss(priv, -1, -1, value, -1);
 }
 
 
@@ -5272,54 +5485,16 @@ 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,
-                            const char *bridge_ifname)
+static int i802_set_ht_params(void *priv, const u8 *ht_capab,
+                             size_t ht_capab_len, const u8 *ht_oper,
+                             size_t ht_oper_len)
 {
-       struct i802_bss *bss = priv;
-       struct wpa_driver_nl80211_data *drv = bss->drv;
-       char name[IFNAMSIZ + 1];
-
-       os_snprintf(name, sizeof(name), "%s.sta%d", bss->ifname, aid);
-       wpa_printf(MSG_DEBUG, "nl80211: Set WDS STA addr=" MACSTR
-                  " aid=%d val=%d name=%s", MAC2STR(addr), aid, val, name);
-       if (val) {
-               if (!if_nametoindex(name)) {
-                       if (nl80211_create_iface(drv, name,
-                                                NL80211_IFTYPE_AP_VLAN,
-                                                NULL, 1) < 0)
-                               return -1;
-                       if (bridge_ifname &&
-                           linux_br_add_if(drv->ioctl_sock, bridge_ifname,
-                                           name) < 0)
-                               return -1;
-               }
-               linux_set_iface_flags(drv->ioctl_sock, name, 1);
-               return i802_set_sta_vlan(priv, addr, name, 0);
-       } else {
-               i802_set_sta_vlan(priv, addr, bss->ifname, 0);
-               return wpa_driver_nl80211_if_remove(priv, WPA_IF_AP_VLAN,
-                                                   name);
-       }
-}
-
-
-static void handle_eapol(int sock, void *eloop_ctx, void *sock_ctx)
-{
-       struct wpa_driver_nl80211_data *drv = eloop_ctx;
-       struct sockaddr_ll lladdr;
-       unsigned char buf[3000];
-       int len;
-       socklen_t fromlen = sizeof(lladdr);
-
-       len = recvfrom(sock, buf, sizeof(buf), 0,
-                      (struct sockaddr *)&lladdr, &fromlen);
-       if (len < 0) {
-               perror("recv");
-               return;
-       }
-
-       if (have_ifidx(drv, lladdr.sll_ifindex))
-               drv_event_eapol_rx(drv->ctx, lladdr.sll_addr, buf, len);
+       if (ht_oper_len >= 6) {
+               /* ht opmode uses 16bit in octet 5 & 6 */
+               u16 ht_opmode = le_to_host16(((u16 *) ht_oper)[2]);
+               return i802_set_bss(priv, -1, -1, -1, ht_opmode);
+       } else
+               return -1;
 }
 
 
@@ -5382,14 +5557,132 @@ static int i802_sta_disassoc(void *priv, const u8 *own_addr, const u8 *addr,
                                            sizeof(mgmt.u.disassoc));
 }
 
+#endif /* HOSTAPD || CONFIG_AP */
+
+#ifdef HOSTAPD
+
+static void add_ifidx(struct wpa_driver_nl80211_data *drv, int ifidx)
+{
+       int i;
+       int *old;
+
+       wpa_printf(MSG_DEBUG, "nl80211: Add own interface ifindex %d",
+                  ifidx);
+       for (i = 0; i < drv->num_if_indices; i++) {
+               if (drv->if_indices[i] == 0) {
+                       drv->if_indices[i] = ifidx;
+                       return;
+               }
+       }
+
+       if (drv->if_indices != drv->default_if_indices)
+               old = drv->if_indices;
+       else
+               old = NULL;
+
+       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;
+               else
+                       drv->if_indices = old;
+               wpa_printf(MSG_ERROR, "Failed to reallocate memory for "
+                          "interfaces");
+               wpa_printf(MSG_ERROR, "Ignoring EAPOL on interface %d", ifidx);
+               return;
+       } else if (!old)
+               os_memcpy(drv->if_indices, drv->default_if_indices,
+                         sizeof(drv->default_if_indices));
+       drv->if_indices[drv->num_if_indices] = ifidx;
+       drv->num_if_indices++;
+}
+
+
+static void del_ifidx(struct wpa_driver_nl80211_data *drv, int ifidx)
+{
+       int i;
+
+       for (i = 0; i < drv->num_if_indices; i++) {
+               if (drv->if_indices[i] == ifidx) {
+                       drv->if_indices[i] = 0;
+                       break;
+               }
+       }
+}
+
+
+static int have_ifidx(struct wpa_driver_nl80211_data *drv, int ifidx)
+{
+       int i;
+
+       for (i = 0; i < drv->num_if_indices; i++)
+               if (drv->if_indices[i] == ifidx)
+                       return 1;
+
+       return 0;
+}
+
+
+static int i802_set_wds_sta(void *priv, const u8 *addr, int aid, int val,
+                            const char *bridge_ifname)
+{
+       struct i802_bss *bss = priv;
+       struct wpa_driver_nl80211_data *drv = bss->drv;
+       char name[IFNAMSIZ + 1];
+
+       os_snprintf(name, sizeof(name), "%s.sta%d", bss->ifname, aid);
+       wpa_printf(MSG_DEBUG, "nl80211: Set WDS STA addr=" MACSTR
+                  " aid=%d val=%d name=%s", MAC2STR(addr), aid, val, name);
+       if (val) {
+               if (!if_nametoindex(name)) {
+                       if (nl80211_create_iface(drv, name,
+                                                NL80211_IFTYPE_AP_VLAN,
+                                                NULL, 1) < 0)
+                               return -1;
+                       if (bridge_ifname &&
+                           linux_br_add_if(drv->ioctl_sock, bridge_ifname,
+                                           name) < 0)
+                               return -1;
+               }
+               linux_set_iface_flags(drv->ioctl_sock, name, 1);
+               return i802_set_sta_vlan(priv, addr, name, 0);
+       } else {
+               i802_set_sta_vlan(priv, addr, bss->ifname, 0);
+               return wpa_driver_nl80211_if_remove(priv, WPA_IF_AP_VLAN,
+                                                   name);
+       }
+}
+
+
+static void handle_eapol(int sock, void *eloop_ctx, void *sock_ctx)
+{
+       struct wpa_driver_nl80211_data *drv = eloop_ctx;
+       struct sockaddr_ll lladdr;
+       unsigned char buf[3000];
+       int len;
+       socklen_t fromlen = sizeof(lladdr);
+
+       len = recvfrom(sock, buf, sizeof(buf), 0,
+                      (struct sockaddr *)&lladdr, &fromlen);
+       if (len < 0) {
+               perror("recv");
+               return;
+       }
+
+       if (have_ifidx(drv, lladdr.sll_ifindex))
+               drv_event_eapol_rx(drv->ctx, lladdr.sll_addr, buf, len);
+}
+
 
 static int i802_check_bridge(struct wpa_driver_nl80211_data *drv,
+                            struct i802_bss *bss,
                             const char *brname, const char *ifname)
 {
        int ifindex;
        char in_br[IFNAMSIZ];
 
-       os_strlcpy(drv->brname, brname, IFNAMSIZ);
+       os_strlcpy(bss->brname, brname, IFNAMSIZ);
        ifindex = if_nametoindex(brname);
        if (ifindex == 0) {
                /*
@@ -5402,7 +5695,7 @@ static int i802_check_bridge(struct wpa_driver_nl80211_data *drv,
                                   brname, strerror(errno));
                        return -1;
                }
-               drv->added_bridge = 1;
+               bss->added_bridge = 1;
                add_ifidx(drv, if_nametoindex(brname));
        }
 
@@ -5429,7 +5722,7 @@ static int i802_check_bridge(struct wpa_driver_nl80211_data *drv,
                           ifname, brname, strerror(errno));
                return -1;
        }
-       drv->added_if_into_bridge = 1;
+       bss->added_if_into_bridge = 1;
 
        return 0;
 }
@@ -5450,6 +5743,7 @@ static void *i802_init(struct hostapd_data *hapd,
                return NULL;
 
        drv = bss->drv;
+       drv->nlmode = NL80211_IFTYPE_AP;
        if (linux_br_get(brname, params->ifname) == 0) {
                wpa_printf(MSG_DEBUG, "nl80211: Interface %s is in bridge %s",
                           params->ifname, brname);
@@ -5493,7 +5787,7 @@ static void *i802_init(struct hostapd_data *hapd,
        }
 
        if (params->num_bridge && params->bridge[0] &&
-           i802_check_bridge(drv, params->bridge[0], params->ifname) < 0)
+           i802_check_bridge(drv, bss, params->bridge[0], params->ifname) < 0)
                goto failed;
 
        if (linux_set_iface_flags(drv->ioctl_sock, bss->ifname, 1))
@@ -5547,14 +5841,16 @@ static enum nl80211_iftype wpa_driver_nl80211_if_type(
 {
        switch (type) {
        case WPA_IF_STATION:
+               return NL80211_IFTYPE_STATION;
        case WPA_IF_P2P_CLIENT:
        case WPA_IF_P2P_GROUP:
-               return NL80211_IFTYPE_STATION;
+               return NL80211_IFTYPE_P2P_CLIENT;
        case WPA_IF_AP_VLAN:
                return NL80211_IFTYPE_AP_VLAN;
        case WPA_IF_AP_BSS:
-       case WPA_IF_P2P_GO:
                return NL80211_IFTYPE_AP;
+       case WPA_IF_P2P_GO:
+               return NL80211_IFTYPE_P2P_GO;
        }
        return -1;
 }
@@ -5604,7 +5900,8 @@ static int nl80211_p2p_interface_addr(struct wpa_driver_nl80211_data *drv,
 static int wpa_driver_nl80211_if_add(void *priv, enum wpa_driver_if_type type,
                                     const char *ifname, const u8 *addr,
                                     void *bss_ctx, void **drv_priv,
-                                    char *force_ifname, u8 *if_addr)
+                                    char *force_ifname, u8 *if_addr,
+                                    const char *bridge)
 {
        struct i802_bss *bss = priv;
        struct wpa_driver_nl80211_data *drv = bss->drv;
@@ -5669,6 +5966,15 @@ static int wpa_driver_nl80211_if_add(void *priv, enum wpa_driver_if_type type,
 #endif /* CONFIG_P2P */
 
 #ifdef HOSTAPD
+       if (bridge &&
+           i802_check_bridge(drv, new_bss, bridge, ifname) < 0) {
+               wpa_printf(MSG_ERROR, "nl80211: Failed to add the new "
+                          "interface %s to a bridge %s", ifname, bridge);
+               nl80211_remove_iface(drv, ifidx);
+               os_free(new_bss);
+               return -1;
+       }
+
        if (type == WPA_IF_AP_BSS) {
                if (linux_set_iface_flags(drv->ioctl_sock, ifname, 1)) {
                        nl80211_remove_iface(drv, ifidx);
@@ -5701,6 +6007,23 @@ static int wpa_driver_nl80211_if_remove(void *priv,
                   __func__, type, ifname, ifindex);
        if (ifindex <= 0)
                return -1;
+
+#ifdef HOSTAPD
+       if (bss->added_if_into_bridge) {
+               if (linux_br_del_if(drv->ioctl_sock, bss->brname, bss->ifname)
+                   < 0)
+                       wpa_printf(MSG_INFO, "nl80211: Failed to remove "
+                                  "interface %s from bridge %s: %s",
+                                  bss->ifname, bss->brname, strerror(errno));
+       }
+       if (bss->added_bridge) {
+               if (linux_br_del(drv->ioctl_sock, bss->brname) < 0)
+                       wpa_printf(MSG_INFO, "nl80211: Failed to remove "
+                                  "bridge %s: %s",
+                                  bss->brname, strerror(errno));
+       }
+#endif /* HOSTAPD */
+
        nl80211_remove_iface(drv, ifindex);
 
 #ifdef HOSTAPD
@@ -5708,16 +6031,19 @@ static int wpa_driver_nl80211_if_remove(void *priv,
                return 0;
 
        if (bss != &drv->first_bss) {
-               struct i802_bss *tbss = &drv->first_bss;
+               struct i802_bss *tbss;
 
-               while (tbss) {
-                       if (tbss->next != bss)
-                               continue;
-
-                       tbss->next = bss->next;
-                       os_free(bss);
-                       break;
+               for (tbss = &drv->first_bss; tbss; tbss = tbss->next) {
+                       if (tbss->next == bss) {
+                               tbss->next = bss->next;
+                               os_free(bss);
+                               bss = NULL;
+                               break;
+                       }
                }
+               if (bss)
+                       wpa_printf(MSG_INFO, "nl80211: %s - could not find "
+                                  "BSS %p in the list", __func__, bss);
        }
 #endif /* HOSTAPD */
 
@@ -5739,8 +6065,9 @@ static int cookie_handler(struct nl_msg *msg, void *arg)
 
 
 static int nl80211_send_frame_cmd(struct wpa_driver_nl80211_data *drv,
-                                 unsigned int freq, const u8 *buf,
-                                 size_t buf_len, u64 *cookie_out)
+                                 unsigned int freq, unsigned int wait,
+                                 const u8 *buf, size_t buf_len,
+                                 u64 *cookie_out)
 {
        struct nl_msg *msg;
        u64 cookie;
@@ -5755,6 +6082,8 @@ static int nl80211_send_frame_cmd(struct wpa_driver_nl80211_data *drv,
 
        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, wait);
+       NLA_PUT_FLAG(msg, NL80211_ATTR_OFFCHANNEL_TX_OK);
        NLA_PUT(msg, NL80211_ATTR_FRAME, buf_len, buf);
 
        cookie = 0;
@@ -5778,6 +6107,7 @@ nla_put_failure:
 
 
 static int wpa_driver_nl80211_send_action(void *priv, unsigned int freq,
+                                         unsigned int wait_time,
                                          const u8 *dst, const u8 *src,
                                          const u8 *bssid,
                                          const u8 *data, size_t data_len)
@@ -5788,8 +6118,8 @@ static int wpa_driver_nl80211_send_action(void *priv, unsigned int freq,
        u8 *buf;
        struct ieee80211_hdr *hdr;
 
-       wpa_printf(MSG_DEBUG, "nl80211: Send Action frame (ifindex=%d)",
-                  drv->ifindex);
+       wpa_printf(MSG_DEBUG, "nl80211: Send Action frame (ifindex=%d, "
+                  "wait=%d ms)", drv->ifindex, wait_time);
 
        buf = os_zalloc(24 + data_len);
        if (buf == NULL)
@@ -5805,7 +6135,8 @@ static int wpa_driver_nl80211_send_action(void *priv, unsigned int freq,
        if (drv->nlmode == NL80211_IFTYPE_AP)
                ret = wpa_driver_nl80211_send_mlme(priv, buf, 24 + data_len);
        else
-               ret = nl80211_send_frame_cmd(drv, freq, buf, 24 + data_len,
+               ret = nl80211_send_frame_cmd(drv, freq, wait_time, buf,
+                                            24 + data_len,
                                             &drv->send_action_cookie);
 
        os_free(buf);
@@ -5813,6 +6144,34 @@ static int wpa_driver_nl80211_send_action(void *priv, unsigned int freq,
 }
 
 
+static void wpa_driver_nl80211_send_action_cancel_wait(void *priv)
+{
+       struct i802_bss *bss = priv;
+       struct wpa_driver_nl80211_data *drv = bss->drv;
+       struct nl_msg *msg;
+       int ret;
+
+       msg = nlmsg_alloc();
+       if (!msg)
+               return;
+
+       genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, 0,
+                   NL80211_CMD_FRAME_WAIT_CANCEL, 0);
+
+       NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex);
+       NLA_PUT_U64(msg, NL80211_ATTR_COOKIE, drv->send_action_cookie);
+
+       ret = send_and_recv_msgs(drv, msg, NULL, NULL);
+       msg = NULL;
+       if (ret)
+               wpa_printf(MSG_DEBUG, "nl80211: wait cancel failed: ret=%d "
+                          "(%s)", ret, strerror(-ret));
+
+ nla_put_failure:
+       nlmsg_free(msg);
+}
+
+
 static int wpa_driver_nl80211_remain_on_channel(void *priv, unsigned int freq,
                                                unsigned int duration)
 {
@@ -6089,8 +6448,8 @@ static int nl80211_send_ft_action(void *priv, u8 action, const u8 *target_ap,
        pos += ETH_ALEN;
        os_memcpy(pos, ies, ies_len);
 
-       ret = wpa_driver_nl80211_send_action(bss, drv->assoc_freq, drv->bssid,
-                                            own_addr, drv->bssid,
+       ret = wpa_driver_nl80211_send_action(bss, drv->assoc_freq, 0,
+                                            drv->bssid, own_addr, drv->bssid,
                                             data, data_len);
        os_free(data);
 
@@ -6136,6 +6495,21 @@ nla_put_failure:
 }
 
 
+static int nl80211_signal_poll(void *priv, struct wpa_signal_info *si)
+{
+       struct i802_bss *bss = priv;
+       struct wpa_driver_nl80211_data *drv = bss->drv;
+       int res;
+
+       os_memset(si, 0, sizeof(*si));
+       res = nl80211_get_link_signal(drv, si);
+       if (res != 0)
+               return res;
+
+       return nl80211_get_link_noise(drv, si);
+}
+
+
 static int nl80211_send_frame(void *priv, const u8 *data, size_t data_len,
                              int encrypt)
 {
@@ -6222,6 +6596,55 @@ static const char * nl80211_get_radio_name(void *priv)
 }
 
 
+static int nl80211_pmkid(struct i802_bss *bss, int cmd, const u8 *bssid,
+                        const u8 *pmkid)
+{
+       struct nl_msg *msg;
+
+       msg = nlmsg_alloc();
+       if (!msg)
+               return -ENOMEM;
+
+       genlmsg_put(msg, 0, 0, genl_family_get_id(bss->drv->nl80211), 0, 0,
+                   cmd, 0);
+
+       NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, if_nametoindex(bss->ifname));
+       if (pmkid)
+               NLA_PUT(msg, NL80211_ATTR_PMKID, 16, pmkid);
+       if (bssid)
+               NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, bssid);
+
+       return send_and_recv_msgs(bss->drv, msg, NULL, NULL);
+ nla_put_failure:
+       return -ENOBUFS;
+}
+
+
+static int nl80211_add_pmkid(void *priv, const u8 *bssid, const u8 *pmkid)
+{
+       struct i802_bss *bss = priv;
+       wpa_printf(MSG_DEBUG, "nl80211: Add PMKID for " MACSTR, MAC2STR(bssid));
+       return nl80211_pmkid(bss, NL80211_CMD_SET_PMKSA, bssid, pmkid);
+}
+
+
+static int nl80211_remove_pmkid(void *priv, const u8 *bssid, const u8 *pmkid)
+{
+       struct i802_bss *bss = priv;
+       wpa_printf(MSG_DEBUG, "nl80211: Delete PMKID for " MACSTR,
+                  MAC2STR(bssid));
+       return nl80211_pmkid(bss, NL80211_CMD_DEL_PMKSA, bssid, pmkid);
+}
+
+
+static int nl80211_flush_pmkid(void *priv)
+{
+       struct i802_bss *bss = priv;
+       wpa_printf(MSG_DEBUG, "nl80211: Flush PMKIDs");
+       return nl80211_pmkid(bss, NL80211_CMD_FLUSH_PMKSA, NULL, NULL);
+}
+
+
 const struct wpa_driver_ops wpa_driver_nl80211_ops = {
        .name = "nl80211",
        .desc = "Linux nl80211/cfg80211",
@@ -6254,25 +6677,29 @@ const struct wpa_driver_ops wpa_driver_nl80211_ops = {
 #ifdef HOSTAPD
        .hapd_init = i802_init,
        .hapd_deinit = i802_deinit,
+       .set_wds_sta = i802_set_wds_sta,
+#endif /* HOSTAPD */
+#if defined(HOSTAPD) || defined(CONFIG_AP)
        .get_seqnum = i802_get_seqnum,
        .flush = i802_flush,
        .read_sta_data = i802_read_sta_data,
-       .sta_deauth = i802_sta_deauth,
-       .sta_disassoc = i802_sta_disassoc,
        .get_inact_sec = i802_get_inact_sec,
        .sta_clear_stats = i802_sta_clear_stats,
        .set_rts = i802_set_rts,
        .set_frag = i802_set_frag,
-       .set_rate_sets = i802_set_rate_sets,
        .set_cts_protect = i802_set_cts_protect,
        .set_preamble = i802_set_preamble,
        .set_short_slot_time = i802_set_short_slot_time,
        .set_tx_queue_params = i802_set_tx_queue_params,
        .set_sta_vlan = i802_set_sta_vlan,
-       .set_wds_sta = i802_set_wds_sta,
-#endif /* HOSTAPD */
+       .set_ht_params = i802_set_ht_params,
+       .set_rate_sets = i802_set_rate_sets,
+       .sta_deauth = i802_sta_deauth,
+       .sta_disassoc = i802_sta_disassoc,
+#endif /* HOSTAPD || CONFIG_AP */
        .set_freq = i802_set_freq,
        .send_action = wpa_driver_nl80211_send_action,
+       .send_action_cancel_wait = wpa_driver_nl80211_send_action_cancel_wait,
        .remain_on_channel = wpa_driver_nl80211_remain_on_channel,
        .cancel_remain_on_channel =
        wpa_driver_nl80211_cancel_remain_on_channel,
@@ -6282,8 +6709,12 @@ const struct wpa_driver_ops wpa_driver_nl80211_ops = {
        .resume = wpa_driver_nl80211_resume,
        .send_ft_action = nl80211_send_ft_action,
        .signal_monitor = nl80211_signal_monitor,
+       .signal_poll = nl80211_signal_poll,
        .send_frame = nl80211_send_frame,
        .set_intra_bss = nl80211_set_intra_bss,
        .set_param = nl80211_set_param,
        .get_radio_name = nl80211_get_radio_name,
+       .add_pmkid = nl80211_add_pmkid,
+       .remove_pmkid = nl80211_remove_pmkid,
+       .flush_pmkid = nl80211_flush_pmkid,
 };