/*
* Generic advertisement service (GAS) query
* Copyright (c) 2009, Atheros Communications
- * Copyright (c) 2011-2013, Qualcomm Atheros, Inc.
+ * Copyright (c) 2011-2014, Qualcomm Atheros, Inc.
* Copyright (c) 2011-2014, Jouni Malinen <j@w1.fi>
*
* This software may be distributed under the terms of the BSD license.
#include "common/ieee802_11_defs.h"
#include "common/gas.h"
#include "common/wpa_ctrl.h"
+#include "rsn_supp/wpa.h"
#include "wpa_supplicant_i.h"
#include "driver_i.h"
#include "offchannel.h"
/** GAS query timeout in seconds */
#define GAS_QUERY_TIMEOUT_PERIOD 2
-/** Retry period for GAS query requests in milliseconds */
-#define GAS_SERVICE_RETRY_PERIOD_MS 500
/**
struct wpabuf *req;
struct wpabuf *adv_proto;
struct wpabuf *resp;
+ struct os_reltime last_oper;
void (*cb)(void *ctx, const u8 *dst, u8 dialog_token,
enum gas_query_result result,
const struct wpabuf *adv_proto,
static void gas_query_tx_comeback_timeout(void *eloop_data, void *user_ctx);
static void gas_query_timeout(void *eloop_data, void *user_ctx);
-static void gas_service_timeout(void *eloop_data, void *user_ctx);
+
+
+static int ms_from_time(struct os_reltime *last)
+{
+ struct os_reltime now, res;
+
+ os_get_reltime(&now);
+ os_reltime_sub(&now, last, &res);
+ return res.sec * 1000 + res.usec / 1000;
+}
/**
offchannel_send_action_done(gas->wpa_s);
eloop_cancel_timeout(gas_query_tx_comeback_timeout, gas, query);
eloop_cancel_timeout(gas_query_timeout, gas, query);
- eloop_cancel_timeout(gas_service_timeout, gas, query);
dl_list_del(&query->list);
query->cb(query->ctx, query->addr, query->dialog_token, result,
query->adv_proto, query->resp, query->status_code);
{
struct gas_query_pending *query;
struct gas_query *gas = wpa_s->gas;
+ int dur;
if (gas->current == NULL) {
wpa_printf(MSG_DEBUG, "GAS: Unexpected TX status: freq=%u dst="
query = gas->current;
+ dur = ms_from_time(&query->last_oper);
wpa_printf(MSG_DEBUG, "GAS: TX status: freq=%u dst=" MACSTR
- " result=%d query=%p dialog_token=%u",
- freq, MAC2STR(dst), result, query, query->dialog_token);
+ " result=%d query=%p dialog_token=%u dur=%d ms",
+ freq, MAC2STR(dst), result, query, query->dialog_token, dur);
if (os_memcmp(dst, query->addr, ETH_ALEN) != 0) {
wpa_printf(MSG_DEBUG, "GAS: TX status for unexpected destination");
return;
}
+ os_get_reltime(&query->last_oper);
if (result == OFFCHANNEL_SEND_ACTION_SUCCESS) {
eloop_cancel_timeout(gas_query_timeout, gas, query);
}
+static int pmf_in_use(struct wpa_supplicant *wpa_s, const u8 *addr)
+{
+ if (wpa_s->current_ssid == NULL ||
+ wpa_s->wpa_state < WPA_4WAY_HANDSHAKE ||
+ os_memcmp(addr, wpa_s->bssid, ETH_ALEN) != 0)
+ return 0;
+ return wpa_sm_pmf_enabled(wpa_s->wpa);
+}
+
+
static int gas_query_tx(struct gas_query *gas, struct gas_query_pending *query,
struct wpabuf *req)
{
- int res;
+ unsigned int wait_time;
+ int res, prot = pmf_in_use(gas->wpa_s, query->addr);
+
wpa_printf(MSG_DEBUG, "GAS: Send action frame to " MACSTR " len=%u "
- "freq=%d", MAC2STR(query->addr),
- (unsigned int) wpabuf_len(req), query->freq);
+ "freq=%d prot=%d", MAC2STR(query->addr),
+ (unsigned int) wpabuf_len(req), query->freq, prot);
+ if (prot) {
+ u8 *categ = wpabuf_mhead_u8(req);
+ *categ = WLAN_ACTION_PROTECTED_DUAL;
+ }
+ os_get_reltime(&query->last_oper);
+ wait_time = 1000;
+ if (gas->wpa_s->max_remain_on_chan &&
+ wait_time > gas->wpa_s->max_remain_on_chan)
+ wait_time = gas->wpa_s->max_remain_on_chan;
res = offchannel_send_action(gas->wpa_s, query->freq, query->addr,
gas->wpa_s->own_addr, query->addr,
- wpabuf_head(req), wpabuf_len(req), 1000,
- gas_query_tx_status, 0);
+ wpabuf_head(req), wpabuf_len(req),
+ wait_time, gas_query_tx_status, 0);
if (res == 0)
query->offchannel_tx_started = 1;
return res;
/**
- * gas_query_rx - Indicate reception of a Public Action frame
+ * gas_query_rx - Indicate reception of a Public Action or Protected Dual frame
* @gas: GAS query data from gas_query_init()
* @da: Destination MAC address of the Action frame
* @sa: Source MAC address of the Action frame
* @bssid: BSSID of the Action frame
+ * @categ: Category of the Action frame
* @data: Payload of the Action frame
* @len: Length of @data
* @freq: Frequency (in MHz) on which the frame was received
* Returns: 0 if the Public Action frame was a GAS frame or -1 if not
*/
int gas_query_rx(struct gas_query *gas, const u8 *da, const u8 *sa,
- const u8 *bssid, const u8 *data, size_t len, int freq)
+ const u8 *bssid, u8 categ, const u8 *data, size_t len,
+ int freq)
{
struct gas_query_pending *query;
u8 action, dialog_token, frag_id = 0, more_frags = 0;
u16 comeback_delay, resp_len;
const u8 *pos, *adv_proto;
+ int prot, pmf;
if (gas == NULL || len < 4)
return -1;
+ prot = categ == WLAN_ACTION_PROTECTED_DUAL;
+ pmf = pmf_in_use(gas->wpa_s, bssid);
+ if (prot && !pmf) {
+ wpa_printf(MSG_DEBUG, "GAS: Drop unexpected protected GAS frame when PMF is disabled");
+ return 0;
+ }
+ if (!prot && pmf) {
+ wpa_printf(MSG_DEBUG, "GAS: Drop unexpected unprotected GAS frame when PMF is enabled");
+ return 0;
+ }
+
pos = data;
action = *pos++;
dialog_token = *pos++;
return -1;
}
+ wpa_printf(MSG_DEBUG, "GAS: Response in %d ms from " MACSTR,
+ ms_from_time(&query->last_oper), MAC2STR(sa));
+
if (query->wait_comeback && action == WLAN_PA_GAS_INITIAL_RESP) {
wpa_printf(MSG_DEBUG, "GAS: Unexpected initial response from "
MACSTR " dialog token %u when waiting for comeback "
query->status_code = WPA_GET_LE16(pos);
pos += 2;
- if (query->status_code != WLAN_STATUS_SUCCESS) {
+ if (query->status_code == WLAN_STATUS_QUERY_RESP_OUTSTANDING &&
+ action == WLAN_PA_GAS_COMEBACK_RESP) {
+ wpa_printf(MSG_DEBUG, "GAS: Allow non-zero status for outstanding comeback response");
+ } else if (query->status_code != WLAN_STATUS_SUCCESS) {
wpa_printf(MSG_DEBUG, "GAS: Query to " MACSTR " dialog token "
"%u failed - status code %u",
MAC2STR(sa), dialog_token, query->status_code);
}
-static void gas_service_timeout(void *eloop_data, void *user_ctx)
-{
- struct gas_query *gas = eloop_data;
- struct wpa_supplicant *wpa_s = gas->wpa_s;
- struct gas_query_pending *query = user_ctx;
- int conn;
-
- conn = wpas_wpa_is_in_progress(wpa_s, 1);
- if (conn) {
- wpa_printf(MSG_DEBUG, "GAS: Delaying GAS query Tx while connection operation is in progress");
- eloop_register_timeout(
- GAS_SERVICE_RETRY_PERIOD_MS / 1000,
- (GAS_SERVICE_RETRY_PERIOD_MS % 1000) * 1000,
- gas_service_timeout, gas, query);
- return;
- }
-
- if (gas_query_tx(gas, query, query->req) < 0) {
- wpa_printf(MSG_DEBUG, "GAS: Failed to send Action frame to "
- MACSTR, MAC2STR(query->addr));
- gas_query_free(query, 1);
- return;
- }
- gas->current = query;
-
- wpa_printf(MSG_DEBUG, "GAS: Starting query timeout for dialog token %u",
- query->dialog_token);
- eloop_register_timeout(GAS_QUERY_TIMEOUT_PERIOD, 0,
- gas_query_timeout, gas, query);
-}
-
-
static int gas_query_dialog_token_available(struct gas_query *gas,
const u8 *dst, u8 dialog_token)
{
static void gas_query_start_cb(struct wpa_radio_work *work, int deinit)
{
struct gas_query_pending *query = work->ctx;
+ struct gas_query *gas = query->gas;
+ struct wpa_supplicant *wpa_s = gas->wpa_s;
if (deinit) {
+ if (work->started) {
+ gas->work = NULL;
+ gas_query_done(gas, query, GAS_QUERY_DELETED_AT_DEINIT);
+ return;
+ }
+
gas_query_free(query, 1);
return;
}
- query->gas->work = work;
- gas_service_timeout(query->gas, query);
+ if (wpas_update_random_addr_disassoc(wpa_s) < 0) {
+ wpa_msg(wpa_s, MSG_INFO,
+ "Failed to assign random MAC address for GAS");
+ gas_query_free(query, 1);
+ radio_work_done(work);
+ return;
+ }
+
+ gas->work = work;
+
+ if (gas_query_tx(gas, query, query->req) < 0) {
+ wpa_printf(MSG_DEBUG, "GAS: Failed to send Action frame to "
+ MACSTR, MAC2STR(query->addr));
+ gas_query_free(query, 1);
+ return;
+ }
+ gas->current = query;
+
+ wpa_printf(MSG_DEBUG, "GAS: Starting query timeout for dialog token %u",
+ query->dialog_token);
+ eloop_register_timeout(GAS_QUERY_TIMEOUT_PERIOD, 0,
+ gas_query_timeout, gas, query);
+
}