GAS: Delay GAS query Tx while another query is in progress

It would be possible to issue another GAS query when a previous one is
still in progress and this could result in conflicting offchannel
operations. Prevent that by delaying GAS query initiation until the
previous operation has been completed.

Signed-hostap: Jouni Malinen <jouni@qca.qualcomm.com>
This commit is contained in:
Kyeyoon Park 2013-10-21 13:13:42 +03:00 committed by Jouni Malinen
parent 7255983b59
commit 24c694b465
3 changed files with 47 additions and 15 deletions

View file

@ -1,7 +1,7 @@
/*
* Generic advertisement service (GAS) query
* Copyright (c) 2009, Atheros Communications
* Copyright (c) 2011, Qualcomm Atheros
* Copyright (c) 2011-2013, Qualcomm Atheros, Inc.
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
@ -21,6 +21,8 @@
/** 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
/**
@ -35,6 +37,7 @@ struct gas_query_pending {
unsigned int offchannel_tx_started:1;
int freq;
u16 status_code;
struct wpabuf *req;
struct wpabuf *adv_proto;
struct wpabuf *resp;
void (*cb)(void *ctx, const u8 *dst, u8 dialog_token,
@ -50,11 +53,13 @@ struct gas_query_pending {
struct gas_query {
struct wpa_supplicant *wpa_s;
struct dl_list pending; /* struct gas_query_pending */
struct gas_query_pending *current;
};
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);
/**
@ -81,13 +86,17 @@ static void gas_query_done(struct gas_query *gas,
struct gas_query_pending *query,
enum gas_query_result result)
{
if (gas->current == query)
gas->current = NULL;
if (query->offchannel_tx_started)
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);
wpabuf_free(query->req);
wpabuf_free(query->adv_proto);
wpabuf_free(query->resp);
os_free(query);
@ -466,6 +475,35 @@ static void gas_query_timeout(void *eloop_data, void *user_ctx)
}
static void gas_service_timeout(void *eloop_data, void *user_ctx)
{
struct gas_query *gas = eloop_data;
struct gas_query_pending *query = user_ctx;
if (gas->current) {
wpa_printf(MSG_DEBUG, "GAS: Delaying GAS query Tx while another 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));
dl_list_del(&query->list);
wpabuf_free(query->req);
os_free(query);
return;
}
gas->current = query;
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)
{
@ -485,7 +523,8 @@ static int gas_query_dialog_token_available(struct gas_query *gas,
* @gas: GAS query data from gas_query_init()
* @dst: Destination MAC address for the query
* @freq: Frequency (in MHz) for the channel on which to send the query
* @req: GAS query payload
* @req: GAS query payload (to be freed by gas_query module in case of success
* return)
* @cb: Callback function for reporting GAS query result and response
* @ctx: Context pointer to use with the @cb call
* Returns: dialog token (>= 0) on success or -1 on failure
@ -524,22 +563,15 @@ int gas_query_req(struct gas_query *gas, const u8 *dst, int freq,
query->freq = freq;
query->cb = cb;
query->ctx = ctx;
query->req = req;
dl_list_add(&gas->pending, &query->list);
*(wpabuf_mhead_u8(req) + 2) = dialog_token;
wpa_printf(MSG_DEBUG, "GAS: Starting request for " MACSTR
" dialog_token %u", MAC2STR(dst), dialog_token);
if (gas_query_tx(gas, query, req) < 0) {
wpa_printf(MSG_DEBUG, "GAS: Failed to send Action frame to "
MACSTR, MAC2STR(query->addr));
dl_list_del(&query->list);
os_free(query);
return -1;
}
eloop_register_timeout(GAS_QUERY_TIMEOUT_PERIOD, 0, gas_query_timeout,
gas, query);
eloop_register_timeout(0, 0, gas_service_timeout, gas, query);
return dialog_token;
}

View file

@ -125,12 +125,12 @@ int hs20_anqp_send_req(struct wpa_supplicant *wpa_s, const u8 *dst, u32 stypes,
res = gas_query_req(wpa_s->gas, dst, freq, buf, anqp_resp_cb, wpa_s);
if (res < 0) {
wpa_printf(MSG_DEBUG, "ANQP: Failed to send Query Request");
wpabuf_free(buf);
ret = -1;
} else
wpa_printf(MSG_DEBUG, "ANQP: Query started with dialog token "
"%u", res);
wpabuf_free(buf);
return ret;
}

View file

@ -244,6 +244,7 @@ static int interworking_anqp_send_req(struct wpa_supplicant *wpa_s,
interworking_anqp_resp_cb, wpa_s);
if (res < 0) {
wpa_printf(MSG_DEBUG, "ANQP: Failed to send Query Request");
wpabuf_free(buf);
ret = -1;
eloop_register_timeout(0, 0, interworking_continue_anqp, wpa_s,
NULL);
@ -251,7 +252,6 @@ static int interworking_anqp_send_req(struct wpa_supplicant *wpa_s,
wpa_printf(MSG_DEBUG, "ANQP: Query started with dialog token "
"%u", res);
wpabuf_free(buf);
return ret;
}
@ -1877,12 +1877,12 @@ int anqp_send_req(struct wpa_supplicant *wpa_s, const u8 *dst,
res = gas_query_req(wpa_s->gas, dst, freq, buf, anqp_resp_cb, wpa_s);
if (res < 0) {
wpa_printf(MSG_DEBUG, "ANQP: Failed to send Query Request");
wpabuf_free(buf);
ret = -1;
} else
wpa_printf(MSG_DEBUG, "ANQP: Query started with dialog token "
"%u", res);
wpabuf_free(buf);
return ret;
}
@ -2177,11 +2177,11 @@ int gas_send_request(struct wpa_supplicant *wpa_s, const u8 *dst,
res = gas_query_req(wpa_s->gas, dst, freq, buf, gas_resp_cb, wpa_s);
if (res < 0) {
wpa_printf(MSG_DEBUG, "GAS: Failed to send Query Request");
wpabuf_free(buf);
ret = -1;
} else
wpa_printf(MSG_DEBUG, "GAS: Query started with dialog token "
"%u", res);
wpabuf_free(buf);
return ret;
}