From 24c694b465ea9535436caf85c0e71d4346e15abd Mon Sep 17 00:00:00 2001 From: Kyeyoon Park Date: Mon, 21 Oct 2013 13:13:42 +0300 Subject: [PATCH] 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 --- wpa_supplicant/gas_query.c | 54 +++++++++++++++++++++++++------- wpa_supplicant/hs20_supplicant.c | 2 +- wpa_supplicant/interworking.c | 6 ++-- 3 files changed, 47 insertions(+), 15 deletions(-) diff --git a/wpa_supplicant/gas_query.c b/wpa_supplicant/gas_query.c index 7ae686eee..a4e675de7 100644 --- a/wpa_supplicant/gas_query.c +++ b/wpa_supplicant/gas_query.c @@ -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; } diff --git a/wpa_supplicant/hs20_supplicant.c b/wpa_supplicant/hs20_supplicant.c index 4048cf76e..5f30313ba 100644 --- a/wpa_supplicant/hs20_supplicant.c +++ b/wpa_supplicant/hs20_supplicant.c @@ -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; } diff --git a/wpa_supplicant/interworking.c b/wpa_supplicant/interworking.c index ba2b2fd48..e29491791 100644 --- a/wpa_supplicant/interworking.c +++ b/wpa_supplicant/interworking.c @@ -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; }