GAS: Delay GAS query Tx while another query is in progress
authorKyeyoon Park <kyeyoonp@qca.qualcomm.com>
Mon, 21 Oct 2013 10:13:42 +0000 (13:13 +0300)
committerJouni Malinen <j@w1.fi>
Mon, 21 Oct 2013 10:13:42 +0000 (13:13 +0300)
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>

wpa_supplicant/gas_query.c
wpa_supplicant/hs20_supplicant.c
wpa_supplicant/interworking.c

index 7ae686e..a4e675d 100644 (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;
 }
index 4048cf7..5f30313 100644 (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;
 }
 
index ba2b2fd..e294917 100644 (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;
 }