Line data Source code
1 : /*
2 : * Generic advertisement service (GAS) query
3 : * Copyright (c) 2009, Atheros Communications
4 : * Copyright (c) 2011-2014, Qualcomm Atheros, Inc.
5 : * Copyright (c) 2011-2014, Jouni Malinen <j@w1.fi>
6 : *
7 : * This software may be distributed under the terms of the BSD license.
8 : * See README for more details.
9 : */
10 :
11 : #include "includes.h"
12 :
13 : #include "common.h"
14 : #include "utils/eloop.h"
15 : #include "common/ieee802_11_defs.h"
16 : #include "common/gas.h"
17 : #include "common/wpa_ctrl.h"
18 : #include "rsn_supp/wpa.h"
19 : #include "wpa_supplicant_i.h"
20 : #include "config.h"
21 : #include "driver_i.h"
22 : #include "offchannel.h"
23 : #include "gas_query.h"
24 :
25 :
26 : /** GAS query timeout in seconds */
27 : #define GAS_QUERY_TIMEOUT_PERIOD 2
28 :
29 : /* GAS query wait-time / duration in ms */
30 : #define GAS_QUERY_WAIT_TIME_INITIAL 1000
31 : #define GAS_QUERY_WAIT_TIME_COMEBACK 150
32 :
33 : /**
34 : * struct gas_query_pending - Pending GAS query
35 : */
36 : struct gas_query_pending {
37 : struct dl_list list;
38 : struct gas_query *gas;
39 : u8 addr[ETH_ALEN];
40 : u8 dialog_token;
41 : u8 next_frag_id;
42 : unsigned int wait_comeback:1;
43 : unsigned int offchannel_tx_started:1;
44 : unsigned int retry:1;
45 : int freq;
46 : u16 status_code;
47 : struct wpabuf *req;
48 : struct wpabuf *adv_proto;
49 : struct wpabuf *resp;
50 : struct os_reltime last_oper;
51 : void (*cb)(void *ctx, const u8 *dst, u8 dialog_token,
52 : enum gas_query_result result,
53 : const struct wpabuf *adv_proto,
54 : const struct wpabuf *resp, u16 status_code);
55 : void *ctx;
56 : };
57 :
58 : /**
59 : * struct gas_query - Internal GAS query data
60 : */
61 : struct gas_query {
62 : struct wpa_supplicant *wpa_s;
63 : struct dl_list pending; /* struct gas_query_pending */
64 : struct gas_query_pending *current;
65 : struct wpa_radio_work *work;
66 : };
67 :
68 :
69 : static void gas_query_tx_comeback_timeout(void *eloop_data, void *user_ctx);
70 : static void gas_query_timeout(void *eloop_data, void *user_ctx);
71 : static void gas_query_rx_comeback_timeout(void *eloop_data, void *user_ctx);
72 : static void gas_query_tx_initial_req(struct gas_query *gas,
73 : struct gas_query_pending *query);
74 : static int gas_query_new_dialog_token(struct gas_query *gas, const u8 *dst);
75 :
76 :
77 1438 : static int ms_from_time(struct os_reltime *last)
78 : {
79 : struct os_reltime now, res;
80 :
81 1438 : os_get_reltime(&now);
82 1438 : os_reltime_sub(&now, last, &res);
83 1438 : return res.sec * 1000 + res.usec / 1000;
84 : }
85 :
86 :
87 : /**
88 : * gas_query_init - Initialize GAS query component
89 : * @wpa_s: Pointer to wpa_supplicant data
90 : * Returns: Pointer to GAS query data or %NULL on failure
91 : */
92 696 : struct gas_query * gas_query_init(struct wpa_supplicant *wpa_s)
93 : {
94 : struct gas_query *gas;
95 :
96 696 : gas = os_zalloc(sizeof(*gas));
97 696 : if (gas == NULL)
98 1 : return NULL;
99 :
100 695 : gas->wpa_s = wpa_s;
101 695 : dl_list_init(&gas->pending);
102 :
103 695 : return gas;
104 : }
105 :
106 :
107 420 : static const char * gas_result_txt(enum gas_query_result result)
108 : {
109 420 : switch (result) {
110 : case GAS_QUERY_SUCCESS:
111 397 : return "SUCCESS";
112 : case GAS_QUERY_FAILURE:
113 3 : return "FAILURE";
114 : case GAS_QUERY_TIMEOUT:
115 9 : return "TIMEOUT";
116 : case GAS_QUERY_PEER_ERROR:
117 4 : return "PEER_ERROR";
118 : case GAS_QUERY_INTERNAL_ERROR:
119 6 : return "INTERNAL_ERROR";
120 : case GAS_QUERY_DELETED_AT_DEINIT:
121 1 : return "DELETED_AT_DEINIT";
122 : }
123 :
124 0 : return "N/A";
125 : }
126 :
127 :
128 424 : static void gas_query_free(struct gas_query_pending *query, int del_list)
129 : {
130 424 : struct gas_query *gas = query->gas;
131 :
132 424 : if (del_list)
133 4 : dl_list_del(&query->list);
134 :
135 424 : if (gas->work && gas->work->ctx == query) {
136 419 : radio_work_done(gas->work);
137 419 : gas->work = NULL;
138 : }
139 :
140 424 : wpabuf_free(query->req);
141 424 : wpabuf_free(query->adv_proto);
142 424 : wpabuf_free(query->resp);
143 424 : os_free(query);
144 424 : }
145 :
146 :
147 420 : static void gas_query_done(struct gas_query *gas,
148 : struct gas_query_pending *query,
149 : enum gas_query_result result)
150 : {
151 3780 : wpa_msg(gas->wpa_s, MSG_INFO, GAS_QUERY_DONE "addr=" MACSTR
152 : " dialog_token=%u freq=%d status_code=%u result=%s",
153 2940 : MAC2STR(query->addr), query->dialog_token, query->freq,
154 420 : query->status_code, gas_result_txt(result));
155 420 : if (gas->current == query)
156 419 : gas->current = NULL;
157 420 : if (query->offchannel_tx_started)
158 417 : offchannel_send_action_done(gas->wpa_s);
159 420 : eloop_cancel_timeout(gas_query_tx_comeback_timeout, gas, query);
160 420 : eloop_cancel_timeout(gas_query_timeout, gas, query);
161 420 : eloop_cancel_timeout(gas_query_rx_comeback_timeout, gas, query);
162 420 : dl_list_del(&query->list);
163 1260 : query->cb(query->ctx, query->addr, query->dialog_token, result,
164 840 : query->adv_proto, query->resp, query->status_code);
165 420 : gas_query_free(query, 0);
166 420 : }
167 :
168 :
169 : /**
170 : * gas_query_deinit - Deinitialize GAS query component
171 : * @gas: GAS query data from gas_query_init()
172 : */
173 733 : void gas_query_deinit(struct gas_query *gas)
174 : {
175 : struct gas_query_pending *query, *next;
176 :
177 733 : if (gas == NULL)
178 771 : return;
179 :
180 695 : dl_list_for_each_safe(query, next, &gas->pending,
181 : struct gas_query_pending, list)
182 0 : gas_query_done(gas, query, GAS_QUERY_DELETED_AT_DEINIT);
183 :
184 695 : os_free(gas);
185 : }
186 :
187 :
188 : static struct gas_query_pending *
189 826 : gas_query_get_pending(struct gas_query *gas, const u8 *addr, u8 dialog_token)
190 : {
191 : struct gas_query_pending *q;
192 856 : dl_list_for_each(q, &gas->pending, struct gas_query_pending, list) {
193 1502 : if (os_memcmp(q->addr, addr, ETH_ALEN) == 0 &&
194 751 : q->dialog_token == dialog_token)
195 721 : return q;
196 : }
197 105 : return NULL;
198 : }
199 :
200 :
201 655 : static int gas_query_append(struct gas_query_pending *query, const u8 *data,
202 : size_t len)
203 : {
204 655 : if (wpabuf_resize(&query->resp, len) < 0) {
205 2 : wpa_printf(MSG_DEBUG, "GAS: No memory to store the response");
206 2 : return -1;
207 : }
208 653 : wpabuf_put_data(query->resp, data, len);
209 653 : return 0;
210 : }
211 :
212 :
213 717 : static void gas_query_tx_status(struct wpa_supplicant *wpa_s,
214 : unsigned int freq, const u8 *dst,
215 : const u8 *src, const u8 *bssid,
216 : const u8 *data, size_t data_len,
217 : enum offchannel_send_action_result result)
218 : {
219 : struct gas_query_pending *query;
220 717 : struct gas_query *gas = wpa_s->gas;
221 : int dur;
222 :
223 717 : if (gas->current == NULL) {
224 0 : wpa_printf(MSG_DEBUG, "GAS: Unexpected TX status: freq=%u dst="
225 : MACSTR " result=%d - no query in progress",
226 0 : freq, MAC2STR(dst), result);
227 0 : return;
228 : }
229 :
230 717 : query = gas->current;
231 :
232 717 : dur = ms_from_time(&query->last_oper);
233 5019 : wpa_printf(MSG_DEBUG, "GAS: TX status: freq=%u dst=" MACSTR
234 : " result=%d query=%p dialog_token=%u dur=%d ms",
235 5019 : freq, MAC2STR(dst), result, query, query->dialog_token, dur);
236 717 : if (os_memcmp(dst, query->addr, ETH_ALEN) != 0) {
237 0 : wpa_printf(MSG_DEBUG, "GAS: TX status for unexpected destination");
238 0 : return;
239 : }
240 717 : os_get_reltime(&query->last_oper);
241 :
242 717 : if (result == OFFCHANNEL_SEND_ACTION_SUCCESS) {
243 717 : eloop_cancel_timeout(gas_query_timeout, gas, query);
244 717 : eloop_register_timeout(GAS_QUERY_TIMEOUT_PERIOD, 0,
245 : gas_query_timeout, gas, query);
246 717 : if (query->wait_comeback && !query->retry) {
247 287 : eloop_cancel_timeout(gas_query_rx_comeback_timeout,
248 : gas, query);
249 287 : eloop_register_timeout(
250 : 0, (GAS_QUERY_WAIT_TIME_COMEBACK + 10) * 1000,
251 : gas_query_rx_comeback_timeout, gas, query);
252 : }
253 : }
254 717 : if (result == OFFCHANNEL_SEND_ACTION_FAILED) {
255 0 : eloop_cancel_timeout(gas_query_timeout, gas, query);
256 0 : eloop_register_timeout(0, 0, gas_query_timeout, gas, query);
257 : }
258 : }
259 :
260 :
261 1548 : static int pmf_in_use(struct wpa_supplicant *wpa_s, const u8 *addr)
262 : {
263 1616 : if (wpa_s->current_ssid == NULL ||
264 136 : wpa_s->wpa_state < WPA_4WAY_HANDSHAKE ||
265 68 : os_memcmp(addr, wpa_s->bssid, ETH_ALEN) != 0)
266 1499 : return 0;
267 49 : return wpa_sm_pmf_enabled(wpa_s->wpa);
268 : }
269 :
270 :
271 720 : static int gas_query_tx(struct gas_query *gas, struct gas_query_pending *query,
272 : struct wpabuf *req, unsigned int wait_time)
273 : {
274 720 : int res, prot = pmf_in_use(gas->wpa_s, query->addr);
275 : const u8 *bssid;
276 720 : const u8 wildcard_bssid[ETH_ALEN] = {
277 : 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
278 : };
279 :
280 5760 : wpa_printf(MSG_DEBUG, "GAS: Send action frame to " MACSTR " len=%u "
281 4320 : "freq=%d prot=%d", MAC2STR(query->addr),
282 720 : (unsigned int) wpabuf_len(req), query->freq, prot);
283 720 : if (prot) {
284 3 : u8 *categ = wpabuf_mhead_u8(req);
285 3 : *categ = WLAN_ACTION_PROTECTED_DUAL;
286 : }
287 720 : os_get_reltime(&query->last_oper);
288 1440 : if (gas->wpa_s->max_remain_on_chan &&
289 720 : wait_time > gas->wpa_s->max_remain_on_chan)
290 0 : wait_time = gas->wpa_s->max_remain_on_chan;
291 723 : if (!gas->wpa_s->conf->gas_address3 ||
292 4 : (gas->wpa_s->current_ssid &&
293 2 : gas->wpa_s->wpa_state >= WPA_ASSOCIATED &&
294 1 : os_memcmp(query->addr, gas->wpa_s->bssid, ETH_ALEN) == 0))
295 718 : bssid = query->addr;
296 : else
297 2 : bssid = wildcard_bssid;
298 1440 : res = offchannel_send_action(gas->wpa_s, query->freq, query->addr,
299 720 : gas->wpa_s->own_addr, bssid,
300 720 : wpabuf_head(req), wpabuf_len(req),
301 : wait_time, gas_query_tx_status, 0);
302 720 : if (res == 0)
303 718 : query->offchannel_tx_started = 1;
304 720 : return res;
305 : }
306 :
307 :
308 297 : static void gas_query_tx_comeback_req(struct gas_query *gas,
309 : struct gas_query_pending *query)
310 : {
311 : struct wpabuf *req;
312 : unsigned int wait_time;
313 :
314 297 : req = gas_build_comeback_req(query->dialog_token);
315 297 : if (req == NULL) {
316 1 : gas_query_done(gas, query, GAS_QUERY_INTERNAL_ERROR);
317 298 : return;
318 : }
319 :
320 296 : wait_time = (query->retry || !query->offchannel_tx_started) ?
321 : GAS_QUERY_WAIT_TIME_INITIAL : GAS_QUERY_WAIT_TIME_COMEBACK;
322 :
323 296 : if (gas_query_tx(gas, query, req, wait_time) < 0) {
324 6 : wpa_printf(MSG_DEBUG, "GAS: Failed to send Action frame to "
325 6 : MACSTR, MAC2STR(query->addr));
326 1 : gas_query_done(gas, query, GAS_QUERY_INTERNAL_ERROR);
327 : }
328 :
329 296 : wpabuf_free(req);
330 : }
331 :
332 :
333 4 : static void gas_query_rx_comeback_timeout(void *eloop_data, void *user_ctx)
334 : {
335 4 : struct gas_query *gas = eloop_data;
336 4 : struct gas_query_pending *query = user_ctx;
337 : int dialog_token;
338 :
339 4 : wpa_printf(MSG_DEBUG,
340 : "GAS: No response to comeback request received (retry=%u)",
341 4 : query->retry);
342 4 : if (gas->current != query || query->retry)
343 0 : return;
344 4 : dialog_token = gas_query_new_dialog_token(gas, query->addr);
345 4 : if (dialog_token < 0)
346 0 : return;
347 4 : wpa_printf(MSG_DEBUG,
348 : "GAS: Retry GAS query due to comeback response timeout");
349 4 : query->retry = 1;
350 4 : query->dialog_token = dialog_token;
351 4 : *(wpabuf_mhead_u8(query->req) + 2) = dialog_token;
352 4 : query->wait_comeback = 0;
353 4 : query->next_frag_id = 0;
354 4 : wpabuf_free(query->adv_proto);
355 4 : query->adv_proto = NULL;
356 4 : eloop_cancel_timeout(gas_query_tx_comeback_timeout, gas, query);
357 4 : eloop_cancel_timeout(gas_query_timeout, gas, query);
358 4 : gas_query_tx_initial_req(gas, query);
359 : }
360 :
361 :
362 41 : static void gas_query_tx_comeback_timeout(void *eloop_data, void *user_ctx)
363 : {
364 41 : struct gas_query *gas = eloop_data;
365 41 : struct gas_query_pending *query = user_ctx;
366 :
367 246 : wpa_printf(MSG_DEBUG, "GAS: Comeback timeout for request to " MACSTR,
368 246 : MAC2STR(query->addr));
369 41 : gas_query_tx_comeback_req(gas, query);
370 41 : }
371 :
372 :
373 41 : static void gas_query_tx_comeback_req_delay(struct gas_query *gas,
374 : struct gas_query_pending *query,
375 : u16 comeback_delay)
376 : {
377 : unsigned int secs, usecs;
378 :
379 41 : if (comeback_delay > 1 && query->offchannel_tx_started) {
380 8 : offchannel_send_action_done(gas->wpa_s);
381 8 : query->offchannel_tx_started = 0;
382 : }
383 :
384 41 : secs = (comeback_delay * 1024) / 1000000;
385 41 : usecs = comeback_delay * 1024 - secs * 1000000;
386 246 : wpa_printf(MSG_DEBUG, "GAS: Send comeback request to " MACSTR
387 246 : " in %u secs %u usecs", MAC2STR(query->addr), secs, usecs);
388 41 : eloop_cancel_timeout(gas_query_tx_comeback_timeout, gas, query);
389 41 : eloop_register_timeout(secs, usecs, gas_query_tx_comeback_timeout,
390 : gas, query);
391 41 : }
392 :
393 :
394 411 : static void gas_query_rx_initial(struct gas_query *gas,
395 : struct gas_query_pending *query,
396 : const u8 *adv_proto, const u8 *resp,
397 : size_t len, u16 comeback_delay)
398 : {
399 3288 : wpa_printf(MSG_DEBUG, "GAS: Received initial response from "
400 : MACSTR " (dialog_token=%u comeback_delay=%u)",
401 2877 : MAC2STR(query->addr), query->dialog_token, comeback_delay);
402 :
403 411 : query->adv_proto = wpabuf_alloc_copy(adv_proto, 2 + adv_proto[1]);
404 411 : if (query->adv_proto == NULL) {
405 1 : gas_query_done(gas, query, GAS_QUERY_INTERNAL_ERROR);
406 1 : return;
407 : }
408 :
409 410 : if (comeback_delay) {
410 39 : query->wait_comeback = 1;
411 39 : gas_query_tx_comeback_req_delay(gas, query, comeback_delay);
412 39 : return;
413 : }
414 :
415 : /* Query was completed without comeback mechanism */
416 371 : if (gas_query_append(query, resp, len) < 0) {
417 1 : gas_query_done(gas, query, GAS_QUERY_INTERNAL_ERROR);
418 1 : return;
419 : }
420 :
421 370 : gas_query_done(gas, query, GAS_QUERY_SUCCESS);
422 : }
423 :
424 :
425 291 : static void gas_query_rx_comeback(struct gas_query *gas,
426 : struct gas_query_pending *query,
427 : const u8 *adv_proto, const u8 *resp,
428 : size_t len, u8 frag_id, u8 more_frags,
429 : u16 comeback_delay)
430 : {
431 2328 : wpa_printf(MSG_DEBUG, "GAS: Received comeback response from "
432 : MACSTR " (dialog_token=%u frag_id=%u more_frags=%u "
433 : "comeback_delay=%u)",
434 2037 : MAC2STR(query->addr), query->dialog_token, frag_id,
435 : more_frags, comeback_delay);
436 291 : eloop_cancel_timeout(gas_query_rx_comeback_timeout, gas, query);
437 :
438 582 : if ((size_t) 2 + adv_proto[1] != wpabuf_len(query->adv_proto) ||
439 291 : os_memcmp(adv_proto, wpabuf_head(query->adv_proto),
440 : wpabuf_len(query->adv_proto)) != 0) {
441 6 : wpa_printf(MSG_DEBUG, "GAS: Advertisement Protocol changed "
442 : "between initial and comeback response from "
443 6 : MACSTR, MAC2STR(query->addr));
444 1 : gas_query_done(gas, query, GAS_QUERY_PEER_ERROR);
445 1 : return;
446 : }
447 :
448 290 : if (comeback_delay) {
449 3 : if (frag_id) {
450 6 : wpa_printf(MSG_DEBUG, "GAS: Invalid comeback response "
451 : "with non-zero frag_id and comeback_delay "
452 6 : "from " MACSTR, MAC2STR(query->addr));
453 1 : gas_query_done(gas, query, GAS_QUERY_PEER_ERROR);
454 1 : return;
455 : }
456 2 : gas_query_tx_comeback_req_delay(gas, query, comeback_delay);
457 2 : return;
458 : }
459 :
460 287 : if (frag_id != query->next_frag_id) {
461 18 : wpa_printf(MSG_DEBUG, "GAS: Unexpected frag_id in response "
462 18 : "from " MACSTR, MAC2STR(query->addr));
463 3 : if (frag_id + 1 == query->next_frag_id) {
464 1 : wpa_printf(MSG_DEBUG, "GAS: Drop frame as possible "
465 : "retry of previous fragment");
466 1 : return;
467 : }
468 2 : gas_query_done(gas, query, GAS_QUERY_PEER_ERROR);
469 2 : return;
470 : }
471 284 : query->next_frag_id++;
472 :
473 284 : if (gas_query_append(query, resp, len) < 0) {
474 1 : gas_query_done(gas, query, GAS_QUERY_INTERNAL_ERROR);
475 1 : return;
476 : }
477 :
478 283 : if (more_frags) {
479 256 : gas_query_tx_comeback_req(gas, query);
480 256 : return;
481 : }
482 :
483 27 : gas_query_done(gas, query, GAS_QUERY_SUCCESS);
484 : }
485 :
486 :
487 : /**
488 : * gas_query_rx - Indicate reception of a Public Action or Protected Dual frame
489 : * @gas: GAS query data from gas_query_init()
490 : * @da: Destination MAC address of the Action frame
491 : * @sa: Source MAC address of the Action frame
492 : * @bssid: BSSID of the Action frame
493 : * @categ: Category of the Action frame
494 : * @data: Payload of the Action frame
495 : * @len: Length of @data
496 : * @freq: Frequency (in MHz) on which the frame was received
497 : * Returns: 0 if the Public Action frame was a GAS frame or -1 if not
498 : */
499 1957 : int gas_query_rx(struct gas_query *gas, const u8 *da, const u8 *sa,
500 : const u8 *bssid, u8 categ, const u8 *data, size_t len,
501 : int freq)
502 : {
503 : struct gas_query_pending *query;
504 1957 : u8 action, dialog_token, frag_id = 0, more_frags = 0;
505 : u16 comeback_delay, resp_len;
506 : const u8 *pos, *adv_proto;
507 : int prot, pmf;
508 : unsigned int left;
509 :
510 1957 : if (gas == NULL || len < 4)
511 23 : return -1;
512 :
513 1934 : pos = data;
514 1934 : action = *pos++;
515 1934 : dialog_token = *pos++;
516 :
517 1934 : if (action != WLAN_PA_GAS_INITIAL_RESP &&
518 : action != WLAN_PA_GAS_COMEBACK_RESP)
519 1106 : return -1; /* Not a GAS response */
520 :
521 828 : prot = categ == WLAN_ACTION_PROTECTED_DUAL;
522 828 : pmf = pmf_in_use(gas->wpa_s, sa);
523 828 : if (prot && !pmf) {
524 1 : wpa_printf(MSG_DEBUG, "GAS: Drop unexpected protected GAS frame when PMF is disabled");
525 1 : return 0;
526 : }
527 827 : if (!prot && pmf) {
528 1 : wpa_printf(MSG_DEBUG, "GAS: Drop unexpected unprotected GAS frame when PMF is enabled");
529 1 : return 0;
530 : }
531 :
532 826 : query = gas_query_get_pending(gas, sa, dialog_token);
533 826 : if (query == NULL) {
534 735 : wpa_printf(MSG_DEBUG, "GAS: No pending query found for " MACSTR
535 630 : " dialog token %u", MAC2STR(sa), dialog_token);
536 105 : return -1;
537 : }
538 :
539 4326 : wpa_printf(MSG_DEBUG, "GAS: Response in %d ms from " MACSTR,
540 4326 : ms_from_time(&query->last_oper), MAC2STR(sa));
541 :
542 721 : if (query->wait_comeback && action == WLAN_PA_GAS_INITIAL_RESP) {
543 7 : wpa_printf(MSG_DEBUG, "GAS: Unexpected initial response from "
544 : MACSTR " dialog token %u when waiting for comeback "
545 6 : "response", MAC2STR(sa), dialog_token);
546 1 : return 0;
547 : }
548 :
549 720 : if (!query->wait_comeback && action == WLAN_PA_GAS_COMEBACK_RESP) {
550 21 : wpa_printf(MSG_DEBUG, "GAS: Unexpected comeback response from "
551 : MACSTR " dialog token %u when waiting for initial "
552 18 : "response", MAC2STR(sa), dialog_token);
553 3 : return 0;
554 : }
555 :
556 717 : query->status_code = WPA_GET_LE16(pos);
557 717 : pos += 2;
558 :
559 717 : if (query->status_code == WLAN_STATUS_QUERY_RESP_OUTSTANDING &&
560 : action == WLAN_PA_GAS_COMEBACK_RESP) {
561 2 : wpa_printf(MSG_DEBUG, "GAS: Allow non-zero status for outstanding comeback response");
562 715 : } else if (query->status_code != WLAN_STATUS_SUCCESS) {
563 21 : wpa_printf(MSG_DEBUG, "GAS: Query to " MACSTR " dialog token "
564 : "%u failed - status code %u",
565 21 : MAC2STR(sa), dialog_token, query->status_code);
566 3 : gas_query_done(gas, query, GAS_QUERY_FAILURE);
567 3 : return 0;
568 : }
569 :
570 714 : if (action == WLAN_PA_GAS_COMEBACK_RESP) {
571 293 : if (pos + 1 > data + len)
572 1 : return 0;
573 292 : frag_id = *pos & 0x7f;
574 292 : more_frags = (*pos & 0x80) >> 7;
575 292 : pos++;
576 : }
577 :
578 : /* Comeback Delay */
579 713 : if (pos + 2 > data + len)
580 1 : return 0;
581 712 : comeback_delay = WPA_GET_LE16(pos);
582 712 : pos += 2;
583 :
584 : /* Advertisement Protocol element */
585 712 : if (pos + 2 > data + len || pos + 2 + pos[1] > data + len) {
586 24 : wpa_printf(MSG_DEBUG, "GAS: No room for Advertisement "
587 : "Protocol element in the response from " MACSTR,
588 24 : MAC2STR(sa));
589 4 : return 0;
590 : }
591 :
592 708 : if (*pos != WLAN_EID_ADV_PROTO) {
593 7 : wpa_printf(MSG_DEBUG, "GAS: Unexpected Advertisement "
594 : "Protocol element ID %u in response from " MACSTR,
595 7 : *pos, MAC2STR(sa));
596 1 : return 0;
597 : }
598 :
599 707 : adv_proto = pos;
600 707 : pos += 2 + pos[1];
601 :
602 : /* Query Response Length */
603 707 : if (pos + 2 > data + len) {
604 2 : wpa_printf(MSG_DEBUG, "GAS: No room for GAS Response Length");
605 2 : return 0;
606 : }
607 705 : resp_len = WPA_GET_LE16(pos);
608 705 : pos += 2;
609 :
610 705 : left = data + len - pos;
611 705 : if (resp_len > left) {
612 18 : wpa_printf(MSG_DEBUG, "GAS: Truncated Query Response in "
613 18 : "response from " MACSTR, MAC2STR(sa));
614 3 : return 0;
615 : }
616 :
617 702 : if (resp_len < left) {
618 6 : wpa_printf(MSG_DEBUG, "GAS: Ignore %u octets of extra data "
619 : "after Query Response from " MACSTR,
620 6 : left - resp_len, MAC2STR(sa));
621 : }
622 :
623 702 : if (action == WLAN_PA_GAS_COMEBACK_RESP)
624 291 : gas_query_rx_comeback(gas, query, adv_proto, pos, resp_len,
625 : frag_id, more_frags, comeback_delay);
626 : else
627 411 : gas_query_rx_initial(gas, query, adv_proto, pos, resp_len,
628 : comeback_delay);
629 :
630 702 : return 0;
631 : }
632 :
633 :
634 9 : static void gas_query_timeout(void *eloop_data, void *user_ctx)
635 : {
636 9 : struct gas_query *gas = eloop_data;
637 9 : struct gas_query_pending *query = user_ctx;
638 :
639 63 : wpa_printf(MSG_DEBUG, "GAS: No response received for query to " MACSTR
640 : " dialog token %u",
641 63 : MAC2STR(query->addr), query->dialog_token);
642 9 : gas_query_done(gas, query, GAS_QUERY_TIMEOUT);
643 9 : }
644 :
645 :
646 433 : static int gas_query_dialog_token_available(struct gas_query *gas,
647 : const u8 *dst, u8 dialog_token)
648 : {
649 : struct gas_query_pending *q;
650 444 : dl_list_for_each(q, &gas->pending, struct gas_query_pending, list) {
651 22 : if (os_memcmp(dst, q->addr, ETH_ALEN) == 0 &&
652 11 : dialog_token == q->dialog_token)
653 0 : return 0;
654 : }
655 :
656 433 : return 1;
657 : }
658 :
659 :
660 424 : static void gas_query_start_cb(struct wpa_radio_work *work, int deinit)
661 : {
662 424 : struct gas_query_pending *query = work->ctx;
663 424 : struct gas_query *gas = query->gas;
664 424 : struct wpa_supplicant *wpa_s = gas->wpa_s;
665 :
666 424 : if (deinit) {
667 3 : if (work->started) {
668 1 : gas->work = NULL;
669 1 : gas_query_done(gas, query, GAS_QUERY_DELETED_AT_DEINIT);
670 1 : return;
671 : }
672 :
673 2 : gas_query_free(query, 1);
674 2 : return;
675 : }
676 :
677 421 : if (wpas_update_random_addr_disassoc(wpa_s) < 0) {
678 1 : wpa_msg(wpa_s, MSG_INFO,
679 : "Failed to assign random MAC address for GAS");
680 1 : gas_query_free(query, 1);
681 1 : radio_work_done(work);
682 1 : return;
683 : }
684 :
685 420 : gas->work = work;
686 420 : gas_query_tx_initial_req(gas, query);
687 : }
688 :
689 :
690 424 : static void gas_query_tx_initial_req(struct gas_query *gas,
691 : struct gas_query_pending *query)
692 : {
693 424 : if (gas_query_tx(gas, query, query->req,
694 : GAS_QUERY_WAIT_TIME_INITIAL) < 0) {
695 6 : wpa_printf(MSG_DEBUG, "GAS: Failed to send Action frame to "
696 6 : MACSTR, MAC2STR(query->addr));
697 1 : gas_query_done(gas, query, GAS_QUERY_INTERNAL_ERROR);
698 425 : return;
699 : }
700 423 : gas->current = query;
701 :
702 423 : wpa_printf(MSG_DEBUG, "GAS: Starting query timeout for dialog token %u",
703 423 : query->dialog_token);
704 423 : eloop_register_timeout(GAS_QUERY_TIMEOUT_PERIOD, 0,
705 : gas_query_timeout, gas, query);
706 : }
707 :
708 :
709 433 : static int gas_query_new_dialog_token(struct gas_query *gas, const u8 *dst)
710 : {
711 : static int next_start = 0;
712 : int dialog_token;
713 :
714 433 : for (dialog_token = 0; dialog_token < 256; dialog_token++) {
715 433 : if (gas_query_dialog_token_available(
716 433 : gas, dst, (next_start + dialog_token) % 256))
717 433 : break;
718 : }
719 433 : if (dialog_token == 256)
720 0 : return -1; /* Too many pending queries */
721 433 : dialog_token = (next_start + dialog_token) % 256;
722 433 : next_start = (dialog_token + 1) % 256;
723 433 : return dialog_token;
724 : }
725 :
726 :
727 : /**
728 : * gas_query_req - Request a GAS query
729 : * @gas: GAS query data from gas_query_init()
730 : * @dst: Destination MAC address for the query
731 : * @freq: Frequency (in MHz) for the channel on which to send the query
732 : * @req: GAS query payload (to be freed by gas_query module in case of success
733 : * return)
734 : * @cb: Callback function for reporting GAS query result and response
735 : * @ctx: Context pointer to use with the @cb call
736 : * Returns: dialog token (>= 0) on success or -1 on failure
737 : */
738 429 : int gas_query_req(struct gas_query *gas, const u8 *dst, int freq,
739 : struct wpabuf *req,
740 : void (*cb)(void *ctx, const u8 *dst, u8 dialog_token,
741 : enum gas_query_result result,
742 : const struct wpabuf *adv_proto,
743 : const struct wpabuf *resp, u16 status_code),
744 : void *ctx)
745 : {
746 : struct gas_query_pending *query;
747 : int dialog_token;
748 :
749 429 : if (wpabuf_len(req) < 3)
750 0 : return -1;
751 :
752 429 : dialog_token = gas_query_new_dialog_token(gas, dst);
753 429 : if (dialog_token < 0)
754 0 : return -1;
755 :
756 429 : query = os_zalloc(sizeof(*query));
757 429 : if (query == NULL)
758 5 : return -1;
759 :
760 424 : query->gas = gas;
761 424 : os_memcpy(query->addr, dst, ETH_ALEN);
762 424 : query->dialog_token = dialog_token;
763 424 : query->freq = freq;
764 424 : query->cb = cb;
765 424 : query->ctx = ctx;
766 424 : query->req = req;
767 424 : dl_list_add(&gas->pending, &query->list);
768 :
769 424 : *(wpabuf_mhead_u8(req) + 2) = dialog_token;
770 :
771 3392 : wpa_msg(gas->wpa_s, MSG_INFO, GAS_QUERY_START "addr=" MACSTR
772 : " dialog_token=%u freq=%d",
773 2968 : MAC2STR(query->addr), query->dialog_token, query->freq);
774 :
775 424 : if (radio_add_work(gas->wpa_s, freq, "gas-query", 0, gas_query_start_cb,
776 : query) < 0) {
777 1 : query->req = NULL; /* caller will free this in error case */
778 1 : gas_query_free(query, 1);
779 1 : return -1;
780 : }
781 :
782 423 : return dialog_token;
783 : }
|