GAS: Retry full GAS query if comeback response is not received

It is possible for a comeback response to get lost especially when going
through a large GAS exchange fragmented to multiple frames in an
environment with interference or other traffic. Make this less likely to
fail the full exchange by trying full GAS query again and using longer
wait time on the GAS comeback exchanges.

Signed-off-by: Jouni Malinen <j@w1.fi>
This commit is contained in:
Jouni Malinen 2015-12-20 19:26:01 +02:00
parent f9a9304479
commit 364282c8c9

View file

@ -40,6 +40,7 @@ struct gas_query_pending {
u8 next_frag_id; u8 next_frag_id;
unsigned int wait_comeback:1; unsigned int wait_comeback:1;
unsigned int offchannel_tx_started:1; unsigned int offchannel_tx_started:1;
unsigned int retry:1;
int freq; int freq;
u16 status_code; u16 status_code;
struct wpabuf *req; struct wpabuf *req;
@ -66,6 +67,10 @@ struct gas_query {
static void gas_query_tx_comeback_timeout(void *eloop_data, void *user_ctx); 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_query_timeout(void *eloop_data, void *user_ctx);
static void gas_query_rx_comeback_timeout(void *eloop_data, void *user_ctx);
static void gas_query_tx_initial_req(struct gas_query *gas,
struct gas_query_pending *query);
static int gas_query_new_dialog_token(struct gas_query *gas, const u8 *dst);
static int ms_from_time(struct os_reltime *last) static int ms_from_time(struct os_reltime *last)
@ -154,6 +159,7 @@ static void gas_query_done(struct gas_query *gas,
offchannel_send_action_done(gas->wpa_s); offchannel_send_action_done(gas->wpa_s);
eloop_cancel_timeout(gas_query_tx_comeback_timeout, gas, query); eloop_cancel_timeout(gas_query_tx_comeback_timeout, gas, query);
eloop_cancel_timeout(gas_query_timeout, gas, query); eloop_cancel_timeout(gas_query_timeout, gas, query);
eloop_cancel_timeout(gas_query_rx_comeback_timeout, gas, query);
dl_list_del(&query->list); dl_list_del(&query->list);
query->cb(query->ctx, query->addr, query->dialog_token, result, query->cb(query->ctx, query->addr, query->dialog_token, result,
query->adv_proto, query->resp, query->status_code); query->adv_proto, query->resp, query->status_code);
@ -238,6 +244,13 @@ static void gas_query_tx_status(struct wpa_supplicant *wpa_s,
eloop_cancel_timeout(gas_query_timeout, gas, query); eloop_cancel_timeout(gas_query_timeout, gas, query);
eloop_register_timeout(GAS_QUERY_TIMEOUT_PERIOD, 0, eloop_register_timeout(GAS_QUERY_TIMEOUT_PERIOD, 0,
gas_query_timeout, gas, query); gas_query_timeout, gas, query);
if (query->wait_comeback && !query->retry) {
eloop_cancel_timeout(gas_query_rx_comeback_timeout,
gas, query);
eloop_register_timeout(
0, (GAS_QUERY_WAIT_TIME_COMEBACK + 10) * 1000,
gas_query_rx_comeback_timeout, gas, query);
}
} }
if (result == OFFCHANNEL_SEND_ACTION_FAILED) { if (result == OFFCHANNEL_SEND_ACTION_FAILED) {
eloop_cancel_timeout(gas_query_timeout, gas, query); eloop_cancel_timeout(gas_query_timeout, gas, query);
@ -294,7 +307,7 @@ static void gas_query_tx_comeback_req(struct gas_query *gas,
return; return;
} }
wait_time = !query->offchannel_tx_started ? wait_time = (query->retry || !query->offchannel_tx_started) ?
GAS_QUERY_WAIT_TIME_INITIAL : GAS_QUERY_WAIT_TIME_COMEBACK; GAS_QUERY_WAIT_TIME_INITIAL : GAS_QUERY_WAIT_TIME_COMEBACK;
if (gas_query_tx(gas, query, req, wait_time) < 0) { if (gas_query_tx(gas, query, req, wait_time) < 0) {
@ -307,6 +320,35 @@ static void gas_query_tx_comeback_req(struct gas_query *gas,
} }
static void gas_query_rx_comeback_timeout(void *eloop_data, void *user_ctx)
{
struct gas_query *gas = eloop_data;
struct gas_query_pending *query = user_ctx;
int dialog_token;
wpa_printf(MSG_DEBUG,
"GAS: No response to comeback request received (retry=%u)",
query->retry);
if (gas->current != query || query->retry)
return;
dialog_token = gas_query_new_dialog_token(gas, query->addr);
if (dialog_token < 0)
return;
wpa_printf(MSG_DEBUG,
"GAS: Retry GAS query due to comeback response timeout");
query->retry = 1;
query->dialog_token = dialog_token;
*(wpabuf_mhead_u8(query->req) + 2) = dialog_token;
query->wait_comeback = 0;
query->next_frag_id = 0;
wpabuf_free(query->adv_proto);
query->adv_proto = NULL;
eloop_cancel_timeout(gas_query_tx_comeback_timeout, gas, query);
eloop_cancel_timeout(gas_query_timeout, gas, query);
gas_query_tx_initial_req(gas, query);
}
static void gas_query_tx_comeback_timeout(void *eloop_data, void *user_ctx) static void gas_query_tx_comeback_timeout(void *eloop_data, void *user_ctx)
{ {
struct gas_query *gas = eloop_data; struct gas_query *gas = eloop_data;
@ -381,6 +423,7 @@ static void gas_query_rx_comeback(struct gas_query *gas,
"comeback_delay=%u)", "comeback_delay=%u)",
MAC2STR(query->addr), query->dialog_token, frag_id, MAC2STR(query->addr), query->dialog_token, frag_id,
more_frags, comeback_delay); more_frags, comeback_delay);
eloop_cancel_timeout(gas_query_rx_comeback_timeout, gas, query);
if ((size_t) 2 + adv_proto[1] != wpabuf_len(query->adv_proto) || if ((size_t) 2 + adv_proto[1] != wpabuf_len(query->adv_proto) ||
os_memcmp(adv_proto, wpabuf_head(query->adv_proto), os_memcmp(adv_proto, wpabuf_head(query->adv_proto),
@ -630,7 +673,13 @@ static void gas_query_start_cb(struct wpa_radio_work *work, int deinit)
} }
gas->work = work; gas->work = work;
gas_query_tx_initial_req(gas, query);
}
static void gas_query_tx_initial_req(struct gas_query *gas,
struct gas_query_pending *query)
{
if (gas_query_tx(gas, query, query->req, if (gas_query_tx(gas, query, query->req,
GAS_QUERY_WAIT_TIME_INITIAL) < 0) { GAS_QUERY_WAIT_TIME_INITIAL) < 0) {
wpa_printf(MSG_DEBUG, "GAS: Failed to send Action frame to " wpa_printf(MSG_DEBUG, "GAS: Failed to send Action frame to "
@ -644,7 +693,24 @@ static void gas_query_start_cb(struct wpa_radio_work *work, int deinit)
query->dialog_token); query->dialog_token);
eloop_register_timeout(GAS_QUERY_TIMEOUT_PERIOD, 0, eloop_register_timeout(GAS_QUERY_TIMEOUT_PERIOD, 0,
gas_query_timeout, gas, query); gas_query_timeout, gas, query);
}
static int gas_query_new_dialog_token(struct gas_query *gas, const u8 *dst)
{
static int next_start = 0;
int dialog_token;
for (dialog_token = 0; dialog_token < 256; dialog_token++) {
if (gas_query_dialog_token_available(
gas, dst, (next_start + dialog_token) % 256))
break;
}
if (dialog_token == 256)
return -1; /* Too many pending queries */
dialog_token = (next_start + dialog_token) % 256;
next_start = (dialog_token + 1) % 256;
return dialog_token;
} }
@ -669,20 +735,13 @@ int gas_query_req(struct gas_query *gas, const u8 *dst, int freq,
{ {
struct gas_query_pending *query; struct gas_query_pending *query;
int dialog_token; int dialog_token;
static int next_start = 0;
if (wpabuf_len(req) < 3) if (wpabuf_len(req) < 3)
return -1; return -1;
for (dialog_token = 0; dialog_token < 256; dialog_token++) { dialog_token = gas_query_new_dialog_token(gas, dst);
if (gas_query_dialog_token_available( if (dialog_token < 0)
gas, dst, (next_start + dialog_token) % 256)) return -1;
break;
}
if (dialog_token == 256)
return -1; /* Too many pending queries */
dialog_token = (next_start + dialog_token) % 256;
next_start = (dialog_token + 1) % 256;
query = os_zalloc(sizeof(*query)); query = os_zalloc(sizeof(*query));
if (query == NULL) if (query == NULL)