/*
* Driver interaction with Linux nl80211/cfg80211
- * Copyright (c) 2002-2008, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2002-2010, Jouni Malinen <j@w1.fi>
* Copyright (c) 2003-2004, Instant802 Networks, Inc.
* Copyright (c) 2005-2006, Devicescape Software, Inc.
* Copyright (c) 2007, Johannes Berg <johannes@sipsolutions.net>
- * 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
#include "includes.h"
#include <sys/ioctl.h>
-#include <net/if_arp.h>
#include <net/if.h>
#include <netlink/genl/genl.h>
#include <netlink/genl/family.h>
#include <netlink/genl/ctrl.h>
+#include <linux/rtnetlink.h>
+#include <netpacket/packet.h>
+#include <linux/filter.h>
#include "nl80211_copy.h"
#include "common.h"
-#include "driver.h"
#include "eloop.h"
#include "common/ieee802_11_defs.h"
-
-#if defined(CONFIG_AP) || defined(HOSTAPD)
-#include <netpacket/packet.h>
-#include <linux/filter.h>
+#include "netlink.h"
+#include "linux_ioctl.h"
#include "radiotap.h"
#include "radiotap_iter.h"
-#endif /* CONFIG_AP || HOSTAPD */
+#include "rfkill.h"
+#include "driver.h"
#ifdef CONFIG_LIBNL20
/* libnl 2.0 compatibility code */
#define nl_handle nl_sock
-#define nl_handle_alloc_cb nl_socket_alloc_cb
-#define nl_handle_destroy nl_socket_free
+#define nl80211_handle_alloc nl_socket_alloc_cb
+#define nl80211_handle_destroy nl_socket_free
+#else
+/*
+ * libnl 1.1 has a bug, it tries to allocate socket numbers densely
+ * but when you free a socket again it will mess up its bitmap and
+ * and use the wrong number the next time it needs a socket ID.
+ * Therefore, we wrap the handle alloc/destroy and add our own pid
+ * accounting.
+ */
+static uint32_t port_bitmap[32] = { 0 };
+
+static struct nl_handle *nl80211_handle_alloc(void *cb)
+{
+ struct nl_handle *handle;
+ uint32_t pid = getpid() & 0x3FFFFF;
+ int i;
+
+ handle = nl_handle_alloc_cb(cb);
+
+ for (i = 0; i < 1024; i++) {
+ if (port_bitmap[i / 32] & (1 << (i % 32)))
+ continue;
+ port_bitmap[i / 32] |= 1 << (i % 32);
+ pid += i << 22;
+ break;
+ }
+
+ nl_socket_set_local_port(handle, pid);
+
+ return handle;
+}
+
+static void nl80211_handle_destroy(struct nl_handle *handle)
+{
+ uint32_t port = nl_socket_get_local_port(handle);
+
+ port >>= 22;
+ port_bitmap[port / 32] &= ~(1 << (port % 32));
+
+ nl_handle_destroy(handle);
+}
#endif /* CONFIG_LIBNL20 */
#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;
struct nl_handle *nl_handle;
struct nl_handle *nl_handle_event;
+ struct nl_handle *nl_handle_preq;
struct nl_cache *nl_cache;
struct nl_cache *nl_cache_event;
+ struct nl_cache *nl_cache_preq;
struct nl_cb *nl_cb;
struct genl_family *nl80211;
size_t ssid_len;
int nlmode;
int ap_scan_as_station;
+ unsigned int assoc_freq;
int monitor_sock;
int monitor_ifidx;
+ 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;
+
+ unsigned int last_mgmt_freq;
+
+ 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 */
int *if_indices;
int num_if_indices;
- struct i802_bss bss;
-
int last_freq;
int last_freq_ht;
#endif /* HOSTAPD */
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);
-
-#if defined(CONFIG_AP) || defined(HOSTAPD)
+ const u8 *addr, int cmd, u16 reason_code,
+ int local_state_change);
static void nl80211_remove_monitor_interface(
struct wpa_driver_nl80211_data *drv);
-#endif /* CONFIG_AP || HOSTAPD */
-
-#ifdef CONFIG_AP
-static void nl80211_remove_iface(struct wpa_driver_nl80211_data *drv,
- int ifidx);
-#endif /* CONFIG_AP */
+static int nl80211_send_frame_cmd(struct wpa_driver_nl80211_data *drv,
+ unsigned int freq, const u8 *buf,
+ size_t buf_len, u64 *cookie);
+static int wpa_driver_nl80211_probe_req_report(void *priv, int report);
#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 void nl80211_remove_iface(struct wpa_driver_nl80211_data *drv,
- int ifidx);
-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 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)
}
-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;
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;
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);
}
+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;
}
-#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);
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);
}
-#ifndef HOSTAPD
-
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;
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 = h->nlmsg_len - _nlmsg_len;
- 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
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);
}
-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;
}
(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()
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 = h->nlmsg_len - _nlmsg_len;
- 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 = h->nlmsg_len - _nlmsg_len;
- 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 */
+}
static void mlme_event_auth(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);
}
}
+static void mlme_event_mgmt(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));
+ if (freq) {
+ event.rx_action.freq = nla_get_u32(freq);
+ drv->last_mgmt_freq = event.rx_action.freq;
+ }
+ if (stype == WLAN_FC_STYPE_ACTION) {
+ 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;
+ wpa_supplicant_event(drv->ctx, EVENT_RX_ACTION, &event);
+ } else {
+ event.rx_mgmt.frame = frame;
+ event.rx_mgmt.frame_len = len;
+ wpa_supplicant_event(drv->ctx, EVENT_RX_MGMT, &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);
mlme_event_assoc(drv, nla_data(frame), nla_len(frame));
break;
case NL80211_CMD_DEAUTHENTICATE:
- drv->associated = 0;
- wpa_supplicant_event(drv->ctx, EVENT_DEAUTH, NULL);
+ mlme_event_deauth_disassoc(drv, EVENT_DEAUTH,
+ nla_data(frame), nla_len(frame));
break;
case NL80211_CMD_DISASSOCIATE:
- drv->associated = 0;
- wpa_supplicant_event(drv->ctx, EVENT_DISASSOC, NULL);
+ mlme_event_deauth_disassoc(drv, EVENT_DISASSOC,
+ nla_data(frame), nla_len(frame));
+ break;
+ case NL80211_CMD_FRAME:
+ mlme_event_mgmt(drv, freq, nla_data(frame), nla_len(frame));
+ break;
+ case NL80211_CMD_FRAME_TX_STATUS:
+ mlme_event_action_tx_status(drv, cookie, nla_data(frame),
+ nla_len(frame), ack);
break;
default:
break;
}
-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 },
+ };
+ struct nlattr *rinfo[NL80211_RATE_INFO_MAX + 1];
+ static struct nla_policy rate_policy[NL80211_RATE_INFO_MAX + 1] = {
+ [NL80211_RATE_INFO_BITRATE] = { .type = NLA_U16 },
+ [NL80211_RATE_INFO_MCS] = { .type = NLA_U8 },
+ [NL80211_RATE_INFO_40_MHZ_WIDTH] = { .type = NLA_FLAG },
+ [NL80211_RATE_INFO_SHORT_GI] = { .type = NLA_FLAG },
+ };
+ struct signal_change *sig_change = 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_change->current_signal =
+ (s8) nla_get_u8(sinfo[NL80211_STA_INFO_SIGNAL]);
+
+ if (sinfo[NL80211_STA_INFO_TX_BITRATE]) {
+ if (nla_parse_nested(rinfo, NL80211_RATE_INFO_MAX,
+ sinfo[NL80211_STA_INFO_TX_BITRATE],
+ rate_policy)) {
+ sig_change->current_txrate = 0;
+ } else {
+ if (rinfo[NL80211_RATE_INFO_BITRATE]) {
+ sig_change->current_txrate =
+ nla_get_u16(rinfo[
+ NL80211_RATE_INFO_BITRATE]) * 100;
+ }
+ }
+ }
+
+ return NL_SKIP;
+}
+
+
+static int nl80211_get_link_signal(struct wpa_driver_nl80211_data *drv,
+ struct signal_change *sig)
+{
+ struct nl_msg *msg;
+
+ sig->current_signal = -9999;
+ sig->current_txrate = 0;
+
+ 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 int get_link_noise(struct nl_msg *msg, void *arg)
+{
+ struct nlattr *tb[NL80211_ATTR_MAX + 1];
+ struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
+ struct nlattr *sinfo[NL80211_SURVEY_INFO_MAX + 1];
+ static struct nla_policy survey_policy[NL80211_SURVEY_INFO_MAX + 1] = {
+ [NL80211_SURVEY_INFO_FREQUENCY] = { .type = NLA_U32 },
+ [NL80211_SURVEY_INFO_NOISE] = { .type = NLA_U8 },
+ };
+ struct signal_change *sig_change = arg;
+
+ nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
+ genlmsg_attrlen(gnlh, 0), NULL);
+
+ if (!tb[NL80211_ATTR_SURVEY_INFO]) {
+ wpa_printf(MSG_DEBUG, "nl80211: survey data missing!");
+ return NL_SKIP;
+ }
+
+ if (nla_parse_nested(sinfo, NL80211_SURVEY_INFO_MAX,
+ tb[NL80211_ATTR_SURVEY_INFO],
+ survey_policy)) {
+ wpa_printf(MSG_DEBUG, "nl80211: failed to parse nested "
+ "attributes!");
+ return NL_SKIP;
+ }
+
+ if (!sinfo[NL80211_SURVEY_INFO_FREQUENCY])
+ return NL_SKIP;
+
+ if (nla_get_u32(sinfo[NL80211_SURVEY_INFO_FREQUENCY]) !=
+ sig_change->frequency)
+ return NL_SKIP;
+
+ if (!sinfo[NL80211_SURVEY_INFO_NOISE])
+ return NL_SKIP;
+
+ sig_change->current_noise =
+ (s8) nla_get_u8(sinfo[NL80211_SURVEY_INFO_NOISE]);
+
+ return NL_SKIP;
+}
+
+
+static int nl80211_get_link_noise(struct wpa_driver_nl80211_data *drv,
+ struct signal_change *sig_change)
+{
+ struct nl_msg *msg;
+
+ sig_change->current_noise = 9999;
+ sig_change->frequency = drv->assoc_freq;
+
+ msg = nlmsg_alloc();
+ if (!msg)
+ return -ENOMEM;
+
+ genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0,
+ NLM_F_DUMP, NL80211_CMD_GET_SURVEY, 0);
+
+ NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex);
+
+ return send_and_recv_msgs(drv, msg, get_link_noise, sig_change);
+ 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;
+ struct signal_change sig;
+ int res;
+
+ if (tb[NL80211_ATTR_CQM] == NULL ||
+ nla_parse_nested(cqm, NL80211_ATTR_CQM_MAX, tb[NL80211_ATTR_CQM],
+ cqm_policy)) {
+ wpa_printf(MSG_DEBUG, "nl80211: Ignore invalid CQM event");
+ return;
+ }
+
+ if (cqm[NL80211_ATTR_CQM_RSSI_THRESHOLD_EVENT] == NULL)
+ return;
+ event = nla_get_u32(cqm[NL80211_ATTR_CQM_RSSI_THRESHOLD_EVENT]);
+
+ os_memset(&ed, 0, sizeof(ed));
+
+ if (event == NL80211_CQM_RSSI_THRESHOLD_EVENT_HIGH) {
+ wpa_printf(MSG_DEBUG, "nl80211: Connection quality monitor "
+ "event: RSSI high");
+ ed.signal_change.above_threshold = 1;
+ } else if (event == NL80211_CQM_RSSI_THRESHOLD_EVENT_LOW) {
+ wpa_printf(MSG_DEBUG, "nl80211: Connection quality monitor "
+ "event: RSSI low");
+ ed.signal_change.above_threshold = 0;
+ } else
+ return;
+
+ res = nl80211_get_link_signal(drv, &sig);
+ if (res == 0) {
+ ed.signal_change.current_signal = sig.current_signal;
+ ed.signal_change.current_txrate = sig.current_txrate;
+ wpa_printf(MSG_DEBUG, "nl80211: Signal: %d dBm txrate: %d",
+ sig.current_signal, sig.current_txrate);
+ }
+
+ res = nl80211_get_link_noise(drv, &sig);
+ if (res == 0) {
+ ed.signal_change.current_noise = sig.current_noise;
+ wpa_printf(MSG_DEBUG, "nl80211: Noise: %d dBm",
+ sig.current_noise);
+ }
+
+ 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, IEEE80211_MODE_AP);
+ wpa_driver_nl80211_set_mode(&drv->first_bss,
+ IEEE80211_MODE_AP);
drv->ap_scan_as_station = 0;
}
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");
*/
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_FRAME:
+ case NL80211_CMD_FRAME_TX_STATUS:
mlme_event(drv, gnlh->cmd, tb[NL80211_ATTR_FRAME],
- tb[NL80211_ATTR_MAC], tb[NL80211_ATTR_TIMED_OUT]);
+ tb[NL80211_ATTR_MAC], tb[NL80211_ATTR_TIMED_OUT],
+ tb[NL80211_ATTR_WIPHY_FREQ], tb[NL80211_ATTR_ACK],
+ tb[NL80211_ATTR_COOKIE]);
break;
case NL80211_CMD_CONNECT:
case NL80211_CMD_ROAM:
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);
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;
+ case NL80211_CMD_REG_CHANGE:
+ wpa_printf(MSG_DEBUG, "nl80211: Regulatory domain change");
+ wpa_supplicant_event(drv->ctx, EVENT_CHANNEL_LIST_CHANGED,
+ NULL);
+ break;
+ case NL80211_CMD_REG_BEACON_HINT:
+ wpa_printf(MSG_DEBUG, "nl80211: Regulatory beacon hint");
+ wpa_supplicant_event(drv->ctx, EVENT_CHANNEL_LIST_CHANGED,
+ NULL);
+ break;
default:
wpa_printf(MSG_DEBUG, "nl80211: Ignored unknown event "
"(cmd=%d)", gnlh->cmd);
static void wpa_driver_nl80211_event_receive(int sock, void *eloop_ctx,
- void *sock_ctx)
+ void *handle)
{
struct nl_cb *cb;
struct wpa_driver_nl80211_data *drv = eloop_ctx;
return;
nl_cb_set(cb, NL_CB_SEQ_CHECK, NL_CB_CUSTOM, no_seq_check, NULL);
nl_cb_set(cb, NL_CB_VALID, NL_CB_CUSTOM, process_event, drv);
- nl_recvmsgs(drv->nl_handle_event, cb);
+ nl_recvmsgs(handle, cb);
nl_cb_put(cb);
}
-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
*/
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;
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;
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)
}
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;
goto err1;
}
- drv->nl_handle = nl_handle_alloc_cb(drv->nl_cb);
+ drv->nl_handle = nl80211_handle_alloc(drv->nl_cb);
if (drv->nl_handle == NULL) {
wpa_printf(MSG_ERROR, "nl80211: Failed to allocate netlink "
"callbacks");
goto err2;
}
- drv->nl_handle_event = nl_handle_alloc_cb(drv->nl_cb);
+ drv->nl_handle_event = nl80211_handle_alloc(drv->nl_cb);
if (drv->nl_handle_event == NULL) {
wpa_printf(MSG_ERROR, "nl80211: Failed to allocate netlink "
"callbacks (event)");
goto err4;
}
+ ret = nl_get_multicast_id(drv, "nl80211", "regulatory");
+ if (ret >= 0)
+ ret = nl_socket_add_membership(drv->nl_handle_event, ret);
+ if (ret < 0) {
+ wpa_printf(MSG_DEBUG, "nl80211: Could not add multicast "
+ "membership for regulatory events: %d (%s)",
+ ret, strerror(-ret));
+ /* Continue without regulatory events */
+ }
+
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,
+ drv->nl_handle_event);
return 0;
err3b:
nl_cache_free(drv->nl_cache);
err3:
- nl_handle_destroy(drv->nl_handle_event);
+ nl80211_handle_destroy(drv->nl_handle_event);
err2b:
- nl_handle_destroy(drv->nl_handle);
+ nl80211_handle_destroy(drv->nl_handle);
err2:
nl_cb_put(drv->nl_cb);
err1:
}
-static int wpa_driver_nl80211_init_link_events(
- struct wpa_driver_nl80211_data *drv)
+static void wpa_driver_nl80211_rfkill_blocked(void *ctx)
{
-#ifdef HOSTAPD
- return 0;
-#else /* HOSTAPD */
- 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;
-#endif /* HOSTAPD */
+ /* rtnetlink ifup handler will report interface as enabled */
}
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;
}
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);
genl_family_put(drv->nl80211);
nl_cache_free(drv->nl_cache);
- nl_handle_destroy(drv->nl_handle);
+ nl80211_handle_destroy(drv->nl_handle);
nl_cb_put(drv->nl_cb);
+ eloop_unregister_read_sock(nl_socket_get_fd(drv->nl_handle_event));
os_free(drv);
return NULL;
}
+static int nl80211_register_frame(struct wpa_driver_nl80211_data *drv,
+ struct nl_handle *nl_handle,
+ u16 type, const u8 *match, size_t match_len)
+{
+ struct nl_msg *msg;
+ int ret = -1;
+
+ msg = nlmsg_alloc();
+ if (!msg)
+ return -1;
+
+ genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, 0,
+ NL80211_CMD_REGISTER_ACTION, 0);
+
+ NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex);
+ NLA_PUT_U16(msg, NL80211_ATTR_FRAME_TYPE, type);
+ NLA_PUT(msg, NL80211_ATTR_FRAME_MATCH, match_len, match);
+
+ ret = send_and_recv(drv, nl_handle, msg, NULL, NULL);
+ msg = NULL;
+ if (ret) {
+ wpa_printf(MSG_DEBUG, "nl80211: Register frame command "
+ "failed (type=%u): ret=%d (%s)",
+ type, ret, strerror(-ret));
+ wpa_hexdump(MSG_DEBUG, "nl80211: Register frame match",
+ match, match_len);
+ goto nla_put_failure;
+ }
+ ret = 0;
+nla_put_failure:
+ nlmsg_free(msg);
+ return ret;
+}
+
+
+static int nl80211_register_action_frame(struct wpa_driver_nl80211_data *drv,
+ const u8 *match, size_t match_len)
+{
+ u16 type = (WLAN_FC_TYPE_MGMT << 2) | (WLAN_FC_STYPE_ACTION << 4);
+ return nl80211_register_frame(drv, drv->nl_handle_event,
+ type, match, match_len);
+}
+
+
+static int nl80211_register_action_frames(struct wpa_driver_nl80211_data *drv)
+{
+#ifdef CONFIG_P2P
+ /* GAS Initial Request */
+ if (nl80211_register_action_frame(drv, (u8 *) "\x04\x0a", 2) < 0)
+ return -1;
+ /* GAS Initial Response */
+ if (nl80211_register_action_frame(drv, (u8 *) "\x04\x0b", 2) < 0)
+ return -1;
+ /* GAS Comeback Request */
+ if (nl80211_register_action_frame(drv, (u8 *) "\x04\x0c", 2) < 0)
+ return -1;
+ /* GAS Comeback Response */
+ if (nl80211_register_action_frame(drv, (u8 *) "\x04\x0d", 2) < 0)
+ return -1;
+ /* P2P Public Action */
+ if (nl80211_register_action_frame(drv,
+ (u8 *) "\x04\x09\x50\x6f\x9a\x09",
+ 6) < 0)
+ return -1;
+ /* P2P Action */
+ if (nl80211_register_action_frame(drv,
+ (u8 *) "\x7f\x50\x6f\x9a\x09",
+ 5) < 0)
+ return -1;
+#endif /* CONFIG_P2P */
+
+ /* FT Action frames */
+ if (nl80211_register_action_frame(drv, (u8 *) "\x06", 1) < 0)
+ return -1;
+ else
+ drv->capa.key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_FT |
+ WPA_DRIVER_CAPA_KEY_MGMT_FT_PSK;
+
+ return 0;
+}
+
+
+static void wpa_driver_nl80211_send_rfkill(void *eloop_ctx, void *timeout_ctx)
+{
+ wpa_supplicant_event(timeout_ctx, EVENT_INTERFACE_DISABLED, NULL);
+}
+
+
static int
wpa_driver_nl80211_finish_drv_init(struct wpa_driver_nl80211_data *drv)
{
- drv->ifindex = if_nametoindex(drv->ifname);
+ struct i802_bss *bss = &drv->first_bss;
+ int send_rfkill_event = 0;
+
+ drv->ifindex = if_nametoindex(bss->ifname);
+ drv->first_bss.ifindex = drv->ifindex;
#ifndef HOSTAPD
- if (wpa_driver_nl80211_set_mode(drv, IEEE80211_MODE_INFRA) < 0) {
+ if (wpa_driver_nl80211_set_mode(bss, IEEE80211_MODE_INFRA) < 0) {
wpa_printf(MSG_DEBUG, "nl80211: Could not configure driver to "
"use managed mode");
}
- if (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 */
-#if defined(CONFIG_AP) || defined(HOSTAPD)
static int wpa_driver_nl80211_del_beacon(struct wpa_driver_nl80211_data *drv)
{
struct nl_msg *msg;
nla_put_failure:
return -ENOBUFS;
}
-#endif /* CONFIG_AP || HOSTAPD */
/**
*/
static void wpa_driver_nl80211_deinit(void *priv)
{
- struct wpa_driver_nl80211_data *drv = priv;
+ struct i802_bss *bss = priv;
+ struct wpa_driver_nl80211_data *drv = bss->drv;
-#if defined(CONFIG_AP) || defined(HOSTAPD)
- nl80211_remove_monitor_interface(drv);
- if (drv->monitor_sock >= 0) {
- eloop_unregister_read_sock(drv->monitor_sock);
- close(drv->monitor_sock);
+ if (drv->nl_handle_preq)
+ wpa_driver_nl80211_probe_req_report(bss, 0);
+ if (drv->added_if_into_bridge) {
+ if (linux_br_del_if(drv->ioctl_sock, drv->brname, bss->ifname)
+ < 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);
-#endif /* CONFIG_AP || HOSTAPD */
#ifdef HOSTAPD
if (drv->last_freq_ht) {
if (drv->if_indices != drv->default_if_indices)
os_free(drv->if_indices);
+#endif /* HOSTAPD */
- wpa_driver_nl80211_free_bss(drv);
-#else /* 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);
- }
-#endif /* HOSTAPD */
+ 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);
genl_family_put(drv->nl80211);
nl_cache_free(drv->nl_cache);
nl_cache_free(drv->nl_cache_event);
- nl_handle_destroy(drv->nl_handle);
- nl_handle_destroy(drv->nl_handle_event);
+ nl80211_handle_destroy(drv->nl_handle);
+ nl80211_handle_destroy(drv->nl_handle_event);
nl_cb_put(drv->nl_cb);
+ os_free(drv->filter_ssids);
+
os_free(drv);
}
{
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");
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;
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);
}
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);
}
* 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;
}
}
-static int bss_info_handler(struct nl_msg *msg, void *arg)
+static const u8 * nl80211_get_ie(const u8 *ies, size_t ies_len, u8 ie)
{
- struct nlattr *tb[NL80211_ATTR_MAX + 1];
- struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
- struct nlattr *bss[NL80211_BSS_MAX + 1];
- static struct nla_policy bss_policy[NL80211_BSS_MAX + 1] = {
- [NL80211_BSS_BSSID] = { .type = NLA_UNSPEC },
- [NL80211_BSS_FREQUENCY] = { .type = NLA_U32 },
- [NL80211_BSS_TSF] = { .type = NLA_U64 },
- [NL80211_BSS_BEACON_INTERVAL] = { .type = NLA_U16 },
- [NL80211_BSS_CAPABILITY] = { .type = NLA_U16 },
- [NL80211_BSS_INFORMATION_ELEMENTS] = { .type = NLA_UNSPEC },
- [NL80211_BSS_SIGNAL_MBM] = { .type = NLA_U32 },
- [NL80211_BSS_SIGNAL_UNSPEC] = { .type = NLA_U8 },
- [NL80211_BSS_STATUS] = { .type = NLA_U32 },
- [NL80211_BSS_SEEN_MS_AGO] = { .type = NLA_U32 },
- };
- struct wpa_scan_results *res = arg;
- struct wpa_scan_res **tmp;
- struct wpa_scan_res *r;
- const u8 *ie;
- size_t ie_len;
+ const u8 *end, *pos;
- nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
- genlmsg_attrlen(gnlh, 0), NULL);
+ 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];
+ struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
+ struct nlattr *bss[NL80211_BSS_MAX + 1];
+ static struct nla_policy bss_policy[NL80211_BSS_MAX + 1] = {
+ [NL80211_BSS_BSSID] = { .type = NLA_UNSPEC },
+ [NL80211_BSS_FREQUENCY] = { .type = NLA_U32 },
+ [NL80211_BSS_TSF] = { .type = NLA_U64 },
+ [NL80211_BSS_BEACON_INTERVAL] = { .type = NLA_U16 },
+ [NL80211_BSS_CAPABILITY] = { .type = NLA_U16 },
+ [NL80211_BSS_INFORMATION_ELEMENTS] = { .type = NLA_UNSPEC },
+ [NL80211_BSS_SIGNAL_MBM] = { .type = NLA_U32 },
+ [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 nl80211_bss_info_arg *_arg = arg;
+ struct wpa_scan_results *res = _arg->res;
+ struct wpa_scan_res **tmp;
+ struct wpa_scan_res *r;
+ const u8 *ie, *beacon_ie;
+ size_t ie_len, beacon_ie_len;
+ u8 *pos;
+
+ nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
+ genlmsg_attrlen(gnlh, 0), NULL);
if (!tb[NL80211_ATTR_BSS])
return NL_SKIP;
if (nla_parse_nested(bss, NL80211_BSS_MAX, tb[NL80211_ATTR_BSS],
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])
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;
{
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);
}
}
" assoc=" MACSTR ")",
MAC2STR(drv->auth_bssid),
MAC2STR(drv->bssid));
+ clear_state_mismatch(drv, r->bssid);
}
}
}
-/**
- * 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)
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 "
}
+/**
+ * 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;
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)",
*/
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();
}
-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)
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;
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;
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;
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:
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]);
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;
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
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;
}
-#if defined(CONFIG_AP) || defined(HOSTAPD)
-
struct phy_info_arg {
u16 *num_modes;
struct hostapd_hw_modes *modes;
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;
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;
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;
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,
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;
mgmt = (struct ieee80211_mgmt *) data;
fc = le_to_host16(mgmt->frame_control);
+ if (drv->nlmode == NL80211_IFTYPE_STATION &&
+ WLAN_FC_GET_TYPE(fc) == WLAN_FC_TYPE_MGMT &&
+ WLAN_FC_GET_STYPE(fc) == WLAN_FC_STYPE_PROBE_RESP) {
+ /*
+ * The use of last_mgmt_freq is a bit of a hack,
+ * but it works due to the single-threaded nature
+ * of wpa_supplicant.
+ */
+ return nl80211_send_frame_cmd(drv, drv->last_mgmt_freq,
+ data, data_len, NULL);
+ }
+
if (WLAN_FC_GET_TYPE(fc) == WLAN_FC_TYPE_MGMT &&
WLAN_FC_GET_STYPE(fc) == WLAN_FC_STYPE_AUTH) {
/*
return wpa_driver_nl80211_send_frame(drv, data, data_len, encrypt);
}
-#endif /* CONFIG_AP || HOSTAPD */
-
-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)
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:
}
-#if defined(CONFIG_AP) || defined(HOSTAPD)
-
static int wpa_driver_nl80211_set_freq(struct wpa_driver_nl80211_data *drv,
int freq, int ht_enabled,
int sec_channel_offset)
}
-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;
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,
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;
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);
return -ENOBUFS;
}
-#endif /* CONFIG_AP || HOSTAPD */
-
static void nl80211_remove_iface(struct wpa_driver_nl80211_data *drv,
int ifidx)
{
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);
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;
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);
}
ifidx = if_nametoindex(ifname);
+ wpa_printf(MSG_DEBUG, "nl80211: New interface %s created: ifindex=%d",
+ ifname, ifidx);
if (ifidx <= 0)
return -1;
#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;
}
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)) {
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;
}
-#ifdef CONFIG_AP
-
-void ap_tx_status(void *ctx, const u8 *addr,
- const u8 *buf, size_t len, int ack);
-void ap_rx_from_unknown_sta(void *ctx, struct ieee80211_hdr *hdr, size_t len);
-void ap_mgmt_rx(void *ctx, u8 *buf, size_t len, u16 stype,
- struct hostapd_frame_info *fi);
-void ap_mgmt_tx_cb(void *ctx, u8 *buf, size_t len, u16 stype, int ok);
-
-#endif /* CONFIG_AP */
-
-#if defined(CONFIG_AP) || defined(HOSTAPD)
-
static void handle_tx_callback(void *ctx, u8 *buf, size_t len, int ok)
{
struct ieee80211_hdr *hdr;
- u16 fc, type, stype;
+ u16 fc;
+ union wpa_event_data event;
hdr = (struct ieee80211_hdr *) buf;
fc = le_to_host16(hdr->frame_control);
- type = WLAN_FC_GET_TYPE(fc);
- stype = WLAN_FC_GET_STYPE(fc);
-
- switch (type) {
- case WLAN_FC_TYPE_MGMT:
- wpa_printf(MSG_DEBUG, "MGMT (TX callback) %s",
- ok ? "ACK" : "fail");
-#ifdef HOSTAPD
- hostapd_mgmt_tx_cb(ctx, buf, len, stype, ok);
-#else /* HOSTAPD */
- ap_mgmt_tx_cb(ctx, buf, len, stype, ok);
-#endif /* HOSTAPD */
- break;
- case WLAN_FC_TYPE_CTRL:
- wpa_printf(MSG_DEBUG, "CTRL (TX callback) %s",
- ok ? "ACK" : "fail");
- break;
- case WLAN_FC_TYPE_DATA:
-#ifdef HOSTAPD
- hostapd_tx_status(ctx, hdr->addr1, buf, len, ok);
-#else /* HOSTAPD */
- ap_tx_status(ctx, hdr->addr1, buf, len, ok);
-#endif /* HOSTAPD */
- break;
- default:
- wpa_printf(MSG_DEBUG, "unknown TX callback frame type %d",
- type);
- break;
- }
+ 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 = buf;
+ event.tx_status.data_len = len;
+ event.tx_status.ack = ok;
+ wpa_supplicant_event(ctx, EVENT_TX_STATUS, &event);
}
static void from_unknown_sta(struct wpa_driver_nl80211_data *drv,
- struct ieee80211_hdr *hdr, size_t len)
+ u8 *buf, size_t len)
{
-#ifdef HOSTAPD
- hostapd_rx_from_unknown_sta(drv->ctx, hdr, len);
-#else /* HOSTAPD */
- ap_rx_from_unknown_sta(drv->ctx, hdr, len);
-#endif /* HOSTAPD */
+ union wpa_event_data event;
+ os_memset(&event, 0, sizeof(event));
+ 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, stype;
+ u16 fc;
+ union wpa_event_data event;
hdr = (struct ieee80211_hdr *) buf;
fc = le_to_host16(hdr->frame_control);
- stype = WLAN_FC_GET_STYPE(fc);
switch (WLAN_FC_GET_TYPE(fc)) {
case WLAN_FC_TYPE_MGMT:
- if (stype != WLAN_FC_STYPE_BEACON &&
- stype != WLAN_FC_STYPE_PROBE_REQ)
- wpa_printf(MSG_MSGDUMP, "MGMT");
-#ifdef HOSTAPD
- hostapd_mgmt_rx(drv->ctx, buf, len, stype, hfi);
-#else /* HOSTAPD */
- ap_mgmt_rx(drv->ctx, buf, len, stype, hfi);
-#endif /* HOSTAPD */
+ os_memset(&event, 0, sizeof(event));
+ event.rx_mgmt.frame = buf;
+ event.rx_mgmt.frame_len = len;
+ 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;
}
}
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);
return;
}
- memset(&hfi, 0, sizeof(hfi));
-
while (1) {
ret = ieee80211_radiotap_iterator_next(&iter);
if (ret == -ENOENT)
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;
}
}
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);
#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),
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
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;
+ }
}
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));
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;
"failed: %d (%s)",
(unsigned long) len, errno, strerror(errno));
}
- free(hdr);
+ os_free(hdr);
return res;
}
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;
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);
/*
return -ENOBUFS;
}
-#endif /* CONFIG_AP || HOSTAPD */
-
-#ifdef CONFIG_AP
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;
return 0;
}
-#endif /* CONFIG_AP */
static int nl80211_leave_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;
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;
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;
-#ifdef CONFIG_AP
if (params->mode == IEEE80211_MODE_AP)
return wpa_driver_nl80211_ap(drv, params);
-#endif /* CONFIG_AP */
if (params->mode == IEEE80211_MODE_IBSS)
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);
}
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);
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;
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:
}
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 */
}
* 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 defined(CONFIG_AP) || defined(HOSTAPD)
if (!ret && nlmode == NL80211_IFTYPE_AP) {
/* Setup additional AP mode functionality if needed */
if (drv->monitor_ifidx < 0 &&
} else if (!ret && nlmode != NL80211_IFTYPE_AP) {
/* Remove additional AP mode functionality */
nl80211_remove_monitor_interface(drv);
+ bss->beacon_set = 0;
}
-#endif /* CONFIG_AP || HOSTAPD */
+
+ if (ret)
+ wpa_printf(MSG_DEBUG, "nl80211: Interface mode change to %d "
+ "from %d failed", nlmode, drv->nlmode);
return ret;
}
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));
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;
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));
#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;
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;
"interfaces");
wpa_printf(MSG_ERROR, "Ignoring EAPOL on interface %d", ifidx);
return;
- }
+ } else if (!old)
+ os_memcpy(drv->if_indices, drv->default_if_indices,
+ sizeof(drv->default_if_indices));
drv->if_indices[drv->num_if_indices] = ifidx;
drv->num_if_indices++;
}
static 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();
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;
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;
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;
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();
* 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:
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));
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:
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;
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)
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();
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:
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)
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);
+ }
}
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);
}
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));
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));
}
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));
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));
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);
genl_family_put(drv->nl80211);
nl_cache_free(drv->nl_cache);
- nl_handle_destroy(drv->nl_handle);
+ nl80211_handle_destroy(drv->nl_handle);
nl_cb_put(drv->nl_cb);
os_free(drv);
{
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,
- const char *ifname, const u8 *addr)
+static int wpa_driver_nl80211_if_add(void *priv, enum wpa_driver_if_type type,
+ const char *ifname, const u8 *addr,
+ void *bss_ctx, void **drv_priv,
+ char *force_ifname, u8 *if_addr)
{
- 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 */
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 */
}
+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 nl80211_send_frame_cmd(struct wpa_driver_nl80211_data *drv,
+ unsigned int freq, const u8 *buf,
+ size_t buf_len, u64 *cookie_out)
+{
+ struct nl_msg *msg;
+ u64 cookie;
+ int ret = -1;
+
+ msg = nlmsg_alloc();
+ if (!msg)
+ return -1;
+
+ genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, 0,
+ NL80211_CMD_FRAME, 0);
+
+ NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex);
+ NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_FREQ, freq);
+ NLA_PUT(msg, NL80211_ATTR_FRAME, buf_len, buf);
+
+ cookie = 0;
+ ret = send_and_recv_msgs(drv, msg, cookie_handler, &cookie);
+ msg = NULL;
+ if (ret) {
+ wpa_printf(MSG_DEBUG, "nl80211: Frame command failed: ret=%d "
+ "(%s)", ret, strerror(-ret));
+ goto nla_put_failure;
+ }
+ wpa_printf(MSG_DEBUG, "nl80211: Frame TX command accepted; "
+ "cookie 0x%llx", (long long unsigned int) cookie);
+
+ if (cookie_out)
+ *cookie_out = cookie;
+
+nla_put_failure:
+ nlmsg_free(msg);
+ return ret;
+}
+
+
+static int wpa_driver_nl80211_send_action(void *priv, unsigned int freq,
+ const u8 *dst, const u8 *src,
+ const u8 *bssid,
+ const u8 *data, size_t data_len)
+{
+ struct i802_bss *bss = priv;
+ struct wpa_driver_nl80211_data *drv = bss->drv;
+ int ret = -1;
+ u8 *buf;
+ struct ieee80211_hdr *hdr;
+
+ wpa_printf(MSG_DEBUG, "nl80211: Send Action frame (ifindex=%d)",
+ drv->ifindex);
+
+ buf = os_zalloc(24 + data_len);
+ if (buf == NULL)
+ return ret;
+ os_memcpy(buf + 24, data, data_len);
+ hdr = (struct ieee80211_hdr *) buf;
+ hdr->frame_control =
+ IEEE80211_FC(WLAN_FC_TYPE_MGMT, WLAN_FC_STYPE_ACTION);
+ os_memcpy(hdr->addr1, dst, ETH_ALEN);
+ os_memcpy(hdr->addr2, src, ETH_ALEN);
+ os_memcpy(hdr->addr3, bssid, ETH_ALEN);
+
+ if (drv->nlmode == NL80211_IFTYPE_AP)
+ ret = wpa_driver_nl80211_send_mlme(priv, buf, 24 + data_len);
+ else
+ ret = nl80211_send_frame_cmd(drv, freq, buf, 24 + data_len,
+ &drv->send_action_cookie);
+
+ os_free(buf);
+ return ret;
+}
+
+
+static int wpa_driver_nl80211_remain_on_channel(void *priv, unsigned int freq,
+ unsigned int duration)
+{
+ struct 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 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;
+ }
+
+ if (!report) {
+ if (drv->nl_handle_preq) {
+ eloop_unregister_read_sock(
+ nl_socket_get_fd(drv->nl_handle_preq));
+ nl_cache_free(drv->nl_cache_preq);
+ nl80211_handle_destroy(drv->nl_handle_preq);
+ drv->nl_handle_preq = NULL;
+ }
+ return 0;
+ }
+
+ if (drv->nl_handle_preq) {
+ wpa_printf(MSG_DEBUG, "nl80211: Probe Request reporting "
+ "already on!");
+ return 0;
+ }
+
+ drv->nl_handle_preq = nl80211_handle_alloc(drv->nl_cb);
+ if (drv->nl_handle_preq == NULL) {
+ wpa_printf(MSG_ERROR, "nl80211: Failed to allocate "
+ "netlink callbacks (preq)");
+ goto out_err1;
+ }
+
+ if (genl_connect(drv->nl_handle_preq)) {
+ wpa_printf(MSG_ERROR, "nl80211: Failed to connect to "
+ "generic netlink (preq)");
+ goto out_err2;
+ return -1;
+ }
+
+#ifdef CONFIG_LIBNL20
+ if (genl_ctrl_alloc_cache(drv->nl_handle_preq,
+ &drv->nl_cache_preq) < 0) {
+ wpa_printf(MSG_ERROR, "nl80211: Failed to allocate generic "
+ "netlink cache (preq)");
+ goto out_err2;
+ }
+#else /* CONFIG_LIBNL20 */
+ drv->nl_cache_preq = genl_ctrl_alloc_cache(drv->nl_handle_preq);
+ if (drv->nl_cache_preq == NULL) {
+ wpa_printf(MSG_ERROR, "nl80211: Failed to allocate generic "
+ "netlink cache (preq)");
+ goto out_err2;
+ }
+#endif /* CONFIG_LIBNL20 */
+
+ if (nl80211_register_frame(drv, drv->nl_handle_preq,
+ (WLAN_FC_TYPE_MGMT << 2) |
+ (WLAN_FC_STYPE_PROBE_REQ << 4),
+ NULL, 0) < 0) {
+ goto out_err3;
+ }
+
+ eloop_register_read_sock(nl_socket_get_fd(drv->nl_handle_preq),
+ wpa_driver_nl80211_event_receive, drv,
+ drv->nl_handle_preq);
+
+ return 0;
+
+ out_err3:
+ nl_cache_free(drv->nl_cache_preq);
+ out_err2:
+ nl80211_handle_destroy(drv->nl_handle_preq);
+ drv->nl_handle_preq = NULL;
+ out_err1:
+ return -1;
+}
+
+
+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);
+}
+
+
+static int nl80211_set_intra_bss(void *priv, int enabled)
+{
+ struct i802_bss *bss = priv;
+ struct wpa_driver_nl80211_data *drv = bss->drv;
+ struct nl_msg *msg;
+
+ msg = nlmsg_alloc();
+ if (!msg)
+ return -ENOMEM;
+
+ genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, 0,
+ NL80211_CMD_SET_BSS, 0);
+
+ NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, if_nametoindex(bss->ifname));
+ NLA_PUT_U8(msg, NL80211_ATTR_AP_ISOLATE, !enabled);
+
+ return send_and_recv_msgs(drv, msg, NULL, NULL);
+ nla_put_failure:
+ return -ENOBUFS;
+}
+
+
const struct wpa_driver_ops wpa_driver_nl80211_ops = {
.name = "nl80211",
.desc = "Linux nl80211/cfg80211",
.set_beacon = wpa_driver_nl80211_set_beacon,
.if_add = wpa_driver_nl80211_if_add,
.if_remove = wpa_driver_nl80211_if_remove,
-#if defined(CONFIG_AP) || defined(HOSTAPD)
.send_mlme = wpa_driver_nl80211_send_mlme,
.get_hw_feature_data = wpa_driver_nl80211_get_hw_feature_data,
.sta_add = wpa_driver_nl80211_sta_add,
.sta_remove = wpa_driver_nl80211_sta_remove,
.hapd_send_eapol = wpa_driver_nl80211_hapd_send_eapol,
.sta_set_flags = wpa_driver_nl80211_sta_set_flags,
-#endif /* CONFIG_AP || HOSTAPD */
#ifdef HOSTAPD
.hapd_init = i802_init,
.hapd_deinit = i802_deinit,
.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,
.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,
+ .set_intra_bss = nl80211_set_intra_bss,
};