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