index 20eb7447446e80a788776096dff0e70d032e176f..fd9b082a2940f4f0964611f605c5e7fa8fea0bdd 100644 (file)
/*
* 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>
* Copyright (c) 2009-2010, Atheros Communications
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#include "includes.h"
#ifdef ANDROID
#include "android_drv.h"
+
+static int wpa_driver_nl80211_driver_cmd(void *priv, char *cmd, char *buf,
+ size_t buf_len);
#endif /* ANDROID */
#ifdef CONFIG_LIBNL20
/* libnl 2.0 compatibility code */
#endif /* CONFIG_LIBNL20 */
-struct nl80211_handles {
- struct nl_handle *handle;
-};
-
-
-static int nl_create_handles(struct nl80211_handles *handles, struct nl_cb *cb,
- const char *dbg)
+static struct nl_handle * nl_create_handle(struct nl_cb *cb, const char *dbg)
{
- if (!handles)
- return -1;
+ struct nl_handle *handle;
- handles->handle = nl80211_handle_alloc(cb);
- if (handles->handle == NULL) {
+ handle = nl80211_handle_alloc(cb);
+ if (handle == NULL) {
wpa_printf(MSG_ERROR, "nl80211: Failed to allocate netlink "
"callbacks (%s)", dbg);
- return -1;
+ return NULL;
}
- if (genl_connect(handles->handle)) {
+ if (genl_connect(handle)) {
wpa_printf(MSG_ERROR, "nl80211: Failed to connect to generic "
"netlink (%s)", dbg);
- goto err;
+ nl80211_handle_destroy(handle);
+ return NULL;
}
- return 0;
-err:
- nl80211_handle_destroy(handles->handle);
- return -1;
+ return handle;
}
-static void nl_destroy_handles(struct nl80211_handles *handles)
+static void nl_destroy_handles(struct nl_handle **handle)
{
- if (handles->handle == NULL)
+ if (*handle == NULL)
return;
- nl80211_handle_destroy(handles->handle);
- handles->handle = NULL;
+ nl80211_handle_destroy(*handle);
+ *handle = NULL;
}
int if_add_ifindex;
struct netlink_data *netlink;
struct nl_cb *nl_cb;
- struct nl80211_handles nl;
+ 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);
unsigned int added_if_into_bridge:1;
unsigned int added_bridge:1;
+ u8 addr[ETH_ALEN];
+
int freq;
- struct nl80211_handles nl_preq;
+ 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;
int ifindex;
int scan_complete_events;
- struct nl80211_handles nl_event;
struct nl_cb *nl_cb;
u8 auth_bssid[ETH_ALEN];
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;
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;
+#ifdef ANDROID
+ u8 wowlan_triggers;
+ u8 wowlan_enabled;
+#endif
};
struct wpa_driver_scan_params *params);
static int android_pno_stop(struct i802_bss *bss);
#endif /* ANDROID */
+#ifdef ANDROID_BRCM_P2P_PATCH
+static void mlme_event_deauth_disassoc(struct wpa_driver_nl80211_data *drv,
+ enum wpa_event_type type,
+ const u8 *frame, size_t len);
+#endif /* ANDROID_BRCM_P2P_PATCH */
#ifdef HOSTAPD
static void add_ifidx(struct wpa_driver_nl80211_data *drv, int ifidx);
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 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->global->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->global->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->global->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;
}
+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;
@@ -532,10 +795,28 @@ static void wpa_driver_nl80211_event_link(struct wpa_driver_nl80211_data *drv,
del ? "removed" : "added");
if (os_strcmp(drv->first_bss.ifname, event.interface_status.ifname) == 0) {
- if (del)
+ if (del) {
+ if (drv->if_removed) {
+ wpa_printf(MSG_DEBUG, "nl80211: if_removed "
+ "already set - ignore event");
+ return;
+ }
drv->if_removed = 1;
- else
+ } else {
+ if (if_nametoindex(drv->first_bss.ifname) == 0) {
+ wpa_printf(MSG_DEBUG, "nl80211: Interface %s "
+ "does not exist - ignore "
+ "RTM_NEWLINK",
+ drv->first_bss.ifname);
+ return;
+ }
+ if (!drv->if_removed) {
+ wpa_printf(MSG_DEBUG, "nl80211: if_removed "
+ "already cleared - ignore event");
+ return;
+ }
drv->if_removed = 0;
+ }
}
wpa_supplicant_event(drv->ctx, EVENT_INTERFACE_STATUS, &event);
wpa_printf(MSG_DEBUG, "nl80211: Ignore interface up "
"event since interface %s is down",
namebuf);
+ } else if (if_nametoindex(drv->first_bss.ifname) == 0) {
+ wpa_printf(MSG_DEBUG, "nl80211: Ignore interface up "
+ "event since interface %s does not exist",
+ drv->first_bss.ifname);
+ } else if (drv->if_removed) {
+ wpa_printf(MSG_DEBUG, "nl80211: Ignore interface up "
+ "event since interface %s is marked "
+ "removed", drv->first_bss.ifname);
} else {
wpa_printf(MSG_DEBUG, "nl80211: Interface up");
drv->if_disabled = 0;
u16 status;
mgmt = (const struct ieee80211_mgmt *) frame;
+#if (defined (CONFIG_AP) || defined (HOSTAPD) ) && defined (ANDROID_BRCM_P2P_PATCH)
+ if (drv->nlmode == NL80211_IFTYPE_AP || drv->nlmode == NL80211_IFTYPE_P2P_GO) {
+ if (len < 24 + sizeof(mgmt->u.assoc_req)) {
+ wpa_printf(MSG_DEBUG, "nl80211: Too short association event "
+ "frame");
+ return;
+ }
+ os_memset(&event, 0, sizeof(event));
+ event.assoc_info.freq = drv->assoc_freq;
+ event.assoc_info.req_ies = (u8 *) mgmt->u.assoc_req.variable;
+ event.assoc_info.req_ies_len = len - 24 - sizeof(mgmt->u.assoc_req);
+ event.assoc_info.addr = mgmt->sa;
+ } else {
+#endif
if (len < 24 + sizeof(mgmt->u.assoc_resp)) {
wpa_printf(MSG_DEBUG, "nl80211: Too short association event "
"frame");
}
event.assoc_info.freq = drv->assoc_freq;
+#if (defined (CONFIG_AP) || defined(HOSTAPD)) && defined (ANDROID_BRCM_P2P_PATCH)
+ }
+#endif
wpa_supplicant_event(drv->ctx, EVENT_ASSOC, &event);
}
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);
}
event.rx_action.data = &mgmt->u.action.category + 1;
event.rx_action.len = frame + len - event.rx_action.data;
wpa_supplicant_event(drv->ctx, EVENT_RX_ACTION, &event);
+#ifdef ANDROID_BRCM_P2P_PATCH
+ } else if (stype == WLAN_FC_STYPE_ASSOC_REQ) {
+ mlme_event_assoc(drv, frame, len);
+ } else if (stype == WLAN_FC_STYPE_DISASSOC) {
+ mlme_event_deauth_disassoc(drv, EVENT_DISASSOC, frame, len);
+ } else if (stype == WLAN_FC_STYPE_DEAUTH) {
+ mlme_event_deauth_disassoc(drv, EVENT_DEAUTH, frame, len);
+#endif /* ANDROID_BRCM_P2P_PATCH */
} else {
event.rx_mgmt.frame = frame;
event.rx_mgmt.frame_len = len;
}
-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);
+#ifdef ANDROID_BRCM_P2P_PATCH
+ if (is_ap_interface(drv->nlmode)) {
+ event.disassoc_info.addr = mgmt->sa;
+ } else
+#endif /* ANDROID_BRCM_P2P_PATCH */
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);
+#ifdef ANDROID_BRCM_P2P_PATCH
+ if (is_ap_interface(drv->nlmode)) {
+ event.deauth_info.addr = mgmt->sa;
+ } else
+#endif /* ANDROID_BRCM_P2P_PATCH */
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,
@@ -1271,6 +1604,14 @@ static void send_scan_event(struct wpa_driver_nl80211_data *drv, int aborted,
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;
return send_and_recv_msgs(drv, msg, get_link_signal, sig);
nla_put_failure:
+ nlmsg_free(msg);
return -ENOBUFS;
}
return send_and_recv_msgs(drv, msg, get_link_noise, sig_change);
nla_put_failure:
+ nlmsg_free(msg);
return -ENOBUFS;
}
return send_and_recv_msgs(drv, msg, get_noise_for_scan_results,
scan_res);
nla_put_failure:
+ nlmsg_free(msg);
return -ENOBUFS;
}
wpa_printf(MSG_DEBUG, "nl80211: Connection quality monitor "
"event: RSSI low");
ed.signal_change.above_threshold = 0;
+ } else if (event == NL80211_CQM_RSSI_BEACON_LOSS) {
+ wpa_printf(MSG_DEBUG, "nl80211: Connection quality monitor "
+ "event: beacon loss!");
+ wpa_supplicant_event(drv->ctx, EVENT_START_ROAMING, &ed);
} else
return;
}
+static void nl80211_roaming_support_event(struct wpa_driver_nl80211_data *drv,
+ struct nlattr *tb[])
+{
+ int enabled;
+ enum wpa_event_type event;
+
+ enabled = (tb[NL80211_ATTR_ROAMING_DISABLED] == NULL);
+
+ if (enabled)
+ event = EVENT_ROAMING_ENABLED;
+ else
+ event = EVENT_ROAMING_DISABLED;
+
+ wpa_printf(MSG_DEBUG, "nl80211: roaming %s",
+ enabled ? "enabled" : "disabled");
+
+ wpa_supplicant_event(drv->ctx, event, NULL);
+}
+
static void nl80211_new_station_event(struct wpa_driver_nl80211_data *drv,
struct nlattr **tb)
{
}
-static int process_drv_event(struct nl_msg *msg, void *arg)
+static void nl80211_spurious_frame(struct i802_bss *bss, struct nlattr **tb,
+ int wds)
{
- struct wpa_driver_nl80211_data *drv = arg;
- struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
- struct nlattr *tb[NL80211_ATTR_MAX + 1];
+ struct wpa_driver_nl80211_data *drv = bss->drv;
+ union wpa_event_data event;
- nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
- genlmsg_attrlen(gnlh, 0), NULL);
+ if (!tb[NL80211_ATTR_MAC])
+ 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(&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_NOTIFY_CQM:
nl80211_cqm_event(drv, tb);
break;
+ case NL80211_CMD_ROAMING_SUPPORT:
+ nl80211_roaming_support_event(drv, tb);
+ break;
case NL80211_CMD_REG_CHANGE:
wpa_printf(MSG_DEBUG, "nl80211: Regulatory domain change");
wpa_supplicant_event(drv->ctx, EVENT_CHANNEL_LIST_CHANGED,
break;
default:
wpa_printf(MSG_DEBUG, "nl80211: Ignored unknown event "
- "(cmd=%d)", gnlh->cmd);
+ "(cmd=%d)", cmd);
break;
}
-
- return NL_SKIP;
}
-static int process_bss_event(struct nl_msg *msg, void *arg)
+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);
- switch (gnlh->cmd) {
- default:
- wpa_printf(MSG_DEBUG, "nl80211: Ignored unknown event "
- "(cmd=%d)", gnlh->cmd);
- break;
+ 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 void wpa_driver_nl80211_event_receive(int sock, void *eloop_ctx,
- void *handle)
+static int process_global_event(struct nl_msg *msg, void *arg)
{
- struct nl_cb *cb = eloop_ctx;
-
- wpa_printf(MSG_DEBUG, "nl80211: Event message available");
+ 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);
+ break;
+ }
+
+ return NL_SKIP;
+}
+
+
+static void wpa_driver_nl80211_event_receive(int sock, void *eloop_ctx,
+ void *handle)
+{
+ struct nl_cb *cb = eloop_ctx;
+
+ wpa_printf(MSG_DEBUG, "nl80211: Event message available");
nl_recvmsgs(handle, cb);
}
return -EINVAL;
return 0;
nla_put_failure:
+ nlmsg_free(msg);
return -EINVAL;
}
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];
case NL80211_IFTYPE_P2P_CLIENT:
p2p_client_supported = 1;
break;
+ case NL80211_IFTYPE_MONITOR:
+ info->monitor_supported = 1;
+ break;
}
}
}
/* default to 5000 since early versions of mac80211 don't set it */
capa->max_remain_on_chan = 5000;
+ if (tb[NL80211_ATTR_SUPPORT_AP_UAPSD])
+ capa->flags |= WPA_DRIVER_FLAGS_AP_UAPSD;
+
+ if (tb[NL80211_ATTR_FEATURE_FLAGS]) {
+ int features = nla_get_u32(tb[NL80211_ATTR_FEATURE_FLAGS]);
+ if (features & NL80211_FEATURE_SCHED_SCAN_INTERVALS)
+ capa->sched_scan_intervals_supported = 1;
+ }
+
if (tb[NL80211_ATTR_MAX_REMAIN_ON_CHANNEL_DURATION])
capa->max_remain_on_chan =
nla_get_u32(tb[NL80211_ATTR_MAX_REMAIN_ON_CHANNEL_DURATION]);
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;
}
drv->poll_command_supported = info.poll_command_supported;
drv->data_tx_status = info.data_tx_status;
+ /*
+ * 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;
+
+ 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;
+ }
+ }
+
+ /*
+ * 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;
+
return 0;
}
+#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;
+
+ if (genl_ctrl_alloc_cache(handle, &cache) < 0) {
+ wpa_printf(MSG_ERROR, "nl80211: Failed to allocate generic "
+ "netlink cache");
+ goto fail;
+ }
+
+ 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 "
return -1;
}
- if (nl_create_handles(&global->nl, global->nl_cb, "nl"))
- return -1;
+ global->nl = nl_create_handle(global->nl_cb, "nl");
+ if (global->nl == NULL)
+ goto err;
- global->nl80211_id = genl_ctrl_resolve(global->nl.handle, "nl80211");
+ global->nl80211_id = genl_ctrl_resolve(global->nl, "nl80211");
if (global->nl80211_id < 0) {
wpa_printf(MSG_ERROR, "nl80211: 'nl80211' generic netlink not "
"found");
- return -1;
+ goto err;
}
- return 0;
-}
-
-
-static int wpa_driver_nl80211_init_nl(struct wpa_driver_nl80211_data *drv)
-{
- struct nl80211_global *global = drv->global;
- int ret;
-
- /* Initialize generic netlink and nl80211 */
-
- if (nl_create_handles(&drv->nl_event, global->nl_cb, "event"))
- goto err3;
+ global->nl_event = nl_create_handle(global->nl_cb, "event");
+ if (global->nl_event == NULL)
+ goto err;
- ret = nl_get_multicast_id(drv, "nl80211", "scan");
+ ret = nl_get_multicast_id(global, "nl80211", "scan");
if (ret >= 0)
- ret = nl_socket_add_membership(drv->nl_event.handle, 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_event.handle, 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_event.handle, 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 */
}
+ 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;
+
+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");
- goto err4;
+ return -1;
}
nl_cb_set(drv->nl_cb, NL_CB_SEQ_CHECK, NL_CB_CUSTOM,
nl_cb_set(drv->nl_cb, NL_CB_VALID, NL_CB_CUSTOM,
process_drv_event, drv);
- eloop_register_read_sock(nl_socket_get_fd(drv->nl_event.handle),
- wpa_driver_nl80211_event_receive, drv->nl_cb,
- drv->nl_event.handle);
-
return 0;
-
-err4:
- nl_destroy_handles(&drv->nl_event);
-err3:
- return -1;
}
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,
}
-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;
+ 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);
+
nl80211_cmd(drv, msg, 0, NL80211_CMD_REGISTER_ACTION);
- NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex);
+ 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_event.handle,
+ 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)
{
+ 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(drv, (u8 *) "\x04\x0e", 2) <
+ 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(drv, (u8 *) "\x0a\x07", 2) < 0)
+ 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);
}
return -1;
if (linux_get_ifhwaddr(drv->global->ioctl_sock, bss->ifname,
- drv->addr))
+ 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);
return send_and_recv_msgs(drv, msg, NULL, NULL);
nla_put_failure:
+ nlmsg_free(msg);
return -ENOBUFS;
}
if (drv->eapol_tx_sock >= 0)
close(drv->eapol_tx_sock);
- if (bss->nl_preq.handle)
+ if (bss->nl_preq)
wpa_driver_nl80211_probe_req_report(bss, 0);
if (bss->added_if_into_bridge) {
if (linux_br_del_if(drv->global->ioctl_sock, bss->brname,
(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");
- eloop_unregister_read_sock(nl_socket_get_fd(drv->nl_event.handle));
- nl_destroy_handles(&drv->nl_event);
nl_cb_put(drv->nl_cb);
nl80211_destroy_bss(&drv->first_bss);
os_free(drv->filter_ssids);
+ os_free(drv->auth_ie);
+
if (drv->in_interface_list)
dl_list_del(&drv->list);
struct nl_msg *msg, *ssids, *freqs, *rates;
size_t i;
+ drv->scan_for_auth = 0;
+
msg = nlmsg_alloc();
ssids = nlmsg_alloc();
freqs = nlmsg_alloc();
}
if (params->p2p_probe) {
+ wpa_printf(MSG_DEBUG, "nl80211: P2P probe - mask SuppRates");
+
/*
* Remove 2.4 GHz rates 1, 2, 5.5, 11 Mbps from supported rates
* by masking out everything else apart from the OFDM rates 6,
* wpa_driver_nl80211_sched_scan - Initiate a scheduled scan
* @priv: Pointer to private driver data from wpa_driver_nl80211_init()
* @params: Scan parameters
- * @interval: Interval between scan cycles in milliseconds
+ * @long_interval: interval between scan cycles after end of short cycles
+ * @short_interval: interval between initial short scan cycles
+ * @num_short_intervals: number of interval short scan intervals
* Returns: 0 on success, -1 on failure or if not supported
*/
static int wpa_driver_nl80211_sched_scan(void *priv,
struct wpa_driver_scan_params *params,
- u32 interval)
+ u32 long_interval,
+ u32 short_interval,
+ u8 num_short_intervals)
{
struct i802_bss *bss = priv;
struct wpa_driver_nl80211_data *drv = bss->drv;
NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex);
- NLA_PUT_U32(msg, NL80211_ATTR_SCHED_SCAN_INTERVAL, interval);
+ NLA_PUT_U32(msg, NL80211_ATTR_SCHED_SCAN_INTERVAL, long_interval);
+
+ if (drv->capa.sched_scan_intervals_supported) {
+ NLA_PUT_U32(msg,
+ NL80211_ATTR_SCHED_SCAN_SHORT_INTERVAL,
+ short_interval);
+
+ NLA_PUT_U8(msg,
+ NL80211_ATTR_SCHED_SCAN_NUM_SHORT_INTERVALS,
+ num_short_intervals);
+ }
if (drv->num_filter_ssids &&
(int) drv->num_filter_ssids <= drv->capa.max_match_sets) {
goto nla_put_failure;
}
- wpa_printf(MSG_DEBUG, "nl80211: Sched scan requested (ret=%d) - "
- "scan interval %d msec", ret, interval);
+ wpa_printf(MSG_DEBUG, "nl80211: Sched scan requested (ret=%d) "
+ "intervals: short=%d ms long=%d ms num_short_intervals=%d"
+ , ret, short_interval, long_interval, num_short_intervals);
nla_put_failure:
nlmsg_free(ssids);
return ret;
nla_put_failure:
+ nlmsg_free(msg);
return -ENOBUFS;
}
}
+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);
nlmsg_free(msg);
goto retry;
}
+
+ 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;
}
-struct phy_info_arg {
- u16 *num_modes;
- struct hostapd_hw_modes *modes;
-};
-
-static int phy_info_handler(struct nl_msg *msg, void *arg)
+static int wpa_driver_nl80211_authenticate_retry(
+ struct wpa_driver_nl80211_data *drv)
{
- struct nlattr *tb_msg[NL80211_ATTR_MAX + 1];
+ 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];
+ }
+ }
+
+ drv->retry_auth = 1;
+ return wpa_driver_nl80211_authenticate(bss, ¶ms);
+}
+
+
+struct phy_info_arg {
+ u16 *num_modes;
+ struct hostapd_hw_modes *modes;
+};
+
+static int phy_info_handler(struct nl_msg *msg, void *arg)
+{
+ struct nlattr *tb_msg[NL80211_ATTR_MAX + 1];
struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
struct phy_info_arg *phy_info = arg;
@@ -4339,14 +5177,16 @@ wpa_driver_nl80211_get_hw_feature_data(void *priv, u16 *num_modes, u16 *flags)
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, int noack)
+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 */
@@ -4401,10 +5241,34 @@ static int wpa_driver_nl80211_send_frame(struct wpa_driver_nl80211_data *drv,
}
-static int wpa_driver_nl80211_send_mlme(void *priv, const u8 *data,
- size_t data_len, int noack)
+static int wpa_driver_nl80211_send_frame(struct i802_bss *bss,
+ const void *data, size_t len,
+ int encrypt, int noack,
+ unsigned int freq, int no_cck,
+ int offchanok, unsigned int wait_time)
+{
+ struct wpa_driver_nl80211_data *drv = bss->drv;
+ u64 cookie;
+
+ if (freq == 0)
+ freq = bss->freq;
+
+ if (drv->use_monitor)
+ return wpa_driver_nl80211_send_mntr(drv, data, len,
+ encrypt, noack);
+
+ return nl80211_send_frame_cmd(bss, freq, wait_time, data, len,
+ &cookie, no_cck, noack, offchanok);
+}
+
+
+static int wpa_driver_nl80211_send_mlme_freq(struct i802_bss *bss,
+ const u8 *data,
+ size_t data_len, int noack,
+ unsigned int freq, int no_cck,
+ int offchanok,
+ unsigned int wait_time)
{
- struct i802_bss *bss = priv;
struct wpa_driver_nl80211_data *drv = bss->drv;
struct ieee80211_mgmt *mgmt;
int encrypt = 1;
* but it works due to the single-threaded nature
* of wpa_supplicant.
*/
- return nl80211_send_frame_cmd(bss, drv->last_mgmt_freq, 0,
+ if (freq == 0)
+ freq = drv->last_mgmt_freq;
+ return nl80211_send_frame_cmd(bss, freq, 0,
data, data_len, NULL, 1, noack,
1);
}
- if (drv->device_ap_sme && is_ap_interface(drv->nlmode)) {
+#ifdef ANDROID_BRCM_P2P_PATCH
+ if (is_ap_interface(drv->nlmode)) {
+ wpa_printf(MSG_DEBUG, "%s: Sending frame on bss freq %d "
+ "using nl80211_send_frame_cmd", __func__,
+ bss->freq);
return nl80211_send_frame_cmd(bss, bss->freq, 0,
- data, data_len, NULL,
- 0, noack, 0);
+ data, data_len,
+ &drv->send_action_cookie, 0,
+ noack, 0);
+ }
+#else /* ANDROID_BRCM_P2P_PATCH */
+ if (drv->device_ap_sme && is_ap_interface(drv->nlmode)) {
+ if (freq == 0)
+ freq = bss->freq;
+ return nl80211_send_frame_cmd(bss, freq, 0,
+ data, data_len,
+ &drv->send_action_cookie,
+ no_cck, noack, offchanok);
}
+#endif /* ANDROID_BRCM_P2P_PATCH */
if (WLAN_FC_GET_TYPE(fc) == WLAN_FC_TYPE_MGMT &&
WLAN_FC_GET_STYPE(fc) == WLAN_FC_STYPE_AUTH) {
encrypt = 0;
}
- return wpa_driver_nl80211_send_frame(drv, data, data_len, encrypt,
- noack);
+ wpa_printf(MSG_DEBUG, "%s: Sending frame using monitor interface/"
+ "l2 socket", __func__);
+ return wpa_driver_nl80211_send_frame(bss, data, data_len, encrypt,
+ noack, freq, no_cck, offchanok,
+ wait_time);
+}
+
+
+static int wpa_driver_nl80211_send_mlme(void *priv, const u8 *data,
+ size_t data_len, int noack)
+{
+ struct i802_bss *bss = priv;
+ return wpa_driver_nl80211_send_mlme_freq(bss, data, data_len, noack,
+ 0, 0, 0, 0);
}
return send_and_recv_msgs(drv, msg, NULL, NULL);
nla_put_failure:
+ nlmsg_free(msg);
return -ENOBUFS;
}
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;
params->short_slot_time, params->ht_opmode,
params->isolate, params->basic_rates);
}
+#if defined(ANDROID_BRCM_P2P_PATCH) && defined(HOSTAPD)
+ wpa_driver_nl80211_probe_req_report(priv, 1);
+#endif
return ret;
nla_put_failure:
+ nlmsg_free(msg);
return -ENOBUFS;
}
NL80211_CHAN_HT40PLUS);
break;
default:
+#ifdef ANDROID_BRCM_P2P_PATCH
+ /* Should be changed to HT20 as a default value because
+ * P2P firmware does not support 11n for BCM4329 */
+ NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_CHANNEL_TYPE,
+ NL80211_CHAN_NO_HT);
+#else /* ANDROID_BRCM_P2P_PATCH */
NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_CHANNEL_TYPE,
NL80211_CHAN_HT20);
+#endif /* ANDROID_BRCM_P2P_PATCH */
break;
}
}
ret = send_and_recv_msgs(drv, msg, NULL, NULL);
+ 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;
}
{
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;
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_%s_STATION "
"result: %d (%s)", params->set ? "SET" : "NEW", ret,
if (ret == -EEXIST)
ret = 0;
nla_put_failure:
+ nlmsg_free(wme);
+ nlmsg_free(msg);
return ret;
}
return 0;
return ret;
nla_put_failure:
+ nlmsg_free(msg);
return -ENOBUFS;
}
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);
}
}
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;
{
struct wpa_driver_nl80211_data *drv = bss->drv;
- if (!drv->device_ap_sme &&
+ 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;
{
struct wpa_driver_nl80211_data *drv = bss->drv;
- if (drv->device_ap_sme)
+ if (drv->device_ap_sme) {
wpa_driver_nl80211_probe_req_report(bss, 0);
- else
+ 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;
}
int res;
int qos = flags & WPA_STA_WMM;
- if (drv->device_ap_sme || drv->data_tx_status)
+ 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 +
pos += 2;
memcpy(pos, data, data_len);
- res = wpa_driver_nl80211_send_frame(drv, (u8 *) hdr, len, encrypt, 0);
+ res = wpa_driver_nl80211_send_frame(bss, (u8 *) hdr, len, encrypt, 0,
+ 0, 0, 0, 0);
if (res < 0) {
wpa_printf(MSG_ERROR, "i802_send_eapol - packet len: %lu - "
"failed: %d (%s)",
return send_and_recv_msgs(drv, msg, NULL, NULL);
nla_put_failure:
+ nlmsg_free(msg);
nlmsg_free(flags);
return -ENOBUFS;
}
if (ret)
goto nla_put_failure;
+ if (params->bssid && params->fixed_bssid) {
+ wpa_printf(MSG_DEBUG, " * BSSID=" MACSTR,
+ MAC2STR(params->bssid));
+ NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, params->bssid);
+ }
+
+ if (params->key_mgmt_suite == KEY_MGMT_802_1X ||
+ params->key_mgmt_suite == KEY_MGMT_PSK ||
+ params->key_mgmt_suite == KEY_MGMT_802_1X_SHA256 ||
+ params->key_mgmt_suite == KEY_MGMT_PSK_SHA256) {
+ wpa_printf(MSG_DEBUG, " * control port");
+ NLA_PUT_FLAG(msg, NL80211_ATTR_CONTROL_PORT);
+ }
+
if (params->wpa_ie) {
wpa_hexdump(MSG_DEBUG,
" * Extra IEs for Beacon/Probe Response frames",
NLA_PUT_U32(msg, NL80211_ATTR_AKM_SUITES, mgmt);
}
+ if (params->disable_ht)
+ NLA_PUT_FLAG(msg, NL80211_ATTR_DISABLE_HT);
+
+ if (params->htcaps && params->htcaps_mask) {
+ int sz = sizeof(struct ieee80211_ht_capabilities);
+ NLA_PUT(msg, NL80211_ATTR_HT_CAPABILITY, sz, params->htcaps);
+ NLA_PUT(msg, NL80211_ATTR_HT_CAPABILITY_MASK, sz,
+ params->htcaps_mask);
+ }
+
ret = nl80211_set_conn_keys(params, msg);
if (ret)
goto nla_put_failure;
params->prev_bssid);
}
+ if (params->disable_ht)
+ NLA_PUT_FLAG(msg, NL80211_ATTR_DISABLE_HT);
+
+ if (params->htcaps && params->htcaps_mask) {
+ int sz = sizeof(struct ieee80211_ht_capabilities);
+ NLA_PUT(msg, NL80211_ATTR_HT_CAPABILITY, sz, params->htcaps);
+ NLA_PUT(msg, NL80211_ATTR_HT_CAPABILITY_MASK, sz,
+ params->htcaps_mask);
+ }
+
if (params->p2p)
wpa_printf(MSG_DEBUG, " * P2P group");
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++) {
- int res;
res = linux_set_iface_flags(drv->global->ioctl_sock,
bss->ifname, 0);
if (res == -EACCES || res == -ENODEV)
}
if (is_ap_interface(nlmode)) {
+ nl80211_mgmt_unsubscribe(bss, "start AP");
/* Setup additional AP mode functionality if needed */
if (nl80211_setup_ap(bss))
return -1;
} else if (was_ap) {
/* Remove additional AP mode functionality */
nl80211_teardown_ap(bss);
+ } else {
+ nl80211_mgmt_unsubscribe(bss, "mode change");
}
- if (!ret && is_p2p_interface(drv->nlmode)) {
- nl80211_disable_11b_rates(drv, drv->ifindex, 1);
- drv->disabled_11b_rates = 1;
- } else if (!ret && drv->disabled_11b_rates) {
- nl80211_disable_11b_rates(drv, drv->ifindex, 0);
- drv->disabled_11b_rates = 0;
- }
+ 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 0;
}
return send_and_recv_msgs(drv, msg, NULL, NULL);
nla_put_failure:
+ nlmsg_free(msg);
return -ENOBUFS;
}
return send_and_recv_msgs(drv, msg, get_key_handler, seq);
nla_put_failure:
+ nlmsg_free(msg);
return -ENOBUFS;
}
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;
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)
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;
}
@@ -6379,6 +7393,7 @@ static int i802_read_sta_data(void *priv, struct hostap_sta_driver_data *data,
return send_and_recv_msgs(drv, msg, get_sta_handler, data);
nla_put_failure:
+ nlmsg_free(msg);
return -ENOBUFS;
}
if (send_and_recv_msgs(drv, msg, NULL, NULL) == 0)
return 0;
+ msg = NULL;
nla_put_failure:
+ nlmsg_free(msg);
return -1;
}
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:
+ nlmsg_free(msg);
return ret;
}
params->own_addr))
goto failed;
+ memcpy(bss->addr, params->own_addr, ETH_ALEN);
+
return bss;
failed:
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;
@@ -6951,13 +7972,19 @@ static int wpa_driver_nl80211_if_add(void *priv, enum wpa_driver_if_type type,
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;
+ new_bss->freq = drv->first_bss.freq;
drv->first_bss.next = new_bss;
if (drv_priv)
*drv_priv = new_bss;
nl80211_init_bss(new_bss);
+
+ /* Subscribe management frames for this WPA_IF_AP_BSS */
+ if (nl80211_setup_ap(new_bss))
+ return -1;
}
#endif /* HOSTAPD */
for (tbss = &drv->first_bss; tbss; tbss = tbss->next) {
if (tbss->next == bss) {
tbss->next = bss->next;
+ /* Unsubscribe management frames */
+ nl80211_teardown_ap(bss);
nl80211_destroy_bss(bss);
os_free(bss);
bss = NULL;
NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, bss->ifindex);
NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_FREQ, freq);
+#ifndef ANDROID_BRCM_P2P_PATCH
if (wait)
NLA_PUT_U32(msg, NL80211_ATTR_DURATION, wait);
- if (offchanok)
+#endif /* ANDROID_BRCM_P2P_PATCH */
+ 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);
struct ieee80211_hdr *hdr;
wpa_printf(MSG_DEBUG, "nl80211: Send Action frame (ifindex=%d, "
- "wait=%d ms no_cck=%d)", drv->ifindex, wait_time, no_cck);
+ "freq=%u MHz wait=%d ms no_cck=%d)",
+ drv->ifindex, freq, 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,
- 0);
+ ret = wpa_driver_nl80211_send_mlme_freq(priv, buf,
+ 24 + data_len,
+ 0, freq, no_cck, 1,
+ wait_time);
else
ret = nl80211_send_frame_cmd(bss, freq, wait_time, buf,
24 + data_len,
@@ -7179,6 +8213,7 @@ static int wpa_driver_nl80211_remain_on_channel(void *priv, unsigned int 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",
@@ -7191,6 +8226,7 @@ static int wpa_driver_nl80211_remain_on_channel(void *priv, unsigned int freq,
"(freq=%d duration=%u): %d (%s)",
freq, duration, ret, strerror(-ret));
nla_put_failure:
+ nlmsg_free(msg);
return -1;
}
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 i802_bss *bss = priv;
struct wpa_driver_nl80211_data *drv = bss->drv;
+ if (drv->nlmode != NL80211_IFTYPE_STATION) {
+ wpa_printf(MSG_DEBUG, "nl80211: probe_req_report control only "
+ "allowed in station mode (iftype=%d)",
+ drv->nlmode);
+ return -1;
+ }
+
if (!report) {
- if (bss->nl_preq.handle) {
+ 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(bss->nl_preq.handle));
+ nl_socket_get_fd(bss->nl_preq));
nl_destroy_handles(&bss->nl_preq);
}
return 0;
}
- if (bss->nl_preq.handle) {
+ if (bss->nl_preq) {
wpa_printf(MSG_DEBUG, "nl80211: Probe Request reporting "
- "already on!");
+ "already on! nl_preq=%p", bss->nl_preq);
return 0;
}
- if (nl_create_handles(&bss->nl_preq, drv->global->nl_cb, "preq"))
+ bss->nl_preq = nl_create_handle(drv->global->nl_cb, "preq");
+ if (bss->nl_preq == NULL)
return -1;
+ wpa_printf(MSG_DEBUG, "nl80211: Enable Probe Request "
+ "reporting nl_preq=%p", bss->nl_preq);
- if (nl80211_register_frame(drv, bss->nl_preq.handle,
+ if (nl80211_register_frame(bss, bss->nl_preq,
(WLAN_FC_TYPE_MGMT << 2) |
(WLAN_FC_STYPE_PROBE_REQ << 4),
NULL, 0) < 0)
goto out_err;
- eloop_register_read_sock(nl_socket_get_fd(bss->nl_preq.handle),
- wpa_driver_nl80211_event_receive, drv->nl_cb,
- bss->nl_preq.handle);
+#ifdef ANDROID_BRCM_P2P_PATCH
+ if (nl80211_register_frame(bss, bss->nl_preq,
+ (WLAN_FC_TYPE_MGMT << 2) |
+ (WLAN_FC_STYPE_ASSOC_REQ << 4),
+ NULL, 0) < 0) {
+ goto out_err;
+ }
+
+ if (nl80211_register_frame(bss, bss->nl_preq,
+ (WLAN_FC_TYPE_MGMT << 2) |
+ (WLAN_FC_STYPE_REASSOC_REQ << 4),
+ NULL, 0) < 0) {
+ goto out_err;
+ }
+
+ if (nl80211_register_frame(bss, bss->nl_preq,
+ (WLAN_FC_TYPE_MGMT << 2) |
+ (WLAN_FC_STYPE_DISASSOC << 4),
+ NULL, 0) < 0) {
+ goto out_err;
+ }
+
+ if (nl80211_register_frame(bss, bss->nl_preq,
+ (WLAN_FC_TYPE_MGMT << 2) |
+ (WLAN_FC_STYPE_DEAUTH << 4),
+ NULL, 0) < 0) {
+ goto out_err;
+ }
+#endif /* ANDROID_BRCM_P2P_PATCH */
+
+ eloop_register_read_sock(nl_socket_get_fd(bss->nl_preq),
+ wpa_driver_nl80211_event_receive, bss->nl_cb,
+ bss->nl_preq);
return 0;
return wpa_driver_nl80211_set_mode(priv, NL80211_IFTYPE_STATION);
}
+static int wpa_driver_nl80211_deinit_p2p_cli(void *priv)
+{
+ struct i802_bss *bss = priv;
+ struct wpa_driver_nl80211_data *drv = bss->drv;
+ if (drv->nlmode != NL80211_IFTYPE_P2P_CLIENT)
+ return -1;
+ return wpa_driver_nl80211_set_mode(priv, NL80211_IFTYPE_STATION);
+}
static void wpa_driver_nl80211_resume(void *priv)
{
@@ -7351,11 +8448,7 @@ static int nl80211_send_ft_action(void *priv, u8 action, const u8 *target_ap,
int ret;
u8 *data, *pos;
size_t data_len;
- u8 own_addr[ETH_ALEN];
-
- if (linux_get_ifhwaddr(drv->global->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 "
msg = NULL;
nla_put_failure:
- if (cqm)
- nlmsg_free(cqm);
+ nlmsg_free(cqm);
nlmsg_free(msg);
return -1;
}
wpa_printf(MSG_DEBUG, "nl80211: Found a match for PHY %s - %s "
MACSTR,
driver->phyname, driver->first_bss.ifname,
- MAC2STR(driver->addr));
+ 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);
int encrypt)
{
struct i802_bss *bss = priv;
- struct wpa_driver_nl80211_data *drv = bss->drv;
- return wpa_driver_nl80211_send_frame(drv, data, data_len, encrypt, 0);
+ return wpa_driver_nl80211_send_frame(bss, data, data_len, encrypt, 0,
+ 0, 0, 0, 0);
}
nl_destroy_handles(&global->nl);
- if (global->nl_cb)
- nl_cb_put(global->nl_cb);
+ 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);
return send_and_recv_msgs(bss->drv, msg, NULL, NULL);
nla_put_failure:
+ nlmsg_free(msg);
return -ENOBUFS;
}
+#ifdef ANDROID
+
+#define MAX_PATTERN_SIZE 256
+#define MAX_MASK_SIZE (MAX_PATTERN_SIZE/8)
+
+/* Describes a single RX filter configuration */
+struct rx_filter {
+ /* name - A human recongmizable name for the filter */
+ char *name;
+
+ /* get_pattern_handler - A handler which enables the user to configure
+ * the pattern dynamically (For example filter according to the HW addr).
+ * If NULL the static pattern configured will be used.
+ * buf - the pattern will be copied to buf
+ * buflen - the size of buf
+ * arg - A generic input argumet which can be passed to the handler
+ */
+ int (* get_pattern_handler) (u8 *buf, int buflen, void* arg);
+
+ /* pattern - A static pattern to filter
+ * This array contains the bytes of the pattern. The mask field
+ * indicates which bytes should be used in the filter and which
+ * can be discarded
+ */
+ u8 pattern[MAX_PATTERN_SIZE];
+
+ /* pattern_len - The number of bytes used in pattern */
+ u8 pattern_len;
+
+ /* mask - A bit mask indicating which bytes in pattern should be
+ * used for filtering. Each bit here corresponds to a byte in pattern
+ */
+ u8 mask[MAX_MASK_SIZE];
+
+ /* mask_len - The number of bytes used in mask */
+ u8 mask_len;
+};
+
+static u8 *nl80211_rx_filter_get_pattern(struct rx_filter *filter, void *arg)
+{
+ if (filter->get_pattern_handler) {
+ if (filter->get_pattern_handler(filter->pattern,
+ filter->pattern_len, arg)) {
+ return NULL;
+ }
+ }
+
+ return filter->pattern;
+}
+
+static int
+nl80211_self_filter_get_pattern_handler(u8 *buf, int buflen, void *arg)
+{
+ int ret;
+ struct i802_bss *bss = (struct i802_bss *)arg;
+
+ ret = linux_get_ifhwaddr(bss->drv->global->ioctl_sock,
+ bss->ifname, buf);
+ if (ret) {
+ wpa_printf(MSG_ERROR, "Failed to get own HW addr (%d)", ret);
+ return -1;
+ }
+ return 0;
+}
+
+static struct rx_filter rx_filters[] = {
+ /* ID 0 */
+ {.name = "self",
+ .pattern = {},
+ .pattern_len = 6,
+ .mask = { BIT(0) | BIT(1) | BIT(2) | BIT(3) | BIT(4) | BIT(5) },
+ .mask_len = 1,
+ .get_pattern_handler = nl80211_self_filter_get_pattern_handler,
+ },
+
+ /* ID 1 */
+ {.name = "bcast",
+ .pattern = {0xFF,0xFF,0xFF,0xFF,0xFF,0xFF},
+ .pattern_len = 6,
+ .mask = { BIT(0) | BIT(1) | BIT(2) | BIT(3) | BIT(4) | BIT(5) },
+ .mask_len = 1,
+ },
+
+ /* ID 2 */
+ {.name = "ipv4mc",
+ .pattern = {0x01,0x00,0x5E},
+ .pattern_len = 3,
+ .mask = { BIT(0) | BIT(1) | BIT(2) },
+ .mask_len = 1,
+ },
+
+ /* ID 3 */
+ {.name = "ipv6mc",
+ .pattern = {0x33,0x33},
+ .pattern_len = 2,
+ .mask = { BIT(0) | BIT(1) },
+ .mask_len = 1,
+ },
+
+ /* ID 4 */
+ {.name = "dhcp",
+ .pattern = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0 , 0 ,
+ 0 , 0 , 0 , 0 , 0 , 0 , 0x45, 0 ,
+ 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0x11,
+ 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ,
+ 0 , 0 , 0 , 0 , 0x00, 0x44},
+ .pattern_len = 38,
+ .mask = { BIT(0) | BIT(1) | BIT(2) | BIT(3) | BIT(4) | BIT(5),
+ BIT(6), /* OCTET 2 */
+ BIT(7), /* OCTET 3 */
+ 0, /* OCTET 4 */
+ BIT(4) | BIT(5) }, /* OCTET 5 */
+ .mask_len = 5,
+ },
+
+ /* ID 5 */
+ {.name = "arp",
+ .pattern = {0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ,
+ 0 , 0 , 0 , 0 , 0x08, 0x06},
+ .pattern_len = 14,
+ .mask = { 0, /* OCTET 1 */
+ BIT(4) | BIT(5) }, /* OCTET 2 */
+ .mask_len = 2,
+ },
+
+ /* ID 6 */
+ {.name = "ssdp",
+ .pattern = {0x01, 0x00, 0x5E, 0 , 0 , 0 , 0 , 0 ,
+ 0 , 0 , 0 , 0 , 0 , 0 , 0x45, 0 ,
+ 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0x11,
+ 0 , 0 , 0 , 0 , 0 , 0 , 0xEF, 0xFF,
+ 0xFF, 0xFA, 0 , 0 , 0x07, 0x6C},
+ .pattern_len = 38,
+ .mask = { BIT(0) | BIT(1) | BIT(2), /* OCTET 1 */
+ BIT(6), /* OCTET 2 */
+ BIT(7), /* OCTET 3 */
+ BIT(6) | BIT(7), /* OCTET 4 */
+ BIT(0) | BIT(1) | BIT(4) | BIT(5) }, /* OCTET 5 */
+ .mask_len = 5,
+ },
+};
+
+#define NR_RX_FILTERS (int)(sizeof(rx_filters) / sizeof(struct rx_filter))
+
+static int nl80211_set_wowlan_triggers(struct i802_bss *bss, int enable)
+{
+ struct nl_msg *msg, *pats = NULL;
+ struct nlattr *wowtrig, *pat;
+ int i, ret = -1;
+ int filters;
+
+ bss->drv->wowlan_enabled = !!enable;
+
+ msg = nlmsg_alloc();
+ if (!msg)
+ return -ENOMEM;
+
+ genlmsg_put(msg, 0, 0, bss->drv->global->nl80211_id, 0,
+ 0, NL80211_CMD_SET_WOWLAN, 0);
+
+ NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, bss->drv->first_bss.ifindex);
+ wowtrig = nla_nest_start(msg, NL80211_ATTR_WOWLAN_TRIGGERS);
+
+ if (!wowtrig) {
+ ret = -ENOBUFS;
+ goto nla_put_failure;
+ }
+
+ if (!enable) {
+ NLA_PUT_FLAG(msg, NL80211_WOWLAN_TRIG_ANY);
+ } else {
+ pats = nlmsg_alloc();
+ if (!pats) {
+ ret = -ENOMEM;
+ goto nla_put_failure;
+ }
+
+ /*
+ * In GB filters 0 and 1 are always set but in ICS they
+ * were completely removed. Add filter 0 (unicast) by default
+ * so unicast traffic won't be dropped in any case.
+ */
+
+ filters = bss->drv->wowlan_triggers |= 1;
+
+ for (i = 0; i < NR_RX_FILTERS; i++) {
+ struct rx_filter *rx_filter = &rx_filters[i];
+ int patnr = 1;
+ u8 *pattern;
+
+ if (!(filters & (1 << i)))
+ continue;
+
+ pattern = nl80211_rx_filter_get_pattern(rx_filter, bss);
+ if (!pattern)
+ continue;
+
+ pat = nla_nest_start(pats, patnr++);
+ NLA_PUT(pats, NL80211_WOWLAN_PKTPAT_MASK,
+ rx_filter->mask_len,
+ rx_filter->mask);
+
+ NLA_PUT(pats, NL80211_WOWLAN_PKTPAT_PATTERN,
+ rx_filter->pattern_len,
+ pattern);
+
+ nla_nest_end(pats, pat);
+ }
+ }
+
+ if (pats)
+ nla_put_nested(msg, NL80211_WOWLAN_TRIG_PKT_PATTERN, pats);
+
+ nla_nest_end(msg, wowtrig);
+
+ ret = send_and_recv_msgs(bss->drv, msg, NULL, NULL);
+
+ if (ret < 0)
+ wpa_printf(MSG_ERROR, "Failed to set WoWLAN trigger:%d\n", ret);
+
+ if (pats)
+ nlmsg_free(pats);
+
+ return 0;
+
+nla_put_failure:
+ nlmsg_free(msg);
+ return ret;
+}
+
+static int nl80211_toggle_wowlan_trigger(struct i802_bss *bss, int nr,
+ int enabled)
+{
+ if (nr >= NR_RX_FILTERS) {
+ wpa_printf(MSG_ERROR, "Unknown filter: %d\n", nr);
+ return -1;
+ }
+
+ if (enabled)
+ bss->drv->wowlan_triggers |= 1 << nr;
+ else
+ bss->drv->wowlan_triggers &= ~(1 << nr);
+
+ if (bss->drv->wowlan_enabled)
+ nl80211_set_wowlan_triggers(bss, 1);
+
+ return 0;
+}
+
+static int nl80211_parse_wowlan_trigger_nr(char *s)
+{
+ long i;
+ char *endp;
+
+ i = strtol(s, &endp, 10);
+
+ if(endp == s)
+ return -1;
+ return i;
+}
+
+static int nl80211_toggle_dropbcast(int enable)
+{
+ char filename[90];
+ int rv;
+ FILE *f;
+
+ snprintf(filename, sizeof(filename) - 1,
+ "/sys/bus/platform/devices/wl12xx/drop_bcast");
+ f = fopen(filename, "w");
+ if (f < 0) {
+ wpa_printf(MSG_DEBUG, "Could not open file %s: %s",
+ filename, strerror(errno));
+ return -1;
+ }
+
+ rv = fprintf(f, "%d", enable);
+ fclose(f);
+ if (rv < 1) {
+ wpa_printf(MSG_DEBUG, "Could not write to file %s: %s",
+ filename, strerror(errno));
+ return -1;
+ }
+
+ return 0;
+}
+
+static int nl80211_dropbcast_get(char *buf, size_t buf_len)
+{
+ char filename[90], value[10], *pos;
+ int f, rv;
+
+ snprintf(filename, sizeof(filename) - 1,
+ "/sys/bus/platform/devices/wl12xx/drop_bcast");
+ f = open(filename, O_RDONLY);
+ if (f < 0) {
+ wpa_printf(MSG_DEBUG, "Could not open file %s: %s",
+ filename, strerror(errno));
+ return -1;
+ }
+
+ rv = read(f, value, sizeof(value) - 1);
+ close(f);
+ if (rv < 0) {
+ wpa_printf(MSG_DEBUG, "Could not read file %s: %s",
+ filename, strerror(errno));
+ return -1;
+ }
+
+ value[rv] = '\0';
+ pos = os_strchr(value, '\n');
+ if (pos)
+ *pos = '\0';
+
+ return snprintf(buf, buf_len, "Drop bcast = %s\n", value);
+}
+
+#endif /* ANDROID */
static int nl80211_add_pmkid(void *priv, const u8 *bssid, const u8 *pmkid)
{
static int nl80211_set_p2p_powersave(void *priv, int legacy_ps, int opp_ps,
int ctwindow)
{
+#ifdef ANDROID_BRCM_P2P_PATCH
+ char buf[MAX_DRV_CMD_SIZE];
+
+ memset(buf, 0, sizeof(buf));
+ wpa_printf(MSG_DEBUG, "%s: Entry", __func__);
+ os_snprintf(buf, sizeof(buf), "P2P_SET_PS %d %d %d", legacy_ps, opp_ps,
+ ctwindow);
+ return wpa_driver_nl80211_driver_cmd(priv, buf, buf, strlen(buf) + 1);
+#else /* ANDROID_BRCM_P2P_PATCH */
struct i802_bss *bss = priv;
wpa_printf(MSG_DEBUG, "nl80211: set_p2p_powersave (legacy_ps=%d "
return -1; /* Not yet supported */
return nl80211_set_power_save(bss, legacy_ps);
+#endif /* ANDROID_BRCM_P2P_PATCH */
}
}
+#define WPA_PS_ENABLED 0
+#define WPA_PS_DISABLED 1
+
+static int wpa_driver_set_power_save(void *priv, int state)
+{
+ return nl80211_set_power_save(priv, state == WPA_PS_ENABLED);
+}
+
+
+static int get_power_mode_handler(struct nl_msg *msg, void *arg)
+{
+ struct nlattr *tb[NL80211_ATTR_MAX + 1];
+ struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
+ int *state = arg;
+
+ nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
+ genlmsg_attrlen(gnlh, 0), NULL);
+
+ if (!tb[NL80211_ATTR_PS_STATE])
+ return NL_SKIP;
+
+ if (state) {
+ int s = (int) nla_get_u32(tb[NL80211_ATTR_PS_STATE]);
+ wpa_printf(MSG_DEBUG, "nl80211: Get power mode = %d", s);
+ *state = (s == NL80211_PS_ENABLED) ?
+ WPA_PS_ENABLED : WPA_PS_DISABLED;
+ }
+
+ return NL_SKIP;
+}
+
+
+static int wpa_driver_get_power_save(void *priv, int *state)
+{
+ struct i802_bss *bss = priv;
+ struct wpa_driver_nl80211_data *drv = bss->drv;
+ struct nl_msg *msg;
+ int ret = -1;
+ enum nl80211_ps_state ps_state;
+
+ msg = nlmsg_alloc();
+ if (!msg)
+ return -1;
+
+ nl80211_cmd(drv, msg, 0, NL80211_CMD_GET_POWER_SAVE);
+
+ NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, bss->ifindex);
+
+ ret = send_and_recv_msgs(drv, msg, get_power_mode_handler, state);
+ msg = NULL;
+ if (ret < 0)
+ wpa_printf(MSG_ERROR, "nl80211: Get power mode fail: %d", ret);
+nla_put_failure:
+ nlmsg_free(msg);
+ return ret;
+}
+
+
static int android_priv_cmd(struct i802_bss *bss, const char *cmd)
{
struct wpa_driver_nl80211_data *drv = bss->drv;
WEXT_PNO_NONSSID_SECTIONS_SIZE + 1) >= (int) sizeof(buf))
break;
wpa_hexdump_ascii(MSG_DEBUG, "For PNO Scan",
- ssid[i].ssid, ssid[i].ssid_len);
+ 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,
return android_priv_cmd(bss, "PNOFORCE 0");
}
+
+static int wpa_driver_nl80211_driver_cmd(void *priv, char *cmd, char *buf,
+ size_t buf_len)
+{
+ struct i802_bss *bss = priv;
+ struct wpa_driver_nl80211_data *drv = bss->drv;
+ struct ifreq ifr;
+ android_wifi_priv_cmd priv_cmd;
+ int ret = 0;
+
+ if (os_strcasecmp(cmd, "STOP") == 0) {
+ linux_set_iface_flags(drv->global->ioctl_sock, bss->ifname, 0);
+ wpa_msg(drv->ctx, MSG_INFO, WPA_EVENT_DRIVER_STATE "STOPPED");
+ } else if (os_strcasecmp(cmd, "START") == 0) {
+ linux_set_iface_flags(drv->global->ioctl_sock, bss->ifname, 1);
+ wpa_msg(drv->ctx, MSG_INFO, WPA_EVENT_DRIVER_STATE "STARTED");
+ } else if (os_strcasecmp(cmd, "MACADDR") == 0) {
+ u8 macaddr[ETH_ALEN] = {};
+
+ ret = linux_get_ifhwaddr(drv->global->ioctl_sock, bss->ifname,
+ macaddr);
+ if (!ret)
+ ret = os_snprintf(buf, buf_len,
+ "Macaddr = " MACSTR "\n",
+ MAC2STR(macaddr));
+ } else if (os_strcasecmp(cmd, "RELOAD") == 0) {
+ wpa_msg(drv->ctx, MSG_INFO, WPA_EVENT_DRIVER_STATE "HANGED");
+ } else if (os_strncasecmp(cmd, "POWERMODE ", 10) == 0) {
+ int state = atoi(cmd + 10);
+ ret = wpa_driver_set_power_save(priv, state);
+ if (ret < 0)
+ wpa_driver_send_hang_msg(drv);
+ else
+ drv_errors = 0;
+ } else if (os_strncasecmp(cmd, "GETPOWER", 8) == 0) {
+ int state = -1;
+ ret = wpa_driver_get_power_save(priv, &state);
+ if (!ret && (state != -1))
+ ret = os_snprintf(buf, buf_len, "POWERMODE = %d\n",
+ state);
+ } else if( os_strncasecmp(cmd, "RXFILTER-ADD ", 13) == 0 ) {
+ int i = nl80211_parse_wowlan_trigger_nr(cmd + 13);
+ if(i < 0)
+ return i;
+ return nl80211_toggle_wowlan_trigger(bss, i, 1);
+ } else if( os_strncasecmp(cmd, "RXFILTER-REMOVE ", 16) == 0 ) {
+ int i = nl80211_parse_wowlan_trigger_nr(cmd + 16);
+ if(i < 0)
+ return i;
+ return nl80211_toggle_wowlan_trigger(bss, i, 0);
+ } else if (os_strcasecmp(cmd, "RXFILTER-START") == 0) {
+ return nl80211_set_wowlan_triggers(bss, 1);
+ } else if (os_strcasecmp(cmd, "RXFILTER-STOP") == 0) {
+ return nl80211_set_wowlan_triggers(bss, 0);
+ } else if (os_strncasecmp(cmd, "DROPBCAST", 9) == 0) {
+ char *value = cmd + 10;
+
+ if (!os_strcasecmp(value, "ENABLE") ||
+ !os_strcasecmp(value, "1")) {
+ ret = nl80211_toggle_dropbcast(1);
+ } else if (!os_strcasecmp(value, "DISABLE") ||
+ !os_strcasecmp(value, "0")) {
+ ret = nl80211_toggle_dropbcast(0);
+ } else if (!os_strcasecmp(value, "GET") ||
+ !os_strlen(value)) {
+ ret = nl80211_dropbcast_get(buf, buf_len);
+ } else {
+ wpa_printf(MSG_ERROR,
+ "Invalid parameter for DROPBCAST: %s",
+ value);
+ ret = -1;
+ }
+ } else if( os_strcasecmp(cmd, "LINKSPEED") == 0 ) {
+ struct wpa_signal_info sig;
+ int linkspeed;
+
+ if (!drv->associated)
+ return -1;
+
+ ret = nl80211_get_link_signal(drv, &sig);
+ if (ret == 0) {
+ linkspeed = sig.current_txrate;
+ ret = os_snprintf(buf, buf_len, "LinkSpeed %d\n",
+ linkspeed);
+ }
+ } else if (os_strcasecmp(cmd, "RSSI") == 0 ||
+ os_strcasecmp(cmd, "RSSI-APPROX") == 0) {
+ struct wpa_signal_info sig;
+ int rssi;
+
+ if (!drv->associated)
+ return -1;
+
+ ret = nl80211_get_link_signal(drv, &sig);
+ if (ret == 0) {
+ rssi = sig.current_signal;
+ ret = os_snprintf(buf, buf_len, "%s rssi %d\n",
+ drv->ssid, rssi);
+ }
+ } else {
+ wpa_printf(MSG_ERROR, "Unsupported command: %s", cmd);
+ ret = -1;
+ }
+
+ return ret;
+}
+
+#ifdef ANDROID_BRCM_P2P_PATCH
+
+static int wpa_driver_set_p2p_noa(void *priv, u8 count, int start,
+ int duration)
+{
+ char buf[MAX_DRV_CMD_SIZE];
+
+ memset(buf, 0, sizeof(buf));
+ wpa_printf(MSG_DEBUG, "%s: Entry", __func__);
+ os_snprintf(buf, sizeof(buf), "P2P_SET_NOA %d %d %d", count, start,
+ duration);
+ return wpa_driver_nl80211_driver_cmd(priv, buf, buf, strlen(buf) + 1);
+}
+
+
+static int wpa_driver_get_p2p_noa(void *priv, u8 *buf, size_t len)
+{
+ char rbuf[MAX_DRV_CMD_SIZE];
+ char *cmd = "P2P_GET_NOA";
+ int ret;
+
+ wpa_printf(MSG_DEBUG, "%s: Entry", __func__);
+ os_memset(buf, 0, len);
+ ret = wpa_driver_nl80211_driver_cmd(priv, cmd, rbuf, sizeof(rbuf));
+ if (ret <= 0)
+ return 0;
+ ret >>= 1;
+ if (ret > (int)len)
+ ret = (int)len;
+ hexstr2bin(rbuf, buf, ret);
+ return ret;
+}
+
+
+static int wpa_driver_set_ap_wps_p2p_ie(void *priv,
+ const struct wpabuf *beacon,
+ const struct wpabuf *proberesp,
+ const struct wpabuf *assocresp)
+{
+ char buf[MAX_WPSP2PIE_CMD_SIZE];
+ struct wpabuf *ap_wps_p2p_ie = NULL;
+ char *_cmd = "SET_AP_WPS_P2P_IE";
+ char *pbuf;
+ int ret = 0;
+ int i;
+ struct cmd_desc {
+ int cmd;
+ const struct wpabuf *src;
+ } cmd_arr[] = {
+ {0x1, beacon},
+ {0x2, proberesp},
+ {0x4, assocresp},
+ {-1, NULL}
+ };
+
+ wpa_printf(MSG_DEBUG, "%s: Entry", __func__);
+ for (i = 0; cmd_arr[i].cmd != -1; i++) {
+ os_memset(buf, 0, sizeof(buf));
+ pbuf = buf;
+ pbuf += sprintf(pbuf, "%s %d", _cmd, cmd_arr[i].cmd);
+ *pbuf++ = '\0';
+ ap_wps_p2p_ie = cmd_arr[i].src ?
+ wpabuf_dup(cmd_arr[i].src) : NULL;
+ if (ap_wps_p2p_ie) {
+ os_memcpy(pbuf, wpabuf_head(ap_wps_p2p_ie),
+ wpabuf_len(ap_wps_p2p_ie));
+ ret = wpa_driver_nl80211_driver_cmd(
+ priv, buf, buf,
+ os_strlen(_cmd) + 3 +
+ wpabuf_len(ap_wps_p2p_ie));
+ wpabuf_free(ap_wps_p2p_ie);
+ if (ret < 0)
+ break;
+ }
+ }
+
+ return ret;
+}
+
+#endif /* ANDROID_BRCM_P2P_PATCH */
+
#endif /* ANDROID */
wpa_driver_nl80211_cancel_remain_on_channel,
.probe_req_report = wpa_driver_nl80211_probe_req_report,
.deinit_ap = wpa_driver_nl80211_deinit_ap,
+ .deinit_p2p_cli = wpa_driver_nl80211_deinit_p2p_cli,
.resume = wpa_driver_nl80211_resume,
.send_ft_action = nl80211_send_ft_action,
.signal_monitor = nl80211_signal_monitor,
.send_tdls_mgmt = nl80211_send_tdls_mgmt,
.tdls_oper = nl80211_tdls_oper,
#endif /* CONFIG_TDLS */
+#ifdef ANDROID_BRCM_P2P_PATCH
+ .get_noa = wpa_driver_get_p2p_noa,
+ .set_noa = wpa_driver_set_p2p_noa,
+ .set_ap_wps_ie = wpa_driver_set_ap_wps_p2p_ie,
+#endif /* ANDROID_BRCM_P2P_PATCH */
+#ifdef ANDROID
+ .driver_cmd = wpa_driver_nl80211_driver_cmd,
+#endif /* ANDROID */
};