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