/*
* Driver interaction with Linux nl80211/cfg80211
- * Copyright (c) 2002-2010, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2002-2012, 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>
#include <linux/rtnetlink.h>
#include <netpacket/packet.h>
#include <linux/filter.h>
+#include <linux/errqueue.h>
#include "nl80211_copy.h"
#include "common.h"
#include "eloop.h"
#include "utils/list.h"
#include "common/ieee802_11_defs.h"
+#include "common/ieee802_11_common.h"
#include "l2_packet/l2_packet.h"
#include "netlink.h"
#include "linux_ioctl.h"
#include "rfkill.h"
#include "driver.h"
+#ifndef SO_WIFI_STATUS
+# if defined(__sparc__)
+# define SO_WIFI_STATUS 0x0025
+# elif defined(__parisc__)
+# define SO_WIFI_STATUS 0x4022
+# else
+# define SO_WIFI_STATUS 41
+# endif
+
+# define SCM_WIFI_STATUS SO_WIFI_STATUS
+#endif
+
+#ifndef SO_EE_ORIGIN_TXSTATUS
+#define SO_EE_ORIGIN_TXSTATUS 4
+#endif
+
+#ifndef PACKET_TX_TIMESTAMP
+#define PACKET_TX_TIMESTAMP 16
+#endif
+
+#ifdef ANDROID
+#include "android_drv.h"
+#endif /* ANDROID */
#ifdef CONFIG_LIBNL20
/* libnl 2.0 compatibility code */
#define nl_handle nl_sock
#endif /* CONFIG_LIBNL20 */
+static struct nl_handle * nl_create_handle(struct nl_cb *cb, const char *dbg)
+{
+ struct nl_handle *handle;
+
+ handle = nl80211_handle_alloc(cb);
+ if (handle == NULL) {
+ wpa_printf(MSG_ERROR, "nl80211: Failed to allocate netlink "
+ "callbacks (%s)", dbg);
+ return NULL;
+ }
+
+ if (genl_connect(handle)) {
+ wpa_printf(MSG_ERROR, "nl80211: Failed to connect to generic "
+ "netlink (%s)", dbg);
+ nl80211_handle_destroy(handle);
+ return NULL;
+ }
+
+ return handle;
+}
+
+
+static void nl_destroy_handles(struct nl_handle **handle)
+{
+ if (*handle == NULL)
+ return;
+ nl80211_handle_destroy(*handle);
+ *handle = NULL;
+}
+
+
#ifndef IFF_LOWER_UP
#define IFF_LOWER_UP 0x10000 /* driver signals L1 up */
#endif
struct nl80211_global {
struct dl_list interfaces;
int if_add_ifindex;
+ struct netlink_data *netlink;
+ struct nl_cb *nl_cb;
+ struct nl_handle *nl;
+ int nl80211_id;
+ int ioctl_sock; /* socket for ioctl() use */
+
+ struct nl_handle *nl_event;
+};
+
+struct nl80211_wiphy_data {
+ struct dl_list list;
+ struct dl_list bsss;
+ struct dl_list drvs;
+
+ struct nl_handle *nl_beacons;
+ struct nl_cb *nl_cb;
+
+ int wiphy_idx;
};
+static void nl80211_global_deinit(void *priv);
+static void wpa_driver_nl80211_deinit(void *priv);
+
struct i802_bss {
struct wpa_driver_nl80211_data *drv;
struct i802_bss *next;
unsigned int beacon_set:1;
unsigned int added_if_into_bridge:1;
unsigned int added_bridge:1;
+
+ u8 addr[ETH_ALEN];
+
+ int freq;
+
+ struct nl_handle *nl_preq, *nl_mgmt;
+ struct nl_cb *nl_cb;
+
+ struct nl80211_wiphy_data *wiphy_data;
+ struct dl_list wiphy_list;
};
struct wpa_driver_nl80211_data {
struct nl80211_global *global;
struct dl_list list;
- u8 addr[ETH_ALEN];
+ struct dl_list wiphy_list;
char phyname[32];
void *ctx;
- struct netlink_data *netlink;
- int ioctl_sock; /* socket for ioctl() use */
int ifindex;
int if_removed;
int if_disabled;
int scan_complete_events;
- 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;
u8 auth_bssid[ETH_ALEN];
u8 bssid[ETH_ALEN];
int monitor_sock;
int monitor_ifidx;
- int no_monitor_iface_capab;
- int disable_11b_rates;
+ int monitor_refcount;
+ unsigned int disabled_11b_rates:1;
unsigned int pending_remain_on_chan:1;
+ unsigned int in_interface_list:1;
+ unsigned int device_ap_sme:1;
+ unsigned int poll_command_supported:1;
+ unsigned int data_tx_status:1;
+ unsigned int scan_for_auth:1;
+ unsigned int retry_auth:1;
+ unsigned int use_monitor:1;
u64 remain_on_chan_cookie;
u64 send_action_cookie;
unsigned int last_mgmt_freq;
- unsigned int ap_oper_freq;
struct wpa_driver_scan_filter *filter_ssids;
size_t num_filter_ssids;
struct i802_bss first_bss;
-#ifdef CONFIG_AP
- struct l2_packet_data *l2;
-#endif /* CONFIG_AP */
+ int eapol_tx_sock;
#ifdef HOSTAPD
int eapol_sock; /* socket for EAPOL frames */
int last_freq;
int last_freq_ht;
#endif /* HOSTAPD */
+
+ /* From failed authentication command */
+ int auth_freq;
+ u8 auth_bssid_[ETH_ALEN];
+ u8 auth_ssid[32];
+ size_t auth_ssid_len;
+ int auth_alg;
+ u8 *auth_ie;
+ size_t auth_ie_len;
+ u8 auth_wep_key[4][16];
+ size_t auth_wep_key_len[4];
+ int auth_wep_tx_keyidx;
+ int auth_local_state_change;
+ int auth_p2p;
};
int local_state_change);
static void nl80211_remove_monitor_interface(
struct wpa_driver_nl80211_data *drv);
-static int nl80211_send_frame_cmd(struct wpa_driver_nl80211_data *drv,
+static int nl80211_send_frame_cmd(struct i802_bss *bss,
unsigned int freq, unsigned int wait,
- const u8 *buf, size_t buf_len, u64 *cookie);
+ const u8 *buf, size_t buf_len, u64 *cookie,
+ int no_cck, int no_ack, int offchanok);
static int wpa_driver_nl80211_probe_req_report(void *priv, int report);
+#ifdef ANDROID
+static int android_pno_start(struct i802_bss *bss,
+ struct wpa_driver_scan_params *params);
+static int android_pno_stop(struct i802_bss *bss);
+#endif /* ANDROID */
#ifdef HOSTAPD
static void add_ifidx(struct wpa_driver_nl80211_data *drv, int ifidx);
enum wpa_driver_if_type type,
const char *ifname);
#else /* HOSTAPD */
-static int have_ifidx(struct wpa_driver_nl80211_data *drv, int ifidx)
+static inline void add_ifidx(struct wpa_driver_nl80211_data *drv, int ifidx)
+{
+}
+
+static inline void del_ifidx(struct wpa_driver_nl80211_data *drv, int ifidx)
+{
+}
+
+static inline int have_ifidx(struct wpa_driver_nl80211_data *drv, int ifidx)
{
return 0;
}
int ifindex, int disabled);
static int nl80211_leave_ibss(struct wpa_driver_nl80211_data *drv);
+static int wpa_driver_nl80211_authenticate_retry(
+ struct wpa_driver_nl80211_data *drv);
static int is_ap_interface(enum nl80211_iftype nlmode)
}
+static int is_p2p_interface(enum nl80211_iftype nlmode)
+{
+ return (nlmode == NL80211_IFTYPE_P2P_CLIENT ||
+ nlmode == NL80211_IFTYPE_P2P_GO);
+}
+
+
struct nl80211_bss_info_arg {
struct wpa_driver_nl80211_data *drv;
struct wpa_scan_results *res;
}
-static int send_and_recv(struct wpa_driver_nl80211_data *drv,
+static int send_and_recv(struct nl80211_global *global,
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;
- cb = nl_cb_clone(drv->nl_cb);
+ cb = nl_cb_clone(global->nl_cb);
if (!cb)
goto out;
}
+static int send_and_recv_msgs_global(struct nl80211_global *global,
+ struct nl_msg *msg,
+ int (*valid_handler)(struct nl_msg *, void *),
+ void *valid_data)
+{
+ return send_and_recv(global, global->nl, msg, valid_handler,
+ valid_data);
+}
+
+
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);
+ return send_and_recv(drv->global, drv->global->nl, msg,
+ valid_handler, valid_data);
}
}
-static int nl_get_multicast_id(struct wpa_driver_nl80211_data *drv,
+static int nl_get_multicast_id(struct nl80211_global *global,
const char *family, const char *group)
{
struct nl_msg *msg;
msg = nlmsg_alloc();
if (!msg)
return -ENOMEM;
- genlmsg_put(msg, 0, 0, genl_ctrl_resolve(drv->nl_handle, "nlctrl"),
+ genlmsg_put(msg, 0, 0, genl_ctrl_resolve(global->nl, "nlctrl"),
0, 0, CTRL_CMD_GETFAMILY, 0);
NLA_PUT_STRING(msg, CTRL_ATTR_FAMILY_NAME, family);
- ret = send_and_recv_msgs(drv, msg, family_handler, &res);
+ ret = send_and_recv_msgs_global(global, msg, family_handler, &res);
msg = NULL;
if (ret == 0)
ret = res.id;
}
+static void * nl80211_cmd(struct wpa_driver_nl80211_data *drv,
+ struct nl_msg *msg, int flags, uint8_t cmd)
+{
+ return genlmsg_put(msg, 0, 0, drv->global->nl80211_id,
+ 0, flags, cmd, 0);
+}
+
+
+struct wiphy_idx_data {
+ int wiphy_idx;
+};
+
+
+static int netdev_info_handler(struct nl_msg *msg, void *arg)
+{
+ struct nlattr *tb[NL80211_ATTR_MAX + 1];
+ struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
+ struct wiphy_idx_data *info = arg;
+
+ nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
+ genlmsg_attrlen(gnlh, 0), NULL);
+
+ if (tb[NL80211_ATTR_WIPHY])
+ info->wiphy_idx = nla_get_u32(tb[NL80211_ATTR_WIPHY]);
+
+ return NL_SKIP;
+}
+
+
+static int nl80211_get_wiphy_index(struct i802_bss *bss)
+{
+ struct nl_msg *msg;
+ struct wiphy_idx_data data = {
+ .wiphy_idx = -1,
+ };
+
+ msg = nlmsg_alloc();
+ if (!msg)
+ return -1;
+
+ nl80211_cmd(bss->drv, msg, 0, NL80211_CMD_GET_INTERFACE);
+
+ NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, bss->ifindex);
+
+ if (send_and_recv_msgs(bss->drv, msg, netdev_info_handler, &data) == 0)
+ return data.wiphy_idx;
+ msg = NULL;
+nla_put_failure:
+ nlmsg_free(msg);
+ return -1;
+}
+
+
+static int nl80211_register_beacons(struct wpa_driver_nl80211_data *drv,
+ struct nl80211_wiphy_data *w)
+{
+ struct nl_msg *msg;
+ int ret = -1;
+
+ msg = nlmsg_alloc();
+ if (!msg)
+ return -1;
+
+ nl80211_cmd(drv, msg, 0, NL80211_CMD_REGISTER_BEACONS);
+
+ NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, w->wiphy_idx);
+
+ ret = send_and_recv(drv->global, w->nl_beacons, msg, NULL, NULL);
+ msg = NULL;
+ if (ret) {
+ wpa_printf(MSG_DEBUG, "nl80211: Register beacons command "
+ "failed: ret=%d (%s)",
+ ret, strerror(-ret));
+ goto nla_put_failure;
+ }
+ ret = 0;
+nla_put_failure:
+ nlmsg_free(msg);
+ return ret;
+}
+
+
+static void nl80211_recv_beacons(int sock, void *eloop_ctx, void *handle)
+{
+ struct nl80211_wiphy_data *w = eloop_ctx;
+
+ wpa_printf(MSG_EXCESSIVE, "nl80211: Beacon event message available");
+
+ nl_recvmsgs(handle, w->nl_cb);
+}
+
+
+static int process_beacon_event(struct nl_msg *msg, void *arg)
+{
+ struct nl80211_wiphy_data *w = arg;
+ struct wpa_driver_nl80211_data *drv;
+ struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
+ struct nlattr *tb[NL80211_ATTR_MAX + 1];
+ union wpa_event_data event;
+
+ nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
+ genlmsg_attrlen(gnlh, 0), NULL);
+
+ if (gnlh->cmd != NL80211_CMD_FRAME) {
+ wpa_printf(MSG_DEBUG, "nl80211: Unexpected beacon event? (%d)",
+ gnlh->cmd);
+ return NL_SKIP;
+ }
+
+ if (!tb[NL80211_ATTR_FRAME])
+ return NL_SKIP;
+
+ dl_list_for_each(drv, &w->drvs, struct wpa_driver_nl80211_data,
+ wiphy_list) {
+ os_memset(&event, 0, sizeof(event));
+ event.rx_mgmt.frame = nla_data(tb[NL80211_ATTR_FRAME]);
+ event.rx_mgmt.frame_len = nla_len(tb[NL80211_ATTR_FRAME]);
+ wpa_supplicant_event(drv->ctx, EVENT_RX_MGMT, &event);
+ }
+
+ return NL_SKIP;
+}
+
+
+static struct nl80211_wiphy_data *
+nl80211_get_wiphy_data_ap(struct i802_bss *bss)
+{
+ static DEFINE_DL_LIST(nl80211_wiphys);
+ struct nl80211_wiphy_data *w;
+ int wiphy_idx, found = 0;
+ struct i802_bss *tmp_bss;
+
+ if (bss->wiphy_data != NULL)
+ return bss->wiphy_data;
+
+ wiphy_idx = nl80211_get_wiphy_index(bss);
+
+ dl_list_for_each(w, &nl80211_wiphys, struct nl80211_wiphy_data, list) {
+ if (w->wiphy_idx == wiphy_idx)
+ goto add;
+ }
+
+ /* alloc new one */
+ w = os_zalloc(sizeof(*w));
+ if (w == NULL)
+ return NULL;
+ w->wiphy_idx = wiphy_idx;
+ dl_list_init(&w->bsss);
+ dl_list_init(&w->drvs);
+
+ w->nl_cb = nl_cb_alloc(NL_CB_DEFAULT);
+ if (!w->nl_cb) {
+ os_free(w);
+ return NULL;
+ }
+ nl_cb_set(w->nl_cb, NL_CB_SEQ_CHECK, NL_CB_CUSTOM, no_seq_check, NULL);
+ nl_cb_set(w->nl_cb, NL_CB_VALID, NL_CB_CUSTOM, process_beacon_event,
+ w);
+
+ w->nl_beacons = nl_create_handle(bss->drv->global->nl_cb,
+ "wiphy beacons");
+ if (w->nl_beacons == NULL) {
+ os_free(w);
+ return NULL;
+ }
+
+ if (nl80211_register_beacons(bss->drv, w)) {
+ nl_destroy_handles(&w->nl_beacons);
+ os_free(w);
+ return NULL;
+ }
+
+ eloop_register_read_sock(nl_socket_get_fd(w->nl_beacons),
+ nl80211_recv_beacons, w, w->nl_beacons);
+
+ dl_list_add(&nl80211_wiphys, &w->list);
+
+add:
+ /* drv entry for this bss already there? */
+ dl_list_for_each(tmp_bss, &w->bsss, struct i802_bss, wiphy_list) {
+ if (tmp_bss->drv == bss->drv) {
+ found = 1;
+ break;
+ }
+ }
+ /* if not add it */
+ if (!found)
+ dl_list_add(&w->drvs, &bss->drv->wiphy_list);
+
+ dl_list_add(&w->bsss, &bss->wiphy_list);
+ bss->wiphy_data = w;
+ return w;
+}
+
+
+static void nl80211_put_wiphy_data_ap(struct i802_bss *bss)
+{
+ struct nl80211_wiphy_data *w = bss->wiphy_data;
+ struct i802_bss *tmp_bss;
+ int found = 0;
+
+ if (w == NULL)
+ return;
+ bss->wiphy_data = NULL;
+ dl_list_del(&bss->wiphy_list);
+
+ /* still any for this drv present? */
+ dl_list_for_each(tmp_bss, &w->bsss, struct i802_bss, wiphy_list) {
+ if (tmp_bss->drv == bss->drv) {
+ found = 1;
+ break;
+ }
+ }
+ /* if not remove it */
+ if (!found)
+ dl_list_del(&bss->drv->wiphy_list);
+
+ if (!dl_list_empty(&w->bsss))
+ return;
+
+ eloop_unregister_read_sock(nl_socket_get_fd(w->nl_beacons));
+
+ nl_cb_put(w->nl_cb);
+ nl_destroy_handles(&w->nl_beacons);
+ dl_list_del(&w->list);
+ os_free(w);
+}
+
+
static int wpa_driver_nl80211_get_bssid(void *priv, u8 *bssid)
{
struct i802_bss *bss = priv;
}
+static struct wpa_driver_nl80211_data *
+nl80211_find_drv(struct nl80211_global *global, int idx, u8 *buf, size_t len)
+{
+ struct wpa_driver_nl80211_data *drv;
+ dl_list_for_each(drv, &global->interfaces,
+ struct wpa_driver_nl80211_data, list) {
+ if (wpa_driver_nl80211_own_ifindex(drv, idx, buf, len) ||
+ have_ifidx(drv, idx))
+ return drv;
+ }
+ return NULL;
+}
+
+
static void wpa_driver_nl80211_event_rtm_newlink(void *ctx,
struct ifinfomsg *ifi,
u8 *buf, size_t len)
{
- struct wpa_driver_nl80211_data *drv = ctx;
+ struct nl80211_global *global = ctx;
+ struct wpa_driver_nl80211_data *drv;
int attrlen, rta_len;
struct rtattr *attr;
u32 brid = 0;
+ char namebuf[IFNAMSIZ];
- if (!wpa_driver_nl80211_own_ifindex(drv, ifi->ifi_index, buf, len) &&
- !have_ifidx(drv, ifi->ifi_index)) {
+ drv = nl80211_find_drv(global, ifi->ifi_index, buf, len);
+ if (!drv) {
wpa_printf(MSG_DEBUG, "nl80211: Ignore event for foreign "
"ifindex %d", ifi->ifi_index);
return;
(ifi->ifi_flags & IFF_DORMANT) ? "[DORMANT]" : "");
if (!drv->if_disabled && !(ifi->ifi_flags & IFF_UP)) {
+ if (if_indextoname(ifi->ifi_index, namebuf) &&
+ linux_iface_up(drv->global->ioctl_sock,
+ drv->first_bss.ifname) > 0) {
+ wpa_printf(MSG_DEBUG, "nl80211: Ignore interface down "
+ "event since interface %s is up", namebuf);
+ return;
+ }
wpa_printf(MSG_DEBUG, "nl80211: Interface down");
if (drv->ignore_if_down_event) {
wpa_printf(MSG_DEBUG, "nl80211: Ignore interface down "
}
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);
+ if (if_indextoname(ifi->ifi_index, namebuf) &&
+ linux_iface_up(drv->global->ioctl_sock,
+ drv->first_bss.ifname) == 0) {
+ wpa_printf(MSG_DEBUG, "nl80211: Ignore interface up "
+ "event since interface %s is down",
+ namebuf);
+ } else {
+ wpa_printf(MSG_DEBUG, "nl80211: Interface up");
+ drv->if_disabled = 0;
+ wpa_supplicant_event(drv->ctx, EVENT_INTERFACE_ENABLED,
+ NULL);
+ }
}
/*
if (drv->operstate == 1 &&
(ifi->ifi_flags & (IFF_LOWER_UP | IFF_DORMANT)) == IFF_LOWER_UP &&
!(ifi->ifi_flags & IFF_RUNNING))
- netlink_send_oper_ifla(drv->netlink, drv->ifindex,
+ netlink_send_oper_ifla(drv->global->netlink, drv->ifindex,
-1, IF_OPER_UP);
attrlen = len;
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 */
}
struct ifinfomsg *ifi,
u8 *buf, size_t len)
{
- struct wpa_driver_nl80211_data *drv = ctx;
+ struct nl80211_global *global = ctx;
+ struct wpa_driver_nl80211_data *drv;
int attrlen, rta_len;
struct rtattr *attr;
u32 brid = 0;
+ drv = nl80211_find_drv(global, ifi->ifi_index, buf, len);
+ if (!drv) {
+ wpa_printf(MSG_DEBUG, "nl80211: Ignore dellink event for "
+ "foreign ifindex %d", ifi->ifi_index);
+ return;
+ }
+
attrlen = len;
attr = (struct rtattr *) buf;
attr = RTA_NEXT(attr, attrlen);
}
-#ifdef HOSTAPD
if (ifi->ifi_family == AF_BRIDGE && brid) {
/* device has been removed from bridge */
char namebuf[IFNAMSIZ];
"%s", brid, namebuf);
del_ifidx(drv, brid);
}
-#endif /* HOSTAPD */
}
if (!msg)
goto nla_put_failure;
- genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, NLM_F_DUMP,
- NL80211_CMD_GET_SCAN, 0);
+ nl80211_cmd(drv, msg, NLM_F_DUMP, NL80211_CMD_GET_SCAN);
NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex);
arg.drv = drv;
static void mlme_event_disconnect(struct wpa_driver_nl80211_data *drv,
- struct nlattr *reason, struct nlattr *addr)
+ struct nlattr *reason, struct nlattr *addr,
+ struct nlattr *by_ap)
{
union wpa_event_data data;
os_memset(&data, 0, sizeof(data));
if (reason)
data.disassoc_info.reason_code = nla_get_u16(reason);
+ data.disassoc_info.locally_generated = by_ap == NULL;
wpa_supplicant_event(drv->ctx, EVENT_DISASSOC, &data);
}
}
-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)
+static void mlme_event_mgmt_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;
+ if (!is_ap_interface(drv->nlmode)) {
+ u64 cookie_val;
- 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;
+ 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);
reason_code = le_to_host16(mgmt->u.deauth.reason_code);
if (type == EVENT_DISASSOC) {
+ event.disassoc_info.locally_generated =
+ !os_memcmp(mgmt->sa, drv->first_bss.addr, ETH_ALEN);
event.disassoc_info.addr = bssid;
event.disassoc_info.reason_code = reason_code;
if (frame + len > mgmt->u.disassoc.variable) {
mgmt->u.disassoc.variable;
}
} else {
+ event.deauth_info.locally_generated =
+ !os_memcmp(mgmt->sa, drv->first_bss.addr, ETH_ALEN);
event.deauth_info.addr = bssid;
event.deauth_info.reason_code = reason_code;
if (frame + len > mgmt->u.deauth.variable) {
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);
+ mlme_event_mgmt_tx_status(drv, cookie, nla_data(frame),
+ nla_len(frame), ack);
break;
case NL80211_CMD_UNPROT_DEAUTHENTICATE:
mlme_event_unprot_disconnect(drv, EVENT_UNPROT_DEAUTH,
int freqs[MAX_REPORT_FREQS];
int num_freqs = 0;
+ if (drv->scan_for_auth) {
+ drv->scan_for_auth = 0;
+ wpa_printf(MSG_DEBUG, "nl80211: Scan results for missing "
+ "cfg80211 BSS entry");
+ wpa_driver_nl80211_authenticate_retry(drv);
+ return;
+ }
+
os_memset(&event, 0, sizeof(event));
info = &event.scan_info;
info->aborted = aborted;
if (!msg)
return -ENOMEM;
- genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0,
- 0, NL80211_CMD_GET_STATION, 0);
+ nl80211_cmd(drv, msg, 0, NL80211_CMD_GET_STATION);
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:
+ nlmsg_free(msg);
return -ENOBUFS;
}
if (!msg)
return -ENOMEM;
- genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0,
- NLM_F_DUMP, NL80211_CMD_GET_SURVEY, 0);
+ nl80211_cmd(drv, msg, NLM_F_DUMP, NL80211_CMD_GET_SURVEY);
NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex);
return send_and_recv_msgs(drv, msg, get_link_noise, sig_change);
nla_put_failure:
+ nlmsg_free(msg);
return -ENOBUFS;
}
-static void nl80211_cqm_event(struct wpa_driver_nl80211_data *drv,
- struct nlattr *tb[])
+static int get_noise_for_scan_results(struct nl_msg *msg, void *arg)
{
- 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 },
- [NL80211_ATTR_CQM_PKT_LOSS_EVENT] = { .type = NLA_U32 },
+ 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 nlattr *cqm[NL80211_ATTR_CQM_MAX + 1];
- enum nl80211_cqm_rssi_threshold_event event;
- union wpa_event_data ed;
- struct wpa_signal_info sig;
- int res;
+ struct wpa_scan_results *scan_results = arg;
+ struct wpa_scan_res *scan_res;
+ size_t i;
- 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;
- }
+ 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_NOISE])
+ return NL_SKIP;
+
+ if (!sinfo[NL80211_SURVEY_INFO_FREQUENCY])
+ return NL_SKIP;
+
+ for (i = 0; i < scan_results->num; ++i) {
+ scan_res = scan_results->res[i];
+ if (!scan_res)
+ continue;
+ if ((int) nla_get_u32(sinfo[NL80211_SURVEY_INFO_FREQUENCY]) !=
+ scan_res->freq)
+ continue;
+ if (!(scan_res->flags & WPA_SCAN_NOISE_INVALID))
+ continue;
+ scan_res->noise = (s8)
+ nla_get_u8(sinfo[NL80211_SURVEY_INFO_NOISE]);
+ scan_res->flags &= ~WPA_SCAN_NOISE_INVALID;
+ }
+
+ return NL_SKIP;
+}
+
+
+static int nl80211_get_noise_for_scan_results(
+ struct wpa_driver_nl80211_data *drv,
+ struct wpa_scan_results *scan_res)
+{
+ struct nl_msg *msg;
+
+ msg = nlmsg_alloc();
+ if (!msg)
+ return -ENOMEM;
+
+ nl80211_cmd(drv, msg, NLM_F_DUMP, NL80211_CMD_GET_SURVEY);
+
+ NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex);
+
+ return send_and_recv_msgs(drv, msg, get_noise_for_scan_results,
+ scan_res);
+ nla_put_failure:
+ nlmsg_free(msg);
+ 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 },
+ [NL80211_ATTR_CQM_PKT_LOSS_EVENT] = { .type = NLA_U32 },
+ };
+ struct nlattr *cqm[NL80211_ATTR_CQM_MAX + 1];
+ enum nl80211_cqm_rssi_threshold_event event;
+ union wpa_event_data ed;
+ struct wpa_signal_info 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;
+ }
os_memset(&ed, 0, sizeof(ed));
addr = nla_data(tb[NL80211_ATTR_MAC]);
wpa_printf(MSG_DEBUG, "nl80211: New station " MACSTR, MAC2STR(addr));
- if (is_ap_interface(drv->nlmode) && drv->no_monitor_iface_capab) {
+ if (is_ap_interface(drv->nlmode) && drv->device_ap_sme) {
u8 *ies = NULL;
size_t ies_len = 0;
if (tb[NL80211_ATTR_IE]) {
wpa_printf(MSG_DEBUG, "nl80211: Delete station " MACSTR,
MAC2STR(addr));
- if (is_ap_interface(drv->nlmode) && drv->no_monitor_iface_capab) {
+ if (is_ap_interface(drv->nlmode) && drv->device_ap_sme) {
drv_event_disassoc(drv->ctx, addr);
return;
}
}
-static int process_event(struct nl_msg *msg, void *arg)
+static void nl80211_client_probe_event(struct wpa_driver_nl80211_data *drv,
+ struct nlattr **tb)
{
- 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_MAC] || !tb[NL80211_ATTR_ACK])
+ return;
- 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;
- }
- }
+ os_memset(&data, 0, sizeof(data));
+ os_memcpy(data.client_poll.addr,
+ nla_data(tb[NL80211_ATTR_MAC]), ETH_ALEN);
+
+ wpa_supplicant_event(drv->ctx, EVENT_DRIVER_CLIENT_POLL_OK, &data);
+}
+
+
+static void nl80211_spurious_frame(struct i802_bss *bss, struct nlattr **tb,
+ int wds)
+{
+ struct wpa_driver_nl80211_data *drv = bss->drv;
+ union wpa_event_data event;
+
+ if (!tb[NL80211_ATTR_MAC])
+ return;
+ os_memset(&event, 0, sizeof(event));
+ event.rx_from_unknown.bssid = bss->addr;
+ event.rx_from_unknown.addr = nla_data(tb[NL80211_ATTR_MAC]);
+ event.rx_from_unknown.wds = wds;
+
+ wpa_supplicant_event(drv->ctx, EVENT_RX_FROM_UNKNOWN, &event);
+}
+
+
+static void do_process_drv_event(struct wpa_driver_nl80211_data *drv,
+ int cmd, struct nlattr **tb)
+{
if (drv->ap_scan_as_station != NL80211_IFTYPE_UNSPECIFIED &&
- (gnlh->cmd == NL80211_CMD_NEW_SCAN_RESULTS ||
- gnlh->cmd == NL80211_CMD_SCAN_ABORTED)) {
+ (cmd == NL80211_CMD_NEW_SCAN_RESULTS ||
+ cmd == NL80211_CMD_SCAN_ABORTED)) {
wpa_driver_nl80211_set_mode(&drv->first_bss,
drv->ap_scan_as_station);
drv->ap_scan_as_station = NL80211_IFTYPE_UNSPECIFIED;
}
- switch (gnlh->cmd) {
+ switch (cmd) {
case NL80211_CMD_TRIGGER_SCAN:
wpa_printf(MSG_DEBUG, "nl80211: Scan trigger");
break;
case NL80211_CMD_ASSOCIATE:
case NL80211_CMD_DEAUTHENTICATE:
case NL80211_CMD_DISASSOCIATE:
- case NL80211_CMD_FRAME:
case NL80211_CMD_FRAME_TX_STATUS:
case NL80211_CMD_UNPROT_DEAUTHENTICATE:
case NL80211_CMD_UNPROT_DISASSOCIATE:
- mlme_event(drv, gnlh->cmd, tb[NL80211_ATTR_FRAME],
+ mlme_event(drv, cmd, tb[NL80211_ATTR_FRAME],
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:
- mlme_event_connect(drv, gnlh->cmd,
+ mlme_event_connect(drv, cmd,
tb[NL80211_ATTR_STATUS_CODE],
tb[NL80211_ATTR_MAC],
tb[NL80211_ATTR_REQ_IE],
break;
case NL80211_CMD_DISCONNECT:
mlme_event_disconnect(drv, tb[NL80211_ATTR_REASON_CODE],
- tb[NL80211_ATTR_MAC]);
+ tb[NL80211_ATTR_MAC],
+ tb[NL80211_ATTR_DISCONNECTED_BY_AP]);
break;
case NL80211_CMD_MICHAEL_MIC_FAILURE:
mlme_event_michael_mic_failure(drv, tb);
case NL80211_CMD_PMKSA_CANDIDATE:
nl80211_pmksa_candidate_event(drv, tb);
break;
+ case NL80211_CMD_PROBE_CLIENT:
+ nl80211_client_probe_event(drv, tb);
+ break;
+ default:
+ wpa_printf(MSG_DEBUG, "nl80211: Ignored unknown event "
+ "(cmd=%d)", cmd);
+ break;
+ }
+}
+
+
+static int process_drv_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];
+
+ 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;
+ }
+ }
+
+ do_process_drv_event(drv, gnlh->cmd, tb);
+ return NL_SKIP;
+}
+
+
+static int process_global_event(struct nl_msg *msg, void *arg)
+{
+ struct nl80211_global *global = arg;
+ struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
+ struct nlattr *tb[NL80211_ATTR_MAX + 1];
+ struct wpa_driver_nl80211_data *drv;
+ int ifidx = -1;
+
+ nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
+ genlmsg_attrlen(gnlh, 0), NULL);
+
+ if (tb[NL80211_ATTR_IFINDEX])
+ ifidx = nla_get_u32(tb[NL80211_ATTR_IFINDEX]);
+
+ dl_list_for_each(drv, &global->interfaces,
+ struct wpa_driver_nl80211_data, list) {
+ if (ifidx == -1 || ifidx == drv->ifindex ||
+ have_ifidx(drv, ifidx))
+ do_process_drv_event(drv, gnlh->cmd, tb);
+ }
+
+ return NL_SKIP;
+}
+
+
+static int process_bss_event(struct nl_msg *msg, void *arg)
+{
+ struct i802_bss *bss = arg;
+ struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
+ struct nlattr *tb[NL80211_ATTR_MAX + 1];
+
+ nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
+ genlmsg_attrlen(gnlh, 0), NULL);
+
+ switch (gnlh->cmd) {
+ case NL80211_CMD_FRAME:
+ case NL80211_CMD_FRAME_TX_STATUS:
+ mlme_event(bss->drv, gnlh->cmd, tb[NL80211_ATTR_FRAME],
+ tb[NL80211_ATTR_MAC], tb[NL80211_ATTR_TIMED_OUT],
+ tb[NL80211_ATTR_WIPHY_FREQ], tb[NL80211_ATTR_ACK],
+ tb[NL80211_ATTR_COOKIE]);
+ break;
+ case NL80211_CMD_UNEXPECTED_FRAME:
+ nl80211_spurious_frame(bss, tb, 0);
+ break;
+ case NL80211_CMD_UNEXPECTED_4ADDR_FRAME:
+ nl80211_spurious_frame(bss, tb, 1);
+ 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 *handle)
{
- struct nl_cb *cb;
- struct wpa_driver_nl80211_data *drv = eloop_ctx;
+ struct nl_cb *cb = eloop_ctx;
wpa_printf(MSG_DEBUG, "nl80211: Event message available");
- cb = nl_cb_clone(drv->nl_cb);
- if (!cb)
- 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(handle, cb);
- nl_cb_put(cb);
}
alpha2[1] = alpha2_arg[1];
alpha2[2] = '\0';
- genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0,
- 0, NL80211_CMD_REQ_SET_REG, 0);
+ nl80211_cmd(drv, msg, 0, NL80211_CMD_REQ_SET_REG);
NLA_PUT_STRING(msg, NL80211_ATTR_REG_ALPHA2, alpha2);
if (send_and_recv_msgs(drv, msg, NULL, NULL))
return -EINVAL;
return 0;
nla_put_failure:
+ nlmsg_free(msg);
return -EINVAL;
}
struct wiphy_info_data {
- int max_scan_ssids;
- int max_sched_scan_ssids;
- int ap_supported;
- int p2p_supported;
- int p2p_concurrent;
- int auth_supported;
- int connect_supported;
- int offchan_tx_supported;
- int max_remain_on_chan;
- int firmware_roam;
- int sched_scan_supported;
+ struct wpa_driver_capa *capa;
+
+ unsigned int error:1;
+ unsigned int device_ap_sme:1;
+ unsigned int poll_command_supported:1;
+ unsigned int data_tx_status:1;
+ unsigned int monitor_supported:1;
};
+static unsigned int probe_resp_offload_support(int supp_protocols)
+{
+ unsigned int prot = 0;
+
+ if (supp_protocols & NL80211_PROBE_RESP_OFFLOAD_SUPPORT_WPS)
+ prot |= WPA_DRIVER_PROBE_RESP_OFFLOAD_WPS;
+ if (supp_protocols & NL80211_PROBE_RESP_OFFLOAD_SUPPORT_WPS2)
+ prot |= WPA_DRIVER_PROBE_RESP_OFFLOAD_WPS2;
+ if (supp_protocols & NL80211_PROBE_RESP_OFFLOAD_SUPPORT_P2P)
+ prot |= WPA_DRIVER_PROBE_RESP_OFFLOAD_P2P;
+ if (supp_protocols & NL80211_PROBE_RESP_OFFLOAD_SUPPORT_80211U)
+ prot |= WPA_DRIVER_PROBE_RESP_OFFLOAD_INTERWORKING;
+
+ return prot;
+}
+
+
static int wiphy_info_handler(struct nl_msg *msg, void *arg)
{
struct nlattr *tb[NL80211_ATTR_MAX + 1];
struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
struct wiphy_info_data *info = arg;
int p2p_go_supported = 0, p2p_client_supported = 0;
+ int p2p_concurrent = 0;
+ int auth_supported = 0, connect_supported = 0;
+ struct wpa_driver_capa *capa = info->capa;
static struct nla_policy
iface_combination_policy[NUM_NL80211_IFACE_COMB] = {
[NL80211_IFACE_COMB_LIMITS] = { .type = NLA_NESTED },
genlmsg_attrlen(gnlh, 0), NULL);
if (tb[NL80211_ATTR_MAX_NUM_SCAN_SSIDS])
- info->max_scan_ssids =
+ capa->max_scan_ssids =
nla_get_u8(tb[NL80211_ATTR_MAX_NUM_SCAN_SSIDS]);
if (tb[NL80211_ATTR_MAX_NUM_SCHED_SCAN_SSIDS])
- info->max_sched_scan_ssids =
+ capa->max_sched_scan_ssids =
nla_get_u8(tb[NL80211_ATTR_MAX_NUM_SCHED_SCAN_SSIDS]);
+ if (tb[NL80211_ATTR_MAX_MATCH_SETS])
+ capa->max_match_sets =
+ nla_get_u8(tb[NL80211_ATTR_MAX_MATCH_SETS]);
+
if (tb[NL80211_ATTR_SUPPORTED_IFTYPES]) {
struct nlattr *nl_mode;
int i;
tb[NL80211_ATTR_SUPPORTED_IFTYPES], i) {
switch (nla_type(nl_mode)) {
case NL80211_IFTYPE_AP:
- info->ap_supported = 1;
+ capa->flags |= WPA_DRIVER_FLAGS_AP;
break;
case NL80211_IFTYPE_P2P_GO:
p2p_go_supported = 1;
case NL80211_IFTYPE_P2P_CLIENT:
p2p_client_supported = 1;
break;
+ case NL80211_IFTYPE_MONITOR:
+ info->monitor_supported = 1;
+ break;
}
}
}
}
if (combination_has_p2p && combination_has_mgd) {
- info->p2p_concurrent = 1;
+ p2p_concurrent = 1;
break;
}
}
}
- info->p2p_supported = p2p_go_supported && p2p_client_supported;
-
if (tb[NL80211_ATTR_SUPPORTED_COMMANDS]) {
struct nlattr *nl_cmd;
int i;
nla_for_each_nested(nl_cmd,
tb[NL80211_ATTR_SUPPORTED_COMMANDS], i) {
- u32 cmd = nla_get_u32(nl_cmd);
- if (cmd == NL80211_CMD_AUTHENTICATE)
- info->auth_supported = 1;
- else if (cmd == NL80211_CMD_CONNECT)
- info->connect_supported = 1;
- else if (cmd == NL80211_CMD_START_SCHED_SCAN)
- info->sched_scan_supported = 1;
+ switch (nla_get_u32(nl_cmd)) {
+ case NL80211_CMD_AUTHENTICATE:
+ auth_supported = 1;
+ break;
+ case NL80211_CMD_CONNECT:
+ connect_supported = 1;
+ break;
+ case NL80211_CMD_START_SCHED_SCAN:
+ capa->sched_scan_supported = 1;
+ break;
+ case NL80211_CMD_PROBE_CLIENT:
+ info->poll_command_supported = 1;
+ break;
+ }
}
}
- if (tb[NL80211_ATTR_OFFCHANNEL_TX_OK])
- info->offchan_tx_supported = 1;
+ if (tb[NL80211_ATTR_OFFCHANNEL_TX_OK]) {
+ wpa_printf(MSG_DEBUG, "nl80211: Using driver-based "
+ "off-channel TX");
+ capa->flags |= WPA_DRIVER_FLAGS_OFFCHANNEL_TX;
+ }
+
+ if (tb[NL80211_ATTR_ROAM_SUPPORT]) {
+ wpa_printf(MSG_DEBUG, "nl80211: Using driver-based roaming");
+ capa->flags |= WPA_DRIVER_FLAGS_BSS_SELECTION;
+ }
+
+ /* default to 5000 since early versions of mac80211 don't set it */
+ capa->max_remain_on_chan = 5000;
- if (tb[NL80211_ATTR_ROAM_SUPPORT])
- info->firmware_roam = 1;
+ if (tb[NL80211_ATTR_SUPPORT_AP_UAPSD])
+ capa->flags |= WPA_DRIVER_FLAGS_AP_UAPSD;
if (tb[NL80211_ATTR_MAX_REMAIN_ON_CHANNEL_DURATION])
- info->max_remain_on_chan =
+ capa->max_remain_on_chan =
nla_get_u32(tb[NL80211_ATTR_MAX_REMAIN_ON_CHANNEL_DURATION]);
+ if (auth_supported)
+ capa->flags |= WPA_DRIVER_FLAGS_SME;
+ else if (!connect_supported) {
+ wpa_printf(MSG_INFO, "nl80211: Driver does not support "
+ "authentication/association or connect commands");
+ info->error = 1;
+ }
+
+ if (p2p_go_supported && p2p_client_supported)
+ capa->flags |= WPA_DRIVER_FLAGS_P2P_CAPABLE;
+ if (p2p_concurrent) {
+ wpa_printf(MSG_DEBUG, "nl80211: Use separate P2P group "
+ "interface (driver advertised support)");
+ capa->flags |= WPA_DRIVER_FLAGS_P2P_CONCURRENT;
+ capa->flags |= WPA_DRIVER_FLAGS_P2P_MGMT_AND_NON_P2P;
+ }
+
+ if (tb[NL80211_ATTR_TDLS_SUPPORT]) {
+ wpa_printf(MSG_DEBUG, "nl80211: TDLS supported");
+ capa->flags |= WPA_DRIVER_FLAGS_TDLS_SUPPORT;
+
+ if (tb[NL80211_ATTR_TDLS_EXTERNAL_SETUP]) {
+ wpa_printf(MSG_DEBUG, "nl80211: TDLS external setup");
+ capa->flags |=
+ WPA_DRIVER_FLAGS_TDLS_EXTERNAL_SETUP;
+ }
+ }
+
+ if (tb[NL80211_ATTR_DEVICE_AP_SME])
+ info->device_ap_sme = 1;
+
+ if (tb[NL80211_ATTR_FEATURE_FLAGS]) {
+ u32 flags = nla_get_u32(tb[NL80211_ATTR_FEATURE_FLAGS]);
+
+ if (flags & NL80211_FEATURE_SK_TX_STATUS)
+ info->data_tx_status = 1;
+ }
+
+ if (tb[NL80211_ATTR_PROBE_RESP_OFFLOAD]) {
+ int protocols =
+ nla_get_u32(tb[NL80211_ATTR_PROBE_RESP_OFFLOAD]);
+ wpa_printf(MSG_DEBUG, "nl80211: Supports Probe Response "
+ "offload in AP mode");
+ capa->flags |= WPA_DRIVER_FLAGS_PROBE_RESP_OFFLOAD;
+ capa->probe_resp_offloads =
+ probe_resp_offload_support(protocols);
+ }
+
return NL_SKIP;
}
struct nl_msg *msg;
os_memset(info, 0, sizeof(*info));
-
- /* default to 5000 since early versions of mac80211 don't set it */
- info->max_remain_on_chan = 5000;
+ info->capa = &drv->capa;
msg = nlmsg_alloc();
if (!msg)
return -1;
- genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0,
- 0, NL80211_CMD_GET_WIPHY, 0);
+ nl80211_cmd(drv, msg, 0, NL80211_CMD_GET_WIPHY);
NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->first_bss.ifindex);
struct wiphy_info_data info;
if (wpa_driver_nl80211_get_info(drv, &info))
return -1;
+
+ if (info.error)
+ return -1;
+
drv->has_capability = 1;
/* For now, assume TKIP, CCMP, WPA, WPA2 are supported */
drv->capa.key_mgmt = WPA_DRIVER_CAPA_KEY_MGMT_WPA |
WPA_DRIVER_AUTH_SHARED |
WPA_DRIVER_AUTH_LEAP;
- drv->capa.max_scan_ssids = info.max_scan_ssids;
- drv->capa.max_sched_scan_ssids = info.max_sched_scan_ssids;
- drv->capa.sched_scan_supported = info.sched_scan_supported;
-
- if (info.ap_supported)
- drv->capa.flags |= WPA_DRIVER_FLAGS_AP;
-
- if (info.auth_supported)
- drv->capa.flags |= WPA_DRIVER_FLAGS_SME;
- else if (!info.connect_supported) {
- wpa_printf(MSG_INFO, "nl80211: Driver does not support "
- "authentication/association or connect commands");
- return -1;
- }
-
- if (info.offchan_tx_supported) {
- wpa_printf(MSG_DEBUG, "nl80211: Using driver-based "
- "off-channel TX");
- drv->capa.flags |= WPA_DRIVER_FLAGS_OFFCHANNEL_TX;
- }
-
- if (info.firmware_roam) {
- wpa_printf(MSG_DEBUG, "nl80211: Using driver-based roaming");
- drv->capa.flags |= WPA_DRIVER_FLAGS_BSS_SELECTION;
- }
-
drv->capa.flags |= WPA_DRIVER_FLAGS_SANE_ERROR_CODES;
drv->capa.flags |= WPA_DRIVER_FLAGS_SET_KEYS_AFTER_ASSOC_DONE;
- if (info.p2p_supported)
- drv->capa.flags |= WPA_DRIVER_FLAGS_P2P_CAPABLE;
- if (info.p2p_concurrent) {
- wpa_printf(MSG_DEBUG, "nl80211: Use separate P2P group "
- "interface (driver advertised support)");
- drv->capa.flags |= WPA_DRIVER_FLAGS_P2P_CONCURRENT;
- drv->capa.flags |= WPA_DRIVER_FLAGS_P2P_MGMT_AND_NON_P2P;
- }
drv->capa.flags |= WPA_DRIVER_FLAGS_EAPOL_TX_STATUS;
drv->capa.flags |= WPA_DRIVER_FLAGS_DEAUTH_TX_STATUS;
- drv->capa.max_remain_on_chan = info.max_remain_on_chan;
-
- return 0;
-}
+ drv->device_ap_sme = info.device_ap_sme;
+ drv->poll_command_supported = info.poll_command_supported;
+ drv->data_tx_status = info.data_tx_status;
-static int wpa_driver_nl80211_init_nl(struct wpa_driver_nl80211_data *drv)
-{
- int ret;
-
- /* Initialize generic netlink and nl80211 */
+ /*
+ * If poll command is supported mac80211 is new enough to
+ * have everything we need to not need monitor interfaces.
+ */
+ drv->use_monitor = !info.poll_command_supported;
- drv->nl_cb = nl_cb_alloc(NL_CB_DEFAULT);
- if (drv->nl_cb == NULL) {
- wpa_printf(MSG_ERROR, "nl80211: Failed to allocate netlink "
- "callbacks");
- goto err1;
+ if (drv->device_ap_sme && drv->use_monitor) {
+ /*
+ * Non-mac80211 drivers may not support monitor interface.
+ * Make sure we do not get stuck with incorrect capability here
+ * by explicitly testing this.
+ */
+ if (!info.monitor_supported) {
+ wpa_printf(MSG_DEBUG, "nl80211: Disable use_monitor "
+ "with device_ap_sme since no monitor mode "
+ "support detected");
+ drv->use_monitor = 0;
+ }
}
- 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;
- }
+ /*
+ * If we aren't going to use monitor interfaces, but the
+ * driver doesn't support data TX status, we won't get TX
+ * status for EAPOL frames.
+ */
+ if (!drv->use_monitor && !info.data_tx_status)
+ drv->capa.flags &= ~WPA_DRIVER_FLAGS_EAPOL_TX_STATUS;
- 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 err2b;
- }
+ return 0;
+}
- if (genl_connect(drv->nl_handle)) {
- wpa_printf(MSG_ERROR, "nl80211: Failed to connect to generic "
- "netlink");
- goto err3;
- }
- if (genl_connect(drv->nl_handle_event)) {
- wpa_printf(MSG_ERROR, "nl80211: Failed to connect to generic "
- "netlink (event)");
- goto err3;
- }
+#ifdef ANDROID
+static int android_genl_ctrl_resolve(struct nl_handle *handle,
+ const char *name)
+{
+ /*
+ * Android ICS has very minimal genl_ctrl_resolve() implementation, so
+ * need to work around that.
+ */
+ struct nl_cache *cache = NULL;
+ struct genl_family *nl80211 = NULL;
+ int id = -1;
-#ifdef CONFIG_LIBNL20
- if (genl_ctrl_alloc_cache(drv->nl_handle, &drv->nl_cache) < 0) {
- wpa_printf(MSG_ERROR, "nl80211: Failed to allocate generic "
- "netlink cache");
- goto err3;
- }
- if (genl_ctrl_alloc_cache(drv->nl_handle_event, &drv->nl_cache_event) <
- 0) {
- wpa_printf(MSG_ERROR, "nl80211: Failed to allocate generic "
- "netlink cache (event)");
- goto err3b;
- }
-#else /* CONFIG_LIBNL20 */
- drv->nl_cache = genl_ctrl_alloc_cache(drv->nl_handle);
- if (drv->nl_cache == NULL) {
+ if (genl_ctrl_alloc_cache(handle, &cache) < 0) {
wpa_printf(MSG_ERROR, "nl80211: Failed to allocate generic "
"netlink cache");
- goto err3;
+ goto fail;
}
- drv->nl_cache_event = genl_ctrl_alloc_cache(drv->nl_handle_event);
- if (drv->nl_cache_event == NULL) {
- wpa_printf(MSG_ERROR, "nl80211: Failed to allocate generic "
- "netlink cache (event)");
- goto err3b;
+
+ nl80211 = genl_ctrl_search_by_name(cache, name);
+ if (nl80211 == NULL)
+ goto fail;
+
+ id = genl_family_get_id(nl80211);
+
+fail:
+ if (nl80211)
+ genl_family_put(nl80211);
+ if (cache)
+ nl_cache_free(cache);
+
+ return id;
+}
+#define genl_ctrl_resolve android_genl_ctrl_resolve
+#endif /* ANDROID */
+
+
+static int wpa_driver_nl80211_init_nl_global(struct nl80211_global *global)
+{
+ int ret;
+
+ global->nl_cb = nl_cb_alloc(NL_CB_DEFAULT);
+ if (global->nl_cb == NULL) {
+ wpa_printf(MSG_ERROR, "nl80211: Failed to allocate netlink "
+ "callbacks");
+ return -1;
}
-#endif /* CONFIG_LIBNL20 */
- drv->nl80211 = genl_ctrl_search_by_name(drv->nl_cache, "nl80211");
- if (drv->nl80211 == NULL) {
+ global->nl = nl_create_handle(global->nl_cb, "nl");
+ if (global->nl == NULL)
+ goto err;
+
+ global->nl80211_id = genl_ctrl_resolve(global->nl, "nl80211");
+ if (global->nl80211_id < 0) {
wpa_printf(MSG_ERROR, "nl80211: 'nl80211' generic netlink not "
"found");
- goto err4;
+ goto err;
}
- ret = nl_get_multicast_id(drv, "nl80211", "scan");
+ global->nl_event = nl_create_handle(global->nl_cb, "event");
+ if (global->nl_event == NULL)
+ goto err;
+
+ ret = nl_get_multicast_id(global, "nl80211", "scan");
if (ret >= 0)
- ret = nl_socket_add_membership(drv->nl_handle_event, ret);
+ ret = nl_socket_add_membership(global->nl_event, ret);
if (ret < 0) {
wpa_printf(MSG_ERROR, "nl80211: Could not add multicast "
"membership for scan events: %d (%s)",
ret, strerror(-ret));
- goto err4;
+ goto err;
}
- ret = nl_get_multicast_id(drv, "nl80211", "mlme");
+ ret = nl_get_multicast_id(global, "nl80211", "mlme");
if (ret >= 0)
- ret = nl_socket_add_membership(drv->nl_handle_event, ret);
+ ret = nl_socket_add_membership(global->nl_event, ret);
if (ret < 0) {
wpa_printf(MSG_ERROR, "nl80211: Could not add multicast "
"membership for mlme events: %d (%s)",
ret, strerror(-ret));
- goto err4;
+ goto err;
}
- ret = nl_get_multicast_id(drv, "nl80211", "regulatory");
+ ret = nl_get_multicast_id(global, "nl80211", "regulatory");
if (ret >= 0)
- ret = nl_socket_add_membership(drv->nl_handle_event, ret);
+ ret = nl_socket_add_membership(global->nl_event, ret);
if (ret < 0) {
wpa_printf(MSG_DEBUG, "nl80211: Could not add multicast "
"membership for regulatory events: %d (%s)",
/* Continue without regulatory events */
}
- eloop_register_read_sock(nl_socket_get_fd(drv->nl_handle_event),
- wpa_driver_nl80211_event_receive, drv,
- drv->nl_handle_event);
+ nl_cb_set(global->nl_cb, NL_CB_SEQ_CHECK, NL_CB_CUSTOM,
+ no_seq_check, NULL);
+ nl_cb_set(global->nl_cb, NL_CB_VALID, NL_CB_CUSTOM,
+ process_global_event, global);
+
+ eloop_register_read_sock(nl_socket_get_fd(global->nl_event),
+ wpa_driver_nl80211_event_receive,
+ global->nl_cb, global->nl_event);
return 0;
-err4:
- nl_cache_free(drv->nl_cache_event);
-err3b:
- nl_cache_free(drv->nl_cache);
-err3:
- nl80211_handle_destroy(drv->nl_handle_event);
-err2b:
- nl80211_handle_destroy(drv->nl_handle);
-err2:
- nl_cb_put(drv->nl_cb);
-err1:
+err:
+ nl_destroy_handles(&global->nl_event);
+ nl_destroy_handles(&global->nl);
+ nl_cb_put(global->nl_cb);
+ global->nl_cb = NULL;
return -1;
}
+static int wpa_driver_nl80211_init_nl(struct wpa_driver_nl80211_data *drv)
+{
+ drv->nl_cb = nl_cb_alloc(NL_CB_DEFAULT);
+ if (!drv->nl_cb) {
+ wpa_printf(MSG_ERROR, "nl80211: Failed to alloc cb struct");
+ return -1;
+ }
+
+ nl_cb_set(drv->nl_cb, NL_CB_SEQ_CHECK, NL_CB_CUSTOM,
+ no_seq_check, NULL);
+ nl_cb_set(drv->nl_cb, NL_CB_VALID, NL_CB_CUSTOM,
+ process_drv_event, drv);
+
+ return 0;
+}
+
+
static void wpa_driver_nl80211_rfkill_blocked(void *ctx)
{
wpa_printf(MSG_DEBUG, "nl80211: RFKILL blocked");
{
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)) {
+ if (linux_set_iface_flags(drv->global->ioctl_sock,
+ drv->first_bss.ifname, 1)) {
wpa_printf(MSG_DEBUG, "nl80211: Could not set interface UP "
"after rfkill unblock");
return;
}
-#ifdef CONFIG_AP
-static void nl80211_l2_read(void *ctx, const u8 *src_addr, const u8 *buf,
- size_t len)
+static void wpa_driver_nl80211_handle_eapol_tx_status(int sock,
+ void *eloop_ctx,
+ void *handle)
+{
+ struct wpa_driver_nl80211_data *drv = eloop_ctx;
+ u8 data[2048];
+ struct msghdr msg;
+ struct iovec entry;
+ struct {
+ struct cmsghdr cm;
+ char control[512];
+ } control;
+ struct cmsghdr *cmsg;
+ int res, found_ee = 0, found_wifi = 0, acked = 0;
+ union wpa_event_data event;
+
+ memset(&msg, 0, sizeof(msg));
+ msg.msg_iov = &entry;
+ msg.msg_iovlen = 1;
+ entry.iov_base = data;
+ entry.iov_len = sizeof(data);
+ msg.msg_control = &control;
+ msg.msg_controllen = sizeof(control);
+
+ res = recvmsg(sock, &msg, MSG_ERRQUEUE);
+ /* if error or not fitting 802.3 header, return */
+ if (res < 14)
+ return;
+
+ for (cmsg = CMSG_FIRSTHDR(&msg); cmsg; cmsg = CMSG_NXTHDR(&msg, cmsg))
+ {
+ if (cmsg->cmsg_level == SOL_SOCKET &&
+ cmsg->cmsg_type == SCM_WIFI_STATUS) {
+ int *ack;
+
+ found_wifi = 1;
+ ack = (void *)CMSG_DATA(cmsg);
+ acked = *ack;
+ }
+
+ if (cmsg->cmsg_level == SOL_PACKET &&
+ cmsg->cmsg_type == PACKET_TX_TIMESTAMP) {
+ struct sock_extended_err *err =
+ (struct sock_extended_err *)CMSG_DATA(cmsg);
+
+ if (err->ee_origin == SO_EE_ORIGIN_TXSTATUS)
+ found_ee = 1;
+ }
+ }
+
+ if (!found_ee || !found_wifi)
+ return;
+
+ memset(&event, 0, sizeof(event));
+ event.eapol_tx_status.dst = data;
+ event.eapol_tx_status.data = data + 14;
+ event.eapol_tx_status.data_len = res - 14;
+ event.eapol_tx_status.ack = acked;
+ wpa_supplicant_event(drv->ctx, EVENT_EAPOL_TX_STATUS, &event);
+}
+
+
+static int nl80211_init_bss(struct i802_bss *bss)
+{
+ bss->nl_cb = nl_cb_alloc(NL_CB_DEFAULT);
+ if (!bss->nl_cb)
+ return -1;
+
+ nl_cb_set(bss->nl_cb, NL_CB_SEQ_CHECK, NL_CB_CUSTOM,
+ no_seq_check, NULL);
+ nl_cb_set(bss->nl_cb, NL_CB_VALID, NL_CB_CUSTOM,
+ process_bss_event, bss);
+
+ return 0;
+}
+
+
+static void nl80211_destroy_bss(struct i802_bss *bss)
{
- wpa_printf(MSG_DEBUG, "nl80211: l2_packet read %u",
- (unsigned int) len);
+ nl_cb_put(bss->nl_cb);
+ bss->nl_cb = NULL;
}
-#endif /* CONFIG_AP */
/**
void *global_priv)
{
struct wpa_driver_nl80211_data *drv;
- struct netlink_config *cfg;
struct rfkill_config *rcfg;
struct i802_bss *bss;
+ if (global_priv == NULL)
+ return NULL;
drv = os_zalloc(sizeof(*drv));
if (drv == NULL)
return NULL;
os_strlcpy(bss->ifname, ifname, sizeof(bss->ifname));
drv->monitor_ifidx = -1;
drv->monitor_sock = -1;
- drv->ioctl_sock = -1;
+ drv->eapol_tx_sock = -1;
drv->ap_scan_as_station = NL80211_IFTYPE_UNSPECIFIED;
if (wpa_driver_nl80211_init_nl(drv)) {
return NULL;
}
- nl80211_get_phy_name(drv);
-
- drv->ioctl_sock = socket(PF_INET, SOCK_DGRAM, 0);
- if (drv->ioctl_sock < 0) {
- perror("socket(PF_INET,SOCK_DGRAM)");
+ if (nl80211_init_bss(bss))
goto failed;
- }
- 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;
- }
+ nl80211_get_phy_name(drv);
rcfg = os_zalloc(sizeof(*rcfg));
if (rcfg == NULL)
if (wpa_driver_nl80211_finish_drv_init(drv))
goto failed;
-#ifdef CONFIG_AP
- drv->l2 = l2_packet_init(ifname, NULL, ETH_P_EAPOL,
- nl80211_l2_read, drv, 0);
-#endif /* CONFIG_AP */
+ drv->eapol_tx_sock = socket(PF_PACKET, SOCK_DGRAM, 0);
+ if (drv->eapol_tx_sock < 0)
+ goto failed;
- if (drv->global)
+ if (drv->data_tx_status) {
+ int enabled = 1;
+
+ if (setsockopt(drv->eapol_tx_sock, SOL_SOCKET, SO_WIFI_STATUS,
+ &enabled, sizeof(enabled)) < 0) {
+ wpa_printf(MSG_DEBUG,
+ "nl80211: wifi status sockopt failed\n");
+ drv->data_tx_status = 0;
+ if (!drv->use_monitor)
+ drv->capa.flags &=
+ ~WPA_DRIVER_FLAGS_EAPOL_TX_STATUS;
+ } else {
+ eloop_register_read_sock(drv->eapol_tx_sock,
+ wpa_driver_nl80211_handle_eapol_tx_status,
+ drv, NULL);
+ }
+ }
+
+ if (drv->global) {
dl_list_add(&drv->global->interfaces, &drv->list);
+ drv->in_interface_list = 1;
+ }
return bss;
failed:
- rfkill_deinit(drv->rfkill);
- netlink_deinit(drv->netlink);
- if (drv->ioctl_sock >= 0)
- close(drv->ioctl_sock);
-
- genl_family_put(drv->nl80211);
- nl_cache_free(drv->nl_cache);
- 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);
+ wpa_driver_nl80211_deinit(bss);
return NULL;
}
-static int nl80211_register_frame(struct wpa_driver_nl80211_data *drv,
+static int nl80211_register_frame(struct i802_bss *bss,
struct nl_handle *nl_handle,
u16 type, const u8 *match, size_t match_len)
{
+ struct wpa_driver_nl80211_data *drv = bss->drv;
struct nl_msg *msg;
int ret = -1;
if (!msg)
return -1;
- genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, 0,
- NL80211_CMD_REGISTER_ACTION, 0);
+ wpa_printf(MSG_DEBUG, "nl80211: Register frame type=0x%x nl_handle=%p",
+ type, nl_handle);
+ wpa_hexdump(MSG_DEBUG, "nl80211: Register frame match",
+ match, match_len);
- NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex);
+ nl80211_cmd(drv, msg, 0, NL80211_CMD_REGISTER_ACTION);
+
+ NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, bss->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);
+ ret = send_and_recv(drv->global, nl_handle, msg, NULL, NULL);
msg = NULL;
if (ret) {
wpa_printf(MSG_DEBUG, "nl80211: Register frame command "
}
-static int nl80211_register_action_frame(struct wpa_driver_nl80211_data *drv,
+static int nl80211_alloc_mgmt_handle(struct i802_bss *bss)
+{
+ struct wpa_driver_nl80211_data *drv = bss->drv;
+
+ if (bss->nl_mgmt) {
+ wpa_printf(MSG_DEBUG, "nl80211: Mgmt reporting "
+ "already on! (nl_mgmt=%p)", bss->nl_mgmt);
+ return -1;
+ }
+
+ bss->nl_mgmt = nl_create_handle(drv->nl_cb, "mgmt");
+ if (bss->nl_mgmt == NULL)
+ return -1;
+
+ eloop_register_read_sock(nl_socket_get_fd(bss->nl_mgmt),
+ wpa_driver_nl80211_event_receive, bss->nl_cb,
+ bss->nl_mgmt);
+
+ return 0;
+}
+
+
+static int nl80211_register_action_frame(struct i802_bss *bss,
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,
+ return nl80211_register_frame(bss, bss->nl_mgmt,
type, match, match_len);
}
-static int nl80211_register_action_frames(struct wpa_driver_nl80211_data *drv)
+static int nl80211_mgmt_subscribe_non_ap(struct i802_bss *bss)
{
-#ifdef CONFIG_P2P
+ struct wpa_driver_nl80211_data *drv = bss->drv;
+
+ if (nl80211_alloc_mgmt_handle(bss))
+ return -1;
+ wpa_printf(MSG_DEBUG, "nl80211: Subscribe to mgmt frames with non-AP "
+ "handle %p", bss->nl_mgmt);
+
+#if defined(CONFIG_P2P) || defined(CONFIG_INTERWORKING)
/* GAS Initial Request */
- if (nl80211_register_action_frame(drv, (u8 *) "\x04\x0a", 2) < 0)
+ if (nl80211_register_action_frame(bss, (u8 *) "\x04\x0a", 2) < 0)
return -1;
/* GAS Initial Response */
- if (nl80211_register_action_frame(drv, (u8 *) "\x04\x0b", 2) < 0)
+ if (nl80211_register_action_frame(bss, (u8 *) "\x04\x0b", 2) < 0)
return -1;
/* GAS Comeback Request */
- if (nl80211_register_action_frame(drv, (u8 *) "\x04\x0c", 2) < 0)
+ if (nl80211_register_action_frame(bss, (u8 *) "\x04\x0c", 2) < 0)
return -1;
/* GAS Comeback Response */
- if (nl80211_register_action_frame(drv, (u8 *) "\x04\x0d", 2) < 0)
+ if (nl80211_register_action_frame(bss, (u8 *) "\x04\x0d", 2) < 0)
return -1;
+#endif /* CONFIG_P2P || CONFIG_INTERWORKING */
+#ifdef CONFIG_P2P
/* P2P Public Action */
- if (nl80211_register_action_frame(drv,
+ if (nl80211_register_action_frame(bss,
(u8 *) "\x04\x09\x50\x6f\x9a\x09",
6) < 0)
return -1;
/* P2P Action */
- if (nl80211_register_action_frame(drv,
+ if (nl80211_register_action_frame(bss,
(u8 *) "\x7f\x50\x6f\x9a\x09",
5) < 0)
return -1;
#endif /* CONFIG_P2P */
#ifdef CONFIG_IEEE80211W
/* SA Query Response */
- if (nl80211_register_action_frame(drv, (u8 *) "\x08\x01", 2) < 0)
+ if (nl80211_register_action_frame(bss, (u8 *) "\x08\x01", 2) < 0)
return -1;
#endif /* CONFIG_IEEE80211W */
+#ifdef CONFIG_TDLS
+ if ((drv->capa.flags & WPA_DRIVER_FLAGS_TDLS_SUPPORT)) {
+ /* TDLS Discovery Response */
+ if (nl80211_register_action_frame(bss, (u8 *) "\x04\x0e", 2) <
+ 0)
+ return -1;
+ }
+#endif /* CONFIG_TDLS */
/* FT Action frames */
- if (nl80211_register_action_frame(drv, (u8 *) "\x06", 1) < 0)
+ if (nl80211_register_action_frame(bss, (u8 *) "\x06", 1) < 0)
return -1;
else
drv->capa.key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_FT |
WPA_DRIVER_CAPA_KEY_MGMT_FT_PSK;
+ /* WNM - BSS Transition Management Request */
+ if (nl80211_register_action_frame(bss, (u8 *) "\x0a\x07", 2) < 0)
+ return -1;
+
+ return 0;
+}
+
+
+static int nl80211_register_spurious_class3(struct i802_bss *bss)
+{
+ struct wpa_driver_nl80211_data *drv = bss->drv;
+ struct nl_msg *msg;
+ int ret = -1;
+
+ msg = nlmsg_alloc();
+ if (!msg)
+ return -1;
+
+ nl80211_cmd(drv, msg, 0, NL80211_CMD_UNEXPECTED_FRAME);
+
+ NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, bss->ifindex);
+
+ ret = send_and_recv(drv->global, bss->nl_mgmt, msg, NULL, NULL);
+ msg = NULL;
+ if (ret) {
+ wpa_printf(MSG_DEBUG, "nl80211: Register spurious class3 "
+ "failed: ret=%d (%s)",
+ ret, strerror(-ret));
+ goto nla_put_failure;
+ }
+ ret = 0;
+nla_put_failure:
+ nlmsg_free(msg);
+ return ret;
+}
+
+
+static int nl80211_mgmt_subscribe_ap(struct i802_bss *bss)
+{
+ static const int stypes[] = {
+ WLAN_FC_STYPE_AUTH,
+ WLAN_FC_STYPE_ASSOC_REQ,
+ WLAN_FC_STYPE_REASSOC_REQ,
+ WLAN_FC_STYPE_DISASSOC,
+ WLAN_FC_STYPE_DEAUTH,
+ WLAN_FC_STYPE_ACTION,
+ WLAN_FC_STYPE_PROBE_REQ,
+/* Beacon doesn't work as mac80211 doesn't currently allow
+ * it, but it wouldn't really be the right thing anyway as
+ * it isn't per interface ... maybe just dump the scan
+ * results periodically for OLBC?
+ */
+// WLAN_FC_STYPE_BEACON,
+ };
+ unsigned int i;
+
+ if (nl80211_alloc_mgmt_handle(bss))
+ return -1;
+ wpa_printf(MSG_DEBUG, "nl80211: Subscribe to mgmt frames with AP "
+ "handle %p", bss->nl_mgmt);
+
+ for (i = 0; i < sizeof(stypes) / sizeof(stypes[0]); i++) {
+ if (nl80211_register_frame(bss, bss->nl_mgmt,
+ (WLAN_FC_TYPE_MGMT << 2) |
+ (stypes[i] << 4),
+ NULL, 0) < 0) {
+ goto out_err;
+ }
+ }
+
+ if (nl80211_register_spurious_class3(bss))
+ goto out_err;
+
+ if (nl80211_get_wiphy_data_ap(bss) == NULL)
+ goto out_err;
+
+ return 0;
+
+out_err:
+ eloop_unregister_read_sock(nl_socket_get_fd(bss->nl_mgmt));
+ nl_destroy_handles(&bss->nl_mgmt);
+ return -1;
+}
+
+
+static int nl80211_mgmt_subscribe_ap_dev_sme(struct i802_bss *bss)
+{
+ if (nl80211_alloc_mgmt_handle(bss))
+ return -1;
+ wpa_printf(MSG_DEBUG, "nl80211: Subscribe to mgmt frames with AP "
+ "handle %p (device SME)", bss->nl_mgmt);
+
+ if (nl80211_register_frame(bss, bss->nl_mgmt,
+ (WLAN_FC_TYPE_MGMT << 2) |
+ (WLAN_FC_STYPE_ACTION << 4),
+ NULL, 0) < 0)
+ goto out_err;
+
return 0;
+
+out_err:
+ eloop_unregister_read_sock(nl_socket_get_fd(bss->nl_mgmt));
+ nl_destroy_handles(&bss->nl_mgmt);
+ return -1;
+}
+
+
+static void nl80211_mgmt_unsubscribe(struct i802_bss *bss, const char *reason)
+{
+ if (bss->nl_mgmt == NULL)
+ return;
+ wpa_printf(MSG_DEBUG, "nl80211: Unsubscribe mgmt frames handle %p "
+ "(%s)", bss->nl_mgmt, reason);
+ eloop_unregister_read_sock(nl_socket_get_fd(bss->nl_mgmt));
+ nl_destroy_handles(&bss->nl_mgmt);
+
+ nl80211_put_wiphy_data_ap(bss);
}
* dynamically added interface (e.g., P2P) that was already configured
* with proper iftype.
*/
- if ((drv->global == NULL ||
- drv->ifindex != drv->global->if_add_ifindex) &&
+ if (drv->ifindex != drv->global->if_add_ifindex &&
wpa_driver_nl80211_set_mode(bss, NL80211_IFTYPE_STATION) < 0) {
- wpa_printf(MSG_DEBUG, "nl80211: Could not configure driver to "
+ wpa_printf(MSG_ERROR, "nl80211: Could not configure driver to "
"use managed mode");
+ return -1;
}
- if (linux_set_iface_flags(drv->ioctl_sock, bss->ifname, 1)) {
+ if (linux_set_iface_flags(drv->global->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",
}
}
- netlink_send_oper_ifla(drv->netlink, drv->ifindex,
+ netlink_send_oper_ifla(drv->global->netlink, drv->ifindex,
1, IF_OPER_DORMANT);
#endif /* HOSTAPD */
if (wpa_driver_nl80211_capa(drv))
return -1;
- if (linux_get_ifhwaddr(drv->ioctl_sock, bss->ifname, drv->addr))
+ if (linux_get_ifhwaddr(drv->global->ioctl_sock, bss->ifname,
+ bss->addr))
return -1;
- 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.
- */
- }
-
if (send_rfkill_event) {
eloop_register_timeout(0, 0, wpa_driver_nl80211_send_rfkill,
drv, drv->ctx);
if (!msg)
return -ENOMEM;
- genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0,
- 0, NL80211_CMD_DEL_BEACON, 0);
+ nl80211_cmd(drv, msg, 0, NL80211_CMD_DEL_BEACON);
NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex);
return send_and_recv_msgs(drv, msg, NULL, NULL);
nla_put_failure:
+ nlmsg_free(msg);
return -ENOBUFS;
}
struct i802_bss *bss = priv;
struct wpa_driver_nl80211_data *drv = bss->drv;
-#ifdef CONFIG_AP
- if (drv->l2)
- l2_packet_deinit(drv->l2);
-#endif /* CONFIG_AP */
+ if (drv->data_tx_status)
+ eloop_unregister_read_sock(drv->eapol_tx_sock);
+ if (drv->eapol_tx_sock >= 0)
+ close(drv->eapol_tx_sock);
- if (drv->nl_handle_preq)
+ if (bss->nl_preq)
wpa_driver_nl80211_probe_req_report(bss, 0);
if (bss->added_if_into_bridge) {
- if (linux_br_del_if(drv->ioctl_sock, bss->brname, bss->ifname)
- < 0)
+ if (linux_br_del_if(drv->global->ioctl_sock, bss->brname,
+ bss->ifname) < 0)
wpa_printf(MSG_INFO, "nl80211: Failed to remove "
"interface %s from bridge %s: %s",
bss->ifname, bss->brname, strerror(errno));
}
if (bss->added_bridge) {
- if (linux_br_del(drv->ioctl_sock, bss->brname) < 0)
+ if (linux_br_del(drv->global->ioctl_sock, bss->brname) < 0)
wpa_printf(MSG_INFO, "nl80211: Failed to remove "
"bridge %s: %s",
bss->brname, strerror(errno));
os_free(drv->if_indices);
#endif /* HOSTAPD */
- if (drv->disable_11b_rates)
+ if (drv->disabled_11b_rates)
nl80211_disable_11b_rates(drv, drv->ifindex, 0);
- netlink_send_oper_ifla(drv->netlink, drv->ifindex, 0, IF_OPER_UP);
- netlink_deinit(drv->netlink);
+ netlink_send_oper_ifla(drv->global->netlink, drv->ifindex, 0,
+ IF_OPER_UP);
rfkill_deinit(drv->rfkill);
eloop_cancel_timeout(wpa_driver_nl80211_scan_timeout, drv, drv->ctx);
- (void) linux_set_iface_flags(drv->ioctl_sock, bss->ifname, 0);
+ (void) linux_set_iface_flags(drv->global->ioctl_sock, bss->ifname, 0);
wpa_driver_nl80211_set_mode(bss, NL80211_IFTYPE_STATION);
+ nl80211_mgmt_unsubscribe(bss, "deinit");
- if (drv->ioctl_sock >= 0)
- close(drv->ioctl_sock);
-
- eloop_unregister_read_sock(nl_socket_get_fd(drv->nl_handle_event));
- genl_family_put(drv->nl80211);
- nl_cache_free(drv->nl_cache);
- nl_cache_free(drv->nl_cache_event);
- nl80211_handle_destroy(drv->nl_handle);
- nl80211_handle_destroy(drv->nl_handle_event);
nl_cb_put(drv->nl_cb);
+ nl80211_destroy_bss(&drv->first_bss);
+
os_free(drv->filter_ssids);
- if (drv->global)
+ os_free(drv->auth_ie);
+
+ if (drv->in_interface_list)
dl_list_del(&drv->list);
os_free(drv);
struct nl_msg *msg, *ssids, *freqs, *rates;
size_t i;
+ drv->scan_for_auth = 0;
+
msg = nlmsg_alloc();
ssids = nlmsg_alloc();
freqs = nlmsg_alloc();
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);
+ nl80211_cmd(drv, msg, 0, NL80211_CMD_TRIGGER_SCAN);
NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex);
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);
+ wpa_hexdump(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);
}
NLA_PUT(rates, NL80211_BAND_2GHZ, 8,
"\x0c\x12\x18\x24\x30\x48\x60\x6c");
nla_put_nested(msg, NL80211_ATTR_SCAN_SUPP_RATES, rates);
+
+ NLA_PUT_FLAG(msg, NL80211_ATTR_TX_NO_CCK_RATE);
}
ret = send_and_recv_msgs(drv, msg, NULL, NULL);
struct i802_bss *bss = priv;
struct wpa_driver_nl80211_data *drv = bss->drv;
int ret = 0;
- struct nl_msg *msg, *ssids, *freqs;
+ struct nl_msg *msg, *ssids, *freqs, *match_set_ssid, *match_sets;
size_t i;
+#ifdef ANDROID
+ if (!drv->capa.sched_scan_supported)
+ return android_pno_start(bss, params);
+#endif /* ANDROID */
+
msg = nlmsg_alloc();
ssids = nlmsg_alloc();
freqs = nlmsg_alloc();
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_START_SCHED_SCAN, 0);
+ nl80211_cmd(drv, msg, 0, NL80211_CMD_START_SCHED_SCAN);
NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex);
NLA_PUT_U32(msg, NL80211_ATTR_SCHED_SCAN_INTERVAL, interval);
+ if (drv->num_filter_ssids &&
+ (int) drv->num_filter_ssids <= drv->capa.max_match_sets) {
+ match_sets = nlmsg_alloc();
+
+ for (i = 0; i < drv->num_filter_ssids; i++) {
+ wpa_hexdump_ascii(MSG_MSGDUMP,
+ "nl80211: Sched scan filter SSID",
+ drv->filter_ssids[i].ssid,
+ drv->filter_ssids[i].ssid_len);
+
+ match_set_ssid = nlmsg_alloc();
+ nla_put(match_set_ssid,
+ NL80211_ATTR_SCHED_SCAN_MATCH_SSID,
+ drv->filter_ssids[i].ssid_len,
+ drv->filter_ssids[i].ssid);
+
+ nla_put_nested(match_sets, i + 1, match_set_ssid);
+
+ nlmsg_free(match_set_ssid);
+ }
+
+ nla_put_nested(msg, NL80211_ATTR_SCHED_SCAN_MATCH,
+ match_sets);
+ nlmsg_free(match_sets);
+ }
+
for (i = 0; i < params->num_ssids; i++) {
wpa_hexdump_ascii(MSG_MSGDUMP, "nl80211: Sched scan SSID",
params->ssids[i].ssid,
int ret = 0;
struct nl_msg *msg;
+#ifdef ANDROID
+ if (!drv->capa.sched_scan_supported)
+ return android_pno_stop(bss);
+#endif /* ANDROID */
+
msg = nlmsg_alloc();
if (!msg)
return -1;
- genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, 0,
- NL80211_CMD_STOP_SCHED_SCAN, 0);
+ nl80211_cmd(drv, msg, 0, NL80211_CMD_STOP_SCHED_SCAN);
NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex);
r->flags |= WPA_SCAN_LEVEL_DBM | WPA_SCAN_QUAL_INVALID;
} else if (bss[NL80211_BSS_SIGNAL_UNSPEC]) {
r->level = nla_get_u8(bss[NL80211_BSS_SIGNAL_UNSPEC]);
- r->flags |= WPA_SCAN_LEVEL_INVALID;
+ r->flags |= WPA_SCAN_QUAL_INVALID;
} else
r->flags |= WPA_SCAN_LEVEL_INVALID | WPA_SCAN_QUAL_INVALID;
if (bss[NL80211_BSS_TSF])
}
-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 *
nl80211_get_scan_results(struct wpa_driver_nl80211_data *drv)
{
if (!msg)
goto nla_put_failure;
- genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, NLM_F_DUMP,
- NL80211_CMD_GET_SCAN, 0);
+ nl80211_cmd(drv, msg, NLM_F_DUMP, NL80211_CMD_GET_SCAN);
NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex);
arg.drv = drv;
ret = send_and_recv_msgs(drv, msg, bss_info_handler, &arg);
msg = NULL;
if (ret == 0) {
- wpa_printf(MSG_DEBUG, "Received scan results (%lu BSSes)",
- (unsigned long) res->num);
+ wpa_printf(MSG_DEBUG, "nl80211: Received scan results (%lu "
+ "BSSes)", (unsigned long) res->num);
+ nl80211_get_noise_for_scan_results(drv, res);
return res;
}
wpa_printf(MSG_DEBUG, "nl80211: Scan result fetch failed: ret=%d "
"set_tx=%d seq_len=%lu key_len=%lu",
__func__, ifindex, alg, addr, key_idx, set_tx,
(unsigned long) seq_len, (unsigned long) key_len);
+#ifdef CONFIG_TDLS
+ if (key_idx == -1)
+ key_idx = 0;
+#endif /* CONFIG_TDLS */
msg = nlmsg_alloc();
if (!msg)
return -ENOMEM;
if (alg == WPA_ALG_NONE) {
- genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0,
- 0, NL80211_CMD_DEL_KEY, 0);
+ nl80211_cmd(drv, msg, 0, NL80211_CMD_DEL_KEY);
} else {
- genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0,
- 0, NL80211_CMD_NEW_KEY, 0);
+ nl80211_cmd(drv, msg, 0, NL80211_CMD_NEW_KEY);
NLA_PUT(msg, NL80211_ATTR_KEY_DATA, key_len, key);
switch (alg) {
case WPA_ALG_WEP:
if (!msg)
return -ENOMEM;
- genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0,
- 0, NL80211_CMD_SET_KEY, 0);
+ nl80211_cmd(drv, msg, 0, NL80211_CMD_SET_KEY);
NLA_PUT_U8(msg, NL80211_ATTR_KEY_IDX, key_idx);
NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, ifindex);
if (alg == WPA_ALG_IGTK)
return ret;
nla_put_failure:
+ nlmsg_free(msg);
return -ENOBUFS;
}
if (!msg)
return -1;
- genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, 0, cmd, 0);
+ nl80211_cmd(drv, msg, 0, cmd);
NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex);
NLA_PUT_U16(msg, NL80211_ATTR_REASON_CODE, reason_code);
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));
+ wpa_dbg(drv->ctx, MSG_DEBUG,
+ "nl80211: MLME command failed: reason=%u ret=%d (%s)",
+ reason_code, ret, strerror(-ret));
goto nla_put_failure;
}
ret = 0;
}
+static void nl80211_copy_auth_params(struct wpa_driver_nl80211_data *drv,
+ struct wpa_driver_auth_params *params)
+{
+ int i;
+
+ drv->auth_freq = params->freq;
+ drv->auth_alg = params->auth_alg;
+ drv->auth_wep_tx_keyidx = params->wep_tx_keyidx;
+ drv->auth_local_state_change = params->local_state_change;
+ drv->auth_p2p = params->p2p;
+
+ if (params->bssid)
+ os_memcpy(drv->auth_bssid_, params->bssid, ETH_ALEN);
+ else
+ os_memset(drv->auth_bssid_, 0, ETH_ALEN);
+
+ if (params->ssid) {
+ os_memcpy(drv->auth_ssid, params->ssid, params->ssid_len);
+ drv->auth_ssid_len = params->ssid_len;
+ } else
+ drv->auth_ssid_len = 0;
+
+
+ os_free(drv->auth_ie);
+ drv->auth_ie = NULL;
+ drv->auth_ie_len = 0;
+ if (params->ie) {
+ drv->auth_ie = os_malloc(params->ie_len);
+ if (drv->auth_ie) {
+ os_memcpy(drv->auth_ie, params->ie, params->ie_len);
+ drv->auth_ie_len = params->ie_len;
+ }
+ }
+
+ for (i = 0; i < 4; i++) {
+ if (params->wep_key[i] && params->wep_key_len[i] &&
+ params->wep_key_len[i] <= 16) {
+ os_memcpy(drv->auth_wep_key[i], params->wep_key[i],
+ params->wep_key_len[i]);
+ drv->auth_wep_key_len[i] = params->wep_key_len[i];
+ } else
+ drv->auth_wep_key_len[i] = 0;
+ }
+}
+
+
static int wpa_driver_nl80211_authenticate(
void *priv, struct wpa_driver_auth_params *params)
{
enum nl80211_auth_type type;
enum nl80211_iftype nlmode;
int count = 0;
+ int is_retry;
+
+ is_retry = drv->retry_auth;
+ drv->retry_auth = 0;
drv->associated = 0;
os_memset(drv->auth_bssid, 0, ETH_ALEN);
wpa_printf(MSG_DEBUG, "nl80211: Authenticate (ifindex=%d)",
drv->ifindex);
- genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, 0,
- NL80211_CMD_AUTHENTICATE, 0);
+ nl80211_cmd(drv, msg, 0, NL80211_CMD_AUTHENTICATE);
for (i = 0; i < 4; i++) {
if (!params->wep_key[i])
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));
+ wpa_dbg(drv->ctx, MSG_DEBUG,
+ "nl80211: MLME command failed (auth): ret=%d (%s)",
+ ret, strerror(-ret));
count++;
if (ret == -EALREADY && count == 1 && params->bssid &&
!params->local_state_change) {
nlmsg_free(msg);
goto retry;
}
- goto nla_put_failure;
+
+ if (ret == -ENOENT && params->freq && !is_retry) {
+ /*
+ * cfg80211 has likely expired the BSS entry even
+ * though it was previously available in our internal
+ * BSS table. To recover quickly, start a single
+ * channel scan on the specified channel.
+ */
+ struct wpa_driver_scan_params scan;
+ int freqs[2];
+
+ os_memset(&scan, 0, sizeof(scan));
+ scan.num_ssids = 1;
+ if (params->ssid) {
+ scan.ssids[0].ssid = params->ssid;
+ scan.ssids[0].ssid_len = params->ssid_len;
+ }
+ freqs[0] = params->freq;
+ freqs[1] = 0;
+ scan.freqs = freqs;
+ wpa_printf(MSG_DEBUG, "nl80211: Trigger single "
+ "channel scan to refresh cfg80211 BSS "
+ "entry");
+ ret = wpa_driver_nl80211_scan(bss, &scan);
+ if (ret == 0) {
+ nl80211_copy_auth_params(drv, params);
+ drv->scan_for_auth = 1;
+ }
+ } else if (is_retry) {
+ /*
+ * Need to indicate this with an event since the return
+ * value from the retry is not delivered to core code.
+ */
+ union wpa_event_data event;
+ wpa_printf(MSG_DEBUG, "nl80211: Authentication retry "
+ "failed");
+ os_memset(&event, 0, sizeof(event));
+ os_memcpy(event.timeout_event.addr, drv->auth_bssid_,
+ ETH_ALEN);
+ wpa_supplicant_event(drv->ctx, EVENT_AUTH_TIMED_OUT,
+ &event);
+ }
+
+ goto nla_put_failure;
+ }
+ ret = 0;
+ wpa_printf(MSG_DEBUG, "nl80211: Authentication request send "
+ "successfully");
+
+nla_put_failure:
+ nlmsg_free(msg);
+ return ret;
+}
+
+
+static int wpa_driver_nl80211_authenticate_retry(
+ struct wpa_driver_nl80211_data *drv)
+{
+ struct wpa_driver_auth_params params;
+ struct i802_bss *bss = &drv->first_bss;
+ int i;
+
+ wpa_printf(MSG_DEBUG, "nl80211: Try to authenticate again");
+
+ os_memset(¶ms, 0, sizeof(params));
+ params.freq = drv->auth_freq;
+ params.auth_alg = drv->auth_alg;
+ params.wep_tx_keyidx = drv->auth_wep_tx_keyidx;
+ params.local_state_change = drv->auth_local_state_change;
+ params.p2p = drv->auth_p2p;
+
+ if (!is_zero_ether_addr(drv->auth_bssid_))
+ params.bssid = drv->auth_bssid_;
+
+ if (drv->auth_ssid_len) {
+ params.ssid = drv->auth_ssid;
+ params.ssid_len = drv->auth_ssid_len;
+ }
+
+ params.ie = drv->auth_ie;
+ params.ie_len = drv->auth_ie_len;
+
+ for (i = 0; i < 4; i++) {
+ if (drv->auth_wep_key_len[i]) {
+ params.wep_key[i] = drv->auth_wep_key[i];
+ params.wep_key_len[i] = drv->auth_wep_key_len[i];
+ }
}
- ret = 0;
- wpa_printf(MSG_DEBUG, "nl80211: Authentication request send "
- "successfully");
-nla_put_failure:
- nlmsg_free(msg);
- return ret;
+ drv->retry_auth = 1;
+ return wpa_driver_nl80211_authenticate(bss, ¶ms);
}
mode = &phy_info->modes[*(phy_info->num_modes)];
memset(mode, 0, sizeof(*mode));
+ mode->flags = HOSTAPD_MODE_FLAG_HT_INFO_KNOWN;
*(phy_info->num_modes) += 1;
nla_parse(tb_band, NL80211_BAND_ATTR_MAX, nla_data(nl_band),
if (!msg)
return -ENOMEM;
- genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0,
- 0, NL80211_CMD_GET_REG, 0);
+ nl80211_cmd(drv, msg, 0, NL80211_CMD_GET_REG);
return send_and_recv_msgs(drv, msg, nl80211_get_reg, results);
}
if (!msg)
return NULL;
- genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0,
- 0, NL80211_CMD_GET_WIPHY, 0);
+ nl80211_cmd(drv, msg, 0, NL80211_CMD_GET_WIPHY);
NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex);
nl80211_set_ht40_flags(drv, &result);
return wpa_driver_nl80211_add_11b(result.modes, num_modes);
}
+ msg = NULL;
nla_put_failure:
+ nlmsg_free(msg);
return NULL;
}
-static int wpa_driver_nl80211_send_frame(struct wpa_driver_nl80211_data *drv,
- const void *data, size_t len,
- int encrypt)
+static int wpa_driver_nl80211_send_mntr(struct wpa_driver_nl80211_data *drv,
+ const void *data, size_t len,
+ int encrypt, int noack)
{
__u8 rtap_hdr[] = {
0x00, 0x00, /* radiotap version */
.msg_flags = 0,
};
int res;
+ u16 txflags = 0;
if (encrypt)
rtap_hdr[8] |= IEEE80211_RADIOTAP_F_WEP;
return -1;
}
+ if (noack)
+ txflags |= IEEE80211_RADIOTAP_F_TX_NOACK;
+ *(le16 *) &rtap_hdr[12] = host_to_le16(txflags);
+
res = sendmsg(drv->monitor_sock, &msg, 0);
if (res < 0) {
wpa_printf(MSG_INFO, "nl80211: sendmsg: %s", strerror(errno));
}
+static int wpa_driver_nl80211_send_frame(struct i802_bss *bss,
+ const void *data, size_t len,
+ int encrypt, int noack)
+{
+ struct wpa_driver_nl80211_data *drv = bss->drv;
+ u64 cookie;
+
+ if (drv->use_monitor)
+ return wpa_driver_nl80211_send_mntr(drv, data, len,
+ encrypt, noack);
+
+ return nl80211_send_frame_cmd(bss, bss->freq, 0, data, len,
+ &cookie, 0, noack, 0);
+}
+
+
static int wpa_driver_nl80211_send_mlme(void *priv, const u8 *data,
- size_t data_len)
+ size_t data_len, int noack)
{
struct i802_bss *bss = priv;
struct wpa_driver_nl80211_data *drv = bss->drv;
* but it works due to the single-threaded nature
* of wpa_supplicant.
*/
- return nl80211_send_frame_cmd(drv, drv->last_mgmt_freq, 0,
- data, data_len, NULL);
+ return nl80211_send_frame_cmd(bss, drv->last_mgmt_freq, 0,
+ data, data_len, NULL, 1, noack,
+ 1);
}
- if (drv->no_monitor_iface_capab && is_ap_interface(drv->nlmode)) {
- return nl80211_send_frame_cmd(drv, drv->ap_oper_freq, 0,
- data, data_len, NULL);
+ if (drv->device_ap_sme && is_ap_interface(drv->nlmode)) {
+ return nl80211_send_frame_cmd(bss, bss->freq, 0,
+ data, data_len, NULL,
+ 0, noack, 0);
}
if (WLAN_FC_GET_TYPE(fc) == WLAN_FC_TYPE_MGMT &&
encrypt = 0;
}
- return wpa_driver_nl80211_send_frame(drv, data, data_len, encrypt);
+ return wpa_driver_nl80211_send_frame(bss, data, data_len, encrypt,
+ noack);
+}
+
+
+static int nl80211_set_bss(struct i802_bss *bss, int cts, int preamble,
+ int slot, int ht_opmode, int ap_isolate,
+ int *basic_rates)
+{
+ struct wpa_driver_nl80211_data *drv = bss->drv;
+ struct nl_msg *msg;
+
+ msg = nlmsg_alloc();
+ if (!msg)
+ return -ENOMEM;
+
+ nl80211_cmd(drv, msg, 0, NL80211_CMD_SET_BSS);
+
+ if (cts >= 0)
+ NLA_PUT_U8(msg, NL80211_ATTR_BSS_CTS_PROT, cts);
+ if (preamble >= 0)
+ NLA_PUT_U8(msg, NL80211_ATTR_BSS_SHORT_PREAMBLE, preamble);
+ if (slot >= 0)
+ NLA_PUT_U8(msg, NL80211_ATTR_BSS_SHORT_SLOT_TIME, slot);
+ if (ht_opmode >= 0)
+ NLA_PUT_U16(msg, NL80211_ATTR_BSS_HT_OPMODE, ht_opmode);
+ if (ap_isolate >= 0)
+ NLA_PUT_U8(msg, NL80211_ATTR_AP_ISOLATE, ap_isolate);
+
+ if (basic_rates) {
+ u8 rates[NL80211_MAX_SUPP_RATES];
+ u8 rates_len = 0;
+ int i;
+
+ for (i = 0; i < NL80211_MAX_SUPP_RATES && basic_rates[i] >= 0;
+ i++)
+ rates[rates_len++] = basic_rates[i] / 5;
+
+ NLA_PUT(msg, NL80211_ATTR_BSS_BASIC_RATES, rates_len, rates);
+ }
+
+ NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, if_nametoindex(bss->ifname));
+
+ return send_and_recv_msgs(drv, msg, NULL, NULL);
+ nla_put_failure:
+ nlmsg_free(msg);
+ return -ENOBUFS;
}
if (beacon_set)
cmd = NL80211_CMD_SET_BEACON;
- genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0,
- 0, cmd, 0);
+ nl80211_cmd(drv, msg, 0, cmd);
NLA_PUT(msg, NL80211_ATTR_BEACON_HEAD, params->head_len, params->head);
NLA_PUT(msg, NL80211_ATTR_BEACON_TAIL, params->tail_len, params->tail);
NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, ifindex);
NLA_PUT_U32(msg, NL80211_ATTR_DTIM_PERIOD, params->dtim_period);
NLA_PUT(msg, NL80211_ATTR_SSID, params->ssid_len,
params->ssid);
+ if (params->proberesp && params->proberesp_len)
+ NLA_PUT(msg, NL80211_ATTR_PROBE_RESP, params->proberesp_len,
+ params->proberesp);
switch (params->hide_ssid) {
case NO_SSID_HIDING:
NLA_PUT_U32(msg, NL80211_ATTR_HIDDEN_SSID,
num_suites * sizeof(u32), suites);
}
+ if (params->key_mgmt_suites & WPA_KEY_MGMT_IEEE8021X &&
+ params->pairwise_ciphers & (WPA_CIPHER_WEP104 | WPA_CIPHER_WEP40))
+ NLA_PUT_FLAG(msg, NL80211_ATTR_CONTROL_PORT_NO_ENCRYPT);
+
num_suites = 0;
if (params->pairwise_ciphers & WPA_CIPHER_CCMP)
suites[num_suites++] = WLAN_CIPHER_SUITE_CCMP;
ret, strerror(-ret));
} else {
bss->beacon_set = 1;
+ nl80211_set_bss(bss, params->cts_protect, params->preamble,
+ params->short_slot_time, params->ht_opmode,
+ params->isolate, params->basic_rates);
}
return ret;
nla_put_failure:
+ nlmsg_free(msg);
return -ENOBUFS;
}
-static int wpa_driver_nl80211_set_freq(struct wpa_driver_nl80211_data *drv,
+static int wpa_driver_nl80211_set_freq(struct i802_bss *bss,
int freq, int ht_enabled,
int sec_channel_offset)
{
+ struct wpa_driver_nl80211_data *drv = bss->drv;
struct nl_msg *msg;
int ret;
if (!msg)
return -1;
- genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, 0,
- NL80211_CMD_SET_WIPHY, 0);
+ nl80211_cmd(drv, msg, 0, NL80211_CMD_SET_WIPHY);
NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex);
NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_FREQ, freq);
}
ret = send_and_recv_msgs(drv, msg, NULL, NULL);
- if (ret == 0)
+ msg = NULL;
+ if (ret == 0) {
+ bss->freq = freq;
return 0;
+ }
wpa_printf(MSG_DEBUG, "nl80211: Failed to set channel (freq=%d): "
"%d (%s)", freq, ret, strerror(-ret));
nla_put_failure:
+ nlmsg_free(msg);
return -1;
}
f |= BIT(NL80211_STA_FLAG_SHORT_PREAMBLE);
if (flags & WPA_STA_MFP)
f |= BIT(NL80211_STA_FLAG_MFP);
+ if (flags & WPA_STA_TDLS_PEER)
+ f |= BIT(NL80211_STA_FLAG_TDLS_PEER);
return f;
}
{
struct i802_bss *bss = priv;
struct wpa_driver_nl80211_data *drv = bss->drv;
- struct nl_msg *msg;
+ struct nl_msg *msg, *wme = NULL;
struct nl80211_sta_flag_update upd;
int ret = -ENOBUFS;
+ if ((params->flags & WPA_STA_TDLS_PEER) &&
+ !(drv->capa.flags & WPA_DRIVER_FLAGS_TDLS_SUPPORT))
+ return -EOPNOTSUPP;
+
msg = nlmsg_alloc();
if (!msg)
return -ENOMEM;
- genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0,
- 0, NL80211_CMD_NEW_STATION, 0);
+ nl80211_cmd(drv, msg, 0, params->set ? NL80211_CMD_SET_STATION :
+ NL80211_CMD_NEW_STATION);
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,
params->supp_rates);
- NLA_PUT_U16(msg, NL80211_ATTR_STA_LISTEN_INTERVAL,
- params->listen_interval);
+ if (!params->set) {
+ NLA_PUT_U16(msg, NL80211_ATTR_STA_AID, params->aid);
+ NLA_PUT_U16(msg, NL80211_ATTR_STA_LISTEN_INTERVAL,
+ params->listen_interval);
+ }
if (params->ht_capabilities) {
NLA_PUT(msg, NL80211_ATTR_HT_CAPABILITY,
sizeof(*params->ht_capabilities),
upd.set = upd.mask;
NLA_PUT(msg, NL80211_ATTR_STA_FLAGS2, sizeof(upd), &upd);
+ if (params->flags & WPA_STA_WMM) {
+ wme = nlmsg_alloc();
+ if (!wme)
+ goto nla_put_failure;
+
+ NLA_PUT_U8(wme, NL80211_STA_WME_UAPSD_QUEUES,
+ params->qosinfo & WMM_QOSINFO_STA_AC_MASK);
+ NLA_PUT_U8(wme, NL80211_STA_WME_MAX_SP,
+ (params->qosinfo > WMM_QOSINFO_STA_SP_SHIFT) &
+ WMM_QOSINFO_STA_SP_MASK);
+ nla_put_nested(msg, NL80211_ATTR_STA_WME, wme);
+ }
+
ret = send_and_recv_msgs(drv, msg, NULL, NULL);
+ msg = NULL;
if (ret)
- wpa_printf(MSG_DEBUG, "nl80211: NL80211_CMD_NEW_STATION "
- "result: %d (%s)", ret, strerror(-ret));
+ wpa_printf(MSG_DEBUG, "nl80211: NL80211_CMD_%s_STATION "
+ "result: %d (%s)", params->set ? "SET" : "NEW", ret,
+ strerror(-ret));
if (ret == -EEXIST)
ret = 0;
nla_put_failure:
+ nlmsg_free(wme);
+ nlmsg_free(msg);
return ret;
}
if (!msg)
return -ENOMEM;
- genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0,
- 0, NL80211_CMD_DEL_STATION, 0);
+ nl80211_cmd(drv, msg, 0, NL80211_CMD_DEL_STATION);
NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX,
if_nametoindex(bss->ifname));
return 0;
return ret;
nla_put_failure:
+ nlmsg_free(msg);
return -ENOBUFS;
}
wpa_printf(MSG_DEBUG, "nl80211: Remove interface ifindex=%d", ifidx);
-#ifdef HOSTAPD
/* stop listening for EAPOL on this interface */
del_ifidx(drv, ifidx);
-#endif /* HOSTAPD */
msg = nlmsg_alloc();
if (!msg)
goto nla_put_failure;
- genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0,
- 0, NL80211_CMD_DEL_INTERFACE, 0);
+ nl80211_cmd(drv, msg, 0, NL80211_CMD_DEL_INTERFACE);
NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, ifidx);
if (send_and_recv_msgs(drv, msg, NULL, NULL) == 0)
return;
+ msg = NULL;
nla_put_failure:
+ nlmsg_free(msg);
wpa_printf(MSG_ERROR, "Failed to remove interface (ifidx=%d)", ifidx);
}
if (!msg)
return -1;
- genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0,
- 0, NL80211_CMD_NEW_INTERFACE, 0);
+ nl80211_cmd(drv, msg, 0, NL80211_CMD_NEW_INTERFACE);
NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex);
NLA_PUT_STRING(msg, NL80211_ATTR_IFNAME, ifname);
NLA_PUT_U32(msg, NL80211_ATTR_IFTYPE, iftype);
}
ret = send_and_recv_msgs(drv, msg, NULL, NULL);
+ msg = NULL;
if (ret) {
nla_put_failure:
+ nlmsg_free(msg);
wpa_printf(MSG_ERROR, "Failed to create interface %s: %d (%s)",
ifname, ret, strerror(-ret));
return ret;
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_MONITOR &&
- linux_set_ifhwaddr(drv->ioctl_sock, ifname, addr)) {
+ linux_set_ifhwaddr(drv->global->ioctl_sock, ifname, addr)) {
nl80211_remove_iface(drv, ifidx);
return -1;
}
wds);
}
- if (ret >= 0 && drv->disable_11b_rates)
+ if (ret >= 0 && is_p2p_interface(iftype))
nl80211_disable_11b_rates(drv, ret, 1);
return ret;
static void from_unknown_sta(struct wpa_driver_nl80211_data *drv,
u8 *buf, size_t len)
{
+ struct ieee80211_hdr *hdr = (void *)buf;
+ u16 fc;
union wpa_event_data event;
+
+ if (len < sizeof(*hdr))
+ return;
+
+ fc = le_to_host16(hdr->frame_control);
+
os_memset(&event, 0, sizeof(event));
- event.rx_from_unknown.frame = buf;
- event.rx_from_unknown.len = len;
+ event.rx_from_unknown.bssid = get_hdr_bssid(hdr, len);
+ event.rx_from_unknown.addr = hdr->addr2;
+ event.rx_from_unknown.wds = (fc & (WLAN_FC_FROMDS | WLAN_FC_TODS)) ==
+ (WLAN_FC_FROMDS | WLAN_FC_TODS);
wpa_supplicant_event(drv->ctx, EVENT_RX_FROM_UNKNOWN, &event);
}
static void nl80211_remove_monitor_interface(
struct wpa_driver_nl80211_data *drv)
{
+ drv->monitor_refcount--;
+ if (drv->monitor_refcount > 0)
+ return;
+
if (drv->monitor_ifidx >= 0) {
nl80211_remove_iface(drv, drv->monitor_ifidx);
drv->monitor_ifidx = -1;
int optval;
socklen_t optlen;
+ if (drv->monitor_ifidx >= 0) {
+ drv->monitor_refcount++;
+ return 0;
+ }
+
if (os_strncmp(drv->first_bss.ifname, "p2p-", 4) == 0) {
/*
* P2P interface name is of the format p2p-%s-%d. For monitor
0);
if (drv->monitor_ifidx == -EOPNOTSUPP) {
+ /*
+ * This is backward compatibility for a few versions of
+ * the kernel only that didn't advertise the right
+ * attributes for the only driver that then supported
+ * AP mode w/o monitor -- ath6kl.
+ */
wpa_printf(MSG_DEBUG, "nl80211: Driver does not support "
"monitor interface type - try to run without it");
- drv->no_monitor_iface_capab = 1;
+ drv->device_ap_sme = 1;
}
if (drv->monitor_ifidx < 0)
return -1;
- if (linux_set_iface_flags(drv->ioctl_sock, buf, 1))
+ if (linux_set_iface_flags(drv->global->ioctl_sock, buf, 1))
goto error;
memset(&ll, 0, sizeof(ll));
}
-#ifdef CONFIG_AP
+static int nl80211_setup_ap(struct i802_bss *bss)
+{
+ struct wpa_driver_nl80211_data *drv = bss->drv;
+
+ wpa_printf(MSG_DEBUG, "nl80211: Setup AP - device_ap_sme=%d "
+ "use_monitor=%d", drv->device_ap_sme, drv->use_monitor);
+
+ /*
+ * Disable Probe Request reporting unless we need it in this way for
+ * devices that include the AP SME, in the other case (unless using
+ * monitor iface) we'll get it through the nl_mgmt socket instead.
+ */
+ if (!drv->device_ap_sme)
+ wpa_driver_nl80211_probe_req_report(bss, 0);
+
+ if (!drv->device_ap_sme && !drv->use_monitor)
+ if (nl80211_mgmt_subscribe_ap(bss))
+ return -1;
+
+ if (drv->device_ap_sme && !drv->use_monitor)
+ if (nl80211_mgmt_subscribe_ap_dev_sme(bss))
+ return -1;
+
+ if (!drv->device_ap_sme && drv->use_monitor &&
+ nl80211_create_monitor_interface(drv) &&
+ !drv->device_ap_sme)
+ return -1;
+
+ if (drv->device_ap_sme &&
+ wpa_driver_nl80211_probe_req_report(bss, 1) < 0) {
+ wpa_printf(MSG_DEBUG, "nl80211: Failed to enable "
+ "Probe Request frame reporting in AP mode");
+ /* Try to survive without this */
+ }
+
+ return 0;
+}
+
+
+static void nl80211_teardown_ap(struct i802_bss *bss)
+{
+ struct wpa_driver_nl80211_data *drv = bss->drv;
+
+ if (drv->device_ap_sme) {
+ wpa_driver_nl80211_probe_req_report(bss, 0);
+ if (!drv->use_monitor)
+ nl80211_mgmt_unsubscribe(bss, "AP teardown (dev SME)");
+ } else if (drv->use_monitor)
+ nl80211_remove_monitor_interface(drv);
+ else
+ nl80211_mgmt_unsubscribe(bss, "AP teardown");
+
+ bss->beacon_set = 0;
+}
+
+
static int nl80211_send_eapol_data(struct i802_bss *bss,
const u8 *addr, const u8 *data,
- size_t data_len, const u8 *own_addr)
+ size_t data_len)
{
- if (bss->drv->l2 == NULL) {
- wpa_printf(MSG_DEBUG, "nl80211: No l2_packet to send EAPOL");
+ struct sockaddr_ll ll;
+ int ret;
+
+ if (bss->drv->eapol_tx_sock < 0) {
+ wpa_printf(MSG_DEBUG, "nl80211: No socket to send EAPOL");
return -1;
}
- if (l2_packet_send(bss->drv->l2, addr, ETH_P_EAPOL, data, data_len) <
- 0)
- return -1;
- return 0;
+ os_memset(&ll, 0, sizeof(ll));
+ ll.sll_family = AF_PACKET;
+ ll.sll_ifindex = bss->ifindex;
+ ll.sll_protocol = htons(ETH_P_PAE);
+ ll.sll_halen = ETH_ALEN;
+ os_memcpy(ll.sll_addr, addr, ETH_ALEN);
+ ret = sendto(bss->drv->eapol_tx_sock, data, data_len, 0,
+ (struct sockaddr *) &ll, sizeof(ll));
+ if (ret < 0)
+ wpa_printf(MSG_ERROR, "nl80211: EAPOL TX: %s",
+ strerror(errno));
+
+ return ret;
}
-#endif /* CONFIG_AP */
static const u8 rfc1042_header[6] = { 0xaa, 0xaa, 0x03, 0x00, 0x00, 0x00 };
int res;
int qos = flags & WPA_STA_WMM;
-#ifdef CONFIG_AP
- if (drv->no_monitor_iface_capab)
- return nl80211_send_eapol_data(bss, addr, data, data_len,
- own_addr);
-#endif /* CONFIG_AP */
+ if (drv->device_ap_sme || !drv->use_monitor)
+ return nl80211_send_eapol_data(bss, addr, data, data_len);
len = sizeof(*hdr) + (qos ? 2 : 0) + sizeof(rfc1042_header) + 2 +
data_len;
pos += 2;
memcpy(pos, data, data_len);
- res = wpa_driver_nl80211_send_frame(drv, (u8 *) hdr, len, encrypt);
+ res = wpa_driver_nl80211_send_frame(bss, (u8 *) hdr, len, encrypt, 0);
if (res < 0) {
wpa_printf(MSG_ERROR, "i802_send_eapol - packet len: %lu - "
"failed: %d (%s)",
return -ENOMEM;
}
- genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0,
- 0, NL80211_CMD_SET_STATION, 0);
+ nl80211_cmd(drv, msg, 0, NL80211_CMD_SET_STATION);
NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX,
if_nametoindex(bss->ifname));
if (total_flags & WPA_STA_MFP)
NLA_PUT_FLAG(flags, NL80211_STA_FLAG_MFP);
+ if (total_flags & WPA_STA_TDLS_PEER)
+ NLA_PUT_FLAG(flags, NL80211_STA_FLAG_TDLS_PEER);
+
if (nla_put_nested(msg, NL80211_ATTR_STA_FLAGS, flags))
goto nla_put_failure;
return send_and_recv_msgs(drv, msg, NULL, NULL);
nla_put_failure:
+ nlmsg_free(msg);
nlmsg_free(flags);
return -ENOBUFS;
}
nlmode = NL80211_IFTYPE_AP;
if (wpa_driver_nl80211_set_mode(&drv->first_bss, nlmode) ||
- wpa_driver_nl80211_set_freq(drv, params->freq, 0, 0)) {
+ wpa_driver_nl80211_set_freq(&drv->first_bss, params->freq, 0, 0)) {
nl80211_remove_monitor_interface(drv);
return -1;
}
- if (drv->no_monitor_iface_capab) {
- if (wpa_driver_nl80211_probe_req_report(&drv->first_bss, 1) < 0)
- {
- wpa_printf(MSG_DEBUG, "nl80211: Failed to enable "
- "Probe Request frame reporting in AP mode");
- /* Try to survive without this */
- }
- }
-
- drv->ap_oper_freq = params->freq;
-
return 0;
}
if (!msg)
return -1;
- genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, 0,
- NL80211_CMD_LEAVE_IBSS, 0);
+ nl80211_cmd(drv, msg, 0, NL80211_CMD_LEAVE_IBSS);
NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex);
ret = send_and_recv_msgs(drv, msg, NULL, NULL);
msg = NULL;
if (!msg)
return -1;
- genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, 0,
- NL80211_CMD_JOIN_IBSS, 0);
+ nl80211_cmd(drv, msg, 0, NL80211_CMD_JOIN_IBSS);
NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex);
if (params->ssid == NULL || params->ssid_len > sizeof(drv->ssid))
if (!msg)
goto nla_put_failure;
- genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, NLM_F_DUMP,
- NL80211_CMD_GET_SCAN, 0);
+ nl80211_cmd(drv, msg, NLM_F_DUMP, NL80211_CMD_GET_SCAN);
NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex);
arg.drv = drv;
return -1;
wpa_printf(MSG_DEBUG, "nl80211: Connect (ifindex=%d)", drv->ifindex);
- genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, 0,
- NL80211_CMD_CONNECT, 0);
+ nl80211_cmd(drv, msg, 0, NL80211_CMD_CONNECT);
NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex);
if (params->bssid) {
wpa_printf(MSG_DEBUG, "nl80211: Associate (ifindex=%d)",
drv->ifindex);
- genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, 0,
- NL80211_CMD_ASSOCIATE, 0);
+ nl80211_cmd(drv, msg, 0, NL80211_CMD_ASSOCIATE);
NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex);
if (params->bssid) {
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));
+ wpa_dbg(drv->ctx, MSG_DEBUG,
+ "nl80211: MLME command failed (assoc): ret=%d (%s)",
+ ret, strerror(-ret));
nl80211_dump_scan(drv);
goto nla_put_failure;
}
if (!msg)
return -ENOMEM;
- genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0,
- 0, NL80211_CMD_SET_INTERFACE, 0);
+ nl80211_cmd(drv, msg, 0, NL80211_CMD_SET_INTERFACE);
NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, ifindex);
NLA_PUT_U32(msg, NL80211_ATTR_IFTYPE, mode);
ret = send_and_recv_msgs(drv, msg, NULL, NULL);
+ msg = NULL;
if (!ret)
return 0;
nla_put_failure:
+ nlmsg_free(msg);
wpa_printf(MSG_DEBUG, "nl80211: Failed to set interface %d to mode %d:"
" %d (%s)", ifindex, mode, ret, strerror(-ret));
return ret;
int ret = -1;
int i;
int was_ap = is_ap_interface(drv->nlmode);
+ int res;
- if (nl80211_set_mode(drv, drv->ifindex, nlmode) == 0) {
+ res = nl80211_set_mode(drv, drv->ifindex, nlmode);
+ if (res == 0) {
drv->nlmode = nlmode;
ret = 0;
goto done;
}
+ if (res == -ENODEV)
+ return -1;
+
if (nlmode == drv->nlmode) {
wpa_printf(MSG_DEBUG, "nl80211: Interface already in "
"requested mode - ignore error");
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) {
+ res = linux_set_iface_flags(drv->global->ioctl_sock,
+ bss->ifname, 0);
+ if (res == -EACCES || res == -ENODEV)
+ break;
+ if (res == 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))
+ if (ret == -EACCES)
+ break;
+ res = linux_set_iface_flags(drv->global->ioctl_sock,
+ bss->ifname, 1);
+ if (res && !ret)
ret = -1;
- if (!ret)
+ else if (ret != -EBUSY)
break;
} else
wpa_printf(MSG_DEBUG, "nl80211: Failed to set "
}
done:
- if (!ret && is_ap_interface(nlmode)) {
+ if (ret) {
+ wpa_printf(MSG_DEBUG, "nl80211: Interface mode change to %d "
+ "from %d failed", nlmode, drv->nlmode);
+ return ret;
+ }
+
+ if (is_ap_interface(nlmode)) {
+ nl80211_mgmt_unsubscribe(bss, "start AP");
/* Setup additional AP mode functionality if needed */
- if (!drv->no_monitor_iface_capab && drv->monitor_ifidx < 0 &&
- nl80211_create_monitor_interface(drv) &&
- !drv->no_monitor_iface_capab)
+ if (nl80211_setup_ap(bss))
return -1;
- } else if (!ret && !is_ap_interface(nlmode)) {
+ } else if (was_ap) {
/* Remove additional AP mode functionality */
- if (was_ap && drv->no_monitor_iface_capab)
- wpa_driver_nl80211_probe_req_report(bss, 0);
- nl80211_remove_monitor_interface(drv);
- bss->beacon_set = 0;
+ nl80211_teardown_ap(bss);
+ } else {
+ nl80211_mgmt_unsubscribe(bss, "mode change");
}
- if (ret)
- wpa_printf(MSG_DEBUG, "nl80211: Interface mode change to %d "
- "from %d failed", nlmode, drv->nlmode);
+ if (!is_ap_interface(nlmode) &&
+ nl80211_mgmt_subscribe_non_ap(bss) < 0)
+ wpa_printf(MSG_DEBUG, "nl80211: Failed to register Action "
+ "frame processing - ignore for now");
- return ret;
+ return 0;
}
wpa_printf(MSG_DEBUG, "%s: operstate %d->%d (%s)",
__func__, drv->operstate, state, state ? "UP" : "DORMANT");
drv->operstate = state;
- return netlink_send_oper_ifla(drv->netlink, drv->ifindex, -1,
+ return netlink_send_oper_ifla(drv->global->netlink, drv->ifindex, -1,
state ? IF_OPER_UP : IF_OPER_DORMANT);
}
if (!msg)
return -ENOMEM;
- genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0,
- 0, NL80211_CMD_SET_STATION, 0);
+ nl80211_cmd(drv, msg, 0, NL80211_CMD_SET_STATION);
NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX,
if_nametoindex(bss->ifname));
return send_and_recv_msgs(drv, msg, NULL, NULL);
nla_put_failure:
+ nlmsg_free(msg);
return -ENOBUFS;
}
static int i802_set_freq(void *priv, struct hostapd_freq_params *freq)
{
struct i802_bss *bss = priv;
- struct wpa_driver_nl80211_data *drv = bss->drv;
- return wpa_driver_nl80211_set_freq(drv, freq->freq, freq->ht_enabled,
+ return wpa_driver_nl80211_set_freq(bss, freq->freq, freq->ht_enabled,
freq->sec_channel_offset);
}
if (!msg)
return -ENOMEM;
- genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0,
- 0, NL80211_CMD_GET_KEY, 0);
+ nl80211_cmd(drv, msg, 0, NL80211_CMD_GET_KEY);
if (addr)
NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, addr);
return send_and_recv_msgs(drv, msg, get_key_handler, seq);
nla_put_failure:
- return -ENOBUFS;
-}
-
-
-static int i802_set_rate_sets(void *priv, int *supp_rates, int *basic_rates,
- int mode)
-{
- 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;
- int i;
-
- 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);
-
- for (i = 0; i < NL80211_MAX_SUPP_RATES && basic_rates[i] >= 0; i++)
- rates[rates_len++] = basic_rates[i] / 5;
-
- NLA_PUT(msg, NL80211_ATTR_BSS_BASIC_RATES, rates_len, rates);
-
- NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, if_nametoindex(bss->ifname));
-
- return send_and_recv_msgs(drv, msg, NULL, NULL);
- nla_put_failure:
+ nlmsg_free(msg);
return -ENOBUFS;
}
else
val = rts;
- genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0,
- 0, NL80211_CMD_SET_WIPHY, 0);
+ nl80211_cmd(drv, msg, 0, NL80211_CMD_SET_WIPHY);
NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex);
NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_RTS_THRESHOLD, val);
ret = send_and_recv_msgs(drv, msg, NULL, NULL);
+ msg = NULL;
if (!ret)
return 0;
nla_put_failure:
+ nlmsg_free(msg);
wpa_printf(MSG_DEBUG, "nl80211: Failed to set RTS threshold %d: "
"%d (%s)", rts, ret, strerror(-ret));
return ret;
else
val = frag;
- genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0,
- 0, NL80211_CMD_SET_WIPHY, 0);
+ nl80211_cmd(drv, msg, 0, NL80211_CMD_SET_WIPHY);
NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex);
NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_FRAG_THRESHOLD, val);
ret = send_and_recv_msgs(drv, msg, NULL, NULL);
+ msg = NULL;
if (!ret)
return 0;
nla_put_failure:
+ nlmsg_free(msg);
wpa_printf(MSG_DEBUG, "nl80211: Failed to set fragmentation threshold "
"%d: %d (%s)", frag, ret, strerror(-ret));
return ret;
struct i802_bss *bss = priv;
struct wpa_driver_nl80211_data *drv = bss->drv;
struct nl_msg *msg;
+ int res;
msg = nlmsg_alloc();
if (!msg)
return -1;
- genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0,
- 0, NL80211_CMD_DEL_STATION, 0);
+ nl80211_cmd(drv, msg, 0, NL80211_CMD_DEL_STATION);
/*
* XXX: FIX! this needs to flush all VLANs too
NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX,
if_nametoindex(bss->ifname));
- return send_and_recv_msgs(drv, msg, NULL, NULL);
+ res = send_and_recv_msgs(drv, msg, NULL, NULL);
+ if (res) {
+ wpa_printf(MSG_DEBUG, "nl80211: Station flush failed: ret=%d "
+ "(%s)", res, strerror(-res));
+ }
+ return res;
nla_put_failure:
+ nlmsg_free(msg);
return -ENOBUFS;
}
if (!msg)
return -ENOMEM;
- genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0,
- 0, NL80211_CMD_GET_STATION, 0);
+ nl80211_cmd(drv, msg, 0, NL80211_CMD_GET_STATION);
NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, addr);
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:
+ nlmsg_free(msg);
return -ENOBUFS;
}
if (!msg)
return -1;
- genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0,
- 0, NL80211_CMD_SET_WIPHY, 0);
+ nl80211_cmd(drv, msg, 0, NL80211_CMD_SET_WIPHY);
NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, if_nametoindex(bss->ifname));
if (send_and_recv_msgs(drv, msg, NULL, NULL) == 0)
return 0;
+ msg = NULL;
nla_put_failure:
+ nlmsg_free(msg);
return -1;
}
-static int i802_set_bss(void *priv, int cts, int preamble, int slot,
- int ht_opmode)
-{
- struct i802_bss *bss = priv;
- struct wpa_driver_nl80211_data *drv = bss->drv;
- 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);
-
- if (cts >= 0)
- NLA_PUT_U8(msg, NL80211_ATTR_BSS_CTS_PROT, cts);
- if (preamble >= 0)
- NLA_PUT_U8(msg, NL80211_ATTR_BSS_SHORT_PREAMBLE, preamble);
- if (slot >= 0)
- NLA_PUT_U8(msg, NL80211_ATTR_BSS_SHORT_SLOT_TIME, slot);
- if (ht_opmode >= 0)
- NLA_PUT_U16(msg, NL80211_ATTR_BSS_HT_OPMODE, ht_opmode);
- NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, if_nametoindex(bss->ifname));
-
- return send_and_recv_msgs(drv, msg, NULL, NULL);
- nla_put_failure:
- return -ENOBUFS;
-}
-
-
-static int i802_set_cts_protect(void *priv, int value)
-{
- return i802_set_bss(priv, value, -1, -1, -1);
-}
-
-
-static int i802_set_preamble(void *priv, int value)
-{
- return i802_set_bss(priv, -1, value, -1, -1);
-}
-
-
-static int i802_set_short_slot_time(void *priv, int value)
-{
- return i802_set_bss(priv, -1, -1, value, -1);
-}
-
-
static int i802_set_sta_vlan(void *priv, const u8 *addr,
const char *ifname, int vlan_id)
{
if (!msg)
return -ENOMEM;
- genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0,
- 0, NL80211_CMD_SET_STATION, 0);
+ nl80211_cmd(drv, msg, 0, NL80211_CMD_SET_STATION);
NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX,
if_nametoindex(bss->ifname));
if_nametoindex(ifname));
ret = send_and_recv_msgs(drv, msg, NULL, NULL);
+ msg = NULL;
if (ret < 0) {
wpa_printf(MSG_ERROR, "nl80211: NL80211_ATTR_STA_VLAN (addr="
MACSTR " ifname=%s vlan_id=%d) failed: %d (%s)",
strerror(-ret));
}
nla_put_failure:
- return ret;
-}
-
-
-static int i802_set_ht_params(void *priv, const u8 *ht_capab,
- size_t ht_capab_len, const u8 *ht_oper,
- size_t ht_oper_len)
-{
- if (ht_oper_len >= 6) {
- /* ht opmode uses 16bit in octet 5 & 6 */
- u16 ht_opmode = le_to_host16(((u16 *) ht_oper)[2]);
- return i802_set_bss(priv, -1, -1, -1, ht_opmode);
- } else
- return -1;
+ nlmsg_free(msg);
+ return ret;
}
mgmt.u.deauth.reason_code = host_to_le16(reason);
return wpa_driver_nl80211_send_mlme(bss, (u8 *) &mgmt,
IEEE80211_HDRLEN +
- sizeof(mgmt.u.deauth));
+ sizeof(mgmt.u.deauth), 0);
}
mgmt.u.disassoc.reason_code = host_to_le16(reason);
return wpa_driver_nl80211_send_mlme(bss, (u8 *) &mgmt,
IEEE80211_HDRLEN +
- sizeof(mgmt.u.disassoc));
+ sizeof(mgmt.u.disassoc), 0);
}
#endif /* HOSTAPD || CONFIG_AP */
NULL, 1) < 0)
return -1;
if (bridge_ifname &&
- linux_br_add_if(drv->ioctl_sock, bridge_ifname,
- name) < 0)
+ linux_br_add_if(drv->global->ioctl_sock,
+ bridge_ifname, name) < 0)
return -1;
}
- linux_set_iface_flags(drv->ioctl_sock, name, 1);
+ linux_set_iface_flags(drv->global->ioctl_sock, name, 1);
return i802_set_sta_vlan(priv, addr, name, 0);
} else {
i802_set_sta_vlan(priv, addr, bss->ifname, 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) {
+ if (linux_br_add(drv->global->ioctl_sock, brname) < 0) {
wpa_printf(MSG_ERROR, "nl80211: Failed to add the "
"bridge interface %s: %s",
brname, strerror(errno));
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) {
+ if (linux_br_del_if(drv->global->ioctl_sock, in_br, ifname) <
+ 0) {
wpa_printf(MSG_ERROR, "nl80211: Failed to "
"remove interface %s from bridge "
"%s: %s",
wpa_printf(MSG_DEBUG, "nl80211: Adding interface %s into bridge %s",
ifname, brname);
- if (linux_br_add_if(drv->ioctl_sock, brname, ifname) < 0) {
+ if (linux_br_add_if(drv->global->ioctl_sock, brname, ifname) < 0) {
wpa_printf(MSG_ERROR, "nl80211: Failed to add interface %s "
"into bridge %s: %s",
ifname, brname, strerror(errno));
int ifindex, br_ifindex;
int br_added = 0;
- bss = wpa_driver_nl80211_init(hapd, params->ifname, NULL);
+ bss = wpa_driver_nl80211_init(hapd, params->ifname,
+ params->global_priv);
if (bss == NULL)
return NULL;
drv = bss->drv;
drv->nlmode = NL80211_IFTYPE_AP;
+ drv->eapol_sock = -1;
+
if (linux_br_get(brname, params->ifname) == 0) {
wpa_printf(MSG_DEBUG, "nl80211: Interface %s is in bridge %s",
params->ifname, brname);
/* start listening for EAPOL on the default AP interface */
add_ifidx(drv, drv->ifindex);
- if (linux_set_iface_flags(drv->ioctl_sock, bss->ifname, 0))
+ if (linux_set_iface_flags(drv->global->ioctl_sock, bss->ifname, 0))
goto failed;
if (params->bssid) {
- if (linux_set_ifhwaddr(drv->ioctl_sock, bss->ifname,
+ if (linux_set_ifhwaddr(drv->global->ioctl_sock, bss->ifname,
params->bssid))
goto failed;
}
i802_check_bridge(drv, bss, params->bridge[0], params->ifname) < 0)
goto failed;
- if (linux_set_iface_flags(drv->ioctl_sock, bss->ifname, 1))
+ if (linux_set_iface_flags(drv->global->ioctl_sock, bss->ifname, 1))
goto failed;
drv->eapol_sock = socket(PF_PACKET, SOCK_DGRAM, htons(ETH_P_PAE));
goto failed;
}
- if (linux_get_ifhwaddr(drv->ioctl_sock, bss->ifname, params->own_addr))
+ if (linux_get_ifhwaddr(drv->global->ioctl_sock, bss->ifname,
+ params->own_addr))
goto failed;
+ memcpy(bss->addr, params->own_addr, ETH_ALEN);
+
return bss;
failed:
- nl80211_remove_monitor_interface(drv);
- 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);
- 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);
+ wpa_driver_nl80211_deinit(bss);
return NULL;
}
struct wpa_driver_nl80211_data *drv;
dl_list_for_each(drv, &global->interfaces,
struct wpa_driver_nl80211_data, list) {
- if (os_memcmp(addr, drv->addr, ETH_ALEN) == 0)
+ if (os_memcmp(addr, drv->first_bss.addr, ETH_ALEN) == 0)
return 1;
}
return 0;
if (!drv->global)
return -1;
- os_memcpy(new_addr, drv->addr, ETH_ALEN);
+ os_memcpy(new_addr, drv->first_bss.addr, ETH_ALEN);
for (idx = 0; idx < 64; idx++) {
- new_addr[0] = drv->addr[0] | 0x02;
+ new_addr[0] = drv->first_bss.addr[0] | 0x02;
new_addr[0] ^= idx << 2;
if (!nl80211_addr_in_use(drv->global, new_addr))
break;
}
if (!addr &&
- linux_get_ifhwaddr(drv->ioctl_sock, bss->ifname, if_addr) < 0) {
+ linux_get_ifhwaddr(drv->global->ioctl_sock, bss->ifname,
+ if_addr) < 0) {
nl80211_remove_iface(drv, ifidx);
return -1;
}
/* Enforce unique P2P Interface Address */
u8 new_addr[ETH_ALEN], own_addr[ETH_ALEN];
- if (linux_get_ifhwaddr(drv->ioctl_sock, bss->ifname, own_addr)
- < 0 ||
- linux_get_ifhwaddr(drv->ioctl_sock, ifname, new_addr) < 0)
- {
+ if (linux_get_ifhwaddr(drv->global->ioctl_sock, bss->ifname,
+ own_addr) < 0 ||
+ linux_get_ifhwaddr(drv->global->ioctl_sock, ifname,
+ new_addr) < 0) {
nl80211_remove_iface(drv, ifidx);
return -1;
}
nl80211_remove_iface(drv, ifidx);
return -1;
}
- if (linux_set_ifhwaddr(drv->ioctl_sock, ifname,
+ if (linux_set_ifhwaddr(drv->global->ioctl_sock, ifname,
new_addr) < 0) {
nl80211_remove_iface(drv, ifidx);
return -1;
}
if (type == WPA_IF_AP_BSS) {
- if (linux_set_iface_flags(drv->ioctl_sock, ifname, 1)) {
+ if (linux_set_iface_flags(drv->global->ioctl_sock, ifname, 1))
+ {
nl80211_remove_iface(drv, ifidx);
os_free(new_bss);
return -1;
}
os_strlcpy(new_bss->ifname, ifname, IFNAMSIZ);
+ os_memcpy(new_bss->addr, if_addr, ETH_ALEN);
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;
+ nl80211_init_bss(new_bss);
}
#endif /* HOSTAPD */
#ifdef HOSTAPD
if (bss->added_if_into_bridge) {
- if (linux_br_del_if(drv->ioctl_sock, bss->brname, bss->ifname)
- < 0)
+ if (linux_br_del_if(drv->global->ioctl_sock, bss->brname,
+ bss->ifname) < 0)
wpa_printf(MSG_INFO, "nl80211: Failed to remove "
"interface %s from bridge %s: %s",
bss->ifname, bss->brname, strerror(errno));
}
if (bss->added_bridge) {
- if (linux_br_del(drv->ioctl_sock, bss->brname) < 0)
+ if (linux_br_del(drv->global->ioctl_sock, bss->brname) < 0)
wpa_printf(MSG_INFO, "nl80211: Failed to remove "
"bridge %s: %s",
bss->brname, strerror(errno));
for (tbss = &drv->first_bss; tbss; tbss = tbss->next) {
if (tbss->next == bss) {
tbss->next = bss->next;
+ nl80211_destroy_bss(bss);
os_free(bss);
bss = NULL;
break;
}
-static int nl80211_send_frame_cmd(struct wpa_driver_nl80211_data *drv,
+static int nl80211_send_frame_cmd(struct i802_bss *bss,
unsigned int freq, unsigned int wait,
const u8 *buf, size_t buf_len,
- u64 *cookie_out)
+ u64 *cookie_out, int no_cck, int no_ack,
+ int offchanok)
{
+ struct wpa_driver_nl80211_data *drv = bss->drv;
struct nl_msg *msg;
u64 cookie;
int ret = -1;
if (!msg)
return -1;
- genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, 0,
- NL80211_CMD_FRAME, 0);
+ nl80211_cmd(drv, msg, 0, NL80211_CMD_FRAME);
- NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex);
+ NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, bss->ifindex);
NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_FREQ, freq);
if (wait)
NLA_PUT_U32(msg, NL80211_ATTR_DURATION, wait);
- NLA_PUT_FLAG(msg, NL80211_ATTR_OFFCHANNEL_TX_OK);
+ if (offchanok && (drv->capa.flags & WPA_DRIVER_FLAGS_OFFCHANNEL_TX))
+ NLA_PUT_FLAG(msg, NL80211_ATTR_OFFCHANNEL_TX_OK);
+ if (no_cck)
+ NLA_PUT_FLAG(msg, NL80211_ATTR_TX_NO_CCK_RATE);
+ if (no_ack)
+ NLA_PUT_FLAG(msg, NL80211_ATTR_DONT_WAIT_FOR_ACK);
+
NLA_PUT(msg, NL80211_ATTR_FRAME, buf_len, buf);
cookie = 0;
freq, wait);
goto nla_put_failure;
}
- wpa_printf(MSG_DEBUG, "nl80211: Frame TX command accepted; "
- "cookie 0x%llx", (long long unsigned int) cookie);
+ wpa_printf(MSG_DEBUG, "nl80211: Frame TX command accepted%s; "
+ "cookie 0x%llx", no_ack ? " (no ACK)" : "",
+ (long long unsigned int) cookie);
if (cookie_out)
- *cookie_out = cookie;
+ *cookie_out = no_ack ? (u64) -1 : cookie;
nla_put_failure:
nlmsg_free(msg);
unsigned int wait_time,
const u8 *dst, const u8 *src,
const u8 *bssid,
- const u8 *data, size_t data_len)
+ const u8 *data, size_t data_len,
+ int no_cck)
{
struct i802_bss *bss = priv;
struct wpa_driver_nl80211_data *drv = bss->drv;
struct ieee80211_hdr *hdr;
wpa_printf(MSG_DEBUG, "nl80211: Send Action frame (ifindex=%d, "
- "wait=%d ms)", drv->ifindex, wait_time);
+ "wait=%d ms no_cck=%d)", drv->ifindex, wait_time, no_cck);
buf = os_zalloc(24 + data_len);
if (buf == NULL)
os_memcpy(hdr->addr3, bssid, ETH_ALEN);
if (is_ap_interface(drv->nlmode))
- ret = wpa_driver_nl80211_send_mlme(priv, buf, 24 + data_len);
+ ret = wpa_driver_nl80211_send_mlme(priv, buf, 24 + data_len,
+ 0);
else
- ret = nl80211_send_frame_cmd(drv, freq, wait_time, buf,
+ ret = nl80211_send_frame_cmd(bss, freq, wait_time, buf,
24 + data_len,
- &drv->send_action_cookie);
+ &drv->send_action_cookie,
+ no_cck, 0, 1);
os_free(buf);
return ret;
if (!msg)
return;
- genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, 0,
- NL80211_CMD_FRAME_WAIT_CANCEL, 0);
+ nl80211_cmd(drv, msg, 0, NL80211_CMD_FRAME_WAIT_CANCEL);
NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex);
NLA_PUT_U64(msg, NL80211_ATTR_COOKIE, drv->send_action_cookie);
if (!msg)
return -1;
- genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, 0,
- NL80211_CMD_REMAIN_ON_CHANNEL, 0);
+ nl80211_cmd(drv, msg, 0, NL80211_CMD_REMAIN_ON_CHANNEL);
NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex);
NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_FREQ, freq);
cookie = 0;
ret = send_and_recv_msgs(drv, msg, cookie_handler, &cookie);
+ msg = NULL;
if (ret == 0) {
wpa_printf(MSG_DEBUG, "nl80211: Remain-on-channel cookie "
"0x%llx for freq=%u MHz duration=%u",
"(freq=%d duration=%u): %d (%s)",
freq, duration, ret, strerror(-ret));
nla_put_failure:
+ nlmsg_free(msg);
return -1;
}
if (!msg)
return -1;
- genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, 0,
- NL80211_CMD_CANCEL_REMAIN_ON_CHANNEL, 0);
+ nl80211_cmd(drv, msg, 0, NL80211_CMD_CANCEL_REMAIN_ON_CHANNEL);
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);
+ msg = 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:
+ nlmsg_free(msg);
return -1;
}
struct wpa_driver_nl80211_data *drv = bss->drv;
if (!report) {
- if (drv->nl_handle_preq) {
+ if (bss->nl_preq && drv->device_ap_sme &&
+ is_ap_interface(drv->nlmode)) {
+ /*
+ * Do not disable Probe Request reporting that was
+ * enabled in nl80211_setup_ap().
+ */
+ wpa_printf(MSG_DEBUG, "nl80211: Skip disabling of "
+ "Probe Request reporting nl_preq=%p while "
+ "in AP mode", bss->nl_preq);
+ } else if (bss->nl_preq) {
+ wpa_printf(MSG_DEBUG, "nl80211: Disable Probe Request "
+ "reporting nl_preq=%p", bss->nl_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;
+ nl_socket_get_fd(bss->nl_preq));
+ nl_destroy_handles(&bss->nl_preq);
}
return 0;
}
- if (drv->nl_handle_preq) {
+ if (bss->nl_preq) {
wpa_printf(MSG_DEBUG, "nl80211: Probe Request reporting "
- "already on!");
+ "already on! nl_preq=%p", bss->nl_preq);
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;
+ bss->nl_preq = nl_create_handle(drv->global->nl_cb, "preq");
+ if (bss->nl_preq == NULL)
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 */
+ wpa_printf(MSG_DEBUG, "nl80211: Enable Probe Request "
+ "reporting nl_preq=%p", bss->nl_preq);
- if (nl80211_register_frame(drv, drv->nl_handle_preq,
+ if (nl80211_register_frame(bss, bss->nl_preq,
(WLAN_FC_TYPE_MGMT << 2) |
(WLAN_FC_STYPE_PROBE_REQ << 4),
- NULL, 0) < 0) {
- goto out_err3;
- }
+ NULL, 0) < 0)
+ goto out_err;
- eloop_register_read_sock(nl_socket_get_fd(drv->nl_handle_preq),
- wpa_driver_nl80211_event_receive, drv,
- drv->nl_handle_preq);
+ eloop_register_read_sock(nl_socket_get_fd(bss->nl_preq),
+ wpa_driver_nl80211_event_receive, bss->nl_cb,
+ bss->nl_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:
+ out_err:
+ nl_destroy_handles(&bss->nl_preq);
return -1;
}
if (!msg)
return -1;
- genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, 0,
- NL80211_CMD_SET_TX_BITRATE_MASK, 0);
+ nl80211_cmd(drv, msg, 0, NL80211_CMD_SET_TX_BITRATE_MASK);
NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, ifindex);
bands = nla_nest_start(msg, NL80211_ATTR_TX_RATES);
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");
+ if (disabled) {
+ 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);
}
-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 i802_bss *bss = priv;
struct wpa_driver_nl80211_data *drv = bss->drv;
- if (linux_set_iface_flags(drv->ioctl_sock, bss->ifname, 1)) {
+ if (linux_set_iface_flags(drv->global->ioctl_sock, bss->ifname, 1)) {
wpa_printf(MSG_DEBUG, "nl80211: Failed to set interface up on "
"resume event");
}
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;
+ const u8 *own_addr = bss->addr;
if (action != 1) {
wpa_printf(MSG_ERROR, "nl80211: Unsupported send_ft_action "
ret = wpa_driver_nl80211_send_action(bss, drv->assoc_freq, 0,
drv->bssid, own_addr, drv->bssid,
- data, data_len);
+ data, data_len, 0);
os_free(data);
return ret;
if (!msg)
return -1;
- genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0,
- 0, NL80211_CMD_SET_CQM, 0);
+ nl80211_cmd(drv, msg, 0, NL80211_CMD_SET_CQM);
NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, bss->ifindex);
msg = NULL;
nla_put_failure:
- if (cqm)
- nlmsg_free(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)
+static int wpa_driver_nl80211_shared_freq(void *priv)
{
struct i802_bss *bss = priv;
struct wpa_driver_nl80211_data *drv = bss->drv;
- return wpa_driver_nl80211_send_frame(drv, data, data_len, encrypt);
-}
+ struct wpa_driver_nl80211_data *driver;
+ int freq = 0;
+
+ /*
+ * If the same PHY is in connected state with some other interface,
+ * then retrieve the assoc freq.
+ */
+ wpa_printf(MSG_DEBUG, "nl80211: Get shared freq for PHY %s",
+ drv->phyname);
+ dl_list_for_each(driver, &drv->global->interfaces,
+ struct wpa_driver_nl80211_data, list) {
+ if (drv == driver ||
+ os_strcmp(drv->phyname, driver->phyname) != 0 ||
+ !driver->associated)
+ continue;
-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;
+ wpa_printf(MSG_DEBUG, "nl80211: Found a match for PHY %s - %s "
+ MACSTR,
+ driver->phyname, driver->first_bss.ifname,
+ MAC2STR(driver->first_bss.addr));
+ freq = nl80211_get_assoc_freq(driver);
+ wpa_printf(MSG_DEBUG, "nl80211: Shared freq for PHY %s: %d",
+ drv->phyname, freq);
+ }
- msg = nlmsg_alloc();
- if (!msg)
- return -ENOMEM;
+ if (!freq)
+ wpa_printf(MSG_DEBUG, "nl80211: No shared interface for "
+ "PHY (%s) in associated state", drv->phyname);
- genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, 0,
- NL80211_CMD_SET_BSS, 0);
+ return freq;
+}
- 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;
+static int nl80211_send_frame(void *priv, const u8 *data, size_t data_len,
+ int encrypt)
+{
+ struct i802_bss *bss = priv;
+ return wpa_driver_nl80211_send_frame(bss, data, data_len, encrypt, 0);
}
static void * nl80211_global_init(void)
{
struct nl80211_global *global;
+ struct netlink_config *cfg;
+
global = os_zalloc(sizeof(*global));
if (global == NULL)
return NULL;
+ global->ioctl_sock = -1;
dl_list_init(&global->interfaces);
global->if_add_ifindex = -1;
+
+ cfg = os_zalloc(sizeof(*cfg));
+ if (cfg == NULL)
+ goto err;
+
+ cfg->ctx = global;
+ cfg->newlink_cb = wpa_driver_nl80211_event_rtm_newlink;
+ cfg->dellink_cb = wpa_driver_nl80211_event_rtm_dellink;
+ global->netlink = netlink_init(cfg);
+ if (global->netlink == NULL) {
+ os_free(cfg);
+ goto err;
+ }
+
+ if (wpa_driver_nl80211_init_nl_global(global) < 0)
+ goto err;
+
+ global->ioctl_sock = socket(PF_INET, SOCK_DGRAM, 0);
+ if (global->ioctl_sock < 0) {
+ perror("socket(PF_INET,SOCK_DGRAM)");
+ goto err;
+ }
+
return global;
+
+err:
+ nl80211_global_deinit(global);
+ return NULL;
}
"nl80211_global_deinit",
dl_list_len(&global->interfaces));
}
+
+ if (global->netlink)
+ netlink_deinit(global->netlink);
+
+ nl_destroy_handles(&global->nl);
+
+ if (global->nl_event) {
+ eloop_unregister_read_sock(
+ nl_socket_get_fd(global->nl_event));
+ nl_destroy_handles(&global->nl_event);
+ }
+
+ nl_cb_put(global->nl_cb);
+
+ if (global->ioctl_sock >= 0)
+ close(global->ioctl_sock);
+
os_free(global);
}
if (!msg)
return -ENOMEM;
- genlmsg_put(msg, 0, 0, genl_family_get_id(bss->drv->nl80211), 0, 0,
- cmd, 0);
+ nl80211_cmd(bss->drv, msg, 0, cmd);
NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, if_nametoindex(bss->ifname));
if (pmkid)
return send_and_recv_msgs(bss->drv, msg, NULL, NULL);
nla_put_failure:
+ nlmsg_free(msg);
return -ENOBUFS;
}
if (!msg)
return;
- genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, 0,
- NL80211_CMD_SET_REKEY_OFFLOAD, 0);
+ nl80211_cmd(drv, msg, 0, NL80211_CMD_SET_REKEY_OFFLOAD);
NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, bss->ifindex);
}
+static void nl80211_send_null_frame(struct i802_bss *bss, const u8 *own_addr,
+ const u8 *addr, int qos)
+{
+ /* send data frame to poll STA and check whether
+ * this frame is ACKed */
+ struct {
+ struct ieee80211_hdr hdr;
+ u16 qos_ctl;
+ } STRUCT_PACKED nulldata;
+ size_t size;
+
+ /* Send data frame to poll STA and check whether this frame is ACKed */
+
+ os_memset(&nulldata, 0, sizeof(nulldata));
+
+ if (qos) {
+ nulldata.hdr.frame_control =
+ IEEE80211_FC(WLAN_FC_TYPE_DATA,
+ WLAN_FC_STYPE_QOS_NULL);
+ size = sizeof(nulldata);
+ } else {
+ nulldata.hdr.frame_control =
+ IEEE80211_FC(WLAN_FC_TYPE_DATA,
+ WLAN_FC_STYPE_NULLFUNC);
+ size = sizeof(struct ieee80211_hdr);
+ }
+
+ nulldata.hdr.frame_control |= host_to_le16(WLAN_FC_FROMDS);
+ os_memcpy(nulldata.hdr.IEEE80211_DA_FROMDS, addr, ETH_ALEN);
+ os_memcpy(nulldata.hdr.IEEE80211_BSSID_FROMDS, own_addr, ETH_ALEN);
+ os_memcpy(nulldata.hdr.IEEE80211_SA_FROMDS, own_addr, ETH_ALEN);
+
+ if (wpa_driver_nl80211_send_mlme(bss, (u8 *) &nulldata, size, 0) < 0)
+ wpa_printf(MSG_DEBUG, "nl80211_send_null_frame: Failed to "
+ "send poll frame");
+}
+
+static void nl80211_poll_client(void *priv, const u8 *own_addr, const u8 *addr,
+ int qos)
+{
+ struct i802_bss *bss = priv;
+ struct wpa_driver_nl80211_data *drv = bss->drv;
+ struct nl_msg *msg;
+
+ if (!drv->poll_command_supported) {
+ nl80211_send_null_frame(bss, own_addr, addr, qos);
+ return;
+ }
+
+ msg = nlmsg_alloc();
+ if (!msg)
+ return;
+
+ nl80211_cmd(drv, msg, 0, NL80211_CMD_PROBE_CLIENT);
+
+ NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, bss->ifindex);
+ NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, addr);
+
+ send_and_recv_msgs(drv, msg, NULL, NULL);
+ return;
+ nla_put_failure:
+ nlmsg_free(msg);
+}
+
+
+static int nl80211_set_power_save(struct i802_bss *bss, int enabled)
+{
+ struct nl_msg *msg;
+
+ msg = nlmsg_alloc();
+ if (!msg)
+ return -ENOMEM;
+
+ nl80211_cmd(bss->drv, msg, 0, NL80211_CMD_SET_POWER_SAVE);
+ NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, bss->ifindex);
+ NLA_PUT_U32(msg, NL80211_ATTR_PS_STATE,
+ enabled ? NL80211_PS_ENABLED : NL80211_PS_DISABLED);
+ return send_and_recv_msgs(bss->drv, msg, NULL, NULL);
+nla_put_failure:
+ nlmsg_free(msg);
+ return -ENOBUFS;
+}
+
+
+static int nl80211_set_p2p_powersave(void *priv, int legacy_ps, int opp_ps,
+ int ctwindow)
+{
+ struct i802_bss *bss = priv;
+
+ wpa_printf(MSG_DEBUG, "nl80211: set_p2p_powersave (legacy_ps=%d "
+ "opp_ps=%d ctwindow=%d)", legacy_ps, opp_ps, ctwindow);
+
+ if (opp_ps != -1 || ctwindow != -1)
+ return -1; /* Not yet supported */
+
+ if (legacy_ps == -1)
+ return 0;
+ if (legacy_ps != 0 && legacy_ps != 1)
+ return -1; /* Not yet supported */
+
+ return nl80211_set_power_save(bss, legacy_ps);
+}
+
+
+#ifdef CONFIG_TDLS
+
+static int nl80211_send_tdls_mgmt(void *priv, const u8 *dst, u8 action_code,
+ u8 dialog_token, u16 status_code,
+ const u8 *buf, size_t len)
+{
+ struct i802_bss *bss = priv;
+ struct wpa_driver_nl80211_data *drv = bss->drv;
+ struct nl_msg *msg;
+
+ if (!(drv->capa.flags & WPA_DRIVER_FLAGS_TDLS_SUPPORT))
+ return -EOPNOTSUPP;
+
+ if (!dst)
+ return -EINVAL;
+
+ msg = nlmsg_alloc();
+ if (!msg)
+ return -ENOMEM;
+
+ nl80211_cmd(drv, msg, 0, NL80211_CMD_TDLS_MGMT);
+ NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex);
+ NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, dst);
+ NLA_PUT_U8(msg, NL80211_ATTR_TDLS_ACTION, action_code);
+ NLA_PUT_U8(msg, NL80211_ATTR_TDLS_DIALOG_TOKEN, dialog_token);
+ NLA_PUT_U16(msg, NL80211_ATTR_STATUS_CODE, status_code);
+ NLA_PUT(msg, NL80211_ATTR_IE, len, buf);
+
+ return send_and_recv_msgs(drv, msg, NULL, NULL);
+
+nla_put_failure:
+ nlmsg_free(msg);
+ return -ENOBUFS;
+}
+
+
+static int nl80211_tdls_oper(void *priv, enum tdls_oper oper, const u8 *peer)
+{
+ struct i802_bss *bss = priv;
+ struct wpa_driver_nl80211_data *drv = bss->drv;
+ struct nl_msg *msg;
+ enum nl80211_tdls_operation nl80211_oper;
+
+ if (!(drv->capa.flags & WPA_DRIVER_FLAGS_TDLS_SUPPORT))
+ return -EOPNOTSUPP;
+
+ switch (oper) {
+ case TDLS_DISCOVERY_REQ:
+ nl80211_oper = NL80211_TDLS_DISCOVERY_REQ;
+ break;
+ case TDLS_SETUP:
+ nl80211_oper = NL80211_TDLS_SETUP;
+ break;
+ case TDLS_TEARDOWN:
+ nl80211_oper = NL80211_TDLS_TEARDOWN;
+ break;
+ case TDLS_ENABLE_LINK:
+ nl80211_oper = NL80211_TDLS_ENABLE_LINK;
+ break;
+ case TDLS_DISABLE_LINK:
+ nl80211_oper = NL80211_TDLS_DISABLE_LINK;
+ break;
+ case TDLS_ENABLE:
+ return 0;
+ case TDLS_DISABLE:
+ return 0;
+ default:
+ return -EINVAL;
+ }
+
+ msg = nlmsg_alloc();
+ if (!msg)
+ return -ENOMEM;
+
+ nl80211_cmd(drv, msg, 0, NL80211_CMD_TDLS_OPER);
+ NLA_PUT_U8(msg, NL80211_ATTR_TDLS_OPERATION, nl80211_oper);
+ NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex);
+ NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, peer);
+
+ return send_and_recv_msgs(drv, msg, NULL, NULL);
+
+nla_put_failure:
+ nlmsg_free(msg);
+ return -ENOBUFS;
+}
+
+#endif /* CONFIG TDLS */
+
+
+#ifdef ANDROID
+
+typedef struct android_wifi_priv_cmd {
+ char *buf;
+ int used_len;
+ int total_len;
+} android_wifi_priv_cmd;
+
+static int drv_errors = 0;
+
+static void wpa_driver_send_hang_msg(struct wpa_driver_nl80211_data *drv)
+{
+ drv_errors++;
+ if (drv_errors > DRV_NUMBER_SEQUENTIAL_ERRORS) {
+ drv_errors = 0;
+ wpa_msg(drv->ctx, MSG_INFO, WPA_EVENT_DRIVER_STATE "HANGED");
+ }
+}
+
+
+static int android_priv_cmd(struct i802_bss *bss, const char *cmd)
+{
+ struct wpa_driver_nl80211_data *drv = bss->drv;
+ struct ifreq ifr;
+ android_wifi_priv_cmd priv_cmd;
+ char buf[MAX_DRV_CMD_SIZE];
+ int ret;
+
+ os_memset(&ifr, 0, sizeof(ifr));
+ os_memset(&priv_cmd, 0, sizeof(priv_cmd));
+ os_strlcpy(ifr.ifr_name, bss->ifname, IFNAMSIZ);
+
+ os_memset(buf, 0, sizeof(buf));
+ os_strlcpy(buf, cmd, sizeof(buf));
+
+ priv_cmd.buf = buf;
+ priv_cmd.used_len = sizeof(buf);
+ priv_cmd.total_len = sizeof(buf);
+ ifr.ifr_data = &priv_cmd;
+
+ ret = ioctl(drv->global->ioctl_sock, SIOCDEVPRIVATE + 1, &ifr);
+ if (ret < 0) {
+ wpa_printf(MSG_ERROR, "%s: failed to issue private commands",
+ __func__);
+ wpa_driver_send_hang_msg(drv);
+ return ret;
+ }
+
+ drv_errors = 0;
+ return 0;
+}
+
+
+static int android_pno_start(struct i802_bss *bss,
+ struct wpa_driver_scan_params *params)
+{
+ struct wpa_driver_nl80211_data *drv = bss->drv;
+ struct ifreq ifr;
+ android_wifi_priv_cmd priv_cmd;
+ int ret = 0, i = 0, bp;
+ char buf[WEXT_PNO_MAX_COMMAND_SIZE];
+
+ bp = WEXT_PNOSETUP_HEADER_SIZE;
+ os_memcpy(buf, WEXT_PNOSETUP_HEADER, bp);
+ buf[bp++] = WEXT_PNO_TLV_PREFIX;
+ buf[bp++] = WEXT_PNO_TLV_VERSION;
+ buf[bp++] = WEXT_PNO_TLV_SUBVERSION;
+ buf[bp++] = WEXT_PNO_TLV_RESERVED;
+
+ while (i < WEXT_PNO_AMOUNT && (size_t) i < params->num_ssids) {
+ /* Check that there is enough space needed for 1 more SSID, the
+ * other sections and null termination */
+ if ((bp + WEXT_PNO_SSID_HEADER_SIZE + MAX_SSID_LEN +
+ WEXT_PNO_NONSSID_SECTIONS_SIZE + 1) >= (int) sizeof(buf))
+ break;
+ wpa_hexdump_ascii(MSG_DEBUG, "For PNO Scan",
+ params->ssids[i].ssid,
+ params->ssids[i].ssid_len);
+ buf[bp++] = WEXT_PNO_SSID_SECTION;
+ buf[bp++] = params->ssids[i].ssid_len;
+ os_memcpy(&buf[bp], params->ssids[i].ssid,
+ params->ssids[i].ssid_len);
+ bp += params->ssids[i].ssid_len;
+ i++;
+ }
+
+ buf[bp++] = WEXT_PNO_SCAN_INTERVAL_SECTION;
+ os_snprintf(&buf[bp], WEXT_PNO_SCAN_INTERVAL_LENGTH + 1, "%x",
+ WEXT_PNO_SCAN_INTERVAL);
+ bp += WEXT_PNO_SCAN_INTERVAL_LENGTH;
+
+ buf[bp++] = WEXT_PNO_REPEAT_SECTION;
+ os_snprintf(&buf[bp], WEXT_PNO_REPEAT_LENGTH + 1, "%x",
+ WEXT_PNO_REPEAT);
+ bp += WEXT_PNO_REPEAT_LENGTH;
+
+ buf[bp++] = WEXT_PNO_MAX_REPEAT_SECTION;
+ os_snprintf(&buf[bp], WEXT_PNO_MAX_REPEAT_LENGTH + 1, "%x",
+ WEXT_PNO_MAX_REPEAT);
+ bp += WEXT_PNO_MAX_REPEAT_LENGTH + 1;
+
+ memset(&ifr, 0, sizeof(ifr));
+ memset(&priv_cmd, 0, sizeof(priv_cmd));
+ os_strncpy(ifr.ifr_name, bss->ifname, IFNAMSIZ);
+
+ priv_cmd.buf = buf;
+ priv_cmd.used_len = bp;
+ priv_cmd.total_len = bp;
+ ifr.ifr_data = &priv_cmd;
+
+ ret = ioctl(drv->global->ioctl_sock, SIOCDEVPRIVATE + 1, &ifr);
+
+ if (ret < 0) {
+ wpa_printf(MSG_ERROR, "ioctl[SIOCSIWPRIV] (pnosetup): %d",
+ ret);
+ wpa_driver_send_hang_msg(drv);
+ return ret;
+ }
+
+ drv_errors = 0;
+
+ return android_priv_cmd(bss, "PNOFORCE 1");
+}
+
+
+static int android_pno_stop(struct i802_bss *bss)
+{
+ return android_priv_cmd(bss, "PNOFORCE 0");
+}
+
+#endif /* ANDROID */
+
+
const struct wpa_driver_ops wpa_driver_nl80211_ops = {
.name = "nl80211",
.desc = "Linux nl80211/cfg80211",
.sta_clear_stats = i802_sta_clear_stats,
.set_rts = i802_set_rts,
.set_frag = i802_set_frag,
- .set_cts_protect = i802_set_cts_protect,
- .set_preamble = i802_set_preamble,
- .set_short_slot_time = i802_set_short_slot_time,
.set_tx_queue_params = i802_set_tx_queue_params,
.set_sta_vlan = i802_set_sta_vlan,
- .set_ht_params = i802_set_ht_params,
- .set_rate_sets = i802_set_rate_sets,
.sta_deauth = i802_sta_deauth,
.sta_disassoc = i802_sta_disassoc,
#endif /* HOSTAPD || CONFIG_AP */
.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,
.signal_poll = nl80211_signal_poll,
.send_frame = nl80211_send_frame,
- .set_intra_bss = nl80211_set_intra_bss,
+ .shared_freq = wpa_driver_nl80211_shared_freq,
.set_param = nl80211_set_param,
.get_radio_name = nl80211_get_radio_name,
.add_pmkid = nl80211_add_pmkid,
.remove_pmkid = nl80211_remove_pmkid,
.flush_pmkid = nl80211_flush_pmkid,
.set_rekey_info = nl80211_set_rekey_info,
+ .poll_client = nl80211_poll_client,
+ .set_p2p_powersave = nl80211_set_p2p_powersave,
+#ifdef CONFIG_TDLS
+ .send_tdls_mgmt = nl80211_send_tdls_mgmt,
+ .tdls_oper = nl80211_tdls_oper,
+#endif /* CONFIG_TDLS */
};