Accumulative patch from commit c6ccf12d3f6f35e295f9522dded535da8af6ab98
c6ccf12 P2P: Use preferred channel list during GO creation
6d956c4 P2P: Re-select channel in invitation case with peer info
010b5f9 P2P: Fix p2p_pref_chan setting from configuration file
d7692b8 dbus: Terminate cleanly on messagebus shutdown
e2396a6 WNM: Enable CONFIG_WNM in Android.mk
65bcd0a WNM: Add sending of BSS Transition Management Query
e27d20b WNM: Add neighbor report processing for BSS Transition Management
f3e907a WPS: Clear connection failure counts on WPS success
170f566 NFC: Connect using learnt credential after NFC Tag read
0af2db7 edit: Fix libreadline history clearing with WPA_TRACE
Change-Id: I0599ea2179869ae8c250ff6b887bb16324fc02f4
Signed-off-by: Dmitry Shmidt <dimitrysh@google.com>
diff --git a/src/common/ieee802_11_defs.h b/src/common/ieee802_11_defs.h
index f782c86..0848590 100644
--- a/src/common/ieee802_11_defs.h
+++ b/src/common/ieee802_11_defs.h
@@ -549,6 +549,14 @@
* Entries (optional) */
u8 variable[0];
} STRUCT_PACKED bss_tm_resp;
+ struct {
+ u8 action; /* 6 */
+ u8 dialog_token;
+ u8 query_reason;
+ /* BSS Transition Candidate List
+ * Entries (optional) */
+ u8 variable[0];
+ } STRUCT_PACKED bss_tm_query;
} u;
} STRUCT_PACKED action;
} u;
@@ -1049,6 +1057,15 @@
#define WNM_BSS_TM_REQ_BSS_TERMINATION_INCLUDED BIT(3)
#define WNM_BSS_TM_REQ_ESS_DISASSOC_IMMINENT BIT(4)
+#define WNM_NEIGHBOR_TSF 1
+#define WNM_NEIGHBOR_CONDENSED_COUNTRY_STRING 2
+#define WNM_NEIGHBOR_BSS_TRANSITION_CANDIDATE 3
+#define WNM_NEIGHBOR_BSS_TERMINATION_DURATION 4
+#define WNM_NEIGHBOR_BEARING 5
+#define WNM_NEIGHBOR_MEASUREMENT_PILOT 66
+#define WNM_NEIGHBOR_RRM_ENABLED_CAPABILITIES 70
+#define WNM_NEIGHBOR_MULTIPLE_BSSID 71
+
/* IEEE Std 802.11-2012, 8.4.2.62 20/40 BSS Coexistence element */
#define WLAN_20_40_BSS_COEX_INFO_REQ BIT(0)
#define WLAN_20_40_BSS_COEX_40MHZ_INTOL BIT(1)
diff --git a/src/p2p/p2p.h b/src/p2p/p2p.h
index 28a0a1d..b197893 100644
--- a/src/p2p/p2p.h
+++ b/src/p2p/p2p.h
@@ -1648,6 +1648,15 @@
*/
int p2p_supported_freq(struct p2p_data *p2p, unsigned int freq);
+/**
+ * p2p_get_pref_freq - Get channel from preferred channel list
+ * @p2p: P2P module context from p2p_init()
+ * @channels: List of channels
+ * Returns: Preferred channel
+ */
+unsigned int p2p_get_pref_freq(struct p2p_data *p2p,
+ const struct p2p_channels *channels);
+
void p2p_update_channel_list(struct p2p_data *p2p, struct p2p_channels *chan);
/**
diff --git a/src/p2p/p2p_invitation.c b/src/p2p/p2p_invitation.c
index 05822c6..5016f1b 100644
--- a/src/p2p/p2p_invitation.c
+++ b/src/p2p/p2p_invitation.c
@@ -322,6 +322,14 @@
status = P2P_SC_FAIL_NO_COMMON_CHANNELS;
goto fail;
}
+ } else if (!(dev->flags & P2P_DEV_FORCE_FREQ) &&
+ !p2p->cfg->cfg_op_channel) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Try to reselect channel selection with "
+ "peer information received; "
+ "previously selected op_class %u channel %u",
+ p2p->op_reg_class, p2p->op_channel);
+ p2p_reselect_channel(p2p, &intersection);
}
op_freq = p2p_channel_to_freq(p2p->op_reg_class,
diff --git a/src/p2p/p2p_utils.c b/src/p2p/p2p_utils.c
index 69bd14b..0da2682 100644
--- a/src/p2p/p2p_utils.c
+++ b/src/p2p/p2p_utils.c
@@ -243,3 +243,31 @@
return p2p_channels_includes(&p2p->cfg->channels, op_reg_class,
op_channel);
}
+
+
+unsigned int p2p_get_pref_freq(struct p2p_data *p2p,
+ const struct p2p_channels *channels)
+{
+ unsigned int i;
+ int freq = 0;
+
+ if (channels == NULL) {
+ if (p2p->cfg->num_pref_chan) {
+ freq = p2p_channel_to_freq(
+ p2p->cfg->pref_chan[0].op_class,
+ p2p->cfg->pref_chan[0].chan);
+ if (freq < 0)
+ freq = 0;
+ }
+ return freq;
+ }
+
+ for (i = 0; p2p->cfg->pref_chan && i < p2p->cfg->num_pref_chan; i++) {
+ freq = p2p_channel_to_freq(p2p->cfg->pref_chan[i].op_class,
+ p2p->cfg->pref_chan[i].chan);
+ if (p2p_channels_includes_freq(channels, freq))
+ return freq;
+ }
+
+ return 0;
+}
diff --git a/src/utils/edit_readline.c b/src/utils/edit_readline.c
index add26fa..c2a5bca 100644
--- a/src/utils/edit_readline.c
+++ b/src/utils/edit_readline.c
@@ -167,9 +167,9 @@
if (filter_cb && filter_cb(edit_cb_ctx, p)) {
h = remove_history(where_history());
if (h) {
- os_free(h->line);
+ free(h->line);
free(h->data);
- os_free(h);
+ free(h);
} else
next_history();
} else
diff --git a/wpa_supplicant/ctrl_iface.c b/wpa_supplicant/ctrl_iface.c
index 8c06334..e8bfe0c 100644
--- a/wpa_supplicant/ctrl_iface.c
+++ b/wpa_supplicant/ctrl_iface.c
@@ -4994,6 +4994,19 @@
return ret;
}
+
+static int wpas_ctrl_iface_wnm_bss_query(struct wpa_supplicant *wpa_s, char *cmd)
+{
+ int query_reason;
+
+ query_reason = atoi(cmd);
+
+ wpa_printf(MSG_DEBUG, "CTRL_IFACE: WNM_BSS_QUERY query_reason=%d",
+ query_reason);
+
+ return wnm_send_bss_transition_mgmt_query(wpa_s, query_reason);
+}
+
#endif /* CONFIG_WNM */
@@ -5631,6 +5644,9 @@
} else if (os_strncmp(buf, "WNM_SLEEP ", 10) == 0) {
if (wpas_ctrl_iface_wnm_sleep(wpa_s, buf + 10))
reply_len = -1;
+ } else if (os_strncmp(buf, "WNM_BSS_QUERY ", 10) == 0) {
+ if (wpas_ctrl_iface_wnm_bss_query(wpa_s, buf + 10))
+ reply_len = -1;
#endif /* CONFIG_WNM */
} else if (os_strcmp(buf, "FLUSH") == 0) {
wpa_supplicant_ctrl_iface_flush(wpa_s);
diff --git a/wpa_supplicant/dbus/dbus_common.c b/wpa_supplicant/dbus/dbus_common.c
index 5d0e31e..6caf740 100644
--- a/wpa_supplicant/dbus/dbus_common.c
+++ b/wpa_supplicant/dbus/dbus_common.c
@@ -17,6 +17,7 @@
#include "dbus_common_i.h"
#include "dbus_new.h"
#include "dbus_old.h"
+#include "../wpa_supplicant_i.h"
#ifndef SIGPOLL
@@ -257,6 +258,22 @@
}
+static DBusHandlerResult disconnect_filter(DBusConnection *conn,
+ DBusMessage *message, void *data)
+{
+ struct wpas_dbus_priv *priv = data;
+
+ if (dbus_message_is_signal(message, DBUS_INTERFACE_LOCAL,
+ "Disconnected")) {
+ wpa_printf(MSG_DEBUG, "dbus: bus disconnected, terminating");
+ dbus_connection_set_exit_on_disconnect(conn, FALSE);
+ wpa_supplicant_terminate_proc(priv->global);
+ return DBUS_HANDLER_RESULT_HANDLED;
+ } else
+ return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+}
+
+
static int wpas_dbus_init_common(struct wpas_dbus_priv *priv)
{
DBusError error;
@@ -265,7 +282,10 @@
/* Get a reference to the system bus */
dbus_error_init(&error);
priv->con = dbus_bus_get(DBUS_BUS_SYSTEM, &error);
- if (!priv->con) {
+ if (priv->con) {
+ dbus_connection_add_filter(priv->con, disconnect_filter, priv,
+ NULL);
+ } else {
wpa_printf(MSG_ERROR, "dbus: Could not acquire the system "
"bus: %s - %s", error.name, error.message);
ret = -1;
@@ -304,6 +324,9 @@
NULL, NULL, NULL);
dbus_connection_set_timeout_functions(priv->con, NULL, NULL,
NULL, NULL, NULL);
+ dbus_connection_remove_filter(priv->con, disconnect_filter,
+ priv);
+
dbus_connection_unref(priv->con);
}
diff --git a/wpa_supplicant/p2p_supplicant.c b/wpa_supplicant/p2p_supplicant.c
index f1e15ff..b760e7f 100644
--- a/wpa_supplicant/p2p_supplicant.c
+++ b/wpa_supplicant/p2p_supplicant.c
@@ -3103,6 +3103,12 @@
wpa_printf(MSG_DEBUG, "P2P: Random operating channel: "
"%d:%d", p2p.op_reg_class, p2p.op_channel);
}
+
+ if (wpa_s->conf->p2p_pref_chan && wpa_s->conf->num_p2p_pref_chan) {
+ p2p.pref_chan = wpa_s->conf->p2p_pref_chan;
+ p2p.num_pref_chan = wpa_s->conf->num_p2p_pref_chan;
+ }
+
if (wpa_s->conf->country[0] && wpa_s->conf->country[1]) {
os_memcpy(p2p.country, wpa_s->conf->country, 2);
p2p.country[2] = 0x04;
@@ -4084,6 +4090,7 @@
{
u8 bssid[ETH_ALEN];
int res;
+ unsigned int pref_freq;
os_memset(params, 0, sizeof(*params));
params->role_go = 1;
@@ -4140,6 +4147,11 @@
params->freq = wpa_s->best_5_freq;
wpa_printf(MSG_DEBUG, "P2P: Set GO freq based on best 5 GHz "
"channel %d MHz", params->freq);
+ } else if ((pref_freq = p2p_get_pref_freq(wpa_s->global->p2p,
+ channels))) {
+ params->freq = pref_freq;
+ wpa_printf(MSG_DEBUG, "P2P: Set GO freq %d MHz from preferred "
+ "channels", params->freq);
} else {
int chan;
for (chan = 0; chan < 11; chan++) {
diff --git a/wpa_supplicant/wnm_sta.c b/wpa_supplicant/wnm_sta.c
index 4d9e453..7de96c5 100644
--- a/wpa_supplicant/wnm_sta.c
+++ b/wpa_supplicant/wnm_sta.c
@@ -14,8 +14,12 @@
#include "wpa_supplicant_i.h"
#include "driver_i.h"
#include "scan.h"
+#include "ctrl_iface.h"
+#include "bss.h"
+#include "wnm_sta.h"
#define MAX_TFS_IE_LEN 1024
+#define WNM_MAX_NEIGHBOR_REPORT 10
/* get the TFS IE from driver */
@@ -294,6 +298,199 @@
}
+void wnm_deallocate_memory(struct wpa_supplicant *wpa_s)
+{
+ int i;
+
+ for (i = 0; i < wpa_s->wnm_num_neighbor_report; i++) {
+ os_free(wpa_s->wnm_neighbor_report_elements[i].tsf_info);
+ os_free(wpa_s->wnm_neighbor_report_elements[i].con_coun_str);
+ os_free(wpa_s->wnm_neighbor_report_elements[i].bss_tran_can);
+ os_free(wpa_s->wnm_neighbor_report_elements[i].bss_term_dur);
+ os_free(wpa_s->wnm_neighbor_report_elements[i].bearing);
+ os_free(wpa_s->wnm_neighbor_report_elements[i].meas_pilot);
+ os_free(wpa_s->wnm_neighbor_report_elements[i].rrm_cap);
+ os_free(wpa_s->wnm_neighbor_report_elements[i].mul_bssid);
+ }
+
+ os_free(wpa_s->wnm_neighbor_report_elements);
+ wpa_s->wnm_neighbor_report_elements = NULL;
+}
+
+
+static void wnm_parse_neighbor_report_elem(struct neighbor_report *rep,
+ u8 id, u8 elen, const u8 *pos)
+{
+ switch (id) {
+ case WNM_NEIGHBOR_TSF:
+ if (elen < 2 + 2) {
+ wpa_printf(MSG_DEBUG, "WNM: Too short TSF");
+ break;
+ }
+ rep->tsf_info = os_zalloc(sizeof(struct tsf_info));
+ if (rep->tsf_info == NULL)
+ break;
+ rep->tsf_info->present = 1;
+ os_memcpy(rep->tsf_info->tsf_offset, pos, 2);
+ os_memcpy(rep->tsf_info->beacon_interval, pos + 2, 2);
+ break;
+ case WNM_NEIGHBOR_CONDENSED_COUNTRY_STRING:
+ if (elen < 2) {
+ wpa_printf(MSG_DEBUG, "WNM: Too short condensed "
+ "country string");
+ break;
+ }
+ rep->con_coun_str =
+ os_zalloc(sizeof(struct condensed_country_string));
+ if (rep->con_coun_str == NULL)
+ break;
+ rep->con_coun_str->present = 1;
+ os_memcpy(rep->con_coun_str->country_string, pos, 2);
+ break;
+ case WNM_NEIGHBOR_BSS_TRANSITION_CANDIDATE:
+ if (elen < 1) {
+ wpa_printf(MSG_DEBUG, "WNM: Too short BSS transition "
+ "candidate");
+ break;
+ }
+ rep->bss_tran_can =
+ os_zalloc(sizeof(struct bss_transition_candidate));
+ if (rep->bss_tran_can == NULL)
+ break;
+ rep->bss_tran_can->present = 1;
+ rep->bss_tran_can->preference = pos[0];
+ break;
+ case WNM_NEIGHBOR_BSS_TERMINATION_DURATION:
+ if (elen < 12) {
+ wpa_printf(MSG_DEBUG, "WNM: Too short BSS termination "
+ "duration");
+ break;
+ }
+ rep->bss_term_dur =
+ os_zalloc(sizeof(struct bss_termination_duration));
+ if (rep->bss_term_dur == NULL)
+ break;
+ rep->bss_term_dur->present = 1;
+ os_memcpy(rep->bss_term_dur->duration, pos, 12);
+ break;
+ case WNM_NEIGHBOR_BEARING:
+ if (elen < 8) {
+ wpa_printf(MSG_DEBUG, "WNM: Too short neighbor "
+ "bearing");
+ break;
+ }
+ rep->bearing = os_zalloc(sizeof(struct bearing));
+ if (rep->bearing == NULL)
+ break;
+ rep->bearing->present = 1;
+ os_memcpy(rep->bearing->bearing, pos, 8);
+ break;
+ case WNM_NEIGHBOR_MEASUREMENT_PILOT:
+ if (elen < 2) {
+ wpa_printf(MSG_DEBUG, "WNM: Too short measurement "
+ "pilot");
+ break;
+ }
+ rep->meas_pilot = os_zalloc(sizeof(struct measurement_pilot));
+ if (rep->meas_pilot == NULL)
+ break;
+ rep->meas_pilot->present = 1;
+ rep->meas_pilot->measurement_pilot = pos[0];
+ rep->meas_pilot->num_vendor_specific = pos[1];
+ os_memcpy(rep->meas_pilot->vendor_specific, pos + 2, elen - 2);
+ break;
+ case WNM_NEIGHBOR_RRM_ENABLED_CAPABILITIES:
+ if (elen < 4) {
+ wpa_printf(MSG_DEBUG, "WNM: Too short RRM enabled "
+ "capabilities");
+ break;
+ }
+ rep->rrm_cap =
+ os_zalloc(sizeof(struct rrm_enabled_capabilities));
+ if (rep->rrm_cap == NULL)
+ break;
+ rep->rrm_cap->present = 1;
+ os_memcpy(rep->rrm_cap->capabilities, pos, 4);
+ break;
+ case WNM_NEIGHBOR_MULTIPLE_BSSID:
+ if (elen < 2) {
+ wpa_printf(MSG_DEBUG, "WNM: Too short multiple BSSID");
+ break;
+ }
+ rep->mul_bssid = os_zalloc(sizeof(struct multiple_bssid));
+ if (rep->mul_bssid == NULL)
+ break;
+ rep->mul_bssid->present = 1;
+ rep->mul_bssid->max_bssid_indicator = pos[0];
+ rep->mul_bssid->num_vendor_specific = pos[1];
+ os_memcpy(rep->mul_bssid->vendor_specific, pos + 2, elen - 2);
+ break;
+ }
+}
+
+
+static void wnm_parse_neighbor_report(struct wpa_supplicant *wpa_s,
+ const u8 *pos, u8 len,
+ struct neighbor_report *rep)
+{
+ u8 left = len;
+
+ if (left < 13) {
+ wpa_printf(MSG_DEBUG, "WNM: Too short neighbor report");
+ return;
+ }
+
+ os_memcpy(rep->bssid, pos, ETH_ALEN);
+ os_memcpy(rep->bssid_information, pos + ETH_ALEN, 4);
+ rep->regulatory_class = *(pos + 10);
+ rep->channel_number = *(pos + 11);
+ rep->phy_type = *(pos + 12);
+
+ pos += 13;
+ left -= 13;
+
+ while (left >= 2) {
+ u8 id, elen;
+
+ id = *pos++;
+ elen = *pos++;
+ wnm_parse_neighbor_report_elem(rep, id, elen, pos);
+ left -= 2 + elen;
+ pos += elen;
+ }
+}
+
+
+static int compare_scan_neighbor_results(struct wpa_supplicant *wpa_s,
+ struct wpa_scan_results *scan_res,
+ struct neighbor_report *neigh_rep,
+ u8 num_neigh_rep, u8 *bssid_to_connect)
+{
+
+ u8 i, j;
+
+ if (scan_res == NULL || num_neigh_rep == 0)
+ return 0;
+
+ for (i = 0; i < num_neigh_rep; i++) {
+ for (j = 0; j < scan_res->num; j++) {
+ /* Check for a better RSSI AP */
+ if (os_memcmp(scan_res->res[j]->bssid,
+ neigh_rep[i].bssid, ETH_ALEN) == 0 &&
+ scan_res->res[j]->level >
+ wpa_s->current_bss->level) {
+ /* Got a BSSID with better RSSI value */
+ os_memcpy(bssid_to_connect, neigh_rep[i].bssid,
+ ETH_ALEN);
+ return 1;
+ }
+ }
+ }
+
+ return 0;
+}
+
+
static void wnm_send_bss_transition_mgmt_resp(struct wpa_supplicant *wpa_s,
u8 dialog_token, u8 status,
u8 delay, const u8 *target_bssid)
@@ -332,29 +529,90 @@
}
+void wnm_scan_response(struct wpa_supplicant *wpa_s,
+ struct wpa_scan_results *scan_res)
+{
+ u8 bssid[ETH_ALEN];
+
+ if (scan_res == NULL) {
+ wpa_printf(MSG_ERROR, "Scan result is NULL");
+ goto send_bss_resp_fail;
+ }
+
+ /* Compare the Neighbor Report and scan results */
+ if (compare_scan_neighbor_results(wpa_s, scan_res,
+ wpa_s->wnm_neighbor_report_elements,
+ wpa_s->wnm_num_neighbor_report,
+ bssid) == 1) {
+ /* Associate to the network */
+ struct wpa_bss *bss;
+ struct wpa_ssid *ssid = wpa_s->current_ssid;
+
+ bss = wpa_bss_get_bssid(wpa_s, bssid);
+ if (!bss) {
+ wpa_printf(MSG_DEBUG, "WNM: Target AP not found from "
+ "BSS table");
+ goto send_bss_resp_fail;
+ }
+
+ /* Send the BSS Management Response - Accept */
+ if (wpa_s->wnm_reply) {
+ wnm_send_bss_transition_mgmt_resp(wpa_s,
+ wpa_s->wnm_dialog_token,
+ 0, /* Accept */
+ 0, NULL);
+ }
+
+ wpa_s->reassociate = 1;
+ wpa_supplicant_connect(wpa_s, bss, ssid);
+ wnm_deallocate_memory(wpa_s);
+ return;
+ }
+
+ /* Send reject response for all the failures */
+send_bss_resp_fail:
+ wnm_deallocate_memory(wpa_s);
+ if (wpa_s->wnm_reply) {
+ wnm_send_bss_transition_mgmt_resp(wpa_s,
+ wpa_s->wnm_dialog_token,
+ 1 /* Reject - unspecified */,
+ 0, NULL);
+ }
+ return;
+}
+
+
static void ieee802_11_rx_bss_trans_mgmt_req(struct wpa_supplicant *wpa_s,
const u8 *pos, const u8 *end,
int reply)
{
- u8 dialog_token;
- u8 mode;
- u16 disassoc_timer;
-
if (pos + 5 > end)
return;
- dialog_token = pos[0];
- mode = pos[1];
- disassoc_timer = WPA_GET_LE16(pos + 2);
+ wpa_s->wnm_dialog_token = pos[0];
+ wpa_s->wnm_mode = pos[1];
+ wpa_s->wnm_dissoc_timer = WPA_GET_LE16(pos + 2);
+ wpa_s->wnm_validity_interval = pos[4];
+ wpa_s->wnm_reply = reply;
wpa_printf(MSG_DEBUG, "WNM: BSS Transition Management Request: "
"dialog_token=%u request_mode=0x%x "
"disassoc_timer=%u validity_interval=%u",
- dialog_token, mode, disassoc_timer, pos[4]);
+ wpa_s->wnm_dialog_token, wpa_s->wnm_mode,
+ wpa_s->wnm_dissoc_timer, wpa_s->wnm_validity_interval);
+
pos += 5;
- if (mode & 0x08)
+
+ if (wpa_s->wnm_mode & 0x08) {
+ if (pos + 12 > end) {
+ wpa_printf(MSG_DEBUG, "WNM: Too short BSS TM Request");
+ return;
+ }
+ os_memcpy(wpa_s->wnm_bss_termination_duration, pos, 12);
pos += 12; /* BSS Termination Duration */
- if (mode & 0x10) {
+ }
+
+ if (wpa_s->wnm_mode & 0x10) {
char url[256];
if (pos + 1 > end || pos + 1 + pos[0] > end) {
wpa_printf(MSG_DEBUG, "WNM: Invalid BSS Transition "
@@ -363,14 +621,15 @@
}
os_memcpy(url, pos + 1, pos[0]);
url[pos[0]] = '\0';
+ pos += 1 + pos[0];
wpa_msg(wpa_s, MSG_INFO, "WNM: ESS Disassociation Imminent - "
"session_info_url=%s", url);
}
- if (mode & 0x04) {
+ if (wpa_s->wnm_mode & 0x04) {
wpa_msg(wpa_s, MSG_INFO, "WNM: Disassociation Imminent - "
- "Disassociation Timer %u", disassoc_timer);
- if (disassoc_timer && !wpa_s->scanning) {
+ "Disassociation Timer %u", wpa_s->wnm_dissoc_timer);
+ if (wpa_s->wnm_dissoc_timer && !wpa_s->scanning) {
/* TODO: mark current BSS less preferred for
* selection */
wpa_printf(MSG_DEBUG, "Trying to find another BSS");
@@ -378,15 +637,85 @@
}
}
- if (reply) {
- /* TODO: add support for reporting Accept */
- wnm_send_bss_transition_mgmt_resp(wpa_s, dialog_token,
+ if (wpa_s->wnm_mode & 0x01) {
+ wpa_msg(wpa_s, MSG_INFO, "WNM: Preferred List Available");
+ wpa_s->wnm_num_neighbor_report = 0;
+ os_free(wpa_s->wnm_neighbor_report_elements);
+ wpa_s->wnm_neighbor_report_elements = os_zalloc(
+ WNM_MAX_NEIGHBOR_REPORT *
+ sizeof(struct neighbor_report));
+ if (wpa_s->wnm_neighbor_report_elements == NULL)
+ return;
+
+ while (pos + 2 <= end &&
+ wpa_s->wnm_num_neighbor_report < WNM_MAX_NEIGHBOR_REPORT)
+ {
+ u8 tag = *pos++;
+ u8 len = *pos++;
+
+ wpa_printf(MSG_DEBUG, "WNM: Neighbor report tag %u",
+ tag);
+ if (pos + len > end) {
+ wpa_printf(MSG_DEBUG, "WNM: Truncated request");
+ return;
+ }
+ wnm_parse_neighbor_report(
+ wpa_s, pos, len,
+ &wpa_s->wnm_neighbor_report_elements[
+ wpa_s->wnm_num_neighbor_report]);
+
+ pos += len;
+ wpa_s->wnm_num_neighbor_report++;
+ }
+
+ wpa_s->scan_res_handler = wnm_scan_response;
+ wpa_supplicant_req_scan(wpa_s, 0, 0);
+ } else if (reply) {
+ wpa_msg(wpa_s, MSG_INFO, "WNM: BSS Transition Management "
+ "Request Mode is zero");
+ wnm_send_bss_transition_mgmt_resp(wpa_s,
+ wpa_s->wnm_dialog_token,
1 /* Reject - unspecified */,
0, NULL);
}
}
+int wnm_send_bss_transition_mgmt_query(struct wpa_supplicant *wpa_s,
+ u8 query_reason)
+{
+ u8 buf[1000], *pos;
+ struct ieee80211_mgmt *mgmt;
+ size_t len;
+ int ret;
+
+ wpa_printf(MSG_DEBUG, "WNM: Send BSS Transition Management Query to "
+ MACSTR " query_reason=%u",
+ MAC2STR(wpa_s->bssid), query_reason);
+
+ mgmt = (struct ieee80211_mgmt *) buf;
+ os_memset(&buf, 0, sizeof(buf));
+ os_memcpy(mgmt->da, wpa_s->bssid, ETH_ALEN);
+ os_memcpy(mgmt->sa, wpa_s->own_addr, ETH_ALEN);
+ os_memcpy(mgmt->bssid, wpa_s->bssid, ETH_ALEN);
+ mgmt->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
+ WLAN_FC_STYPE_ACTION);
+ mgmt->u.action.category = WLAN_ACTION_WNM;
+ mgmt->u.action.u.bss_tm_query.action = WNM_BSS_TRANS_MGMT_QUERY;
+ mgmt->u.action.u.bss_tm_query.dialog_token = 0;
+ mgmt->u.action.u.bss_tm_query.query_reason = query_reason;
+ pos = mgmt->u.action.u.bss_tm_query.variable;
+
+ len = pos - (u8 *) &mgmt->u.action.category;
+
+ ret = wpa_drv_send_action(wpa_s, wpa_s->assoc_freq, 0, wpa_s->bssid,
+ wpa_s->own_addr, wpa_s->bssid,
+ &mgmt->u.action.category, len, 0);
+
+ return ret;
+}
+
+
void ieee802_11_rx_wnm_action(struct wpa_supplicant *wpa_s,
struct rx_action *action)
{
@@ -418,6 +747,7 @@
ieee802_11_rx_wnmsleep_resp(wpa_s, action->data, action->len);
break;
default:
+ wpa_printf(MSG_ERROR, "WNM: Unknown request");
break;
}
}
diff --git a/wpa_supplicant/wnm_sta.h b/wpa_supplicant/wnm_sta.h
index 3f9d88b..2933926 100644
--- a/wpa_supplicant/wnm_sta.h
+++ b/wpa_supplicant/wnm_sta.h
@@ -12,10 +12,79 @@
struct rx_action;
struct wpa_supplicant;
+struct tsf_info {
+ u8 present;
+ u8 tsf_offset[2];
+ u8 beacon_interval[2];
+};
+
+struct condensed_country_string {
+ u8 present;
+ u8 country_string[2];
+};
+
+struct bss_transition_candidate {
+ u8 present;
+ u8 preference;
+};
+
+struct bss_termination_duration {
+ u8 present;
+ u8 duration[12];
+};
+
+struct bearing {
+ u8 present;
+ u8 bearing[8];
+};
+
+struct measurement_pilot {
+ u8 present;
+ u8 measurement_pilot;
+ u8 num_vendor_specific;
+ u8 vendor_specific[255];
+};
+
+struct rrm_enabled_capabilities {
+ u8 present;
+ u8 capabilities[4];
+};
+
+struct multiple_bssid {
+ u8 present;
+ u8 max_bssid_indicator;
+ u8 num_vendor_specific;
+ u8 vendor_specific[255];
+};
+
+struct neighbor_report {
+ u8 bssid[ETH_ALEN];
+ u8 bssid_information[4];
+ u8 regulatory_class;
+ u8 channel_number;
+ u8 phy_type;
+ struct tsf_info *tsf_info;
+ struct condensed_country_string *con_coun_str;
+ struct bss_transition_candidate *bss_tran_can;
+ struct bss_termination_duration *bss_term_dur;
+ struct bearing *bearing;
+ struct measurement_pilot *meas_pilot;
+ struct rrm_enabled_capabilities *rrm_cap;
+ struct multiple_bssid *mul_bssid;
+};
+
+
int ieee802_11_send_wnmsleep_req(struct wpa_supplicant *wpa_s,
u8 action, u16 intval, struct wpabuf *tfs_req);
void ieee802_11_rx_wnm_action(struct wpa_supplicant *wpa_s,
struct rx_action *action);
+void wnm_scan_response(struct wpa_supplicant *wpa_s,
+ struct wpa_scan_results *scan_res);
+
+int wnm_send_bss_transition_mgmt_query(struct wpa_supplicant *wpa_s,
+ u8 query_reason);
+void wnm_deallocate_memory(struct wpa_supplicant *wpa_s);
+
#endif /* WNM_STA_H */
diff --git a/wpa_supplicant/wpa_cli.c b/wpa_supplicant/wpa_cli.c
index b66dcd0..61d4b61 100644
--- a/wpa_supplicant/wpa_cli.c
+++ b/wpa_supplicant/wpa_cli.c
@@ -2315,6 +2315,12 @@
return wpa_cli_cmd(ctrl, "WNM_SLEEP", 0, argc, argv);
}
+
+static int wpa_cli_cmd_wnm_bss_query(struct wpa_ctrl *ctrl, int argc, char *argv[])
+{
+ return wpa_cli_cmd(ctrl, "WNM_BSS_QUERY", 1, argc, argv);
+}
+
#endif /* CONFIG_WNM */
@@ -2798,6 +2804,8 @@
#ifdef CONFIG_WNM
{ "wnm_sleep", wpa_cli_cmd_wnm_sleep, NULL, cli_cmd_flag_none,
"<enter/exit> [interval=#] = enter/exit WNM-Sleep mode" },
+ { "wnm_bss_query", wpa_cli_cmd_wnm_bss_query, NULL, cli_cmd_flag_none,
+ "<query reason> = Send BSS Transition Management Query" },
#endif /* CONFIG_WNM */
{ "raw", wpa_cli_cmd_raw, NULL, cli_cmd_flag_sensitive,
"<params..> = Sent unprocessed command" },
diff --git a/wpa_supplicant/wpa_supplicant.c b/wpa_supplicant/wpa_supplicant.c
index dbdaf19..1a7c0ff 100644
--- a/wpa_supplicant/wpa_supplicant.c
+++ b/wpa_supplicant/wpa_supplicant.c
@@ -49,6 +49,7 @@
#include "scan.h"
#include "offchannel.h"
#include "hs20_supplicant.h"
+#include "wnm_sta.h"
const char *wpa_supplicant_version =
"wpa_supplicant v" VERSION_STR "\n"
@@ -470,6 +471,9 @@
wpa_s->disallow_aps_ssid = NULL;
wnm_bss_keep_alive_deinit(wpa_s);
+#ifdef CONFIG_WNM
+ wnm_deallocate_memory(wpa_s);
+#endif /* CONFIG_WNM */
ext_password_deinit(wpa_s->ext_pw);
wpa_s->ext_pw = NULL;
diff --git a/wpa_supplicant/wpa_supplicant_i.h b/wpa_supplicant/wpa_supplicant_i.h
index 7559a75..a2a189b 100644
--- a/wpa_supplicant/wpa_supplicant_i.h
+++ b/wpa_supplicant/wpa_supplicant_i.h
@@ -690,6 +690,17 @@
u8 last_gas_dialog_token;
unsigned int no_keep_alive:1;
+
+#ifdef CONFIG_WNM
+ u8 wnm_dialog_token;
+ u8 wnm_reply;
+ u8 wnm_num_neighbor_report;
+ u8 wnm_mode;
+ u16 wnm_dissoc_timer;
+ u8 wnm_validity_interval;
+ u8 wnm_bss_termination_duration[12];
+ struct neighbor_report *wnm_neighbor_report_elements;
+#endif /* CONFIG_WNM */
};
diff --git a/wpa_supplicant/wps_supplicant.c b/wpa_supplicant/wps_supplicant.c
index b376fb0..d73e023 100644
--- a/wpa_supplicant/wps_supplicant.c
+++ b/wpa_supplicant/wps_supplicant.c
@@ -648,6 +648,9 @@
wpa_msg(wpa_s, MSG_INFO, WPS_EVENT_SUCCESS);
wpa_s->wps_success = 1;
wpas_notify_wps_event_success(wpa_s);
+ if (wpa_s->current_ssid)
+ wpas_clear_temp_disabled(wpa_s, wpa_s->current_ssid, 1);
+ wpa_s->extra_blacklist_count = 0;
/*
* Enable the networks disabled during wpas_wps_reassoc after 10
@@ -964,21 +967,10 @@
}
-static void wpas_wps_reassoc(struct wpa_supplicant *wpa_s,
- struct wpa_ssid *selected, const u8 *bssid)
+static void wpas_wps_temp_disable(struct wpa_supplicant *wpa_s,
+ struct wpa_ssid *selected)
{
struct wpa_ssid *ssid;
- struct wpa_bss *bss;
-
- wpa_s->after_wps = 0;
- wpa_s->known_wps_freq = 0;
- if (bssid) {
- bss = wpa_bss_get_bssid_latest(wpa_s, bssid);
- if (bss && bss->freq > 0) {
- wpa_s->known_wps_freq = 1;
- wpa_s->wps_freq = bss->freq;
- }
- }
if (wpa_s->current_ssid)
wpa_supplicant_deauthenticate(
@@ -1006,6 +998,26 @@
}
ssid = ssid->next;
}
+}
+
+
+static void wpas_wps_reassoc(struct wpa_supplicant *wpa_s,
+ struct wpa_ssid *selected, const u8 *bssid)
+{
+ struct wpa_bss *bss;
+
+ wpa_s->after_wps = 0;
+ wpa_s->known_wps_freq = 0;
+ if (bssid) {
+ bss = wpa_bss_get_bssid_latest(wpa_s, bssid);
+ if (bss && bss->freq > 0) {
+ wpa_s->known_wps_freq = 1;
+ wpa_s->wps_freq = bss->freq;
+ }
+ }
+
+ wpas_wps_temp_disable(wpa_s, selected);
+
wpa_s->disconnected = 0;
wpa_s->reassociate = 1;
wpa_s->scan_runs = 0;
@@ -2087,6 +2099,15 @@
{
wpa_s->wps_ap_channel = 0;
+ /*
+ * Disable existing networks temporarily to allow the newly learned
+ * credential to be preferred. Enable the temporarily disabled networks
+ * after 10 seconds.
+ */
+ wpas_wps_temp_disable(wpa_s, NULL);
+ eloop_register_timeout(10, 0, wpas_wps_reenable_networks_cb, wpa_s,
+ NULL);
+
if (wps_oob_use_cred(wpa_s->wps, attr) < 0)
return -1;