X-Git-Url: http://www.project-moonshot.org/gitweb/?a=blobdiff_plain;f=src%2Fdrivers%2Fdriver_nl80211.c;h=3f91be44d8389c8a6ce73fc2ac76b2e20137c207;hb=8602b0f21325ed32642c90b6ecbef0721e34083c;hp=0c12109629d7fdfc9063c06facdffa78c3fae18a;hpb=3b31db5199dba0b3749d92ca4fe6633694711fa8;p=libeap.git diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c index 0c12109..3f91be4 100644 --- a/src/drivers/driver_nl80211.c +++ b/src/drivers/driver_nl80211.c @@ -1,10 +1,10 @@ /* * Driver interaction with Linux nl80211/cfg80211 - * Copyright (c) 2002-2008, Jouni Malinen + * Copyright (c) 2002-2010, Jouni Malinen * 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 @@ -18,20 +18,23 @@ #include "includes.h" #include -#include #include #include #include #include +#include #include #include #include "nl80211_copy.h" #include "common.h" -#include "radiotap.h" -#include "radiotap_iter.h" #include "eloop.h" #include "common/ieee802_11_defs.h" +#include "netlink.h" +#include "linux_ioctl.h" +#include "radiotap.h" +#include "radiotap_iter.h" +#include "rfkill.h" #include "driver.h" #ifdef CONFIG_LIBNL20 @@ -57,18 +60,22 @@ #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; }; struct wpa_driver_nl80211_data { void *ctx; - int link_event_sock; + 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; @@ -90,11 +97,24 @@ struct wpa_driver_nl80211_data { size_t ssid_len; int nlmode; int ap_scan_as_station; + unsigned int assoc_freq; int monitor_sock; int monitor_ifidx; + int probe_req_report; + 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 */ @@ -103,8 +123,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 */ @@ -117,18 +135,31 @@ 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, + int ifindex, int disabled); + /* nl80211 code */ static int ack_handler(struct nl_msg *msg, void *arg) @@ -160,10 +191,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; @@ -172,7 +203,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; @@ -187,7 +218,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); @@ -195,6 +226,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; @@ -257,117 +298,10 @@ nla_put_failure: } -#ifdef HOSTAPD -static int get_ifhwaddr(struct wpa_driver_nl80211_data *drv, - const char *ifname, u8 *addr) -{ - struct ifreq ifr; - - os_memset(&ifr, 0, sizeof(ifr)); - os_strlcpy(ifr.ifr_name, ifname, IFNAMSIZ); - if (ioctl(drv->ioctl_sock, SIOCGIFHWADDR, &ifr)) { - wpa_printf(MSG_ERROR, "%s: ioctl(SIOCGIFHWADDR): %d (%s)", - ifname, errno, strerror(errno)); - return -1; - } - - if (ifr.ifr_hwaddr.sa_family != ARPHRD_ETHER) { - wpa_printf(MSG_ERROR, "%s: Invalid HW-addr family 0x%04x", - ifname, ifr.ifr_hwaddr.sa_family); - return -1; - } - os_memcpy(addr, ifr.ifr_hwaddr.sa_data, ETH_ALEN); - - return 0; -} - - -static int set_ifhwaddr(struct wpa_driver_nl80211_data *drv, - const char *ifname, const u8 *addr) -{ - struct ifreq ifr; - - os_memset(&ifr, 0, sizeof(ifr)); - os_strlcpy(ifr.ifr_name, ifname, IFNAMSIZ); - os_memcpy(ifr.ifr_hwaddr.sa_data, addr, ETH_ALEN); - ifr.ifr_hwaddr.sa_family = ARPHRD_ETHER; - - if (ioctl(drv->ioctl_sock, SIOCSIFHWADDR, &ifr)) { - wpa_printf(MSG_DEBUG, "%s: ioctl(SIOCSIFHWADDR): %d (%s)", - ifname, errno, strerror(errno)); - return -1; - } - - return 0; -} -#endif /* HOSTAPD */ - - -static int wpa_driver_nl80211_send_oper_ifla( - struct wpa_driver_nl80211_data *drv, - int linkmode, int operstate) -{ - struct { - struct nlmsghdr hdr; - struct ifinfomsg ifinfo; - char opts[16]; - } req; - struct rtattr *rta; - static int nl_seq; - ssize_t ret; - - os_memset(&req, 0, sizeof(req)); - - req.hdr.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg)); - req.hdr.nlmsg_type = RTM_SETLINK; - req.hdr.nlmsg_flags = NLM_F_REQUEST; - req.hdr.nlmsg_seq = ++nl_seq; - req.hdr.nlmsg_pid = 0; - - req.ifinfo.ifi_family = AF_UNSPEC; - req.ifinfo.ifi_type = 0; - req.ifinfo.ifi_index = drv->ifindex; - req.ifinfo.ifi_flags = 0; - req.ifinfo.ifi_change = 0; - - if (linkmode != -1) { - rta = aliasing_hide_typecast( - ((char *) &req + NLMSG_ALIGN(req.hdr.nlmsg_len)), - struct rtattr); - rta->rta_type = IFLA_LINKMODE; - rta->rta_len = RTA_LENGTH(sizeof(char)); - *((char *) RTA_DATA(rta)) = linkmode; - req.hdr.nlmsg_len = NLMSG_ALIGN(req.hdr.nlmsg_len) + - RTA_LENGTH(sizeof(char)); - } - if (operstate != -1) { - rta = aliasing_hide_typecast( - ((char *) &req + NLMSG_ALIGN(req.hdr.nlmsg_len)), - struct rtattr); - rta->rta_type = IFLA_OPERSTATE; - rta->rta_len = RTA_LENGTH(sizeof(char)); - *((char *) RTA_DATA(rta)) = operstate; - req.hdr.nlmsg_len = NLMSG_ALIGN(req.hdr.nlmsg_len) + - RTA_LENGTH(sizeof(char)); - } - - wpa_printf(MSG_DEBUG, "nl80211: Operstate: linkmode=%d, operstate=%d", - linkmode, operstate); - - ret = send(drv->link_event_sock, &req, req.hdr.nlmsg_len, 0); - if (ret < 0) { - wpa_printf(MSG_DEBUG, "nl80211: Sending operstate IFLA failed:" - " %s (assume operstate is not supported)", - strerror(errno)); - } - - return ret < 0 ? -1 : 0; -} - - 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); @@ -377,7 +311,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); @@ -386,8 +321,7 @@ static int wpa_driver_nl80211_get_ssid(void *priv, u8 *ssid) static void wpa_driver_nl80211_event_link(struct wpa_driver_nl80211_data *drv, - void *ctx, char *buf, size_t len, - int del) + char *buf, size_t len, int del) { union wpa_event_data event; @@ -403,38 +337,30 @@ 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 drv->if_removed = 0; } - wpa_supplicant_event(ctx, EVENT_INTERFACE_STATUS, &event); + wpa_supplicant_event(drv->ctx, EVENT_INTERFACE_STATUS, &event); } static int wpa_driver_nl80211_own_ifname(struct wpa_driver_nl80211_data *drv, - struct nlmsghdr *h) + u8 *buf, size_t len) { - struct ifinfomsg *ifi; - int attrlen, _nlmsg_len, rta_len; + int attrlen, rta_len; struct rtattr *attr; - ifi = NLMSG_DATA(h); - - _nlmsg_len = NLMSG_ALIGN(sizeof(struct ifinfomsg)); - - attrlen = NLMSG_PAYLOAD(h, sizeof(struct ifinfomsg)); - if (attrlen < 0) - return 0; - - attr = (struct rtattr *) (((char *) ifi) + _nlmsg_len); + attrlen = len; + attr = (struct rtattr *) buf; rta_len = RTA_ALIGN(sizeof(struct rtattr)); while (RTA_OK(attr, attrlen)) { 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 @@ -448,13 +374,13 @@ static int wpa_driver_nl80211_own_ifname(struct wpa_driver_nl80211_data *drv, static int wpa_driver_nl80211_own_ifindex(struct wpa_driver_nl80211_data *drv, - int ifindex, struct nlmsghdr *h) + int ifindex, u8 *buf, size_t len) { if (drv->ifindex == ifindex) return 1; - if (drv->if_removed && wpa_driver_nl80211_own_ifname(drv, h)) { - drv->ifindex = if_nametoindex(drv->ifname); + if (drv->if_removed && wpa_driver_nl80211_own_ifname(drv, buf, len)) { + 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); @@ -465,22 +391,19 @@ static int wpa_driver_nl80211_own_ifindex(struct wpa_driver_nl80211_data *drv, } -static void wpa_driver_nl80211_event_rtm_newlink(struct wpa_driver_nl80211_data *drv, - void *ctx, struct nlmsghdr *h, - size_t len) +static void wpa_driver_nl80211_event_rtm_newlink(void *ctx, + struct ifinfomsg *ifi, + u8 *buf, size_t len) { - struct ifinfomsg *ifi; - int attrlen, _nlmsg_len, rta_len; - struct rtattr * attr; - - if (len < sizeof(*ifi)) - return; - - ifi = NLMSG_DATA(h); + struct wpa_driver_nl80211_data *drv = ctx; + int attrlen, rta_len; + struct rtattr *attr; + u32 brid = 0; - if (!wpa_driver_nl80211_own_ifindex(drv, ifi->ifi_index, h)) { - 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; } @@ -491,6 +414,19 @@ static void wpa_driver_nl80211_event_rtm_newlink(struct wpa_driver_nl80211_data (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() @@ -500,127 +436,70 @@ static void wpa_driver_nl80211_event_rtm_newlink(struct wpa_driver_nl80211_data if (drv->operstate == 1 && (ifi->ifi_flags & (IFF_LOWER_UP | IFF_DORMANT)) == IFF_LOWER_UP && !(ifi->ifi_flags & IFF_RUNNING)) - wpa_driver_nl80211_send_oper_ifla(drv, -1, IF_OPER_UP); - - _nlmsg_len = NLMSG_ALIGN(sizeof(struct ifinfomsg)); - - attrlen = NLMSG_PAYLOAD(h, sizeof(struct ifinfomsg)); - if (attrlen < 0) - return; - - attr = (struct rtattr *) (((char *) ifi) + _nlmsg_len); + netlink_send_oper_ifla(drv->netlink, drv->ifindex, + -1, IF_OPER_UP); + attrlen = len; + attr = (struct rtattr *) buf; rta_len = RTA_ALIGN(sizeof(struct rtattr)); while (RTA_OK(attr, attrlen)) { if (attr->rta_type == IFLA_IFNAME) { wpa_driver_nl80211_event_link( - drv, 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 */ } -static void wpa_driver_nl80211_event_rtm_dellink(struct wpa_driver_nl80211_data *drv, - void *ctx, struct nlmsghdr *h, - size_t len) +static void wpa_driver_nl80211_event_rtm_dellink(void *ctx, + struct ifinfomsg *ifi, + u8 *buf, size_t len) { - struct ifinfomsg *ifi; - int attrlen, _nlmsg_len, rta_len; - struct rtattr * attr; - - if (len < sizeof(*ifi)) - return; - - ifi = NLMSG_DATA(h); - - _nlmsg_len = NLMSG_ALIGN(sizeof(struct ifinfomsg)); - - attrlen = NLMSG_PAYLOAD(h, sizeof(struct ifinfomsg)); - if (attrlen < 0) - return; + struct wpa_driver_nl80211_data *drv = ctx; + int attrlen, rta_len; + struct rtattr *attr; + u32 brid = 0; - attr = (struct rtattr *) (((char *) ifi) + _nlmsg_len); + attrlen = len; + attr = (struct rtattr *) buf; rta_len = RTA_ALIGN(sizeof(struct rtattr)); while (RTA_OK(attr, attrlen)) { if (attr->rta_type == IFLA_IFNAME) { wpa_driver_nl80211_event_link( - drv, 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); } -} - - -static void wpa_driver_nl80211_event_receive_link(int sock, void *eloop_ctx, - void *sock_ctx) -{ - char buf[8192]; - int left; - struct sockaddr_nl from; - socklen_t fromlen; - struct nlmsghdr *h; - int max_events = 10; - -try_again: - fromlen = sizeof(from); - left = recvfrom(sock, buf, sizeof(buf), MSG_DONTWAIT, - (struct sockaddr *) &from, &fromlen); - if (left < 0) { - if (errno != EINTR && errno != EAGAIN) - perror("recvfrom(netlink)"); - return; - } - - h = (struct nlmsghdr *) buf; - while (left >= (int) sizeof(*h)) { - int len, plen; - - len = h->nlmsg_len; - plen = len - sizeof(*h); - if (len > left || plen < 0) { - wpa_printf(MSG_DEBUG, "Malformed netlink message: " - "len=%d left=%d plen=%d", - len, left, plen); - break; - } - - switch (h->nlmsg_type) { - case RTM_NEWLINK: - wpa_driver_nl80211_event_rtm_newlink(eloop_ctx, sock_ctx, - h, plen); - break; - case RTM_DELLINK: - wpa_driver_nl80211_event_rtm_dellink(eloop_ctx, sock_ctx, - h, plen); - break; - } - - len = NLMSG_ALIGN(len); - left -= len; - h = (struct nlmsghdr *) ((char *) h + len); - } - if (left > 0) { - wpa_printf(MSG_DEBUG, "%d extra bytes in the end of netlink " - "message", left); - } - - if (--max_events > 0) { - /* - * Try to receive all events in one eloop call in order to - * limit race condition on cases where AssocInfo event, Assoc - * event, and EAPOL frames are received more or less at the - * same time. We want to process the event messages first - * before starting EAPOL processing. - */ - goto try_again; +#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 */ } @@ -690,6 +569,8 @@ static void mlme_event_assoc(struct wpa_driver_nl80211_data *drv, len - 24 - sizeof(mgmt->u.assoc_resp); } + event.assoc_info.freq = drv->assoc_freq; + wpa_supplicant_event(drv->ctx, EVENT_ASSOC, &event); } @@ -765,9 +646,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); @@ -792,12 +796,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_ACTION: + mlme_event_action(drv, freq, nla_data(frame), nla_len(frame)); + break; + case NL80211_CMD_ACTION_TX_STATUS: + mlme_event_action_tx_status(drv, cookie, nla_data(frame), + nla_len(frame), ack); break; default: break; @@ -858,34 +869,218 @@ static void mlme_event_join_ibss(struct wpa_driver_nl80211_data *drv, } -static int process_event(struct nl_msg *msg, void *arg) +static void mlme_event_remain_on_channel(struct wpa_driver_nl80211_data *drv, + int cancel_event, struct nlattr *tb[]) +{ + unsigned int freq, chan_type, duration; + union wpa_event_data data; + u64 cookie; + + if (tb[NL80211_ATTR_WIPHY_FREQ]) + freq = nla_get_u32(tb[NL80211_ATTR_WIPHY_FREQ]); + else + freq = 0; + + if (tb[NL80211_ATTR_WIPHY_CHANNEL_TYPE]) + chan_type = nla_get_u32(tb[NL80211_ATTR_WIPHY_CHANNEL_TYPE]); + else + chan_type = 0; + + if (tb[NL80211_ATTR_DURATION]) + duration = nla_get_u32(tb[NL80211_ATTR_DURATION]); + else + duration = 0; + + if (tb[NL80211_ATTR_COOKIE]) + cookie = nla_get_u64(tb[NL80211_ATTR_COOKIE]); + else + cookie = 0; + + wpa_printf(MSG_DEBUG, "nl80211: Remain-on-channel event (cancel=%d " + "freq=%u channel_type=%u duration=%u cookie=0x%llx (%s))", + cancel_event, freq, chan_type, duration, + (long long unsigned int) cookie, + cookie == drv->remain_on_chan_cookie ? "match" : "unknown"); + + if (cookie != drv->remain_on_chan_cookie) + return; /* not for us */ + + drv->pending_remain_on_chan = !cancel_event; + + os_memset(&data, 0, sizeof(data)); + data.remain_on_channel.freq = freq; + data.remain_on_channel.duration = duration; + wpa_supplicant_event(drv->ctx, cancel_event ? + EVENT_CANCEL_REMAIN_ON_CHANNEL : + EVENT_REMAIN_ON_CHANNEL, &data); +} + + +static void send_scan_event(struct wpa_driver_nl80211_data *drv, int aborted, + struct nlattr *tb[]) +{ + union wpa_event_data event; + struct nlattr *nl; + int rem; + struct scan_info *info; +#define MAX_REPORT_FREQS 50 + int freqs[MAX_REPORT_FREQS]; + int num_freqs = 0; + + os_memset(&event, 0, sizeof(event)); + info = &event.scan_info; + info->aborted = aborted; + + if (tb[NL80211_ATTR_SCAN_SSIDS]) { + nla_for_each_nested(nl, tb[NL80211_ATTR_SCAN_SSIDS], rem) { + struct wpa_driver_scan_ssid *s = + &info->ssids[info->num_ssids]; + s->ssid = nla_data(nl); + s->ssid_len = nla_len(nl); + info->num_ssids++; + if (info->num_ssids == WPAS_MAX_SCAN_SSIDS) + break; + } + } + if (tb[NL80211_ATTR_SCAN_FREQUENCIES]) { + nla_for_each_nested(nl, tb[NL80211_ATTR_SCAN_FREQUENCIES], rem) + { + freqs[num_freqs] = nla_get_u32(nl); + num_freqs++; + if (num_freqs == MAX_REPORT_FREQS - 1) + break; + } + info->freqs = freqs; + info->num_freqs = num_freqs; + } + wpa_supplicant_event(drv->ctx, EVENT_SCAN_RESULTS, &event); +} + + +static int get_link_signal(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]; + 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; - if (tb[NL80211_ATTR_IFINDEX]) { - int ifindex = nla_get_u32(tb[NL80211_ATTR_IFINDEX]); - if (ifindex != drv->ifindex) { - wpa_printf(MSG_DEBUG, "nl80211: Ignored event (cmd=%d)" - " for foreign interface (ifindex %d)", - gnlh->cmd, ifindex); - return NL_SKIP; - } - } + *sig = (s8) nla_get_u8(sinfo[NL80211_STA_INFO_SIGNAL]); + return NL_SKIP; +} - 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); - drv->ap_scan_as_station = 0; + +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; } - switch (gnlh->cmd) { - case NL80211_CMD_TRIGGER_SCAN: + 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 && !have_ifidx(drv, ifindex)) { + wpa_printf(MSG_DEBUG, "nl80211: Ignored event (cmd=%d)" + " for foreign interface (ifindex %d)", + gnlh->cmd, ifindex); + return NL_SKIP; + } + } + + 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->first_bss, + IEEE80211_MODE_AP); + drv->ap_scan_as_station = 0; + } + + switch (gnlh->cmd) { + case NL80211_CMD_TRIGGER_SCAN: wpa_printf(MSG_DEBUG, "nl80211: Scan trigger"); break; case NL80211_CMD_NEW_SCAN_RESULTS: @@ -893,7 +1088,7 @@ static int process_event(struct nl_msg *msg, void *arg) drv->scan_complete_events = 1; eloop_cancel_timeout(wpa_driver_nl80211_scan_timeout, drv, drv->ctx); - wpa_supplicant_event(drv->ctx, EVENT_SCAN_RESULTS, NULL); + send_scan_event(drv, 0, tb); break; case NL80211_CMD_SCAN_ABORTED: wpa_printf(MSG_DEBUG, "nl80211: Scan aborted"); @@ -903,14 +1098,18 @@ static int process_event(struct nl_msg *msg, void *arg) */ eloop_cancel_timeout(wpa_driver_nl80211_scan_timeout, drv, drv->ctx); - wpa_supplicant_event(drv->ctx, EVENT_SCAN_RESULTS, NULL); + send_scan_event(drv, 1, tb); break; case NL80211_CMD_AUTHENTICATE: case NL80211_CMD_ASSOCIATE: case NL80211_CMD_DEAUTHENTICATE: case NL80211_CMD_DISASSOCIATE: + case NL80211_CMD_ACTION: + case NL80211_CMD_ACTION_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: @@ -931,7 +1130,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); @@ -939,6 +1142,15 @@ static int process_event(struct nl_msg *msg, void *arg) case NL80211_CMD_JOIN_IBSS: mlme_event_join_ibss(drv, tb); break; + case NL80211_CMD_REMAIN_ON_CHANNEL: + mlme_event_remain_on_channel(drv, 0, tb); + break; + case NL80211_CMD_CANCEL_REMAIN_ON_CHANNEL: + mlme_event_remain_on_channel(drv, 1, tb); + break; + case NL80211_CMD_NOTIFY_CQM: + nl80211_cqm_event(drv, tb); + break; default: wpa_printf(MSG_DEBUG, "nl80211: Ignored unknown event " "(cmd=%d)", gnlh->cmd); @@ -967,43 +1179,6 @@ static void wpa_driver_nl80211_event_receive(int sock, void *eloop_ctx, } -static int hostapd_set_iface_flags(struct wpa_driver_nl80211_data *drv, - const char *ifname, int dev_up) -{ - struct ifreq ifr; - - if (drv->ioctl_sock < 0) - return -1; - - os_memset(&ifr, 0, sizeof(ifr)); - os_strlcpy(ifr.ifr_name, ifname, IFNAMSIZ); - - if (ioctl(drv->ioctl_sock, SIOCGIFFLAGS, &ifr) != 0) { - perror("ioctl[SIOCGIFFLAGS]"); - wpa_printf(MSG_DEBUG, "Could not read interface flags (%s)", - ifname); - return -1; - } - - if (dev_up) { - if (ifr.ifr_flags & IFF_UP) - return 0; - ifr.ifr_flags |= IFF_UP; - } else { - if (!(ifr.ifr_flags & IFF_UP)) - return 0; - ifr.ifr_flags &= ~IFF_UP; - } - - if (ioctl(drv->ioctl_sock, SIOCSIFFLAGS, &ifr) != 0) { - perror("ioctl[SIOCSIFFLAGS]"); - return -1; - } - - return 0; -} - - /** * wpa_driver_nl80211_set_country - ask nl80211 to set the regulatory domain * @priv: driver_nl80211 private data @@ -1015,7 +1190,8 @@ static int hostapd_set_iface_flags(struct wpa_driver_nl80211_data *drv, */ 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; @@ -1104,7 +1280,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; @@ -1130,6 +1306,9 @@ static int wpa_driver_nl80211_capa(struct wpa_driver_nl80211_data *drv) WPA_DRIVER_CAPA_ENC_WEP104 | WPA_DRIVER_CAPA_ENC_TKIP | WPA_DRIVER_CAPA_ENC_CCMP; + drv->capa.auth = WPA_DRIVER_AUTH_OPEN | + WPA_DRIVER_AUTH_SHARED | + WPA_DRIVER_AUTH_LEAP; drv->capa.max_scan_ssids = info.max_scan_ssids; if (info.ap_supported) @@ -1144,14 +1323,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; @@ -1245,7 +1425,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; @@ -1264,32 +1444,26 @@ err1: } -static int wpa_driver_nl80211_init_link_events( - struct wpa_driver_nl80211_data *drv) +static void wpa_driver_nl80211_rfkill_blocked(void *ctx) { - int s; - struct sockaddr_nl local; + wpa_printf(MSG_DEBUG, "nl80211: RFKILL blocked"); + /* + * This may be for any interface; use ifdown event to disable + * interface. + */ +} - s = socket(PF_NETLINK, SOCK_RAW, NETLINK_ROUTE); - if (s < 0) { - perror("socket(PF_NETLINK,SOCK_RAW,NETLINK_ROUTE)"); - return -1; - } - os_memset(&local, 0, sizeof(local)); - local.nl_family = AF_NETLINK; - local.nl_groups = RTMGRP_LINK; - if (bind(s, (struct sockaddr *) &local, sizeof(local)) < 0) { - perror("bind(netlink)"); - close(s); - return -1; +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; } - - eloop_register_read_sock(s, wpa_driver_nl80211_event_receive_link, drv, - drv->ctx); - drv->link_event_sock = s; - - return 0; + /* rtnetlink ifup handler will report interface as enabled */ } @@ -1303,18 +1477,22 @@ static int wpa_driver_nl80211_init_link_events( 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->link_event_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; } @@ -1325,17 +1503,39 @@ static void * wpa_driver_nl80211_init(void *ctx, const char *ifname) goto failed; } - if (wpa_driver_nl80211_init_link_events(drv) || - wpa_driver_nl80211_finish_drv_init(drv)) + cfg = os_zalloc(sizeof(*cfg)); + if (cfg == NULL) + goto failed; + cfg->ctx = drv; + cfg->newlink_cb = wpa_driver_nl80211_event_rtm_newlink; + cfg->dellink_cb = wpa_driver_nl80211_event_rtm_dellink; + drv->netlink = netlink_init(cfg); + if (drv->netlink == NULL) { + 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: - if (drv->link_event_sock >= 0) { - eloop_unregister_read_sock(drv->link_event_sock); - close(drv->link_event_sock); - } + rfkill_deinit(drv->rfkill); + netlink_deinit(drv->netlink); if (drv->ioctl_sock >= 0) close(drv->ioctl_sock); @@ -1343,51 +1543,142 @@ failed: nl_cache_free(drv->nl_cache); nl_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_action_frame(struct wpa_driver_nl80211_data *drv, + 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(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_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 (hostapd_set_iface_flags(drv, 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)) return -1; - wpa_driver_nl80211_send_oper_ifla(drv, 1, IF_OPER_DORMANT); + netlink_send_oper_ifla(drv->netlink, drv->ifindex, + 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) @@ -1417,14 +1708,25 @@ 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; - nl80211_remove_monitor_interface(drv); - if (drv->monitor_sock >= 0) { - eloop_unregister_read_sock(drv->monitor_sock); - close(drv->monitor_sock); + if (drv->added_if_into_bridge) { + 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", + bss->ifname, drv->brname, strerror(errno)); + } + if (drv->added_bridge) { + if (linux_br_del(drv->ioctl_sock, drv->brname) < 0) + wpa_printf(MSG_INFO, "nl80211: Failed to remove " + "bridge %s: %s", + drv->brname, strerror(errno)); } + nl80211_remove_monitor_interface(drv); + if (drv->nlmode == NL80211_IFTYPE_AP) wpa_driver_nl80211_del_beacon(drv); @@ -1444,21 +1746,19 @@ 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 */ - wpa_driver_nl80211_send_oper_ifla(priv, 0, IF_OPER_UP); + if (drv->disable_11b_rates) + nl80211_disable_11b_rates(drv, drv->ifindex, 0); - if (drv->link_event_sock >= 0) { - eloop_unregister_read_sock(drv->link_event_sock); - close(drv->link_event_sock); - } + 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) hostapd_set_iface_flags(drv, 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); @@ -1471,6 +1771,11 @@ static void wpa_driver_nl80211_deinit(void *priv) nl_handle_destroy(drv->nl_handle_event); nl_cb_put(drv->nl_cb); + eloop_cancel_timeout(wpa_driver_nl80211_probe_req_report_timeout, + drv, NULL); + + os_free(drv->filter_ssids); + os_free(drv); } @@ -1487,7 +1792,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"); @@ -1504,7 +1810,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; @@ -1519,12 +1826,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); } @@ -1532,13 +1847,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); } @@ -1553,12 +1873,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; } @@ -1598,6 +1918,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]; @@ -1614,12 +1985,15 @@ static int bss_info_handler(struct nl_msg *msg, void *arg) [NL80211_BSS_SIGNAL_UNSPEC] = { .type = NLA_U8 }, [NL80211_BSS_STATUS] = { .type = NLA_U32 }, [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; - size_t ie_len; + const u8 *ie, *beacon_ie; + size_t ie_len, beacon_ie_len; + u8 *pos; nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0), genlmsg_attrlen(gnlh, 0), NULL); @@ -1635,8 +2009,19 @@ static int bss_info_handler(struct nl_msg *msg, void *arg) ie = NULL; ie_len = 0; } + if (bss[NL80211_BSS_BEACON_IES]) { + beacon_ie = nla_data(bss[NL80211_BSS_BEACON_IES]); + beacon_ie_len = nla_len(bss[NL80211_BSS_BEACON_IES]); + } else { + beacon_ie = NULL; + beacon_ie_len = 0; + } - r = os_zalloc(sizeof(*r) + ie_len); + 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; if (bss[NL80211_BSS_BSSID]) @@ -1663,8 +2048,14 @@ static int bss_info_handler(struct nl_msg *msg, void *arg) if (bss[NL80211_BSS_SEEN_MS_AGO]) r->age = nla_get_u32(bss[NL80211_BSS_SEEN_MS_AGO]); r->ie_len = ie_len; - if (ie) - os_memcpy(r + 1, ie, ie_len); + pos = (u8 *) (r + 1); + if (ie) { + os_memcpy(pos, ie, ie_len); + pos += ie_len; + } + r->beacon_ie_len = beacon_ie_len; + if (beacon_ie) + os_memcpy(pos, beacon_ie, beacon_ie_len); if (bss[NL80211_BSS_STATUS]) { enum nl80211_bss_status status; @@ -1699,10 +2090,10 @@ static void clear_state_mismatch(struct wpa_driver_nl80211_data *drv, { if (drv->capa.flags & WPA_DRIVER_FLAGS_SME) { wpa_printf(MSG_DEBUG, "nl80211: Clear possible state " - "mismatch"); + "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); } } @@ -1728,6 +2119,7 @@ static void wpa_driver_nl80211_check_bss_status( " assoc=" MACSTR ")", MAC2STR(drv->auth_bssid), MAC2STR(drv->bssid)); + clear_state_mismatch(drv, r->bssid); } } @@ -1757,18 +2149,27 @@ static void wpa_driver_nl80211_check_bss_status( } -/** - * 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 void wpa_scan_results_free(struct wpa_scan_results *res) +{ + size_t i; + + if (res == NULL) + return; + + for (i = 0; i < res->num; i++) + os_free(res->res[i]); + os_free(res->res); + os_free(res); +} + + 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) @@ -1781,12 +2182,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 " @@ -1798,13 +2200,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, - wpa_alg alg, const u8 *addr, int key_idx, - int set_tx, + enum wpa_alg alg, const u8 *addr, + int key_idx, int set_tx, const u8 *seq, size_t seq_len, const u8 *key, size_t key_len) { - 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; @@ -1866,7 +2312,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)", @@ -1878,9 +2324,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(); @@ -1909,7 +2358,7 @@ nla_put_failure: } -static int nl_add_key(struct nl_msg *msg, wpa_alg alg, +static int nl_add_key(struct nl_msg *msg, enum wpa_alg alg, int key_idx, int defkey, const u8 *seq, size_t seq_len, const u8 *key, size_t key_len) @@ -2018,7 +2467,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; @@ -2032,6 +2482,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; @@ -2051,43 +2503,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; @@ -2095,8 +2552,11 @@ 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(drv, IEEE80211_MODE_INFRA) < 0) + if (wpa_driver_nl80211_set_mode(priv, IEEE80211_MODE_INFRA) < 0) return -1; retry: @@ -2113,8 +2573,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]); @@ -2146,22 +2606,22 @@ 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 & AUTH_ALG_OPEN_SYSTEM) + if (params->auth_alg & WPA_AUTH_ALG_OPEN) type = NL80211_AUTHTYPE_OPEN_SYSTEM; - else if (params->auth_alg & AUTH_ALG_SHARED_KEY) + else if (params->auth_alg & WPA_AUTH_ALG_SHARED) type = NL80211_AUTHTYPE_SHARED_KEY; - else if (params->auth_alg & AUTH_ALG_LEAP) + else if (params->auth_alg & WPA_AUTH_ALG_LEAP) type = NL80211_AUTHTYPE_NETWORK_EAP; - else if (params->auth_alg & AUTH_ALG_FT) + else if (params->auth_alg & WPA_AUTH_ALG_FT) type = NL80211_AUTHTYPE_FT; else goto nla_put_failure; 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; @@ -2169,7 +2629,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 @@ -2178,7 +2639,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; @@ -2238,7 +2699,7 @@ static int phy_info_handler(struct nl_msg *msg, void *arg) return NL_SKIP; nla_for_each_nested(nl_band, tb_msg[NL80211_ATTR_WIPHY_BANDS], rem_band) { - mode = realloc(phy_info->modes, (*phy_info->num_modes + 1) * sizeof(*mode)); + mode = os_realloc(phy_info->modes, (*phy_info->num_modes + 1) * sizeof(*mode)); if (!mode) return NL_SKIP; phy_info->modes = mode; @@ -2284,7 +2745,7 @@ static int phy_info_handler(struct nl_msg *msg, void *arg) mode->num_channels++; } - mode->channels = calloc(mode->num_channels, sizeof(struct hostapd_channel_data)); + mode->channels = os_zalloc(mode->num_channels * sizeof(struct hostapd_channel_data)); if (!mode->channels) return NL_SKIP; @@ -2346,7 +2807,7 @@ static int phy_info_handler(struct nl_msg *msg, void *arg) mode->num_rates++; } - mode->rates = calloc(mode->num_rates, sizeof(int)); + mode->rates = os_zalloc(mode->num_rates * sizeof(int)); if (!mode->rates) return NL_SKIP; @@ -2448,7 +2909,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, @@ -2517,7 +2979,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; @@ -2543,27 +3006,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) @@ -2587,11 +3043,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: @@ -2642,10 +3094,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; @@ -2656,7 +3109,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, @@ -2682,7 +3135,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; @@ -2694,7 +3148,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); @@ -2711,6 +3165,8 @@ static void nl80211_remove_iface(struct wpa_driver_nl80211_data *drv, { struct nl_msg *msg; + wpa_printf(MSG_DEBUG, "nl80211: Remove interface ifindex=%d", ifidx); + #ifdef HOSTAPD /* stop listening for EAPOL on this interface */ del_ifidx(drv, ifidx); @@ -2727,15 +3183,14 @@ 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); } static int nl80211_create_iface_once(struct wpa_driver_nl80211_data *drv, const char *ifname, enum nl80211_iftype iftype, - const u8 *addr) + const u8 *addr, int wds) { struct nl_msg *msg, *flags = NULL; int ifidx; @@ -2766,6 +3221,8 @@ static int nl80211_create_iface_once(struct wpa_driver_nl80211_data *drv, if (err) goto nla_put_failure; + } else if (wds) { + NLA_PUT_U8(msg, NL80211_ATTR_4ADDR, wds); } ret = send_and_recv_msgs(drv, msg, NULL, NULL); @@ -2777,6 +3234,8 @@ static int nl80211_create_iface_once(struct wpa_driver_nl80211_data *drv, } ifidx = if_nametoindex(ifname); + wpa_printf(MSG_DEBUG, "nl80211: New interface %s created: ifindex=%d", + ifname, ifidx); if (ifidx <= 0) return -1; @@ -2784,13 +3243,13 @@ static int nl80211_create_iface_once(struct wpa_driver_nl80211_data *drv, #ifdef HOSTAPD /* start listening for EAPOL on this interface */ add_ifidx(drv, ifidx); +#endif /* HOSTAPD */ - if (addr && iftype == NL80211_IFTYPE_AP && - set_ifhwaddr(drv, ifname, addr)) { + if (addr && iftype != NL80211_IFTYPE_MONITOR && + linux_set_ifhwaddr(drv->ioctl_sock, ifname, addr)) { nl80211_remove_iface(drv, ifidx); return -1; } -#endif /* HOSTAPD */ return ifidx; } @@ -2798,11 +3257,11 @@ static int nl80211_create_iface_once(struct wpa_driver_nl80211_data *drv, static int nl80211_create_iface(struct wpa_driver_nl80211_data *drv, const char *ifname, enum nl80211_iftype iftype, - const u8 *addr) + const u8 *addr, int wds) { int ret; - ret = nl80211_create_iface_once(drv, ifname, iftype, addr); + ret = nl80211_create_iface_once(drv, ifname, iftype, addr, wds); /* if error occured and interface exists already */ if (ret == -ENFILE && if_nametoindex(ifname)) { @@ -2812,9 +3271,13 @@ static int nl80211_create_iface(struct wpa_driver_nl80211_data *drv, nl80211_remove_iface(drv, if_nametoindex(ifname)); /* Try to create the interface again */ - ret = nl80211_create_iface_once(drv, ifname, iftype, addr); + ret = nl80211_create_iface_once(drv, ifname, iftype, addr, + wds); } + if (ret >= 0 && drv->disable_11b_rates) + nl80211_disable_11b_rates(drv, ret, 1); + return ret; } @@ -2840,19 +3303,18 @@ static void handle_tx_callback(void *ctx, u8 *buf, size_t len, int ok) static void from_unknown_sta(struct wpa_driver_nl80211_data *drv, - struct ieee80211_hdr *hdr, size_t len) + u8 *buf, size_t len) { union wpa_event_data event; os_memset(&event, 0, sizeof(event)); - event.rx_from_unknown.hdr = hdr; + event.rx_from_unknown.frame = buf; event.rx_from_unknown.len = len; wpa_supplicant_event(drv->ctx, EVENT_RX_FROM_UNKNOWN, &event); } static void handle_frame(struct wpa_driver_nl80211_data *drv, - u8 *buf, size_t len, - struct hostapd_frame_info *hfi) + u8 *buf, size_t len, int datarate, int ssi_signal) { struct ieee80211_hdr *hdr; u16 fc; @@ -2866,16 +3328,17 @@ static void handle_frame(struct wpa_driver_nl80211_data *drv, os_memset(&event, 0, sizeof(event)); event.rx_mgmt.frame = buf; event.rx_mgmt.frame_len = len; - event.rx_mgmt.fi = hfi; + event.rx_mgmt.datarate = datarate; + event.rx_mgmt.ssi_signal = ssi_signal; wpa_supplicant_event(drv->ctx, EVENT_RX_MGMT, &event); break; case WLAN_FC_TYPE_CTRL: /* can only get here with PS-Poll frames */ wpa_printf(MSG_DEBUG, "CTRL"); - from_unknown_sta(drv, hdr, len); + from_unknown_sta(drv, buf, len); break; case WLAN_FC_TYPE_DATA: - from_unknown_sta(drv, hdr, len); + from_unknown_sta(drv, buf, len); break; } } @@ -2888,7 +3351,7 @@ static void handle_monitor_read(int sock, void *eloop_ctx, void *sock_ctx) unsigned char buf[3000]; struct ieee80211_radiotap_iterator iter; int ret; - struct hostapd_frame_info hfi; + int datarate = 0, ssi_signal = 0; int injected = 0, failed = 0, rxflags = 0; len = recv(sock, buf, sizeof(buf), 0); @@ -2897,13 +3360,17 @@ static void handle_monitor_read(int sock, void *eloop_ctx, void *sock_ctx) return; } + if (drv->nlmode == NL80211_IFTYPE_STATION && !drv->probe_req_report) { + wpa_printf(MSG_DEBUG, "nl80211: Ignore monitor interface " + "frame since Probe Request reporting is disabled"); + return; + } + if (ieee80211_radiotap_iterator_init(&iter, (void*)buf, len)) { printf("received invalid radiotap frame\n"); return; } - memset(&hfi, 0, sizeof(hfi)); - while (1) { ret = ieee80211_radiotap_iterator_next(&iter); if (ret == -ENOENT) @@ -2928,15 +3395,13 @@ static void handle_monitor_read(int sock, void *eloop_ctx, void *sock_ctx) case IEEE80211_RADIOTAP_DATA_RETRIES: break; case IEEE80211_RADIOTAP_CHANNEL: - /* TODO convert from freq/flags to channel number - hfi.channel = XXX; - */ + /* TODO: convert from freq/flags to channel number */ break; case IEEE80211_RADIOTAP_RATE: - hfi.datarate = *iter.this_arg * 5; + datarate = *iter.this_arg * 5; break; case IEEE80211_RADIOTAP_DB_ANTSIGNAL: - hfi.ssi_signal = *iter.this_arg; + ssi_signal = *iter.this_arg; break; } } @@ -2946,7 +3411,7 @@ static void handle_monitor_read(int sock, void *eloop_ctx, void *sock_ctx) if (!injected) handle_frame(drv, buf + iter.max_length, - len - iter.max_length, &hfi); + len - iter.max_length, datarate, ssi_signal); else handle_tx_callback(drv->ctx, buf + iter.max_length, len - iter.max_length, !failed); @@ -2999,7 +3464,7 @@ static struct sock_filter msock_filter_insns[] = { #if 0 /* - * drop non-data frames, WDS frames + * drop non-data frames */ /* load the lower byte of the frame control field */ BPF_STMT(BPF_LD | BPF_B | BPF_IND, 0), @@ -3007,13 +3472,13 @@ static struct sock_filter msock_filter_insns[] = { BPF_STMT(BPF_ALU | BPF_AND | BPF_K, 0x0c), /* drop non-data frames */ BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 8, 0, FAIL), +#endif /* load the upper byte of the frame control field */ - BPF_STMT(BPF_LD | BPF_B | BPF_IND, 0), + BPF_STMT(BPF_LD | BPF_B | BPF_IND, 1), /* mask off toDS/fromDS */ BPF_STMT(BPF_ALU | BPF_AND | BPF_K, 0x03), - /* drop WDS frames */ - BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 3, FAIL, 0), -#endif + /* accept WDS frames */ + BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 3, PASS, 0), /* * add header length to index @@ -3104,6 +3569,11 @@ static void nl80211_remove_monitor_interface( nl80211_remove_iface(drv, drv->monitor_ifidx); drv->monitor_ifidx = -1; } + if (drv->monitor_sock >= 0) { + eloop_unregister_read_sock(drv->monitor_sock); + close(drv->monitor_sock); + drv->monitor_sock = -1; + } } @@ -3115,16 +3585,17 @@ 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 = - nl80211_create_iface(drv, buf, NL80211_IFTYPE_MONITOR, NULL); + nl80211_create_iface(drv, buf, NL80211_IFTYPE_MONITOR, NULL, + 0); if (drv->monitor_ifidx < 0) return -1; - if (hostapd_set_iface_flags(drv, buf, 1)) + if (linux_set_iface_flags(drv->ioctl_sock, buf, 1)) goto error; memset(&ll, 0, sizeof(ll)); @@ -3174,7 +3645,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; @@ -3232,7 +3704,7 @@ static int wpa_driver_nl80211_hapd_send_eapol( "failed: %d (%s)", (unsigned long) len, errno, strerror(errno)); } - free(hdr); + os_free(hdr); return res; } @@ -3256,10 +3728,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; @@ -3277,7 +3750,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); /* @@ -3316,7 +3789,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; @@ -3367,7 +3843,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; @@ -3473,13 +3949,13 @@ static int wpa_driver_nl80211_connect( NLA_PUT(msg, NL80211_ATTR_IE, params->wpa_ie_len, params->wpa_ie); - if (params->auth_alg & AUTH_ALG_OPEN_SYSTEM) + if (params->auth_alg & WPA_AUTH_ALG_OPEN) type = NL80211_AUTHTYPE_OPEN_SYSTEM; - else if (params->auth_alg & AUTH_ALG_SHARED_KEY) + else if (params->auth_alg & WPA_AUTH_ALG_SHARED) type = NL80211_AUTHTYPE_SHARED_KEY; - else if (params->auth_alg & AUTH_ALG_LEAP) + else if (params->auth_alg & WPA_AUTH_ALG_LEAP) type = NL80211_AUTHTYPE_NETWORK_EAP; - else if (params->auth_alg & AUTH_ALG_FT) + else if (params->auth_alg & WPA_AUTH_ALG_FT) type = NL80211_AUTHTYPE_FT; else goto nla_put_failure; @@ -3581,7 +4057,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; @@ -3592,7 +4069,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); } @@ -3617,7 +4094,9 @@ static int wpa_driver_nl80211_associate( if (params->freq) { wpa_printf(MSG_DEBUG, " * freq=%d", params->freq); NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_FREQ, params->freq); - } + drv->assoc_freq = params->freq; + } else + drv->assoc_freq = 0; if (params->ssid) { wpa_hexdump_ascii(MSG_DEBUG, " * SSID", params->ssid, params->ssid_len); @@ -3647,11 +4126,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; @@ -3691,9 +4174,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: @@ -3716,6 +4201,8 @@ static int wpa_driver_nl80211_set_mode(void *priv, int mode) } if (nlmode == drv->nlmode) { + wpa_printf(MSG_DEBUG, "nl80211: Interface already in " + "requested mode - ignore error"); ret = 0; goto done; /* Already in the requested mode */ } @@ -3724,15 +4211,30 @@ static int wpa_driver_nl80211_set_mode(void *priv, int mode) * take the device down, try to set the mode again, and bring the * device back up. */ - if (hostapd_set_iface_flags(drv, drv->ifname, 0) == 0) { - /* Try to set the mode again while the interface is down */ - ret = nl80211_set_mode(drv, drv->ifindex, nlmode); - if (hostapd_set_iface_flags(drv, drv->ifname, 1)) - 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) + if (!ret) { + wpa_printf(MSG_DEBUG, "nl80211: Mode change succeeded while " + "interface is down"); drv->nlmode = nlmode; + } done: if (!ret && nlmode == NL80211_IFTYPE_AP) { @@ -3743,8 +4245,13 @@ done: } else if (!ret && nlmode != NL80211_IFTYPE_AP) { /* Remove additional AP mode functionality */ nl80211_remove_monitor_interface(drv); + bss->beacon_set = 0; } + if (ret) + wpa_printf(MSG_DEBUG, "nl80211: Interface mode change to %d " + "from %d failed", nlmode, drv->nlmode); + return ret; } @@ -3752,7 +4259,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)); @@ -3762,19 +4270,21 @@ 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"); drv->operstate = state; - return wpa_driver_nl80211_send_oper_ifla( - drv, -1, state ? IF_OPER_UP : IF_OPER_DORMANT); + return netlink_send_oper_ifla(drv->netlink, drv->ifindex, -1, + state ? IF_OPER_UP : IF_OPER_DORMANT); } 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; @@ -3786,7 +4296,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)); @@ -3803,20 +4313,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; @@ -3836,8 +4332,8 @@ static void add_ifidx(struct wpa_driver_nl80211_data *drv, int ifidx) else old = NULL; - drv->if_indices = realloc(old, - sizeof(int) * (drv->num_if_indices + 1)); + drv->if_indices = os_realloc(old, + sizeof(int) * (drv->num_if_indices + 1)); if (!drv->if_indices) { if (!old) drv->if_indices = drv->default_if_indices; @@ -3847,7 +4343,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++; } @@ -3910,7 +4408,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(); @@ -3936,7 +4435,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; @@ -3954,27 +4454,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; @@ -4005,7 +4510,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; @@ -4036,7 +4542,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(); @@ -4050,7 +4557,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: @@ -4112,7 +4619,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)); @@ -4124,7 +4632,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: @@ -4135,7 +4643,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; @@ -4146,7 +4655,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) @@ -4178,7 +4687,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(); @@ -4195,8 +4705,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: @@ -4225,8 +4734,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) @@ -4236,14 +4747,43 @@ 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 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 (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, bss->ifname, 0); + return wpa_driver_nl80211_if_remove(priv, WPA_IF_AP_VLAN, + name); + } } @@ -4262,13 +4802,8 @@ static void handle_eapol(int sock, void *eloop_ctx, void *sock_ctx) return; } - if (have_ifidx(drv, lladdr.sll_ifindex)) { - void *ctx; - ctx = hostapd_sta_get_bss(drv->ctx, lladdr.sll_addr); - if (!ctx) - return; - hostapd_eapol_receive(ctx, lladdr.sll_addr, buf, len); - } + if (have_ifidx(drv, lladdr.sll_ifindex)) + drv_event_eapol_rx(drv->ctx, lladdr.sll_addr, buf, len); } @@ -4297,7 +4832,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)); @@ -4307,7 +4842,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)); } @@ -4316,7 +4851,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)); @@ -4326,49 +4861,126 @@ 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)); } +static int i802_check_bridge(struct wpa_driver_nl80211_data *drv, + const char *brname, const char *ifname) +{ + int ifindex; + char in_br[IFNAMSIZ]; + + os_strlcpy(drv->brname, brname, IFNAMSIZ); + ifindex = if_nametoindex(brname); + if (ifindex == 0) { + /* + * Bridge was configured, but the bridge device does + * not exist. Try to add it now. + */ + if (linux_br_add(drv->ioctl_sock, brname) < 0) { + wpa_printf(MSG_ERROR, "nl80211: Failed to add the " + "bridge interface %s: %s", + brname, strerror(errno)); + return -1; + } + drv->added_bridge = 1; + add_ifidx(drv, if_nametoindex(brname)); + } + + if (linux_br_get(in_br, ifname) == 0) { + if (os_strcmp(in_br, brname) == 0) + return 0; /* already in the bridge */ + + wpa_printf(MSG_DEBUG, "nl80211: Removing interface %s from " + "bridge %s", ifname, in_br); + if (linux_br_del_if(drv->ioctl_sock, in_br, ifname) < 0) { + wpa_printf(MSG_ERROR, "nl80211: Failed to " + "remove interface %s from bridge " + "%s: %s", + ifname, brname, strerror(errno)); + return -1; + } + } + + wpa_printf(MSG_DEBUG, "nl80211: Adding interface %s into bridge %s", + ifname, brname); + if (linux_br_add_if(drv->ioctl_sock, brname, ifname) < 0) { + wpa_printf(MSG_ERROR, "nl80211: Failed to add interface %s " + "into bridge %s: %s", + ifname, brname, strerror(errno)); + return -1; + } + drv->added_if_into_bridge = 1; + + return 0; +} + + 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); + br_ifindex = if_nametoindex(brname); + } else { + brname[0] = '\0'; + br_ifindex = 0; + } drv->num_if_indices = sizeof(drv->default_if_indices) / sizeof(int); drv->if_indices = drv->default_if_indices; for (i = 0; i < params->num_bridge; i++) { - if (params->bridge[i]) - add_ifidx(drv, if_nametoindex(params->bridge[i])); + if (params->bridge[i]) { + ifindex = if_nametoindex(params->bridge[i]); + if (ifindex) + add_ifidx(drv, ifindex); + if (ifindex == br_ifindex) + br_added = 1; + } } + if (!br_added && br_ifindex && + (params->num_bridge == 0 || !params->bridge[0])) + add_ifidx(drv, br_ifindex); /* start listening for EAPOL on the default AP interface */ add_ifidx(drv, drv->ifindex); - if (hostapd_set_iface_flags(drv, drv->ifname, 0)) + if (linux_set_iface_flags(drv->ioctl_sock, bss->ifname, 0)) goto failed; if (params->bssid) { - if (set_ifhwaddr(drv, drv->ifname, params->bssid)) + 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; } - if (hostapd_set_iface_flags(drv, drv->ifname, 1)) + if (params->num_bridge && params->bridge[0] && + i802_check_bridge(drv, params->bridge[0], params->ifname) < 0) + goto failed; + + 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)); @@ -4383,10 +4995,10 @@ static void *i802_init(struct hostapd_data *hapd, goto failed; } - if (get_ifhwaddr(drv, drv->ifname, params->own_addr)) + if (linux_get_ifhwaddr(drv->ioctl_sock, bss->ifname, params->own_addr)) goto failed; - return drv; + return bss; failed: nl80211_remove_monitor_interface(drv); @@ -4416,52 +5028,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); + 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 (hostapd_set_iface_flags(priv, ifname, 1)) { - nl80211_remove_iface(priv, ifidx); - os_free(bss); + if (linux_set_iface_flags(drv->ioctl_sock, ifname, 1)) { + nl80211_remove_iface(drv, ifidx); + 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 */ @@ -4473,24 +5100,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 */ @@ -4499,6 +5132,394 @@ static int wpa_driver_nl80211_if_remove(void *priv, } +static int cookie_handler(struct nl_msg *msg, void *arg) +{ + struct nlattr *tb[NL80211_ATTR_MAX + 1]; + struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg)); + u64 *cookie = arg; + nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0), + genlmsg_attrlen(gnlh, 0), NULL); + if (tb[NL80211_ATTR_COOKIE]) + *cookie = nla_get_u64(tb[NL80211_ATTR_COOKIE]); + return NL_SKIP; +} + + +static int wpa_driver_nl80211_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; + struct nl_msg *msg; + u8 *buf; + struct ieee80211_hdr *hdr; + u64 cookie; + + 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); + os_free(buf); + return ret; + } + + msg = nlmsg_alloc(); + if (!msg) { + os_free(buf); + return -1; + } + + genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, 0, + NL80211_CMD_ACTION, 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, 24 + data_len, buf); + os_free(buf); + buf = NULL; + + cookie = 0; + ret = send_and_recv_msgs(drv, msg, cookie_handler, &cookie); + msg = NULL; + if (ret) { + wpa_printf(MSG_DEBUG, "nl80211: Action command failed: ret=%d " + "(%s)", ret, strerror(-ret)); + goto nla_put_failure; + } + wpa_printf(MSG_DEBUG, "nl80211: Action TX command accepted; " + "cookie 0x%llx", (long long unsigned int) cookie); + drv->send_action_cookie = cookie; + ret = 0; + +nla_put_failure: + os_free(buf); + nlmsg_free(msg); + return ret; +} + + +static int wpa_driver_nl80211_remain_on_channel(void *priv, unsigned int freq, + unsigned int duration) +{ + struct i802_bss *bss = priv; + struct wpa_driver_nl80211_data *drv = bss->drv; + struct nl_msg *msg; + int ret; + u64 cookie; + + msg = nlmsg_alloc(); + if (!msg) + return -1; + + genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, 0, + NL80211_CMD_REMAIN_ON_CHANNEL, 0); + + NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex); + NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_FREQ, freq); + NLA_PUT_U32(msg, NL80211_ATTR_DURATION, duration); + + cookie = 0; + ret = send_and_recv_msgs(drv, msg, cookie_handler, &cookie); + if (ret == 0) { + wpa_printf(MSG_DEBUG, "nl80211: Remain-on-channel cookie " + "0x%llx for freq=%u MHz duration=%u", + (long long unsigned int) cookie, freq, duration); + drv->remain_on_chan_cookie = cookie; + return 0; + } + wpa_printf(MSG_DEBUG, "nl80211: Failed to request remain-on-channel " + "(freq=%d duration=%u): %d (%s)", + freq, duration, ret, strerror(-ret)); +nla_put_failure: + return -1; +} + + +static int wpa_driver_nl80211_cancel_remain_on_channel(void *priv) +{ + struct i802_bss *bss = priv; + struct wpa_driver_nl80211_data *drv = bss->drv; + struct nl_msg *msg; + int ret; + + if (!drv->pending_remain_on_chan) { + wpa_printf(MSG_DEBUG, "nl80211: No pending remain-on-channel " + "to cancel"); + return -1; + } + + wpa_printf(MSG_DEBUG, "nl80211: Cancel remain-on-channel with cookie " + "0x%llx", + (long long unsigned int) drv->remain_on_chan_cookie); + + msg = nlmsg_alloc(); + if (!msg) + return -1; + + genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, 0, + NL80211_CMD_CANCEL_REMAIN_ON_CHANNEL, 0); + + NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex); + NLA_PUT_U64(msg, NL80211_ATTR_COOKIE, drv->remain_on_chan_cookie); + + ret = send_and_recv_msgs(drv, msg, NULL, NULL); + if (ret == 0) + return 0; + wpa_printf(MSG_DEBUG, "nl80211: Failed to cancel remain-on-channel: " + "%d (%s)", ret, strerror(-ret)); +nla_put_failure: + return -1; +} + + +static void wpa_driver_nl80211_probe_req_report_timeout(void *eloop_ctx, + void *timeout_ctx) +{ + struct wpa_driver_nl80211_data *drv = eloop_ctx; + if (drv->monitor_ifidx < 0) + return; /* monitor interface already removed */ + + if (drv->nlmode != NL80211_IFTYPE_STATION) + return; /* not in station mode anymore */ + + if (drv->probe_req_report) + return; /* reporting enabled */ + + wpa_printf(MSG_DEBUG, "nl80211: Remove monitor interface due to no " + "Probe Request reporting needed anymore"); + nl80211_remove_monitor_interface(drv); +} + + +static int wpa_driver_nl80211_probe_req_report(void *priv, int report) +{ + struct 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 " + "allowed in station mode (iftype=%d)", + drv->nlmode); + return -1; + } + drv->probe_req_report = report; + + if (report) { + eloop_cancel_timeout( + wpa_driver_nl80211_probe_req_report_timeout, + drv, NULL); + if (drv->monitor_ifidx < 0 && + nl80211_create_monitor_interface(drv)) + return -1; + } else { + /* + * It takes a while to remove the monitor interface, so try to + * avoid doing this if it is needed again shortly. Instead, + * schedule the interface to be removed later if no need for it + * is seen. + */ + wpa_printf(MSG_DEBUG, "nl80211: Scheduling monitor interface " + "to be removed after 10 seconds of no use"); + eloop_register_timeout( + 10, 0, wpa_driver_nl80211_probe_req_report_timeout, + drv, NULL); + } + + return 0; +} + + +static int nl80211_disable_11b_rates(struct wpa_driver_nl80211_data *drv, + int ifindex, int disabled) +{ + struct nl_msg *msg; + struct nlattr *bands, *band; + int ret; + + msg = nlmsg_alloc(); + if (!msg) + return -1; + + genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, 0, + NL80211_CMD_SET_TX_BITRATE_MASK, 0); + NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, ifindex); + + bands = nla_nest_start(msg, NL80211_ATTR_TX_RATES); + if (!bands) + goto nla_put_failure; + + /* + * Disable 2 GHz rates 1, 2, 5.5, 11 Mbps by masking out everything + * else apart from 6, 9, 12, 18, 24, 36, 48, 54 Mbps from non-MCS + * rates. All 5 GHz rates are left enabled. + */ + band = nla_nest_start(msg, NL80211_BAND_2GHZ); + if (!band) + goto nla_put_failure; + NLA_PUT(msg, NL80211_TXRATE_LEGACY, 8, + "\x0c\x12\x18\x24\x30\x48\x60\x6c"); + nla_nest_end(msg, band); + + nla_nest_end(msg, bands); + + ret = send_and_recv_msgs(drv, msg, NULL, NULL); + msg = NULL; + if (ret) { + wpa_printf(MSG_DEBUG, "nl80211: Set TX rates failed: ret=%d " + "(%s)", ret, strerror(-ret)); + } + + return ret; + +nla_put_failure: + nlmsg_free(msg); + return -1; +} + + +static int wpa_driver_nl80211_disable_11b_rates(void *priv, int disabled) +{ + struct 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); +} + + +static int wpa_driver_nl80211_deinit_ap(void *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(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); +} + + const struct wpa_driver_ops wpa_driver_nl80211_ops = { .name = "nl80211", .desc = "Linux nl80211/cfg80211", @@ -4536,7 +5557,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, @@ -4545,5 +5565,18 @@ const struct wpa_driver_ops wpa_driver_nl80211_ops = { .set_short_slot_time = i802_set_short_slot_time, .set_tx_queue_params = i802_set_tx_queue_params, .set_sta_vlan = i802_set_sta_vlan, + .set_wds_sta = i802_set_wds_sta, #endif /* HOSTAPD */ + .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, + .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, };