X-Git-Url: http://www.project-moonshot.org/gitweb/?a=blobdiff_plain;f=src%2Fdrivers%2Fdriver_nl80211.c;h=8984468c10140faf17bdea52870c06ffed06e8c3;hb=9884f9cc1cb20dfa3329558c2b2bd86d5bacb9be;hp=1f742ed0ca8ab58151f21cc7babdbdd81ee5cd71;hpb=582507be85205742ebb96d7e42ae5d2f4d5d4ddf;p=libeap.git diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c index 1f742ed..8984468 100644 --- a/src/drivers/driver_nl80211.c +++ b/src/drivers/driver_nl80211.c @@ -4,7 +4,7 @@ * Copyright (c) 2003-2004, Instant802 Networks, Inc. * Copyright (c) 2005-2006, Devicescape Software, Inc. * Copyright (c) 2007, Johannes Berg - * Copyright (c) 2009, Atheros Communications + * Copyright (c) 2009-2010, Atheros Communications * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as @@ -22,6 +22,7 @@ #include #include #include +#include #include #include #include "nl80211_copy.h" @@ -33,13 +34,54 @@ #include "linux_ioctl.h" #include "radiotap.h" #include "radiotap_iter.h" +#include "rfkill.h" #include "driver.h" #ifdef CONFIG_LIBNL20 /* libnl 2.0 compatibility code */ #define nl_handle nl_sock -#define nl_handle_alloc_cb nl_socket_alloc_cb -#define nl_handle_destroy nl_socket_free +#define nl80211_handle_alloc nl_socket_alloc_cb +#define nl80211_handle_destroy nl_socket_free +#else +/* + * libnl 1.1 has a bug, it tries to allocate socket numbers densely + * but when you free a socket again it will mess up its bitmap and + * and use the wrong number the next time it needs a socket ID. + * Therefore, we wrap the handle alloc/destroy and add our own pid + * accounting. + */ +static uint32_t port_bitmap[32] = { 0 }; + +static struct nl_handle *nl80211_handle_alloc(void *cb) +{ + struct nl_handle *handle; + uint32_t pid = getpid() & 0x3FFFFF; + int i; + + handle = nl_handle_alloc_cb(cb); + + for (i = 0; i < 1024; i++) { + if (port_bitmap[i / 32] & (1 << (i % 32))) + continue; + port_bitmap[i / 32] |= 1 << (i % 32); + pid += i << 22; + break; + } + + nl_socket_set_local_port(handle, pid); + + return handle; +} + +static void nl80211_handle_destroy(struct nl_handle *handle) +{ + uint32_t port = nl_socket_get_local_port(handle); + + port >>= 22; + port_bitmap[port / 32] &= ~(1 << (port % 32)); + + nl_handle_destroy(handle); +} #endif /* CONFIG_LIBNL20 */ @@ -58,8 +100,10 @@ #endif struct i802_bss { + struct wpa_driver_nl80211_data *drv; struct i802_bss *next; int ifindex; + char ifname[IFNAMSIZ + 1]; unsigned int beacon_set:1; }; @@ -67,10 +111,11 @@ struct wpa_driver_nl80211_data { void *ctx; struct netlink_data *netlink; int ioctl_sock; /* socket for ioctl() use */ - char ifname[IFNAMSIZ + 1]; char brname[IFNAMSIZ]; int ifindex; int if_removed; + int if_disabled; + struct rfkill_data *rfkill; struct wpa_driver_capa capa; int has_capability; @@ -99,12 +144,17 @@ struct wpa_driver_nl80211_data { int probe_req_report; int disable_11b_rates; - unsigned int beacon_set:1; 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; + + struct wpa_driver_scan_filter *filter_ssids; + size_t num_filter_ssids; + + struct i802_bss first_bss; #ifdef HOSTAPD int eapol_sock; /* socket for EAPOL frames */ @@ -113,8 +163,6 @@ struct wpa_driver_nl80211_data { int *if_indices; int num_if_indices; - struct i802_bss bss; - int last_freq; int last_freq_ht; #endif /* HOSTAPD */ @@ -127,21 +175,26 @@ static int wpa_driver_nl80211_set_mode(void *priv, int mode); static int wpa_driver_nl80211_finish_drv_init(struct wpa_driver_nl80211_data *drv); static int wpa_driver_nl80211_mlme(struct wpa_driver_nl80211_data *drv, - const u8 *addr, int cmd, u16 reason_code); + const u8 *addr, int cmd, u16 reason_code, + int local_state_change); static void nl80211_remove_monitor_interface( struct wpa_driver_nl80211_data *drv); #ifdef HOSTAPD static void add_ifidx(struct wpa_driver_nl80211_data *drv, int ifidx); 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 have_ifidx(struct wpa_driver_nl80211_data *drv, int ifidx); static int wpa_driver_nl80211_if_remove(void *priv, enum wpa_driver_if_type type, const char *ifname); +#else /* HOSTAPD */ +static int have_ifidx(struct wpa_driver_nl80211_data *drv, int ifidx) +{ + return 0; +} #endif /* HOSTAPD */ +static int i802_set_freq(void *priv, struct hostapd_freq_params *freq); 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, @@ -178,10 +231,10 @@ static int no_seq_check(struct nl_msg *msg, void *arg) } -static int send_and_recv_msgs(struct wpa_driver_nl80211_data *drv, - struct nl_msg *msg, - int (*valid_handler)(struct nl_msg *, void *), - void *valid_data) +static int send_and_recv(struct wpa_driver_nl80211_data *drv, + struct nl_handle *nl_handle, struct nl_msg *msg, + int (*valid_handler)(struct nl_msg *, void *), + void *valid_data) { struct nl_cb *cb; int err = -ENOMEM; @@ -190,7 +243,7 @@ static int send_and_recv_msgs(struct wpa_driver_nl80211_data *drv, if (!cb) goto out; - err = nl_send_auto_complete(drv->nl_handle, msg); + err = nl_send_auto_complete(nl_handle, msg); if (err < 0) goto out; @@ -205,7 +258,7 @@ static int send_and_recv_msgs(struct wpa_driver_nl80211_data *drv, valid_handler, valid_data); while (err > 0) - nl_recvmsgs(drv->nl_handle, cb); + nl_recvmsgs(nl_handle, cb); out: nl_cb_put(cb); nlmsg_free(msg); @@ -213,6 +266,16 @@ static int send_and_recv_msgs(struct wpa_driver_nl80211_data *drv, } +static int send_and_recv_msgs(struct wpa_driver_nl80211_data *drv, + struct nl_msg *msg, + int (*valid_handler)(struct nl_msg *, void *), + void *valid_data) +{ + return send_and_recv(drv, drv->nl_handle, msg, valid_handler, + valid_data); +} + + struct family_data { const char *group; int id; @@ -277,7 +340,8 @@ nla_put_failure: static int wpa_driver_nl80211_get_bssid(void *priv, u8 *bssid) { - struct wpa_driver_nl80211_data *drv = priv; + struct i802_bss *bss = priv; + struct wpa_driver_nl80211_data *drv = bss->drv; if (!drv->associated) return -1; os_memcpy(bssid, drv->bssid, ETH_ALEN); @@ -287,7 +351,8 @@ static int wpa_driver_nl80211_get_bssid(void *priv, u8 *bssid) static int wpa_driver_nl80211_get_ssid(void *priv, u8 *ssid) { - struct wpa_driver_nl80211_data *drv = priv; + struct i802_bss *bss = priv; + struct wpa_driver_nl80211_data *drv = bss->drv; if (!drv->associated) return -1; os_memcpy(ssid, drv->ssid, drv->ssid_len); @@ -312,7 +377,7 @@ static void wpa_driver_nl80211_event_link(struct wpa_driver_nl80211_data *drv, event.interface_status.ifname, del ? "removed" : "added"); - if (os_strcmp(drv->ifname, event.interface_status.ifname) == 0) { + if (os_strcmp(drv->first_bss.ifname, event.interface_status.ifname) == 0) { if (del) drv->if_removed = 1; else @@ -335,7 +400,7 @@ static int wpa_driver_nl80211_own_ifname(struct wpa_driver_nl80211_data *drv, rta_len = RTA_ALIGN(sizeof(struct rtattr)); while (RTA_OK(attr, attrlen)) { if (attr->rta_type == IFLA_IFNAME) { - if (os_strcmp(((char *) attr) + rta_len, drv->ifname) + if (os_strcmp(((char *) attr) + rta_len, drv->first_bss.ifname) == 0) return 1; else @@ -355,7 +420,7 @@ static int wpa_driver_nl80211_own_ifindex(struct wpa_driver_nl80211_data *drv, return 1; if (drv->if_removed && wpa_driver_nl80211_own_ifname(drv, buf, len)) { - drv->ifindex = if_nametoindex(drv->ifname); + drv->first_bss.ifindex = if_nametoindex(drv->first_bss.ifname); wpa_printf(MSG_DEBUG, "nl80211: Update ifindex for a removed " "interface"); wpa_driver_nl80211_finish_drv_init(drv); @@ -373,10 +438,12 @@ static void wpa_driver_nl80211_event_rtm_newlink(void *ctx, struct wpa_driver_nl80211_data *drv = ctx; int attrlen, rta_len; struct rtattr *attr; + u32 brid = 0; - if (!wpa_driver_nl80211_own_ifindex(drv, ifi->ifi_index, buf, len)) { - wpa_printf(MSG_DEBUG, "Ignore event for foreign ifindex %d", - ifi->ifi_index); + if (!wpa_driver_nl80211_own_ifindex(drv, ifi->ifi_index, buf, len) && + !have_ifidx(drv, ifi->ifi_index)) { + wpa_printf(MSG_DEBUG, "nl80211: Ignore event for foreign " + "ifindex %d", ifi->ifi_index); return; } @@ -387,6 +454,19 @@ static void wpa_driver_nl80211_event_rtm_newlink(void *ctx, (ifi->ifi_flags & IFF_RUNNING) ? "[RUNNING]" : "", (ifi->ifi_flags & IFF_LOWER_UP) ? "[LOWER_UP]" : "", (ifi->ifi_flags & IFF_DORMANT) ? "[DORMANT]" : ""); + + if (!drv->if_disabled && !(ifi->ifi_flags & IFF_UP)) { + wpa_printf(MSG_DEBUG, "nl80211: Interface down"); + drv->if_disabled = 1; + wpa_supplicant_event(drv->ctx, EVENT_INTERFACE_DISABLED, NULL); + } + + if (drv->if_disabled && (ifi->ifi_flags & IFF_UP)) { + wpa_printf(MSG_DEBUG, "nl80211: Interface up"); + drv->if_disabled = 0; + wpa_supplicant_event(drv->ctx, EVENT_INTERFACE_ENABLED, NULL); + } + /* * Some drivers send the association event before the operup event--in * this case, lifting operstate in wpa_driver_nl80211_set_operstate() @@ -408,9 +488,21 @@ static void wpa_driver_nl80211_event_rtm_newlink(void *ctx, drv, ((char *) attr) + rta_len, attr->rta_len - rta_len, 0); - } + } else if (attr->rta_type == IFLA_MASTER) + brid = nla_get_u32((struct nlattr *) attr); attr = RTA_NEXT(attr, attrlen); } + +#ifdef HOSTAPD + if (ifi->ifi_family == AF_BRIDGE && brid) { + /* device has been added to bridge */ + char namebuf[IFNAMSIZ]; + if_indextoname(brid, namebuf); + wpa_printf(MSG_DEBUG, "nl80211: Add ifindex %u for bridge %s", + brid, namebuf); + add_ifidx(drv, brid); + } +#endif /* HOSTAPD */ } @@ -421,6 +513,7 @@ static void wpa_driver_nl80211_event_rtm_dellink(void *ctx, struct wpa_driver_nl80211_data *drv = ctx; int attrlen, rta_len; struct rtattr *attr; + u32 brid = 0; attrlen = len; attr = (struct rtattr *) buf; @@ -432,9 +525,21 @@ static void wpa_driver_nl80211_event_rtm_dellink(void *ctx, drv, ((char *) attr) + rta_len, attr->rta_len - rta_len, 1); - } + } else if (attr->rta_type == IFLA_MASTER) + brid = nla_get_u32((struct nlattr *) attr); attr = RTA_NEXT(attr, attrlen); } + +#ifdef HOSTAPD + if (ifi->ifi_family == AF_BRIDGE && brid) { + /* device has been removed from bridge */ + char namebuf[IFNAMSIZ]; + if_indextoname(brid, namebuf); + wpa_printf(MSG_DEBUG, "nl80211: Remove ifindex %u for bridge " + "%s", brid, namebuf); + del_ifidx(drv, brid); + } +#endif /* HOSTAPD */ } @@ -581,9 +686,132 @@ static void mlme_timeout_event(struct wpa_driver_nl80211_data *drv, } +static void mlme_event_action(struct wpa_driver_nl80211_data *drv, + struct nlattr *freq, const u8 *frame, size_t len) +{ + const struct ieee80211_mgmt *mgmt; + union wpa_event_data event; + u16 fc, stype; + + mgmt = (const struct ieee80211_mgmt *) frame; + if (len < 24) { + wpa_printf(MSG_DEBUG, "nl80211: Too short action frame"); + return; + } + + fc = le_to_host16(mgmt->frame_control); + stype = WLAN_FC_GET_STYPE(fc); + + os_memset(&event, 0, sizeof(event)); + event.rx_action.da = mgmt->da; + event.rx_action.sa = mgmt->sa; + event.rx_action.bssid = mgmt->bssid; + event.rx_action.category = mgmt->u.action.category; + event.rx_action.data = &mgmt->u.action.category + 1; + event.rx_action.len = frame + len - event.rx_action.data; + if (freq) + event.rx_action.freq = nla_get_u32(freq); + wpa_supplicant_event(drv->ctx, EVENT_RX_ACTION, &event); +} + + +static void mlme_event_action_tx_status(struct wpa_driver_nl80211_data *drv, + struct nlattr *cookie, const u8 *frame, + size_t len, struct nlattr *ack) +{ + union wpa_event_data event; + const struct ieee80211_hdr *hdr; + u16 fc; + u64 cookie_val; + + if (!cookie) + return; + + cookie_val = nla_get_u64(cookie); + wpa_printf(MSG_DEBUG, "nl80211: Action TX status: cookie=0%llx%s " + "(ack=%d)", + (long long unsigned int) cookie_val, + cookie_val == drv->send_action_cookie ? + " (match)" : " (unknown)", ack != NULL); + if (cookie_val != drv->send_action_cookie) + return; + + hdr = (const struct ieee80211_hdr *) frame; + fc = le_to_host16(hdr->frame_control); + + os_memset(&event, 0, sizeof(event)); + event.tx_status.type = WLAN_FC_GET_TYPE(fc); + event.tx_status.stype = WLAN_FC_GET_STYPE(fc); + event.tx_status.dst = hdr->addr1; + event.tx_status.data = frame; + event.tx_status.data_len = len; + event.tx_status.ack = ack != NULL; + wpa_supplicant_event(drv->ctx, EVENT_TX_STATUS, &event); +} + + +static void mlme_event_deauth_disassoc(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; + const u8 *bssid = NULL; + u16 reason_code = 0; + + mgmt = (const struct ieee80211_mgmt *) frame; + if (len >= 24) { + bssid = mgmt->bssid; + + if (drv->associated != 0 && + os_memcmp(bssid, drv->bssid, ETH_ALEN) != 0 && + os_memcmp(bssid, drv->auth_bssid, ETH_ALEN) != 0) { + /* + * We have presumably received this deauth as a + * response to a clear_state_mismatch() outgoing + * deauth. Don't let it take us offline! + */ + wpa_printf(MSG_DEBUG, "nl80211: Deauth received " + "from Unknown BSSID " MACSTR " -- ignoring", + MAC2STR(bssid)); + return; + } + } + + drv->associated = 0; + 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_DISASSOC) { + event.disassoc_info.addr = bssid; + event.disassoc_info.reason_code = reason_code; + if (frame + len > mgmt->u.disassoc.variable) { + event.disassoc_info.ie = mgmt->u.disassoc.variable; + event.disassoc_info.ie_len = frame + len - + mgmt->u.disassoc.variable; + } + } else { + event.deauth_info.addr = bssid; + event.deauth_info.reason_code = reason_code; + if (frame + len > mgmt->u.deauth.variable) { + event.deauth_info.ie = mgmt->u.deauth.variable; + event.deauth_info.ie_len = frame + len - + mgmt->u.deauth.variable; + } + } + + 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) + struct nlattr *addr, struct nlattr *timed_out, + struct nlattr *freq, struct nlattr *ack, + struct nlattr *cookie) { if (timed_out && addr) { mlme_timeout_event(drv, cmd, addr); @@ -608,12 +836,19 @@ static void mlme_event(struct wpa_driver_nl80211_data *drv, mlme_event_assoc(drv, nla_data(frame), nla_len(frame)); break; case NL80211_CMD_DEAUTHENTICATE: - drv->associated = 0; - wpa_supplicant_event(drv->ctx, EVENT_DEAUTH, NULL); + mlme_event_deauth_disassoc(drv, EVENT_DEAUTH, + nla_data(frame), nla_len(frame)); break; case NL80211_CMD_DISASSOCIATE: - drv->associated = 0; - wpa_supplicant_event(drv->ctx, EVENT_DISASSOC, NULL); + mlme_event_deauth_disassoc(drv, EVENT_DISASSOC, + nla_data(frame), nla_len(frame)); + break; + case NL80211_CMD_FRAME: + mlme_event_action(drv, freq, nla_data(frame), nla_len(frame)); + break; + case NL80211_CMD_FRAME_TX_STATUS: + mlme_event_action_tx_status(drv, cookie, nla_data(frame), + nla_len(frame), ack); break; default: break; @@ -762,18 +997,113 @@ static void send_scan_event(struct wpa_driver_nl80211_data *drv, int aborted, } +static int get_link_signal(struct nl_msg *msg, void *arg) +{ + struct nlattr *tb[NL80211_ATTR_MAX + 1]; + struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg)); + struct nlattr *sinfo[NL80211_STA_INFO_MAX + 1]; + static struct nla_policy policy[NL80211_STA_INFO_MAX + 1] = { + [NL80211_STA_INFO_SIGNAL] = { .type = NLA_U8 }, + }; + int *sig = arg; + + nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0), + genlmsg_attrlen(gnlh, 0), NULL); + if (!tb[NL80211_ATTR_STA_INFO] || + nla_parse_nested(sinfo, NL80211_STA_INFO_MAX, + tb[NL80211_ATTR_STA_INFO], policy)) + return NL_SKIP; + if (!sinfo[NL80211_STA_INFO_SIGNAL]) + return NL_SKIP; + + *sig = (s8) nla_get_u8(sinfo[NL80211_STA_INFO_SIGNAL]); + return NL_SKIP; +} + + +static int nl80211_get_link_signal(struct wpa_driver_nl80211_data *drv, + int *sig) +{ + struct nl_msg *msg; + + *sig = -9999; + + msg = nlmsg_alloc(); + if (!msg) + return -ENOMEM; + + genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, + 0, NL80211_CMD_GET_STATION, 0); + + NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex); + NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, drv->bssid); + + return send_and_recv_msgs(drv, msg, get_link_signal, sig); + nla_put_failure: + return -ENOBUFS; +} + + +static void nl80211_cqm_event(struct wpa_driver_nl80211_data *drv, + struct nlattr *tb[]) +{ + static struct nla_policy cqm_policy[NL80211_ATTR_CQM_MAX + 1] = { + [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 }, + }; + struct nlattr *cqm[NL80211_ATTR_CQM_MAX + 1]; + enum nl80211_cqm_rssi_threshold_event event; + union wpa_event_data ed; + int sig, res; + + if (tb[NL80211_ATTR_CQM] == NULL || + nla_parse_nested(cqm, NL80211_ATTR_CQM_MAX, tb[NL80211_ATTR_CQM], + cqm_policy)) { + wpa_printf(MSG_DEBUG, "nl80211: Ignore invalid CQM event"); + 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"); + ed.signal_change.above_threshold = 1; + } else if (event == NL80211_CQM_RSSI_THRESHOLD_EVENT_LOW) { + wpa_printf(MSG_DEBUG, "nl80211: Connection quality monitor " + "event: RSSI low"); + ed.signal_change.above_threshold = 0; + } else + return; + + res = nl80211_get_link_signal(drv, &sig); + if (res == 0) { + ed.signal_change.current_signal = sig; + wpa_printf(MSG_DEBUG, "nl80211: Signal: %d dBm", sig); + } + + wpa_supplicant_event(drv->ctx, EVENT_SIGNAL_CHANGE, &ed); +} + + static int process_event(struct nl_msg *msg, void *arg) { struct wpa_driver_nl80211_data *drv = arg; struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg)); struct nlattr *tb[NL80211_ATTR_MAX + 1]; + union wpa_event_data data; nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0), genlmsg_attrlen(gnlh, 0), NULL); if (tb[NL80211_ATTR_IFINDEX]) { int ifindex = nla_get_u32(tb[NL80211_ATTR_IFINDEX]); - if (ifindex != drv->ifindex) { + if (ifindex != drv->ifindex && !have_ifidx(drv, ifindex)) { wpa_printf(MSG_DEBUG, "nl80211: Ignored event (cmd=%d)" " for foreign interface (ifindex %d)", gnlh->cmd, ifindex); @@ -784,7 +1114,8 @@ static int process_event(struct nl_msg *msg, void *arg) if (drv->ap_scan_as_station && (gnlh->cmd == NL80211_CMD_NEW_SCAN_RESULTS || gnlh->cmd == NL80211_CMD_SCAN_ABORTED)) { - wpa_driver_nl80211_set_mode(drv, IEEE80211_MODE_AP); + wpa_driver_nl80211_set_mode(&drv->first_bss, + IEEE80211_MODE_AP); drv->ap_scan_as_station = 0; } @@ -813,8 +1144,12 @@ static int process_event(struct nl_msg *msg, void *arg) case NL80211_CMD_ASSOCIATE: case NL80211_CMD_DEAUTHENTICATE: case NL80211_CMD_DISASSOCIATE: + case NL80211_CMD_FRAME: + case NL80211_CMD_FRAME_TX_STATUS: mlme_event(drv, gnlh->cmd, tb[NL80211_ATTR_FRAME], - tb[NL80211_ATTR_MAC], tb[NL80211_ATTR_TIMED_OUT]); + tb[NL80211_ATTR_MAC], tb[NL80211_ATTR_TIMED_OUT], + tb[NL80211_ATTR_WIPHY_FREQ], tb[NL80211_ATTR_ACK], + tb[NL80211_ATTR_COOKIE]); break; case NL80211_CMD_CONNECT: case NL80211_CMD_ROAM: @@ -835,7 +1170,11 @@ static int process_event(struct nl_msg *msg, void *arg) break; } drv->associated = 0; - wpa_supplicant_event(drv->ctx, EVENT_DISASSOC, NULL); + os_memset(&data, 0, sizeof(data)); + if (tb[NL80211_ATTR_REASON_CODE]) + data.disassoc_info.reason_code = + nla_get_u16(tb[NL80211_ATTR_REASON_CODE]); + wpa_supplicant_event(drv->ctx, EVENT_DISASSOC, &data); break; case NL80211_CMD_MICHAEL_MIC_FAILURE: mlme_event_michael_mic_failure(drv, tb); @@ -849,6 +1188,9 @@ static int process_event(struct nl_msg *msg, void *arg) case NL80211_CMD_CANCEL_REMAIN_ON_CHANNEL: mlme_event_remain_on_channel(drv, 1, tb); break; + case NL80211_CMD_NOTIFY_CQM: + nl80211_cqm_event(drv, tb); + break; default: wpa_printf(MSG_DEBUG, "nl80211: Ignored unknown event " "(cmd=%d)", gnlh->cmd); @@ -888,7 +1230,8 @@ static void wpa_driver_nl80211_event_receive(int sock, void *eloop_ctx, */ static int wpa_driver_nl80211_set_country(void *priv, const char *alpha2_arg) { - struct wpa_driver_nl80211_data *drv = priv; + struct i802_bss *bss = priv; + struct wpa_driver_nl80211_data *drv = bss->drv; char alpha2[3]; struct nl_msg *msg; @@ -977,7 +1320,7 @@ static int wpa_driver_nl80211_get_info(struct wpa_driver_nl80211_data *drv, genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, 0, NL80211_CMD_GET_WIPHY, 0); - NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex); + NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->first_bss.ifindex); if (send_and_recv_msgs(drv, msg, wiphy_info_handler, info) == 0) return 0; @@ -1020,14 +1363,15 @@ static int wpa_driver_nl80211_capa(struct wpa_driver_nl80211_data *drv) } 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; return 0; } #endif /* HOSTAPD */ -static int wpa_driver_nl80211_init_nl(struct wpa_driver_nl80211_data *drv, - void *ctx) +static int wpa_driver_nl80211_init_nl(struct wpa_driver_nl80211_data *drv) { int ret; @@ -1040,14 +1384,14 @@ static int wpa_driver_nl80211_init_nl(struct wpa_driver_nl80211_data *drv, goto err1; } - drv->nl_handle = nl_handle_alloc_cb(drv->nl_cb); + drv->nl_handle = nl80211_handle_alloc(drv->nl_cb); if (drv->nl_handle == NULL) { wpa_printf(MSG_ERROR, "nl80211: Failed to allocate netlink " "callbacks"); goto err2; } - drv->nl_handle_event = nl_handle_alloc_cb(drv->nl_cb); + drv->nl_handle_event = nl80211_handle_alloc(drv->nl_cb); if (drv->nl_handle_event == NULL) { wpa_printf(MSG_ERROR, "nl80211: Failed to allocate netlink " "callbacks (event)"); @@ -1121,7 +1465,7 @@ static int wpa_driver_nl80211_init_nl(struct wpa_driver_nl80211_data *drv, } eloop_register_read_sock(nl_socket_get_fd(drv->nl_handle_event), - wpa_driver_nl80211_event_receive, drv, ctx); + wpa_driver_nl80211_event_receive, drv, NULL); return 0; @@ -1130,9 +1474,9 @@ err4: err3b: nl_cache_free(drv->nl_cache); err3: - nl_handle_destroy(drv->nl_handle_event); + nl80211_handle_destroy(drv->nl_handle_event); err2b: - nl_handle_destroy(drv->nl_handle); + nl80211_handle_destroy(drv->nl_handle); err2: nl_cb_put(drv->nl_cb); err1: @@ -1140,6 +1484,29 @@ err1: } +static void wpa_driver_nl80211_rfkill_blocked(void *ctx) +{ + wpa_printf(MSG_DEBUG, "nl80211: RFKILL blocked"); + /* + * This may be for any interface; use ifdown event to disable + * interface. + */ +} + + +static void wpa_driver_nl80211_rfkill_unblocked(void *ctx) +{ + struct wpa_driver_nl80211_data *drv = ctx; + wpa_printf(MSG_DEBUG, "nl80211: RFKILL unblocked"); + if (linux_set_iface_flags(drv->ioctl_sock, drv->first_bss.ifname, 1)) { + wpa_printf(MSG_DEBUG, "nl80211: Could not set interface UP " + "after rfkill unblock"); + return; + } + /* rtnetlink ifup handler will report interface as enabled */ +} + + /** * wpa_driver_nl80211_init - Initialize nl80211 driver interface * @ctx: context to be used when calling wpa_supplicant functions, @@ -1151,17 +1518,21 @@ static void * wpa_driver_nl80211_init(void *ctx, const char *ifname) { struct wpa_driver_nl80211_data *drv; struct netlink_config *cfg; + struct rfkill_config *rcfg; + struct i802_bss *bss; drv = os_zalloc(sizeof(*drv)); if (drv == NULL) return NULL; drv->ctx = ctx; - os_strlcpy(drv->ifname, ifname, sizeof(drv->ifname)); + bss = &drv->first_bss; + bss->drv = drv; + os_strlcpy(bss->ifname, ifname, sizeof(bss->ifname)); drv->monitor_ifidx = -1; drv->monitor_sock = -1; drv->ioctl_sock = -1; - if (wpa_driver_nl80211_init_nl(drv, ctx)) { + if (wpa_driver_nl80211_init_nl(drv)) { os_free(drv); return NULL; } @@ -1183,41 +1554,154 @@ static void * wpa_driver_nl80211_init(void *ctx, const char *ifname) os_free(cfg); goto failed; } + + rcfg = os_zalloc(sizeof(*rcfg)); + if (rcfg == NULL) + goto failed; + rcfg->ctx = drv; + os_strlcpy(rcfg->ifname, ifname, sizeof(rcfg->ifname)); + rcfg->blocked_cb = wpa_driver_nl80211_rfkill_blocked; + rcfg->unblocked_cb = wpa_driver_nl80211_rfkill_unblocked; + drv->rfkill = rfkill_init(rcfg); + if (drv->rfkill == NULL) { + wpa_printf(MSG_DEBUG, "nl80211: RFKILL status not available"); + os_free(rcfg); + } + if (wpa_driver_nl80211_finish_drv_init(drv)) goto failed; - return drv; + return bss; failed: + rfkill_deinit(drv->rfkill); netlink_deinit(drv->netlink); if (drv->ioctl_sock >= 0) close(drv->ioctl_sock); genl_family_put(drv->nl80211); nl_cache_free(drv->nl_cache); - nl_handle_destroy(drv->nl_handle); + nl80211_handle_destroy(drv->nl_handle); nl_cb_put(drv->nl_cb); + eloop_unregister_read_sock(nl_socket_get_fd(drv->nl_handle_event)); os_free(drv); return NULL; } +static int nl80211_register_frame(struct wpa_driver_nl80211_data *drv, + u16 type, const u8 *match, size_t match_len) +{ + struct nl_msg *msg; + int ret = -1; + + msg = nlmsg_alloc(); + if (!msg) + return -1; + + genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, 0, + NL80211_CMD_REGISTER_ACTION, 0); + + NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex); + NLA_PUT_U16(msg, NL80211_ATTR_FRAME_TYPE, type); + NLA_PUT(msg, NL80211_ATTR_FRAME_MATCH, match_len, match); + + ret = send_and_recv(drv, drv->nl_handle_event, msg, NULL, NULL); + msg = NULL; + if (ret) { + wpa_printf(MSG_DEBUG, "nl80211: Register Action command " + "failed: ret=%d (%s)", ret, strerror(-ret)); + wpa_hexdump(MSG_DEBUG, "nl80211: Register Action match", + match, match_len); + goto nla_put_failure; + } + ret = 0; +nla_put_failure: + nlmsg_free(msg); + return ret; +} + + +static int nl80211_register_action_frame(struct wpa_driver_nl80211_data *drv, + const u8 *match, size_t match_len) +{ + u16 type = (WLAN_FC_TYPE_MGMT << 2) | (WLAN_FC_STYPE_ACTION << 4); + return nl80211_register_frame(drv, type, match, match_len); +} + + +static int nl80211_register_action_frames(struct wpa_driver_nl80211_data *drv) +{ +#ifdef CONFIG_P2P + /* GAS Initial Request */ + if (nl80211_register_action_frame(drv, (u8 *) "\x04\x0a", 2) < 0) + return -1; + /* GAS Initial Response */ + if (nl80211_register_action_frame(drv, (u8 *) "\x04\x0b", 2) < 0) + return -1; + /* GAS Comeback Request */ + if (nl80211_register_action_frame(drv, (u8 *) "\x04\x0c", 2) < 0) + return -1; + /* GAS Comeback Response */ + if (nl80211_register_action_frame(drv, (u8 *) "\x04\x0d", 2) < 0) + return -1; + /* P2P Public Action */ + if (nl80211_register_action_frame(drv, + (u8 *) "\x04\x09\x50\x6f\x9a\x09", + 6) < 0) + return -1; + /* P2P Action */ + if (nl80211_register_action_frame(drv, + (u8 *) "\x7f\x50\x6f\x9a\x09", + 5) < 0) + return -1; +#endif /* CONFIG_P2P */ + + /* FT Action frames */ + if (nl80211_register_action_frame(drv, (u8 *) "\x06", 1) < 0) + return -1; + else + drv->capa.key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_FT | + WPA_DRIVER_CAPA_KEY_MGMT_FT_PSK; + + return 0; +} + + +static void wpa_driver_nl80211_send_rfkill(void *eloop_ctx, void *timeout_ctx) +{ + wpa_supplicant_event(timeout_ctx, EVENT_INTERFACE_DISABLED, NULL); +} + + static int wpa_driver_nl80211_finish_drv_init(struct wpa_driver_nl80211_data *drv) { - drv->ifindex = if_nametoindex(drv->ifname); + struct i802_bss *bss = &drv->first_bss; + int send_rfkill_event = 0; + + drv->ifindex = if_nametoindex(bss->ifname); + drv->first_bss.ifindex = drv->ifindex; #ifndef HOSTAPD - if (wpa_driver_nl80211_set_mode(drv, IEEE80211_MODE_INFRA) < 0) { + if (wpa_driver_nl80211_set_mode(bss, IEEE80211_MODE_INFRA) < 0) { wpa_printf(MSG_DEBUG, "nl80211: Could not configure driver to " "use managed mode"); } - 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; + if (linux_set_iface_flags(drv->ioctl_sock, bss->ifname, 1)) { + if (rfkill_is_blocked(drv->rfkill)) { + wpa_printf(MSG_DEBUG, "nl80211: Could not yet enable " + "interface '%s' due to rfkill", + bss->ifname); + drv->if_disabled = 1; + send_rfkill_event = 1; + } else { + wpa_printf(MSG_ERROR, "nl80211: Could not set " + "interface '%s' UP", bss->ifname); + return -1; + } } if (wpa_driver_nl80211_capa(drv)) @@ -1227,22 +1711,23 @@ wpa_driver_nl80211_finish_drv_init(struct wpa_driver_nl80211_data *drv) 1, IF_OPER_DORMANT); #endif /* HOSTAPD */ - return 0; -} - + if (nl80211_register_action_frames(drv) < 0) { + wpa_printf(MSG_DEBUG, "nl80211: Failed to register Action " + "frame processing - ignore for now"); + /* + * Older kernel versions did not support this, so ignore the + * error for now. Some functionality may not be available + * because of this. + */ + } -#ifdef HOSTAPD -static void wpa_driver_nl80211_free_bss(struct wpa_driver_nl80211_data *drv) -{ - struct i802_bss *bss, *prev; - bss = drv->bss.next; - while (bss) { - prev = bss; - bss = bss->next; - os_free(bss); + if (send_rfkill_event) { + eloop_register_timeout(0, 0, wpa_driver_nl80211_send_rfkill, + drv, drv->ctx); } + + return 0; } -#endif /* HOSTAPD */ static int wpa_driver_nl80211_del_beacon(struct wpa_driver_nl80211_data *drv) @@ -1272,14 +1757,15 @@ static int wpa_driver_nl80211_del_beacon(struct wpa_driver_nl80211_data *drv) */ static void wpa_driver_nl80211_deinit(void *priv) { - struct wpa_driver_nl80211_data *drv = priv; + struct i802_bss *bss = priv; + struct wpa_driver_nl80211_data *drv = bss->drv; if (drv->added_if_into_bridge) { - if (linux_br_del_if(drv->ioctl_sock, drv->brname, drv->ifname) + if (linux_br_del_if(drv->ioctl_sock, drv->brname, bss->ifname) < 0) wpa_printf(MSG_INFO, "nl80211: Failed to remove " "interface %s from bridge %s: %s", - drv->ifname, drv->brname, strerror(errno)); + bss->ifname, drv->brname, strerror(errno)); } if (drv->added_bridge) { if (linux_br_del(drv->ioctl_sock, drv->brname) < 0) @@ -1309,8 +1795,6 @@ static void wpa_driver_nl80211_deinit(void *priv) if (drv->if_indices != drv->default_if_indices) os_free(drv->if_indices); - - wpa_driver_nl80211_free_bss(drv); #endif /* HOSTAPD */ if (drv->disable_11b_rates) @@ -1318,11 +1802,12 @@ static void wpa_driver_nl80211_deinit(void *priv) netlink_send_oper_ifla(drv->netlink, drv->ifindex, 0, IF_OPER_UP); netlink_deinit(drv->netlink); + rfkill_deinit(drv->rfkill); eloop_cancel_timeout(wpa_driver_nl80211_scan_timeout, drv, drv->ctx); - (void) linux_set_iface_flags(drv->ioctl_sock, drv->ifname, 0); - wpa_driver_nl80211_set_mode(drv, IEEE80211_MODE_INFRA); + (void) linux_set_iface_flags(drv->ioctl_sock, bss->ifname, 0); + wpa_driver_nl80211_set_mode(bss, IEEE80211_MODE_INFRA); if (drv->ioctl_sock >= 0) close(drv->ioctl_sock); @@ -1331,13 +1816,15 @@ static void wpa_driver_nl80211_deinit(void *priv) genl_family_put(drv->nl80211); nl_cache_free(drv->nl_cache); nl_cache_free(drv->nl_cache_event); - nl_handle_destroy(drv->nl_handle); - nl_handle_destroy(drv->nl_handle_event); + nl80211_handle_destroy(drv->nl_handle); + nl80211_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->filter_ssids); + os_free(drv); } @@ -1354,7 +1841,8 @@ static void wpa_driver_nl80211_scan_timeout(void *eloop_ctx, void *timeout_ctx) { struct wpa_driver_nl80211_data *drv = eloop_ctx; if (drv->ap_scan_as_station) { - wpa_driver_nl80211_set_mode(drv, IEEE80211_MODE_AP); + wpa_driver_nl80211_set_mode(&drv->first_bss, + IEEE80211_MODE_AP); drv->ap_scan_as_station = 0; } wpa_printf(MSG_DEBUG, "Scan timeout - try to get results"); @@ -1371,7 +1859,8 @@ static void wpa_driver_nl80211_scan_timeout(void *eloop_ctx, void *timeout_ctx) static int wpa_driver_nl80211_scan(void *priv, struct wpa_driver_scan_params *params) { - struct wpa_driver_nl80211_data *drv = priv; + struct i802_bss *bss = priv; + struct wpa_driver_nl80211_data *drv = bss->drv; int ret = 0, timeout; struct nl_msg *msg, *ssids, *freqs; size_t i; @@ -1386,12 +1875,20 @@ static int wpa_driver_nl80211_scan(void *priv, return -1; } + os_free(drv->filter_ssids); + drv->filter_ssids = params->filter_ssids; + params->filter_ssids = NULL; + drv->num_filter_ssids = params->num_filter_ssids; + genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, 0, NL80211_CMD_TRIGGER_SCAN, 0); NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex); for (i = 0; i < params->num_ssids; i++) { + wpa_hexdump_ascii(MSG_MSGDUMP, "nl80211: Scan SSID", + params->ssids[i].ssid, + params->ssids[i].ssid_len); NLA_PUT(ssids, i + 1, params->ssids[i].ssid_len, params->ssids[i].ssid); } @@ -1399,13 +1896,18 @@ static int wpa_driver_nl80211_scan(void *priv, nla_put_nested(msg, NL80211_ATTR_SCAN_SSIDS, ssids); if (params->extra_ies) { + wpa_hexdump_ascii(MSG_MSGDUMP, "nl80211: Scan extra IEs", + params->extra_ies, params->extra_ies_len); NLA_PUT(msg, NL80211_ATTR_IE, params->extra_ies_len, params->extra_ies); } if (params->freqs) { - for (i = 0; params->freqs[i]; i++) + for (i = 0; params->freqs[i]; i++) { + wpa_printf(MSG_MSGDUMP, "nl80211: Scan frequency %u " + "MHz", params->freqs[i]); NLA_PUT_U32(freqs, i + 1, params->freqs[i]); + } nla_put_nested(msg, NL80211_ATTR_SCAN_FREQUENCIES, freqs); } @@ -1420,12 +1922,12 @@ static int wpa_driver_nl80211_scan(void *priv, * mac80211 does not allow scan requests in AP mode, so * try to do this in station mode. */ - if (wpa_driver_nl80211_set_mode(drv, + if (wpa_driver_nl80211_set_mode(bss, IEEE80211_MODE_INFRA)) goto nla_put_failure; if (wpa_driver_nl80211_scan(drv, params)) { - wpa_driver_nl80211_set_mode(drv, + wpa_driver_nl80211_set_mode(bss, IEEE80211_MODE_AP); goto nla_put_failure; } @@ -1465,6 +1967,57 @@ nla_put_failure: } +static const u8 * nl80211_get_ie(const u8 *ies, size_t ies_len, u8 ie) +{ + const u8 *end, *pos; + + if (ies == NULL) + return NULL; + + pos = ies; + end = ies + ies_len; + + while (pos + 1 < end) { + if (pos + 2 + pos[1] > end) + break; + if (pos[0] == ie) + return pos; + pos += 2 + pos[1]; + } + + return NULL; +} + + +static int nl80211_scan_filtered(struct wpa_driver_nl80211_data *drv, + const u8 *ie, size_t ie_len) +{ + const u8 *ssid; + size_t i; + + if (drv->filter_ssids == NULL) + return 0; + + ssid = nl80211_get_ie(ie, ie_len, WLAN_EID_SSID); + if (ssid == NULL) + return 1; + + for (i = 0; i < drv->num_filter_ssids; i++) { + if (ssid[1] == drv->filter_ssids[i].ssid_len && + os_memcmp(ssid + 2, drv->filter_ssids[i].ssid, ssid[1]) == + 0) + return 0; + } + + return 1; +} + + +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]; @@ -1483,7 +2036,8 @@ static int bss_info_handler(struct nl_msg *msg, void *arg) [NL80211_BSS_SEEN_MS_AGO] = { .type = NLA_U32 }, [NL80211_BSS_BEACON_IES] = { .type = NLA_UNSPEC }, }; - struct wpa_scan_results *res = arg; + struct nl80211_bss_info_arg *_arg = arg; + struct wpa_scan_results *res = _arg->res; struct wpa_scan_res **tmp; struct wpa_scan_res *r; const u8 *ie, *beacon_ie; @@ -1512,6 +2066,10 @@ static int bss_info_handler(struct nl_msg *msg, void *arg) beacon_ie_len = 0; } + if (nl80211_scan_filtered(_arg->drv, ie ? ie : beacon_ie, + ie ? ie_len : beacon_ie_len)) + return NL_SKIP; + r = os_zalloc(sizeof(*r) + ie_len + beacon_ie_len); if (r == NULL) return NL_SKIP; @@ -1584,7 +2142,7 @@ static void clear_state_mismatch(struct wpa_driver_nl80211_data *drv, "mismatch (" MACSTR ")", MAC2STR(addr)); wpa_driver_nl80211_mlme(drv, addr, NL80211_CMD_DEAUTHENTICATE, - WLAN_REASON_PREV_AUTH_NOT_VALID); + WLAN_REASON_PREV_AUTH_NOT_VALID, 1); } } @@ -1654,18 +2212,13 @@ static void wpa_scan_results_free(struct wpa_scan_results *res) } -/** - * wpa_driver_nl80211_get_scan_results - Fetch the latest scan results - * @priv: Pointer to private wext data from wpa_driver_nl80211_init() - * Returns: Scan results on success, -1 on failure - */ static struct wpa_scan_results * -wpa_driver_nl80211_get_scan_results(void *priv) +nl80211_get_scan_results(struct wpa_driver_nl80211_data *drv) { - struct wpa_driver_nl80211_data *drv = priv; struct nl_msg *msg; struct wpa_scan_results *res; int ret; + struct nl80211_bss_info_arg arg; res = os_zalloc(sizeof(*res)); if (res == NULL) @@ -1678,12 +2231,13 @@ wpa_driver_nl80211_get_scan_results(void *priv) NL80211_CMD_GET_SCAN, 0); NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex); - ret = send_and_recv_msgs(drv, msg, bss_info_handler, res); + arg.drv = drv; + arg.res = res; + ret = send_and_recv_msgs(drv, msg, bss_info_handler, &arg); msg = NULL; if (ret == 0) { wpa_printf(MSG_DEBUG, "Received scan results (%lu BSSes)", (unsigned long) res->num); - wpa_driver_nl80211_check_bss_status(drv, res); return res; } wpa_printf(MSG_DEBUG, "nl80211: Scan result fetch failed: ret=%d " @@ -1695,13 +2249,57 @@ nla_put_failure: } +/** + * wpa_driver_nl80211_get_scan_results - Fetch the latest scan results + * @priv: Pointer to private wext data from wpa_driver_nl80211_init() + * Returns: Scan results on success, -1 on failure + */ +static struct wpa_scan_results * +wpa_driver_nl80211_get_scan_results(void *priv) +{ + struct i802_bss *bss = priv; + struct wpa_driver_nl80211_data *drv = bss->drv; + struct wpa_scan_results *res; + + res = nl80211_get_scan_results(drv); + if (res) + wpa_driver_nl80211_check_bss_status(drv, res); + return res; +} + + +static void nl80211_dump_scan(struct wpa_driver_nl80211_data *drv) +{ + struct wpa_scan_results *res; + size_t i; + + res = nl80211_get_scan_results(drv); + if (res == NULL) { + wpa_printf(MSG_DEBUG, "nl80211: Failed to get scan results"); + return; + } + + wpa_printf(MSG_DEBUG, "nl80211: Scan result dump"); + for (i = 0; i < res->num; i++) { + struct wpa_scan_res *r = res->res[i]; + wpa_printf(MSG_DEBUG, "nl80211: %d/%d " MACSTR "%s%s", + (int) i, (int) res->num, MAC2STR(r->bssid), + r->flags & WPA_SCAN_AUTHENTICATED ? " [auth]" : "", + r->flags & WPA_SCAN_ASSOCIATED ? " [assoc]" : ""); + } + + wpa_scan_results_free(res); +} + + static int wpa_driver_nl80211_set_key(const char *ifname, void *priv, enum wpa_alg alg, const u8 *addr, int key_idx, int set_tx, const u8 *seq, size_t seq_len, const u8 *key, size_t key_len) { - struct wpa_driver_nl80211_data *drv = priv; + struct i802_bss *bss = priv; + struct wpa_driver_nl80211_data *drv = bss->drv; int ifindex = if_nametoindex(ifname); struct nl_msg *msg; int ret; @@ -1763,7 +2361,7 @@ static int wpa_driver_nl80211_set_key(const char *ifname, void *priv, NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, ifindex); ret = send_and_recv_msgs(drv, msg, NULL, NULL); - if (ret == -ENOENT && alg == WPA_ALG_NONE) + if ((ret == -ENOENT || ret == -ENOLINK) && alg == WPA_ALG_NONE) ret = 0; if (ret) wpa_printf(MSG_DEBUG, "nl80211: set_key failed; err=%d %s)", @@ -1775,9 +2373,12 @@ static int wpa_driver_nl80211_set_key(const char *ifname, void *priv, */ if (ret || !set_tx || alg == WPA_ALG_NONE) return ret; -#ifdef HOSTAPD /* FIX: is this needed? */ +#ifdef HOSTAPD if (addr) return ret; +#else /* HOSTAPD */ + if (drv->nlmode == NL80211_IFTYPE_AP && addr) + return ret; #endif /* HOSTAPD */ msg = nlmsg_alloc(); @@ -1915,7 +2516,8 @@ nla_put_failure: static int wpa_driver_nl80211_mlme(struct wpa_driver_nl80211_data *drv, - const u8 *addr, int cmd, u16 reason_code) + const u8 *addr, int cmd, u16 reason_code, + int local_state_change) { int ret = -1; struct nl_msg *msg; @@ -1929,6 +2531,8 @@ static int wpa_driver_nl80211_mlme(struct wpa_driver_nl80211_data *drv, NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex); NLA_PUT_U16(msg, NL80211_ATTR_REASON_CODE, reason_code); NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, addr); + if (local_state_change) + NLA_PUT_FLAG(msg, NL80211_ATTR_LOCAL_STATE_CHANGE); ret = send_and_recv_msgs(drv, msg, NULL, NULL); msg = NULL; @@ -1948,43 +2552,48 @@ nla_put_failure: static int wpa_driver_nl80211_disconnect(struct wpa_driver_nl80211_data *drv, const u8 *addr, int reason_code) { - wpa_printf(MSG_DEBUG, "%s", __func__); + wpa_printf(MSG_DEBUG, "%s(addr=" MACSTR " reason_code=%d)", + __func__, MAC2STR(addr), reason_code); drv->associated = 0; return wpa_driver_nl80211_mlme(drv, addr, NL80211_CMD_DISCONNECT, - reason_code); + reason_code, 0); } static int wpa_driver_nl80211_deauthenticate(void *priv, const u8 *addr, int reason_code) { - struct wpa_driver_nl80211_data *drv = priv; + struct i802_bss *bss = priv; + struct wpa_driver_nl80211_data *drv = bss->drv; if (!(drv->capa.flags & WPA_DRIVER_FLAGS_SME)) return wpa_driver_nl80211_disconnect(drv, addr, reason_code); - wpa_printf(MSG_DEBUG, "%s", __func__); + wpa_printf(MSG_DEBUG, "%s(addr=" MACSTR " reason_code=%d)", + __func__, MAC2STR(addr), reason_code); drv->associated = 0; return wpa_driver_nl80211_mlme(drv, addr, NL80211_CMD_DEAUTHENTICATE, - reason_code); + reason_code, 0); } static int wpa_driver_nl80211_disassociate(void *priv, const u8 *addr, int reason_code) { - struct wpa_driver_nl80211_data *drv = priv; + struct i802_bss *bss = priv; + struct wpa_driver_nl80211_data *drv = bss->drv; if (!(drv->capa.flags & WPA_DRIVER_FLAGS_SME)) return wpa_driver_nl80211_disconnect(drv, addr, reason_code); wpa_printf(MSG_DEBUG, "%s", __func__); drv->associated = 0; return wpa_driver_nl80211_mlme(drv, addr, NL80211_CMD_DISASSOCIATE, - reason_code); + reason_code, 0); } static int wpa_driver_nl80211_authenticate( void *priv, struct wpa_driver_auth_params *params) { - struct wpa_driver_nl80211_data *drv = priv; + struct i802_bss *bss = priv; + struct wpa_driver_nl80211_data *drv = bss->drv; int ret = -1, i; struct nl_msg *msg; enum nl80211_auth_type type; @@ -1996,7 +2605,7 @@ static int wpa_driver_nl80211_authenticate( if (drv->nlmode != NL80211_IFTYPE_STATION) wpa_driver_nl80211_set_mode(priv, IEEE80211_MODE_INFRA); - if (wpa_driver_nl80211_set_mode(drv, IEEE80211_MODE_INFRA) < 0) + if (wpa_driver_nl80211_set_mode(priv, IEEE80211_MODE_INFRA) < 0) return -1; retry: @@ -2013,8 +2622,8 @@ retry: for (i = 0; i < 4; i++) { if (!params->wep_key[i]) continue; - wpa_driver_nl80211_set_key(drv->ifname, drv, WPA_ALG_WEP, NULL, - i, + wpa_driver_nl80211_set_key(bss->ifname, priv, WPA_ALG_WEP, + NULL, i, i == params->wep_tx_keyidx, NULL, 0, params->wep_key[i], params->wep_key_len[i]); @@ -2046,10 +2655,6 @@ retry: wpa_hexdump(MSG_DEBUG, " * IEs", params->ie, params->ie_len); if (params->ie) NLA_PUT(msg, NL80211_ATTR_IE, params->ie_len, params->ie); - /* - * 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 & WPA_AUTH_ALG_OPEN) type = NL80211_AUTHTYPE_OPEN_SYSTEM; else if (params->auth_alg & WPA_AUTH_ALG_SHARED) @@ -2062,6 +2667,10 @@ retry: goto nla_put_failure; wpa_printf(MSG_DEBUG, " * Auth Type %d", type); NLA_PUT_U32(msg, NL80211_ATTR_AUTH_TYPE, type); + if (params->local_state_change) { + wpa_printf(MSG_DEBUG, " * Local state change only"); + NLA_PUT_FLAG(msg, NL80211_ATTR_LOCAL_STATE_CHANGE); + } ret = send_and_recv_msgs(drv, msg, NULL, NULL); msg = NULL; @@ -2069,7 +2678,8 @@ retry: wpa_printf(MSG_DEBUG, "nl80211: MLME command failed: ret=%d " "(%s)", ret, strerror(-ret)); count++; - if (ret == -EALREADY && count == 1 && params->bssid) { + if (ret == -EALREADY && count == 1 && params->bssid && + !params->local_state_change) { /* * mac80211 does not currently accept new * authentication if we are already authenticated. As a @@ -2078,7 +2688,7 @@ retry: wpa_printf(MSG_DEBUG, "nl80211: Retry authentication " "after forced deauthentication"); wpa_driver_nl80211_deauthenticate( - drv, params->bssid, + bss, params->bssid, WLAN_REASON_PREV_AUTH_NOT_VALID); nlmsg_free(msg); goto retry; @@ -2348,7 +2958,8 @@ wpa_driver_nl80211_add_11b(struct hostapd_hw_modes *modes, u16 *num_modes) static struct hostapd_hw_modes * wpa_driver_nl80211_get_hw_feature_data(void *priv, u16 *num_modes, u16 *flags) { - struct wpa_driver_nl80211_data *drv = priv; + struct i802_bss *bss = priv; + struct wpa_driver_nl80211_data *drv = bss->drv; struct nl_msg *msg; struct phy_info_arg result = { .num_modes = num_modes, @@ -2417,7 +3028,8 @@ static int wpa_driver_nl80211_send_frame(struct wpa_driver_nl80211_data *drv, static int wpa_driver_nl80211_send_mlme(void *priv, const u8 *data, size_t data_len) { - struct wpa_driver_nl80211_data *drv = priv; + struct i802_bss *bss = priv; + struct wpa_driver_nl80211_data *drv = bss->drv; struct ieee80211_mgmt *mgmt; int encrypt = 1; u16 fc; @@ -2443,27 +3055,20 @@ static int wpa_driver_nl80211_send_mlme(void *priv, const u8 *data, } -static int wpa_driver_nl80211_set_beacon(const char *ifname, void *priv, +static int wpa_driver_nl80211_set_beacon(void *priv, const u8 *head, size_t head_len, const u8 *tail, size_t tail_len, int dtim_period, int beacon_int) { - struct wpa_driver_nl80211_data *drv = priv; + struct i802_bss *bss = priv; + struct wpa_driver_nl80211_data *drv = bss->drv; struct nl_msg *msg; u8 cmd = NL80211_CMD_NEW_BEACON; int ret; int beacon_set; - int ifindex = if_nametoindex(ifname); -#ifdef HOSTAPD - struct i802_bss *bss; + int ifindex = if_nametoindex(bss->ifname); - bss = get_bss(drv, ifindex); - if (bss == NULL) - return -ENOENT; beacon_set = bss->beacon_set; -#else /* HOSTAPD */ - beacon_set = drv->beacon_set; -#endif /* HOSTAPD */ msg = nlmsg_alloc(); if (!msg) @@ -2487,11 +3092,7 @@ static int wpa_driver_nl80211_set_beacon(const char *ifname, void *priv, wpa_printf(MSG_DEBUG, "nl80211: Beacon set failed: %d (%s)", ret, strerror(-ret)); } else { -#ifdef HOSTAPD bss->beacon_set = 1; -#else /* HOSTAPD */ - drv->beacon_set = 1; -#endif /* HOSTAPD */ } return ret; nla_put_failure: @@ -2542,10 +3143,11 @@ nla_put_failure: } -static int wpa_driver_nl80211_sta_add(const char *ifname, void *priv, +static int wpa_driver_nl80211_sta_add(void *priv, struct hostapd_sta_add_params *params) { - struct wpa_driver_nl80211_data *drv = priv; + struct i802_bss *bss = priv; + struct wpa_driver_nl80211_data *drv = bss->drv; struct nl_msg *msg; int ret = -ENOBUFS; @@ -2556,7 +3158,7 @@ static int wpa_driver_nl80211_sta_add(const char *ifname, void *priv, genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, 0, NL80211_CMD_NEW_STATION, 0); - NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, if_nametoindex(ifname)); + NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, if_nametoindex(bss->ifname)); NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, params->addr); NLA_PUT_U16(msg, NL80211_ATTR_STA_AID, params->aid); NLA_PUT(msg, NL80211_ATTR_STA_SUPPORTED_RATES, params->supp_rates_len, @@ -2582,7 +3184,8 @@ static int wpa_driver_nl80211_sta_add(const char *ifname, void *priv, static int wpa_driver_nl80211_sta_remove(void *priv, const u8 *addr) { - struct wpa_driver_nl80211_data *drv = priv; + struct i802_bss *bss = priv; + struct wpa_driver_nl80211_data *drv = bss->drv; struct nl_msg *msg; int ret; @@ -2594,7 +3197,7 @@ static int wpa_driver_nl80211_sta_remove(void *priv, const u8 *addr) 0, NL80211_CMD_DEL_STATION, 0); NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, - if_nametoindex(drv->ifname)); + if_nametoindex(bss->ifname)); NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, addr); ret = send_and_recv_msgs(drv, msg, NULL, NULL); @@ -2629,8 +3232,7 @@ static void nl80211_remove_iface(struct wpa_driver_nl80211_data *drv, if (send_and_recv_msgs(drv, msg, NULL, NULL) == 0) return; nla_put_failure: - wpa_printf(MSG_ERROR, "Failed to remove interface (ifidx=%d).\n", - ifidx); + wpa_printf(MSG_ERROR, "Failed to remove interface (ifidx=%d)", ifidx); } @@ -3032,7 +3634,7 @@ nl80211_create_monitor_interface(struct wpa_driver_nl80211_data *drv) int optval; socklen_t optlen; - snprintf(buf, IFNAMSIZ, "mon.%s", drv->ifname); + snprintf(buf, IFNAMSIZ, "mon.%s", drv->first_bss.ifname); buf[IFNAMSIZ - 1] = '\0'; drv->monitor_ifidx = @@ -3092,7 +3694,8 @@ 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) { - struct wpa_driver_nl80211_data *drv = priv; + struct i802_bss *bss = priv; + struct wpa_driver_nl80211_data *drv = bss->drv; struct ieee80211_hdr *hdr; size_t len; u8 *pos; @@ -3174,10 +3777,11 @@ static u32 sta_flags_nl80211(int flags) static int wpa_driver_nl80211_sta_set_flags(void *priv, const u8 *addr, - int total_flags, int flags_or, - int flags_and) + int total_flags, + int flags_or, int flags_and) { - struct wpa_driver_nl80211_data *drv = priv; + struct i802_bss *bss = priv; + struct wpa_driver_nl80211_data *drv = bss->drv; struct nl_msg *msg, *flags = NULL; struct nl80211_sta_flag_update upd; @@ -3195,7 +3799,7 @@ static int wpa_driver_nl80211_sta_set_flags(void *priv, const u8 *addr, 0, NL80211_CMD_SET_STATION, 0); NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, - if_nametoindex(drv->ifname)); + if_nametoindex(bss->ifname)); NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, addr); /* @@ -3234,7 +3838,10 @@ static int wpa_driver_nl80211_sta_set_flags(void *priv, const u8 *addr, static int wpa_driver_nl80211_ap(struct wpa_driver_nl80211_data *drv, struct wpa_driver_associate_params *params) { - if (wpa_driver_nl80211_set_mode(drv, params->mode) || + if (params->p2p) + wpa_printf(MSG_DEBUG, "nl80211: Setup AP operations for P2P " + "group (GO)"); + if (wpa_driver_nl80211_set_mode(&drv->first_bss, params->mode) || wpa_driver_nl80211_set_freq(drv, params->freq, 0, 0)) { nl80211_remove_monitor_interface(drv); return -1; @@ -3285,7 +3892,7 @@ static int wpa_driver_nl80211_ibss(struct wpa_driver_nl80211_data *drv, wpa_printf(MSG_DEBUG, "nl80211: Join IBSS (ifindex=%d)", drv->ifindex); - if (wpa_driver_nl80211_set_mode(drv, params->mode)) { + if (wpa_driver_nl80211_set_mode(&drv->first_bss, params->mode)) { wpa_printf(MSG_INFO, "nl80211: Failed to set interface into " "IBSS mode"); return -1; @@ -3499,7 +4106,8 @@ nla_put_failure: static int wpa_driver_nl80211_associate( void *priv, struct wpa_driver_associate_params *params) { - struct wpa_driver_nl80211_data *drv = priv; + struct i802_bss *bss = priv; + struct wpa_driver_nl80211_data *drv = bss->drv; int ret = -1; struct nl_msg *msg; @@ -3510,7 +4118,7 @@ static int wpa_driver_nl80211_associate( return wpa_driver_nl80211_ibss(drv, params); if (!(drv->capa.flags & WPA_DRIVER_FLAGS_SME)) { - if (wpa_driver_nl80211_set_mode(drv, params->mode) < 0) + if (wpa_driver_nl80211_set_mode(priv, params->mode) < 0) return -1; return wpa_driver_nl80211_connect(drv, params); } @@ -3567,11 +4175,15 @@ static int wpa_driver_nl80211_associate( params->prev_bssid); } + if (params->p2p) + wpa_printf(MSG_DEBUG, " * P2P group"); + ret = send_and_recv_msgs(drv, msg, NULL, NULL); msg = NULL; if (ret) { wpa_printf(MSG_DEBUG, "nl80211: MLME command failed: ret=%d " "(%s)", ret, strerror(-ret)); + nl80211_dump_scan(drv); goto nla_put_failure; } ret = 0; @@ -3611,9 +4223,11 @@ nla_put_failure: static int wpa_driver_nl80211_set_mode(void *priv, int mode) { - struct wpa_driver_nl80211_data *drv = priv; + struct i802_bss *bss = priv; + struct wpa_driver_nl80211_data *drv = bss->drv; int ret = -1; int nlmode; + int i; switch (mode) { case 0: @@ -3646,11 +4260,23 @@ 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 (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 (linux_set_iface_flags(drv->ioctl_sock, drv->ifname, 1)) - ret = -1; + wpa_printf(MSG_DEBUG, "nl80211: Try mode change after setting " + "interface down"); + for (i = 0; i < 10; i++) { + if (linux_set_iface_flags(drv->ioctl_sock, bss->ifname, 0) == + 0) { + /* Try to set the mode again while the interface is + * down */ + ret = nl80211_set_mode(drv, drv->ifindex, nlmode); + if (linux_set_iface_flags(drv->ioctl_sock, bss->ifname, + 1)) + ret = -1; + if (!ret) + break; + } else + wpa_printf(MSG_DEBUG, "nl80211: Failed to set " + "interface down"); + os_sleep(0, 100000); } if (!ret) { @@ -3668,9 +4294,7 @@ done: } else if (!ret && nlmode != NL80211_IFTYPE_AP) { /* Remove additional AP mode functionality */ nl80211_remove_monitor_interface(drv); -#ifndef HOSTAPD - drv->beacon_set = 0; -#endif /* HOSTAPD */ + bss->beacon_set = 0; } if (ret) @@ -3684,7 +4308,8 @@ done: static int wpa_driver_nl80211_get_capa(void *priv, struct wpa_driver_capa *capa) { - struct wpa_driver_nl80211_data *drv = priv; + struct i802_bss *bss = priv; + struct wpa_driver_nl80211_data *drv = bss->drv; if (!drv->has_capability) return -1; os_memcpy(capa, &drv->capa, sizeof(*capa)); @@ -3694,7 +4319,8 @@ static int wpa_driver_nl80211_get_capa(void *priv, static int wpa_driver_nl80211_set_operstate(void *priv, int state) { - struct wpa_driver_nl80211_data *drv = priv; + struct i802_bss *bss = priv; + struct wpa_driver_nl80211_data *drv = bss->drv; wpa_printf(MSG_DEBUG, "%s: operstate %d->%d (%s)", __func__, drv->operstate, state, state ? "UP" : "DORMANT"); @@ -3706,7 +4332,8 @@ static int wpa_driver_nl80211_set_operstate(void *priv, int state) static int wpa_driver_nl80211_set_supp_port(void *priv, int authorized) { - struct wpa_driver_nl80211_data *drv = priv; + struct i802_bss *bss = priv; + struct wpa_driver_nl80211_data *drv = bss->drv; struct nl_msg *msg; struct nl80211_sta_flag_update upd; @@ -3718,7 +4345,7 @@ static int wpa_driver_nl80211_set_supp_port(void *priv, int authorized) 0, NL80211_CMD_SET_STATION, 0); NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, - if_nametoindex(drv->ifname)); + if_nametoindex(bss->ifname)); NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, drv->bssid); os_memset(&upd, 0, sizeof(upd)); @@ -3735,20 +4362,6 @@ static int wpa_driver_nl80211_set_supp_port(void *priv, int authorized) #ifdef HOSTAPD -static struct i802_bss * get_bss(struct wpa_driver_nl80211_data *drv, - int ifindex) -{ - struct i802_bss *bss = &drv->bss; - while (bss) { - if (ifindex == bss->ifindex) - return bss; - bss = bss->next; - } - wpa_printf(MSG_DEBUG, "nl80211: get_bss(%d) failed", ifindex); - return NULL; -} - - static void add_ifidx(struct wpa_driver_nl80211_data *drv, int ifidx) { int i; @@ -3779,7 +4392,9 @@ static void add_ifidx(struct wpa_driver_nl80211_data *drv, int ifidx) "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++; } @@ -3842,7 +4457,8 @@ static int get_key_handler(struct nl_msg *msg, void *arg) static int i802_get_seqnum(const char *iface, void *priv, const u8 *addr, int idx, u8 *seq) { - struct wpa_driver_nl80211_data *drv = priv; + struct i802_bss *bss = priv; + struct wpa_driver_nl80211_data *drv = bss->drv; struct nl_msg *msg; msg = nlmsg_alloc(); @@ -3868,7 +4484,8 @@ static int i802_get_seqnum(const char *iface, void *priv, const u8 *addr, static int i802_set_rate_sets(void *priv, int *supp_rates, int *basic_rates, int mode) { - struct wpa_driver_nl80211_data *drv = priv; + struct i802_bss *bss = priv; + struct wpa_driver_nl80211_data *drv = bss->drv; struct nl_msg *msg; u8 rates[NL80211_MAX_SUPP_RATES]; u8 rates_len = 0; @@ -3886,27 +4503,32 @@ static int i802_set_rate_sets(void *priv, int *supp_rates, int *basic_rates, NLA_PUT(msg, NL80211_ATTR_BSS_BASIC_RATES, rates_len, rates); - /* TODO: multi-BSS support */ - NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, if_nametoindex(drv->ifname)); + NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, if_nametoindex(bss->ifname)); return send_and_recv_msgs(drv, msg, NULL, NULL); nla_put_failure: return -ENOBUFS; } +#endif /* HOSTAPD */ + /* Set kernel driver on given frequency (MHz) */ static int i802_set_freq(void *priv, struct hostapd_freq_params *freq) { - struct wpa_driver_nl80211_data *drv = priv; + 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) { - struct wpa_driver_nl80211_data *drv = priv; + struct i802_bss *bss = priv; + struct wpa_driver_nl80211_data *drv = bss->drv; struct nl_msg *msg; int ret = -ENOBUFS; u32 val; @@ -3937,7 +4559,8 @@ nla_put_failure: static int i802_set_frag(void *priv, int frag) { - struct wpa_driver_nl80211_data *drv = priv; + struct i802_bss *bss = priv; + struct wpa_driver_nl80211_data *drv = bss->drv; struct nl_msg *msg; int ret = -ENOBUFS; u32 val; @@ -3968,7 +4591,8 @@ nla_put_failure: static int i802_flush(void *priv) { - struct wpa_driver_nl80211_data *drv = priv; + struct i802_bss *bss = priv; + struct wpa_driver_nl80211_data *drv = bss->drv; struct nl_msg *msg; msg = nlmsg_alloc(); @@ -3982,7 +4606,7 @@ static int i802_flush(void *priv) * XXX: FIX! this needs to flush all VLANs too */ NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, - if_nametoindex(drv->ifname)); + if_nametoindex(bss->ifname)); return send_and_recv_msgs(drv, msg, NULL, NULL); nla_put_failure: @@ -4044,7 +4668,8 @@ static int get_sta_handler(struct nl_msg *msg, void *arg) static int i802_read_sta_data(void *priv, struct hostap_sta_driver_data *data, const u8 *addr) { - struct wpa_driver_nl80211_data *drv = priv; + struct i802_bss *bss = priv; + struct wpa_driver_nl80211_data *drv = bss->drv; struct nl_msg *msg; os_memset(data, 0, sizeof(*data)); @@ -4056,7 +4681,7 @@ static int i802_read_sta_data(void *priv, struct hostap_sta_driver_data *data, 0, NL80211_CMD_GET_STATION, 0); NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, addr); - NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, if_nametoindex(drv->ifname)); + NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, if_nametoindex(bss->ifname)); return send_and_recv_msgs(drv, msg, get_sta_handler, data); nla_put_failure: @@ -4067,7 +4692,8 @@ static int i802_read_sta_data(void *priv, struct hostap_sta_driver_data *data, static int i802_set_tx_queue_params(void *priv, int queue, int aifs, int cw_min, int cw_max, int burst_time) { - struct wpa_driver_nl80211_data *drv = priv; + struct i802_bss *bss = priv; + struct wpa_driver_nl80211_data *drv = bss->drv; struct nl_msg *msg; struct nlattr *txq, *params; @@ -4078,7 +4704,7 @@ static int i802_set_tx_queue_params(void *priv, int queue, int aifs, genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, 0, NL80211_CMD_SET_WIPHY, 0); - NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, if_nametoindex(drv->ifname)); + NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, if_nametoindex(bss->ifname)); txq = nla_nest_start(msg, NL80211_ATTR_WIPHY_TXQ_PARAMS); if (!txq) @@ -4110,7 +4736,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) { - struct wpa_driver_nl80211_data *drv = priv; + struct i802_bss *bss = priv; + struct wpa_driver_nl80211_data *drv = bss->drv; struct nl_msg *msg; msg = nlmsg_alloc(); @@ -4127,8 +4754,7 @@ static int i802_set_bss(void *priv, int cts, int preamble, int slot) if (slot >= 0) NLA_PUT_U8(msg, NL80211_ATTR_BSS_SHORT_SLOT_TIME, slot); - /* TODO: multi-BSS support */ - NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, if_nametoindex(drv->ifname)); + NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, if_nametoindex(bss->ifname)); return send_and_recv_msgs(drv, msg, NULL, NULL); nla_put_failure: @@ -4157,8 +4783,10 @@ static int i802_set_short_slot_time(void *priv, int value) static int i802_set_sta_vlan(void *priv, const u8 *addr, const char *ifname, int vlan_id) { - struct wpa_driver_nl80211_data *drv = priv; + struct i802_bss *bss = priv; + struct wpa_driver_nl80211_data *drv = bss->drv; struct nl_msg *msg; + int ret = -ENOBUFS; msg = nlmsg_alloc(); if (!msg) @@ -4168,31 +4796,40 @@ static int i802_set_sta_vlan(void *priv, const u8 *addr, 0, NL80211_CMD_SET_STATION, 0); NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, - if_nametoindex(drv->ifname)); + if_nametoindex(bss->ifname)); NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, addr); NLA_PUT_U32(msg, NL80211_ATTR_STA_VLAN, if_nametoindex(ifname)); - return send_and_recv_msgs(drv, msg, NULL, NULL); + ret = send_and_recv_msgs(drv, msg, NULL, NULL); + if (ret < 0) { + wpa_printf(MSG_ERROR, "nl80211: NL80211_ATTR_STA_VLAN (addr=" + MACSTR " ifname=%s vlan_id=%d) failed: %d (%s)", + MAC2STR(addr), ifname, vlan_id, ret, + strerror(-ret)); + } nla_put_failure: - return -ENOBUFS; + return ret; } 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]; + 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", drv->ifname, aid); + 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 (nl80211_create_iface(priv, name, NL80211_IFTYPE_AP_VLAN, + if (nl80211_create_iface(drv, name, NL80211_IFTYPE_AP_VLAN, NULL, 1) < 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, drv->ifname, 0); + i802_set_sta_vlan(priv, addr, bss->ifname, 0); return wpa_driver_nl80211_if_remove(priv, WPA_IF_AP_VLAN, name); } @@ -4244,7 +4881,7 @@ static int i802_sta_clear_stats(void *priv, const u8 *addr) static int i802_sta_deauth(void *priv, const u8 *own_addr, const u8 *addr, int reason) { - struct wpa_driver_nl80211_data *drv = priv; + struct i802_bss *bss = priv; struct ieee80211_mgmt mgmt; memset(&mgmt, 0, sizeof(mgmt)); @@ -4254,7 +4891,7 @@ static int i802_sta_deauth(void *priv, const u8 *own_addr, const u8 *addr, memcpy(mgmt.sa, own_addr, ETH_ALEN); memcpy(mgmt.bssid, own_addr, ETH_ALEN); mgmt.u.deauth.reason_code = host_to_le16(reason); - return wpa_driver_nl80211_send_mlme(drv, (u8 *) &mgmt, + return wpa_driver_nl80211_send_mlme(bss, (u8 *) &mgmt, IEEE80211_HDRLEN + sizeof(mgmt.u.deauth)); } @@ -4263,7 +4900,7 @@ static int i802_sta_deauth(void *priv, const u8 *own_addr, const u8 *addr, static int i802_sta_disassoc(void *priv, const u8 *own_addr, const u8 *addr, int reason) { - struct wpa_driver_nl80211_data *drv = priv; + struct i802_bss *bss = priv; struct ieee80211_mgmt mgmt; memset(&mgmt, 0, sizeof(mgmt)); @@ -4273,7 +4910,7 @@ static int i802_sta_disassoc(void *priv, const u8 *own_addr, const u8 *addr, memcpy(mgmt.sa, own_addr, ETH_ALEN); memcpy(mgmt.bssid, own_addr, ETH_ALEN); mgmt.u.disassoc.reason_code = host_to_le16(reason); - return wpa_driver_nl80211_send_mlme(drv, (u8 *) &mgmt, + return wpa_driver_nl80211_send_mlme(bss, (u8 *) &mgmt, IEEE80211_HDRLEN + sizeof(mgmt.u.disassoc)); } @@ -4335,17 +4972,17 @@ static void *i802_init(struct hostapd_data *hapd, struct wpa_init_params *params) { struct wpa_driver_nl80211_data *drv; + struct i802_bss *bss; size_t i; char brname[IFNAMSIZ]; int ifindex, br_ifindex; int br_added = 0; - drv = wpa_driver_nl80211_init(hapd, params->ifname); - if (drv == NULL) + bss = wpa_driver_nl80211_init(hapd, params->ifname); + if (bss == NULL) return NULL; - drv->bss.ifindex = drv->ifindex; - + drv = bss->drv; if (linux_br_get(brname, params->ifname) == 0) { wpa_printf(MSG_DEBUG, "nl80211: Interface %s is in bridge %s", params->ifname, brname); @@ -4373,18 +5010,18 @@ static void *i802_init(struct hostapd_data *hapd, /* start listening for EAPOL on the default AP interface */ add_ifidx(drv, drv->ifindex); - if (linux_set_iface_flags(drv->ioctl_sock, drv->ifname, 0)) + if (linux_set_iface_flags(drv->ioctl_sock, bss->ifname, 0)) goto failed; if (params->bssid) { - if (linux_set_ifhwaddr(drv->ioctl_sock, drv->ifname, + if (linux_set_ifhwaddr(drv->ioctl_sock, bss->ifname, params->bssid)) goto failed; } - if (wpa_driver_nl80211_set_mode(drv, IEEE80211_MODE_AP)) { + if (wpa_driver_nl80211_set_mode(bss, IEEE80211_MODE_AP)) { wpa_printf(MSG_ERROR, "nl80211: Failed to set interface %s " - "into AP mode", drv->ifname); + "into AP mode", bss->ifname); goto failed; } @@ -4392,7 +5029,7 @@ static void *i802_init(struct hostapd_data *hapd, i802_check_bridge(drv, params->bridge[0], params->ifname) < 0) goto failed; - if (linux_set_iface_flags(drv->ioctl_sock, drv->ifname, 1)) + if (linux_set_iface_flags(drv->ioctl_sock, bss->ifname, 1)) goto failed; drv->eapol_sock = socket(PF_PACKET, SOCK_DGRAM, htons(ETH_P_PAE)); @@ -4407,10 +5044,10 @@ static void *i802_init(struct hostapd_data *hapd, goto failed; } - if (linux_get_ifhwaddr(drv->ioctl_sock, drv->ifname, params->own_addr)) + if (linux_get_ifhwaddr(drv->ioctl_sock, bss->ifname, params->own_addr)) goto failed; - return drv; + return bss; failed: nl80211_remove_monitor_interface(drv); @@ -4419,7 +5056,7 @@ failed: genl_family_put(drv->nl80211); nl_cache_free(drv->nl_cache); - nl_handle_destroy(drv->nl_handle); + nl80211_handle_destroy(drv->nl_handle); nl_cb_put(drv->nl_cb); os_free(drv); @@ -4440,53 +5077,67 @@ static enum nl80211_iftype wpa_driver_nl80211_if_type( { switch (type) { case WPA_IF_STATION: + case WPA_IF_P2P_CLIENT: + case WPA_IF_P2P_GROUP: return NL80211_IFTYPE_STATION; case WPA_IF_AP_VLAN: return NL80211_IFTYPE_AP_VLAN; case WPA_IF_AP_BSS: + case WPA_IF_P2P_GO: return NL80211_IFTYPE_AP; } return -1; } -static int wpa_driver_nl80211_if_add(const char *iface, void *priv, - enum wpa_driver_if_type type, +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 *bss_ctx, void **drv_priv, + char *force_ifname, u8 *if_addr) { - struct wpa_driver_nl80211_data *drv = priv; + struct i802_bss *bss = priv; + struct wpa_driver_nl80211_data *drv = bss->drv; int ifidx; #ifdef HOSTAPD - struct i802_bss *bss = NULL; + struct i802_bss *new_bss = NULL; if (type == WPA_IF_AP_BSS) { - bss = os_zalloc(sizeof(*bss)); - if (bss == NULL) + new_bss = os_zalloc(sizeof(*new_bss)); + if (new_bss == NULL) return -1; } #endif /* HOSTAPD */ + if (addr) + os_memcpy(if_addr, addr, ETH_ALEN); ifidx = nl80211_create_iface(drv, ifname, wpa_driver_nl80211_if_type(type), addr, 0); if (ifidx < 0) { #ifdef HOSTAPD - os_free(bss); + os_free(new_bss); #endif /* HOSTAPD */ return -1; } + if (!addr && + linux_get_ifhwaddr(drv->ioctl_sock, bss->ifname, if_addr) < 0) + return -1; + #ifdef HOSTAPD if (type == WPA_IF_AP_BSS) { if (linux_set_iface_flags(drv->ioctl_sock, ifname, 1)) { nl80211_remove_iface(drv, ifidx); - os_free(bss); + os_free(new_bss); return -1; } - bss->ifindex = ifidx; - bss->next = drv->bss.next; - drv->bss.next = bss; + os_strlcpy(new_bss->ifname, ifname, IFNAMSIZ); + new_bss->ifindex = ifidx; + new_bss->drv = drv; + new_bss->next = drv->first_bss.next; + drv->first_bss.next = new_bss; + if (drv_priv) + *drv_priv = new_bss; } #endif /* HOSTAPD */ @@ -4498,24 +5149,30 @@ static int wpa_driver_nl80211_if_remove(void *priv, enum wpa_driver_if_type type, const char *ifname) { - struct wpa_driver_nl80211_data *drv = priv; + struct i802_bss *bss = priv; + struct wpa_driver_nl80211_data *drv = bss->drv; int ifindex = if_nametoindex(ifname); + wpa_printf(MSG_DEBUG, "nl80211: %s(type=%d ifname=%s) ifindex=%d", + __func__, type, ifname, ifindex); + if (ifindex <= 0) + return -1; nl80211_remove_iface(drv, ifindex); #ifdef HOSTAPD - if (type == WPA_IF_AP_BSS) { - struct i802_bss *bss, *prev; - prev = &drv->bss; - bss = drv->bss.next; - while (bss) { - if (ifindex == bss->ifindex) { - prev->next = bss->next; - os_free(bss); - break; - } - prev = bss; - bss = bss->next; + if (type != WPA_IF_AP_BSS) + return 0; + + if (bss != &drv->first_bss) { + struct i802_bss *tbss = &drv->first_bss; + + while (tbss) { + if (tbss->next != bss) + continue; + + tbss->next = bss->next; + os_free(bss); + break; } } #endif /* HOSTAPD */ @@ -4537,10 +5194,86 @@ 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) +{ + struct nl_msg *msg; + u64 cookie; + int ret = -1; + + msg = nlmsg_alloc(); + if (!msg) + return -1; + + genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, 0, + NL80211_CMD_FRAME, 0); + + NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex); + NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_FREQ, freq); + NLA_PUT(msg, NL80211_ATTR_FRAME, buf_len, buf); + + cookie = 0; + ret = send_and_recv_msgs(drv, msg, cookie_handler, &cookie); + msg = NULL; + if (ret) { + wpa_printf(MSG_DEBUG, "nl80211: Frame command failed: ret=%d " + "(%s)", ret, strerror(-ret)); + goto nla_put_failure; + } + wpa_printf(MSG_DEBUG, "nl80211: Frame TX command accepted; " + "cookie 0x%llx", (long long unsigned int) cookie); + + if (cookie_out) + *cookie_out = cookie; + +nla_put_failure: + nlmsg_free(msg); + return ret; +} + + +static int wpa_driver_nl80211_send_action(void *priv, unsigned int freq, + const u8 *dst, const u8 *src, + const u8 *bssid, + const u8 *data, size_t data_len) +{ + struct i802_bss *bss = priv; + struct wpa_driver_nl80211_data *drv = bss->drv; + int ret = -1; + u8 *buf; + struct ieee80211_hdr *hdr; + + wpa_printf(MSG_DEBUG, "nl80211: Send Action frame (ifindex=%d)", + drv->ifindex); + + buf = os_zalloc(24 + data_len); + if (buf == NULL) + return ret; + os_memcpy(buf + 24, data, data_len); + hdr = (struct ieee80211_hdr *) buf; + hdr->frame_control = + IEEE80211_FC(WLAN_FC_TYPE_MGMT, WLAN_FC_STYPE_ACTION); + os_memcpy(hdr->addr1, dst, ETH_ALEN); + os_memcpy(hdr->addr2, src, ETH_ALEN); + os_memcpy(hdr->addr3, bssid, ETH_ALEN); + + 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, + &drv->send_action_cookie); + + os_free(buf); + return ret; +} + + static int wpa_driver_nl80211_remain_on_channel(void *priv, unsigned int freq, unsigned int duration) { - struct wpa_driver_nl80211_data *drv = priv; + struct i802_bss *bss = priv; + struct wpa_driver_nl80211_data *drv = bss->drv; struct nl_msg *msg; int ret; u64 cookie; @@ -4566,7 +5299,8 @@ static int wpa_driver_nl80211_remain_on_channel(void *priv, unsigned int freq, return 0; } wpa_printf(MSG_DEBUG, "nl80211: Failed to request remain-on-channel " - "(freq=%d): %d (%s)", freq, ret, strerror(-ret)); + "(freq=%d duration=%u): %d (%s)", + freq, duration, ret, strerror(-ret)); nla_put_failure: return -1; } @@ -4574,7 +5308,8 @@ nla_put_failure: static int wpa_driver_nl80211_cancel_remain_on_channel(void *priv) { - struct wpa_driver_nl80211_data *drv = priv; + struct i802_bss *bss = priv; + struct wpa_driver_nl80211_data *drv = bss->drv; struct nl_msg *msg; int ret; @@ -4629,7 +5364,8 @@ static void wpa_driver_nl80211_probe_req_report_timeout(void *eloop_ctx, static int wpa_driver_nl80211_probe_req_report(void *priv, int report) { - struct wpa_driver_nl80211_data *drv = priv; + struct i802_bss *bss = priv; + struct wpa_driver_nl80211_data *drv = bss->drv; if (drv->nlmode != NL80211_IFTYPE_STATION) { wpa_printf(MSG_DEBUG, "nl80211: probe_req_report control only " @@ -4664,30 +5400,6 @@ static int wpa_driver_nl80211_probe_req_report(void *priv, int report) } -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) { @@ -4738,7 +5450,8 @@ nla_put_failure: static int wpa_driver_nl80211_disable_11b_rates(void *priv, int disabled) { - struct wpa_driver_nl80211_data *drv = priv; + struct i802_bss *bss = priv; + struct wpa_driver_nl80211_data *drv = bss->drv; drv->disable_11b_rates = disabled; return nl80211_disable_11b_rates(drv, drv->ifindex, disabled); } @@ -4746,11 +5459,142 @@ static int wpa_driver_nl80211_disable_11b_rates(void *priv, int disabled) static int wpa_driver_nl80211_deinit_ap(void *priv) { - struct wpa_driver_nl80211_data *drv = priv; + struct i802_bss *bss = priv; + struct wpa_driver_nl80211_data *drv = bss->drv; if (drv->nlmode != NL80211_IFTYPE_AP) return -1; wpa_driver_nl80211_del_beacon(drv); - return wpa_driver_nl80211_set_mode(drv, IEEE80211_MODE_INFRA); + return wpa_driver_nl80211_set_mode(priv, IEEE80211_MODE_INFRA); +} + + +static void wpa_driver_nl80211_resume(void *priv) +{ + struct i802_bss *bss = priv; + struct wpa_driver_nl80211_data *drv = bss->drv; + if (linux_set_iface_flags(drv->ioctl_sock, bss->ifname, 1)) { + wpa_printf(MSG_DEBUG, "nl80211: Failed to set interface up on " + "resume event"); + } +} + + +static int nl80211_send_ft_action(void *priv, u8 action, const u8 *target_ap, + const u8 *ies, size_t ies_len) +{ + struct i802_bss *bss = priv; + struct wpa_driver_nl80211_data *drv = bss->drv; + int ret; + u8 *data, *pos; + size_t data_len; + u8 own_addr[ETH_ALEN]; + + if (linux_get_ifhwaddr(drv->ioctl_sock, bss->ifname, own_addr) < 0) + return -1; + + if (action != 1) { + wpa_printf(MSG_ERROR, "nl80211: Unsupported send_ft_action " + "action %d", action); + return -1; + } + + /* + * Action frame payload: + * Category[1] = 6 (Fast BSS Transition) + * Action[1] = 1 (Fast BSS Transition Request) + * STA Address + * Target AP Address + * FT IEs + */ + + data_len = 2 + 2 * ETH_ALEN + ies_len; + data = os_malloc(data_len); + if (data == NULL) + return -1; + pos = data; + *pos++ = 0x06; /* FT Action category */ + *pos++ = action; + os_memcpy(pos, own_addr, ETH_ALEN); + pos += ETH_ALEN; + os_memcpy(pos, target_ap, ETH_ALEN); + 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, + data, data_len); + os_free(data); + + return ret; +} + + +static int nl80211_signal_monitor(void *priv, int threshold, int hysteresis) +{ + struct i802_bss *bss = priv; + struct wpa_driver_nl80211_data *drv = bss->drv; + struct nl_msg *msg, *cqm = NULL; + + wpa_printf(MSG_DEBUG, "nl80211: Signal monitor threshold=%d " + "hysteresis=%d", threshold, hysteresis); + + msg = nlmsg_alloc(); + if (!msg) + return -1; + + genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, + 0, NL80211_CMD_SET_CQM, 0); + + NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, bss->ifindex); + + cqm = nlmsg_alloc(); + if (cqm == NULL) + return -1; + + NLA_PUT_U32(cqm, NL80211_ATTR_CQM_RSSI_THOLD, threshold); + NLA_PUT_U32(cqm, NL80211_ATTR_CQM_RSSI_HYST, hysteresis); + nla_put_nested(msg, NL80211_ATTR_CQM, cqm); + + if (send_and_recv_msgs(drv, msg, NULL, NULL) == 0) + return 0; + msg = NULL; + +nla_put_failure: + if (cqm) + nlmsg_free(cqm); + nlmsg_free(msg); + return -1; +} + + +static int nl80211_send_frame(void *priv, const u8 *data, size_t data_len, + int encrypt) +{ + struct i802_bss *bss = priv; + struct wpa_driver_nl80211_data *drv = bss->drv; + return wpa_driver_nl80211_send_frame(drv, data, data_len, encrypt); +} + + +static int nl80211_set_intra_bss(void *priv, int enabled) +{ + struct i802_bss *bss = priv; + struct wpa_driver_nl80211_data *drv = bss->drv; + struct nl_msg *msg; + + msg = nlmsg_alloc(); + if (!msg) + return -ENOMEM; + + genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, 0, + NL80211_CMD_SET_BSS, 0); + + NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, if_nametoindex(bss->ifname)); + NLA_PUT_U8(msg, NL80211_ATTR_AP_ISOLATE, !enabled); + + return send_and_recv_msgs(drv, msg, NULL, NULL); + nla_put_failure: + return -ENOBUFS; } @@ -4791,7 +5635,6 @@ const struct wpa_driver_ops wpa_driver_nl80211_ops = { .sta_disassoc = i802_sta_disassoc, .get_inact_sec = i802_get_inact_sec, .sta_clear_stats = i802_sta_clear_stats, - .set_freq = i802_set_freq, .set_rts = i802_set_rts, .set_frag = i802_set_frag, .set_rate_sets = i802_set_rate_sets, @@ -4802,12 +5645,17 @@ 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 */ + .set_freq = i802_set_freq, + .send_action = wpa_driver_nl80211_send_action, .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, .deinit_ap = wpa_driver_nl80211_deinit_ap, + .resume = wpa_driver_nl80211_resume, + .send_ft_action = nl80211_send_ft_action, + .signal_monitor = nl80211_signal_monitor, + .send_frame = nl80211_send_frame, + .set_intra_bss = nl80211_set_intra_bss, };