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