X-Git-Url: http://www.project-moonshot.org/gitweb/?p=mech_eap.git;a=blobdiff_plain;f=wpa_supplicant%2Fgas_query.c;h=691de0345d136c993c54d5ae24ecd55fd732c8ba;hp=fda7c849a07462c829945140c2a9bc780e08d556;hb=e8afaad7fd9a7fc9a761ed2f8a117bbb6ac9c730;hpb=b9e6d7001d5408b3dd9c9e74df3f75732823d216 diff --git a/wpa_supplicant/gas_query.c b/wpa_supplicant/gas_query.c index fda7c84..691de03 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-2013, Qualcomm Atheros, Inc. + * Copyright (c) 2011-2014, Qualcomm Atheros, Inc. * Copyright (c) 2011-2014, Jouni Malinen * * This software may be distributed under the terms of the BSD license. @@ -15,7 +15,9 @@ #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 "config.h" #include "driver_i.h" #include "offchannel.h" #include "gas_query.h" @@ -23,9 +25,10 @@ /** 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 +/* GAS query wait-time / duration in ms */ +#define GAS_QUERY_WAIT_TIME_INITIAL 1000 +#define GAS_QUERY_WAIT_TIME_COMEBACK 150 /** * struct gas_query_pending - Pending GAS query @@ -38,11 +41,13 @@ struct gas_query_pending { u8 next_frag_id; unsigned int wait_comeback:1; unsigned int offchannel_tx_started:1; + unsigned int retry:1; int freq; u16 status_code; 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, @@ -63,7 +68,20 @@ struct gas_query { 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 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) +{ + struct os_reltime now, res; + + os_get_reltime(&now); + os_reltime_sub(&now, last, &res); + return res.sec * 1000 + res.usec / 1000; +} /** @@ -99,8 +117,6 @@ static const char * gas_result_txt(enum gas_query_result result) return "PEER_ERROR"; case GAS_QUERY_INTERNAL_ERROR: return "INTERNAL_ERROR"; - case GAS_QUERY_CANCELLED: - return "CANCELLED"; case GAS_QUERY_DELETED_AT_DEINIT: return "DELETED_AT_DEINIT"; } @@ -142,7 +158,7 @@ static void gas_query_done(struct gas_query *gas, 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); + eloop_cancel_timeout(gas_query_rx_comeback_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); @@ -202,6 +218,7 @@ static void gas_query_tx_status(struct wpa_supplicant *wpa_s, { 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=" @@ -212,18 +229,27 @@ static void gas_query_tx_status(struct wpa_supplicant *wpa_s, 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); eloop_register_timeout(GAS_QUERY_TIMEOUT_PERIOD, 0, 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) { eloop_cancel_timeout(gas_query_timeout, gas, query); @@ -232,17 +258,47 @@ static void gas_query_tx_status(struct wpa_supplicant *wpa_s, } +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) + struct wpabuf *req, unsigned int wait_time) { - int res; + int res, prot = pmf_in_use(gas->wpa_s, query->addr); + const u8 *bssid; + const u8 wildcard_bssid[ETH_ALEN] = { + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff + }; + 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); + 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; + if (!gas->wpa_s->conf->gas_address3 || + (gas->wpa_s->current_ssid && + gas->wpa_s->wpa_state >= WPA_ASSOCIATED && + os_memcmp(query->addr, gas->wpa_s->bssid, ETH_ALEN) == 0)) + bssid = query->addr; + else + bssid = wildcard_bssid; 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); + gas->wpa_s->own_addr, bssid, + wpabuf_head(req), wpabuf_len(req), + wait_time, gas_query_tx_status, 0); if (res == 0) query->offchannel_tx_started = 1; return res; @@ -253,6 +309,7 @@ static void gas_query_tx_comeback_req(struct gas_query *gas, struct gas_query_pending *query) { struct wpabuf *req; + unsigned int wait_time; req = gas_build_comeback_req(query->dialog_token); if (req == NULL) { @@ -260,7 +317,10 @@ static void gas_query_tx_comeback_req(struct gas_query *gas, return; } - if (gas_query_tx(gas, query, req) < 0) { + wait_time = (query->retry || !query->offchannel_tx_started) ? + GAS_QUERY_WAIT_TIME_INITIAL : GAS_QUERY_WAIT_TIME_COMEBACK; + + if (gas_query_tx(gas, query, req, wait_time) < 0) { wpa_printf(MSG_DEBUG, "GAS: Failed to send Action frame to " MACSTR, MAC2STR(query->addr)); gas_query_done(gas, query, GAS_QUERY_INTERNAL_ERROR); @@ -270,6 +330,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) { struct gas_query *gas = eloop_data; @@ -287,6 +376,11 @@ static void gas_query_tx_comeback_req_delay(struct gas_query *gas, { unsigned int secs, usecs; + if (comeback_delay > 1 && query->offchannel_tx_started) { + offchannel_send_action_done(gas->wpa_s); + query->offchannel_tx_started = 0; + } + secs = (comeback_delay * 1024) / 1000000; usecs = comeback_delay * 1024 - secs * 1000000; wpa_printf(MSG_DEBUG, "GAS: Send comeback request to " MACSTR @@ -339,6 +433,7 @@ static void gas_query_rx_comeback(struct gas_query *gas, "comeback_delay=%u)", MAC2STR(query->addr), query->dialog_token, frag_id, 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) || os_memcmp(adv_proto, wpabuf_head(query->adv_proto), @@ -390,23 +485,27 @@ static void gas_query_rx_comeback(struct gas_query *gas, /** - * 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; + unsigned int left; if (gas == NULL || len < 4) return -1; @@ -419,6 +518,17 @@ int gas_query_rx(struct gas_query *gas, const u8 *da, const u8 *sa, action != WLAN_PA_GAS_COMEBACK_RESP) return -1; /* Not a GAS response */ + prot = categ == WLAN_ACTION_PROTECTED_DUAL; + pmf = pmf_in_use(gas->wpa_s, sa); + 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; + } + query = gas_query_get_pending(gas, sa, dialog_token); if (query == NULL) { wpa_printf(MSG_DEBUG, "GAS: No pending query found for " MACSTR @@ -426,6 +536,9 @@ int gas_query_rx(struct gas_query *gas, const u8 *da, const u8 *sa, 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 " @@ -443,7 +556,10 @@ int gas_query_rx(struct gas_query *gas, const u8 *da, const u8 *sa, 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); @@ -491,17 +607,17 @@ int gas_query_rx(struct gas_query *gas, const u8 *da, const u8 *sa, resp_len = WPA_GET_LE16(pos); pos += 2; - if (pos + resp_len > data + len) { + left = data + len - pos; + if (resp_len > left) { wpa_printf(MSG_DEBUG, "GAS: Truncated Query Response in " "response from " MACSTR, MAC2STR(sa)); return 0; } - if (pos + resp_len < data + len) { + if (resp_len < left) { wpa_printf(MSG_DEBUG, "GAS: Ignore %u octets of extra data " "after Query Response from " MACSTR, - (unsigned int) (data + len - pos - resp_len), - MAC2STR(sa)); + left - resp_len, MAC2STR(sa)); } if (action == WLAN_PA_GAS_COMEBACK_RESP) @@ -527,38 +643,6 @@ 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 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) { @@ -576,14 +660,67 @@ static int gas_query_dialog_token_available(struct gas_query *gas, 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; + } + + 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; } - query->gas->work = work; - gas_service_timeout(query->gas, query); + 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, + GAS_QUERY_WAIT_TIME_INITIAL) < 0) { + wpa_printf(MSG_DEBUG, "GAS: Failed to send Action frame to " + MACSTR, MAC2STR(query->addr)); + gas_query_done(gas, query, GAS_QUERY_INTERNAL_ERROR); + 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_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; } @@ -608,20 +745,13 @@ int gas_query_req(struct gas_query *gas, const u8 *dst, int freq, { struct gas_query_pending *query; int dialog_token; - static int next_start = 0; if (wpabuf_len(req) < 3) return -1; - 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; + dialog_token = gas_query_new_dialog_token(gas, dst); + if (dialog_token < 0) + return -1; query = os_zalloc(sizeof(*query)); if (query == NULL) @@ -644,26 +774,10 @@ int gas_query_req(struct gas_query *gas, const u8 *dst, int freq, if (radio_add_work(gas->wpa_s, freq, "gas-query", 0, gas_query_start_cb, query) < 0) { + query->req = NULL; /* caller will free this in error case */ gas_query_free(query, 1); return -1; } return dialog_token; } - - -/** - * gas_query_cancel - Cancel a pending GAS query - * @gas: GAS query data from gas_query_init() - * @dst: Destination MAC address for the query - * @dialog_token: Dialog token from gas_query_req() - */ -void gas_query_cancel(struct gas_query *gas, const u8 *dst, u8 dialog_token) -{ - struct gas_query_pending *query; - - query = gas_query_get_pending(gas, dst, dialog_token); - if (query) - gas_query_done(gas, query, GAS_QUERY_CANCELLED); - -}