Home | History | Annotate | Download | only in wpa_supplicant
      1 /*
      2  * Generic advertisement service (GAS) query
      3  * Copyright (c) 2009, Atheros Communications
      4  * Copyright (c) 2011, Qualcomm Atheros
      5  *
      6  * This software may be distributed under the terms of the BSD license.
      7  * See README for more details.
      8  */
      9 
     10 #include "includes.h"
     11 
     12 #include "common.h"
     13 #include "utils/eloop.h"
     14 #include "common/ieee802_11_defs.h"
     15 #include "common/gas.h"
     16 #include "wpa_supplicant_i.h"
     17 #include "driver_i.h"
     18 #include "offchannel.h"
     19 #include "gas_query.h"
     20 
     21 
     22 #define GAS_QUERY_TIMEOUT_PERIOD 5
     23 
     24 
     25 struct gas_query_pending {
     26 	struct dl_list list;
     27 	u8 addr[ETH_ALEN];
     28 	u8 dialog_token;
     29 	u8 next_frag_id;
     30 	unsigned int wait_comeback:1;
     31 	unsigned int offchannel_tx_started:1;
     32 	int freq;
     33 	u16 status_code;
     34 	struct wpabuf *adv_proto;
     35 	struct wpabuf *resp;
     36 	void (*cb)(void *ctx, const u8 *dst, u8 dialog_token,
     37 		   enum gas_query_result result,
     38 		   const struct wpabuf *adv_proto,
     39 		   const struct wpabuf *resp, u16 status_code);
     40 	void *ctx;
     41 };
     42 
     43 struct gas_query {
     44 	struct wpa_supplicant *wpa_s;
     45 	struct dl_list pending; /* struct gas_query_pending */
     46 };
     47 
     48 
     49 static void gas_query_tx_comeback_timeout(void *eloop_data, void *user_ctx);
     50 static void gas_query_timeout(void *eloop_data, void *user_ctx);
     51 
     52 
     53 struct gas_query * gas_query_init(struct wpa_supplicant *wpa_s)
     54 {
     55 	struct gas_query *gas;
     56 
     57 	gas = os_zalloc(sizeof(*gas));
     58 	if (gas == NULL)
     59 		return NULL;
     60 
     61 	gas->wpa_s = wpa_s;
     62 	dl_list_init(&gas->pending);
     63 
     64 	return gas;
     65 }
     66 
     67 
     68 static void gas_query_done(struct gas_query *gas,
     69 			   struct gas_query_pending *query,
     70 			   enum gas_query_result result)
     71 {
     72 	if (query->offchannel_tx_started)
     73 		offchannel_send_action_done(gas->wpa_s);
     74 	eloop_cancel_timeout(gas_query_tx_comeback_timeout, gas, query);
     75 	eloop_cancel_timeout(gas_query_timeout, gas, query);
     76 	dl_list_del(&query->list);
     77 	query->cb(query->ctx, query->addr, query->dialog_token, result,
     78 		  query->adv_proto, query->resp, query->status_code);
     79 	wpabuf_free(query->adv_proto);
     80 	wpabuf_free(query->resp);
     81 	os_free(query);
     82 }
     83 
     84 
     85 void gas_query_deinit(struct gas_query *gas)
     86 {
     87 	struct gas_query_pending *query, *next;
     88 
     89 	if (gas == NULL)
     90 		return;
     91 
     92 	dl_list_for_each_safe(query, next, &gas->pending,
     93 			      struct gas_query_pending, list)
     94 		gas_query_done(gas, query, GAS_QUERY_DELETED_AT_DEINIT);
     95 
     96 	os_free(gas);
     97 }
     98 
     99 
    100 static struct gas_query_pending *
    101 gas_query_get_pending(struct gas_query *gas, const u8 *addr, u8 dialog_token)
    102 {
    103 	struct gas_query_pending *q;
    104 	dl_list_for_each(q, &gas->pending, struct gas_query_pending, list) {
    105 		if (os_memcmp(q->addr, addr, ETH_ALEN) == 0 &&
    106 		    q->dialog_token == dialog_token)
    107 			return q;
    108 	}
    109 	return NULL;
    110 }
    111 
    112 
    113 static int gas_query_append(struct gas_query_pending *query, const u8 *data,
    114 			    size_t len)
    115 {
    116 	if (wpabuf_resize(&query->resp, len) < 0) {
    117 		wpa_printf(MSG_DEBUG, "GAS: No memory to store the response");
    118 		return -1;
    119 	}
    120 	wpabuf_put_data(query->resp, data, len);
    121 	return 0;
    122 }
    123 
    124 
    125 static int gas_query_tx(struct gas_query *gas, struct gas_query_pending *query,
    126 			struct wpabuf *req)
    127 {
    128 	int res;
    129 	wpa_printf(MSG_DEBUG, "GAS: Send action frame to " MACSTR " len=%u "
    130 		   "freq=%d", MAC2STR(query->addr),
    131 		   (unsigned int) wpabuf_len(req), query->freq);
    132 	res = offchannel_send_action(gas->wpa_s, query->freq, query->addr,
    133 				     gas->wpa_s->own_addr, query->addr,
    134 				     wpabuf_head(req), wpabuf_len(req), 1000,
    135 				     NULL, 0);
    136 	if (res == 0)
    137 		query->offchannel_tx_started = 1;
    138 	return res;
    139 }
    140 
    141 
    142 static void gas_query_tx_comeback_req(struct gas_query *gas,
    143 				      struct gas_query_pending *query)
    144 {
    145 	struct wpabuf *req;
    146 
    147 	req = gas_build_comeback_req(query->dialog_token);
    148 	if (req == NULL) {
    149 		gas_query_done(gas, query, GAS_QUERY_INTERNAL_ERROR);
    150 		return;
    151 	}
    152 
    153 	if (gas_query_tx(gas, query, req) < 0) {
    154 		wpa_printf(MSG_DEBUG, "GAS: Failed to send Action frame to "
    155 			   MACSTR, MAC2STR(query->addr));
    156 		gas_query_done(gas, query, GAS_QUERY_INTERNAL_ERROR);
    157 	}
    158 
    159 	wpabuf_free(req);
    160 }
    161 
    162 
    163 static void gas_query_tx_comeback_timeout(void *eloop_data, void *user_ctx)
    164 {
    165 	struct gas_query *gas = eloop_data;
    166 	struct gas_query_pending *query = user_ctx;
    167 
    168 	wpa_printf(MSG_DEBUG, "GAS: Comeback timeout for request to " MACSTR,
    169 		   MAC2STR(query->addr));
    170 	gas_query_tx_comeback_req(gas, query);
    171 }
    172 
    173 
    174 static void gas_query_tx_comeback_req_delay(struct gas_query *gas,
    175 					    struct gas_query_pending *query,
    176 					    u16 comeback_delay)
    177 {
    178 	unsigned int secs, usecs;
    179 
    180 	secs = (comeback_delay * 1024) / 1000000;
    181 	usecs = comeback_delay * 1024 - secs * 1000000;
    182 	wpa_printf(MSG_DEBUG, "GAS: Send comeback request to " MACSTR
    183 		   " in %u secs %u usecs", MAC2STR(query->addr), secs, usecs);
    184 	eloop_cancel_timeout(gas_query_tx_comeback_timeout, gas, query);
    185 	eloop_register_timeout(secs, usecs, gas_query_tx_comeback_timeout,
    186 			       gas, query);
    187 }
    188 
    189 
    190 static void gas_query_rx_initial(struct gas_query *gas,
    191 				 struct gas_query_pending *query,
    192 				 const u8 *adv_proto, const u8 *resp,
    193 				 size_t len, u16 comeback_delay)
    194 {
    195 	wpa_printf(MSG_DEBUG, "GAS: Received initial response from "
    196 		   MACSTR " (dialog_token=%u comeback_delay=%u)",
    197 		   MAC2STR(query->addr), query->dialog_token, comeback_delay);
    198 
    199 	query->adv_proto = wpabuf_alloc_copy(adv_proto, 2 + adv_proto[1]);
    200 	if (query->adv_proto == NULL) {
    201 		gas_query_done(gas, query, GAS_QUERY_INTERNAL_ERROR);
    202 		return;
    203 	}
    204 
    205 	if (comeback_delay) {
    206 		query->wait_comeback = 1;
    207 		gas_query_tx_comeback_req_delay(gas, query, comeback_delay);
    208 		return;
    209 	}
    210 
    211 	/* Query was completed without comeback mechanism */
    212 	if (gas_query_append(query, resp, len) < 0) {
    213 		gas_query_done(gas, query, GAS_QUERY_INTERNAL_ERROR);
    214 		return;
    215 	}
    216 
    217 	gas_query_done(gas, query, GAS_QUERY_SUCCESS);
    218 }
    219 
    220 
    221 static void gas_query_rx_comeback(struct gas_query *gas,
    222 				  struct gas_query_pending *query,
    223 				  const u8 *adv_proto, const u8 *resp,
    224 				  size_t len, u8 frag_id, u8 more_frags,
    225 				  u16 comeback_delay)
    226 {
    227 	wpa_printf(MSG_DEBUG, "GAS: Received comeback response from "
    228 		   MACSTR " (dialog_token=%u frag_id=%u more_frags=%u "
    229 		   "comeback_delay=%u)",
    230 		   MAC2STR(query->addr), query->dialog_token, frag_id,
    231 		   more_frags, comeback_delay);
    232 
    233 	if ((size_t) 2 + adv_proto[1] != wpabuf_len(query->adv_proto) ||
    234 	    os_memcmp(adv_proto, wpabuf_head(query->adv_proto),
    235 		      wpabuf_len(query->adv_proto)) != 0) {
    236 		wpa_printf(MSG_DEBUG, "GAS: Advertisement Protocol changed "
    237 			   "between initial and comeback response from "
    238 			   MACSTR, MAC2STR(query->addr));
    239 		gas_query_done(gas, query, GAS_QUERY_PEER_ERROR);
    240 		return;
    241 	}
    242 
    243 	if (comeback_delay) {
    244 		if (frag_id) {
    245 			wpa_printf(MSG_DEBUG, "GAS: Invalid comeback response "
    246 				   "with non-zero frag_id and comeback_delay "
    247 				   "from " MACSTR, MAC2STR(query->addr));
    248 			gas_query_done(gas, query, GAS_QUERY_PEER_ERROR);
    249 			return;
    250 		}
    251 		gas_query_tx_comeback_req_delay(gas, query, comeback_delay);
    252 		return;
    253 	}
    254 
    255 	if (frag_id != query->next_frag_id) {
    256 		wpa_printf(MSG_DEBUG, "GAS: Unexpected frag_id in response "
    257 			   "from " MACSTR, MAC2STR(query->addr));
    258 		gas_query_done(gas, query, GAS_QUERY_PEER_ERROR);
    259 		return;
    260 	}
    261 	query->next_frag_id++;
    262 
    263 	if (gas_query_append(query, resp, len) < 0) {
    264 		gas_query_done(gas, query, GAS_QUERY_INTERNAL_ERROR);
    265 		return;
    266 	}
    267 
    268 	if (more_frags) {
    269 		gas_query_tx_comeback_req(gas, query);
    270 		return;
    271 	}
    272 
    273 	gas_query_done(gas, query, GAS_QUERY_SUCCESS);
    274 }
    275 
    276 
    277 int gas_query_rx(struct gas_query *gas, const u8 *da, const u8 *sa,
    278 		 const u8 *bssid, const u8 *data, size_t len, int freq)
    279 {
    280 	struct gas_query_pending *query;
    281 	u8 action, dialog_token, frag_id = 0, more_frags = 0;
    282 	u16 comeback_delay, resp_len;
    283 	const u8 *pos, *adv_proto;
    284 
    285 	if (gas == NULL || len < 4)
    286 		return -1;
    287 
    288 	pos = data;
    289 	action = *pos++;
    290 	dialog_token = *pos++;
    291 
    292 	if (action != WLAN_PA_GAS_INITIAL_RESP &&
    293 	    action != WLAN_PA_GAS_COMEBACK_RESP)
    294 		return -1; /* Not a GAS response */
    295 
    296 	query = gas_query_get_pending(gas, sa, dialog_token);
    297 	if (query == NULL) {
    298 		wpa_printf(MSG_DEBUG, "GAS: No pending query found for " MACSTR
    299 			   " dialog token %u", MAC2STR(sa), dialog_token);
    300 		return -1;
    301 	}
    302 
    303 	if (query->wait_comeback && action == WLAN_PA_GAS_INITIAL_RESP) {
    304 		wpa_printf(MSG_DEBUG, "GAS: Unexpected initial response from "
    305 			   MACSTR " dialog token %u when waiting for comeback "
    306 			   "response", MAC2STR(sa), dialog_token);
    307 		return 0;
    308 	}
    309 
    310 	if (!query->wait_comeback && action == WLAN_PA_GAS_COMEBACK_RESP) {
    311 		wpa_printf(MSG_DEBUG, "GAS: Unexpected comeback response from "
    312 			   MACSTR " dialog token %u when waiting for initial "
    313 			   "response", MAC2STR(sa), dialog_token);
    314 		return 0;
    315 	}
    316 
    317 	query->status_code = WPA_GET_LE16(pos);
    318 	pos += 2;
    319 
    320 	if (query->status_code != WLAN_STATUS_SUCCESS) {
    321 		wpa_printf(MSG_DEBUG, "GAS: Query to " MACSTR " dialog token "
    322 			   "%u failed - status code %u",
    323 			   MAC2STR(sa), dialog_token, query->status_code);
    324 		gas_query_done(gas, query, GAS_QUERY_FAILURE);
    325 		return 0;
    326 	}
    327 
    328 	if (action == WLAN_PA_GAS_COMEBACK_RESP) {
    329 		if (pos + 1 > data + len)
    330 			return 0;
    331 		frag_id = *pos & 0x7f;
    332 		more_frags = (*pos & 0x80) >> 7;
    333 		pos++;
    334 	}
    335 
    336 	/* Comeback Delay */
    337 	if (pos + 2 > data + len)
    338 		return 0;
    339 	comeback_delay = WPA_GET_LE16(pos);
    340 	pos += 2;
    341 
    342 	/* Advertisement Protocol element */
    343 	if (pos + 2 > data + len || pos + 2 + pos[1] > data + len) {
    344 		wpa_printf(MSG_DEBUG, "GAS: No room for Advertisement "
    345 			   "Protocol element in the response from " MACSTR,
    346 			   MAC2STR(sa));
    347 		return 0;
    348 	}
    349 
    350 	if (*pos != WLAN_EID_ADV_PROTO) {
    351 		wpa_printf(MSG_DEBUG, "GAS: Unexpected Advertisement "
    352 			   "Protocol element ID %u in response from " MACSTR,
    353 			   *pos, MAC2STR(sa));
    354 		return 0;
    355 	}
    356 
    357 	adv_proto = pos;
    358 	pos += 2 + pos[1];
    359 
    360 	/* Query Response Length */
    361 	if (pos + 2 > data + len) {
    362 		wpa_printf(MSG_DEBUG, "GAS: No room for GAS Response Length");
    363 		return 0;
    364 	}
    365 	resp_len = WPA_GET_LE16(pos);
    366 	pos += 2;
    367 
    368 	if (pos + resp_len > data + len) {
    369 		wpa_printf(MSG_DEBUG, "GAS: Truncated Query Response in "
    370 			   "response from " MACSTR, MAC2STR(sa));
    371 		return 0;
    372 	}
    373 
    374 	if (pos + resp_len < data + len) {
    375 		wpa_printf(MSG_DEBUG, "GAS: Ignore %u octets of extra data "
    376 			   "after Query Response from " MACSTR,
    377 			   (unsigned int) (data + len - pos - resp_len),
    378 			   MAC2STR(sa));
    379 	}
    380 
    381 	if (action == WLAN_PA_GAS_COMEBACK_RESP)
    382 		gas_query_rx_comeback(gas, query, adv_proto, pos, resp_len,
    383 				      frag_id, more_frags, comeback_delay);
    384 	else
    385 		gas_query_rx_initial(gas, query, adv_proto, pos, resp_len,
    386 				     comeback_delay);
    387 
    388 	return 0;
    389 }
    390 
    391 
    392 static void gas_query_timeout(void *eloop_data, void *user_ctx)
    393 {
    394 	struct gas_query *gas = eloop_data;
    395 	struct gas_query_pending *query = user_ctx;
    396 
    397 	wpa_printf(MSG_DEBUG, "GAS: No response received for query to " MACSTR,
    398 		   MAC2STR(query->addr));
    399 	gas_query_done(gas, query, GAS_QUERY_TIMEOUT);
    400 }
    401 
    402 
    403 static int gas_query_dialog_token_available(struct gas_query *gas,
    404 					    const u8 *dst, u8 dialog_token)
    405 {
    406 	struct gas_query_pending *q;
    407 	dl_list_for_each(q, &gas->pending, struct gas_query_pending, list) {
    408 		if (os_memcmp(dst, q->addr, ETH_ALEN) == 0 &&
    409 		    dialog_token == q->dialog_token)
    410 			return 0;
    411 	}
    412 
    413 	return 1;
    414 }
    415 
    416 
    417 int gas_query_req(struct gas_query *gas, const u8 *dst, int freq,
    418 		  struct wpabuf *req,
    419 		  void (*cb)(void *ctx, const u8 *dst, u8 dialog_token,
    420 			     enum gas_query_result result,
    421 			     const struct wpabuf *adv_proto,
    422 			     const struct wpabuf *resp, u16 status_code),
    423 		  void *ctx)
    424 {
    425 	struct gas_query_pending *query;
    426 	int dialog_token;
    427 
    428 	if (wpabuf_len(req) < 3)
    429 		return -1;
    430 
    431 	for (dialog_token = 0; dialog_token < 256; dialog_token++) {
    432 		if (gas_query_dialog_token_available(gas, dst, dialog_token))
    433 			break;
    434 	}
    435 	if (dialog_token == 256)
    436 		return -1; /* Too many pending queries */
    437 
    438 	query = os_zalloc(sizeof(*query));
    439 	if (query == NULL)
    440 		return -1;
    441 
    442 	os_memcpy(query->addr, dst, ETH_ALEN);
    443 	query->dialog_token = dialog_token;
    444 	query->freq = freq;
    445 	query->cb = cb;
    446 	query->ctx = ctx;
    447 	dl_list_add(&gas->pending, &query->list);
    448 
    449 	*(wpabuf_mhead_u8(req) + 2) = dialog_token;
    450 
    451 	wpa_printf(MSG_DEBUG, "GAS: Starting request for " MACSTR
    452 		   " dialog_token %u", MAC2STR(dst), dialog_token);
    453 	if (gas_query_tx(gas, query, req) < 0) {
    454 		wpa_printf(MSG_DEBUG, "GAS: Failed to send Action frame to "
    455 			   MACSTR, MAC2STR(query->addr));
    456 		os_free(query);
    457 		return -1;
    458 	}
    459 
    460 	eloop_register_timeout(GAS_QUERY_TIMEOUT_PERIOD, 0, gas_query_timeout,
    461 			       gas, query);
    462 
    463 	return dialog_token;
    464 }
    465 
    466 
    467 void gas_query_cancel(struct gas_query *gas, const u8 *dst, u8 dialog_token)
    468 {
    469 	struct gas_query_pending *query;
    470 
    471 	query = gas_query_get_pending(gas, dst, dialog_token);
    472 	if (query)
    473 		gas_query_done(gas, query, GAS_QUERY_CANCELLED);
    474 
    475 }
    476