Coverage Report

Created: 2025-07-11 06:14

/src/hostap/wpa_supplicant/gas_query.c
Line
Count
Source (jump to first uncovered line)
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
0
#define GAS_QUERY_TIMEOUT_PERIOD 2
28
29
/* GAS query wait-time / duration in ms */
30
0
#define GAS_QUERY_WAIT_TIME_INITIAL 1000
31
0
#define GAS_QUERY_WAIT_TIME_COMEBACK 150
32
33
0
#define GAS_QUERY_MAX_COMEBACK_DELAY 60000
34
35
/**
36
 * struct gas_query_pending - Pending GAS query
37
 */
38
struct gas_query_pending {
39
  struct dl_list list;
40
  struct gas_query *gas;
41
  u8 addr[ETH_ALEN];
42
  u8 dialog_token;
43
  u8 next_frag_id;
44
  unsigned int wait_comeback:1;
45
  unsigned int offchannel_tx_started:1;
46
  unsigned int retry:1;
47
  unsigned int wildcard_bssid:1;
48
  unsigned int maintain_addr:1;
49
  int freq;
50
  u16 status_code;
51
  struct wpabuf *req;
52
  struct wpabuf *adv_proto;
53
  struct wpabuf *resp;
54
  struct os_reltime last_oper;
55
  void (*cb)(void *ctx, const u8 *dst, u8 dialog_token,
56
       enum gas_query_result result,
57
       const struct wpabuf *adv_proto,
58
       const struct wpabuf *resp, u16 status_code);
59
  void *ctx;
60
  u8 sa[ETH_ALEN];
61
};
62
63
/**
64
 * struct gas_query - Internal GAS query data
65
 */
66
struct gas_query {
67
  struct wpa_supplicant *wpa_s;
68
  struct dl_list pending; /* struct gas_query_pending */
69
  struct gas_query_pending *current;
70
  struct wpa_radio_work *work;
71
  struct os_reltime last_mac_addr_rand;
72
  int last_rand_sa_type;
73
  u8 rand_addr[ETH_ALEN];
74
};
75
76
77
static void gas_query_tx_comeback_timeout(void *eloop_data, void *user_ctx);
78
static void gas_query_timeout(void *eloop_data, void *user_ctx);
79
static void gas_query_rx_comeback_timeout(void *eloop_data, void *user_ctx);
80
static void gas_query_tx_initial_req(struct gas_query *gas,
81
             struct gas_query_pending *query);
82
static int gas_query_new_dialog_token(struct gas_query *gas, const u8 *dst);
83
84
85
static int ms_from_time(struct os_reltime *last)
86
0
{
87
0
  struct os_reltime now, res;
88
89
0
  os_get_reltime(&now);
90
0
  os_reltime_sub(&now, last, &res);
91
0
  return res.sec * 1000 + res.usec / 1000;
92
0
}
93
94
95
/**
96
 * gas_query_init - Initialize GAS query component
97
 * @wpa_s: Pointer to wpa_supplicant data
98
 * Returns: Pointer to GAS query data or %NULL on failure
99
 */
100
struct gas_query * gas_query_init(struct wpa_supplicant *wpa_s)
101
0
{
102
0
  struct gas_query *gas;
103
104
0
  gas = os_zalloc(sizeof(*gas));
105
0
  if (gas == NULL)
106
0
    return NULL;
107
108
0
  gas->wpa_s = wpa_s;
109
0
  dl_list_init(&gas->pending);
110
111
0
  return gas;
112
0
}
113
114
115
static const char * gas_result_txt(enum gas_query_result result)
116
0
{
117
0
  switch (result) {
118
0
  case GAS_QUERY_SUCCESS:
119
0
    return "SUCCESS";
120
0
  case GAS_QUERY_FAILURE:
121
0
    return "FAILURE";
122
0
  case GAS_QUERY_TIMEOUT:
123
0
    return "TIMEOUT";
124
0
  case GAS_QUERY_PEER_ERROR:
125
0
    return "PEER_ERROR";
126
0
  case GAS_QUERY_INTERNAL_ERROR:
127
0
    return "INTERNAL_ERROR";
128
0
  case GAS_QUERY_STOPPED:
129
0
    return "STOPPED";
130
0
  case GAS_QUERY_DELETED_AT_DEINIT:
131
0
    return "DELETED_AT_DEINIT";
132
0
  }
133
134
0
  return "N/A";
135
0
}
136
137
138
static void gas_query_free(struct gas_query_pending *query, int del_list)
139
0
{
140
0
  struct gas_query *gas = query->gas;
141
142
0
  if (del_list)
143
0
    dl_list_del(&query->list);
144
145
0
  if (gas->work && gas->work->ctx == query) {
146
0
    radio_work_done(gas->work);
147
0
    gas->work = NULL;
148
0
  }
149
150
0
  eloop_cancel_timeout(gas_query_tx_comeback_timeout, gas, query);
151
0
  eloop_cancel_timeout(gas_query_timeout, gas, query);
152
0
  eloop_cancel_timeout(gas_query_rx_comeback_timeout, gas, query);
153
154
0
  wpabuf_free(query->req);
155
0
  wpabuf_free(query->adv_proto);
156
0
  wpabuf_free(query->resp);
157
0
  os_free(query);
158
0
}
159
160
161
static void gas_query_done(struct gas_query *gas,
162
         struct gas_query_pending *query,
163
         enum gas_query_result result)
164
0
{
165
0
  wpa_msg(gas->wpa_s, MSG_INFO, GAS_QUERY_DONE "addr=" MACSTR
166
0
    " dialog_token=%u freq=%d status_code=%u result=%s",
167
0
    MAC2STR(query->addr), query->dialog_token, query->freq,
168
0
    query->status_code, gas_result_txt(result));
169
0
  if (gas->current == query)
170
0
    gas->current = NULL;
171
0
  if (query->offchannel_tx_started)
172
0
    offchannel_send_action_done(gas->wpa_s);
173
0
  dl_list_del(&query->list);
174
0
  query->cb(query->ctx, query->addr, query->dialog_token, result,
175
0
      query->adv_proto, query->resp, query->status_code);
176
0
  gas_query_free(query, 0);
177
0
}
178
179
180
/**
181
 * gas_query_deinit - Deinitialize GAS query component
182
 * @gas: GAS query data from gas_query_init()
183
 */
184
void gas_query_deinit(struct gas_query *gas)
185
0
{
186
0
  struct gas_query_pending *query, *next;
187
188
0
  if (gas == NULL)
189
0
    return;
190
191
0
  dl_list_for_each_safe(query, next, &gas->pending,
192
0
            struct gas_query_pending, list)
193
0
    gas_query_done(gas, query, GAS_QUERY_DELETED_AT_DEINIT);
194
195
0
  os_free(gas);
196
0
}
197
198
199
static struct gas_query_pending *
200
gas_query_get_pending(struct gas_query *gas, const u8 *addr, u8 dialog_token)
201
0
{
202
0
  struct gas_query_pending *q;
203
0
  struct wpa_supplicant *wpa_s = gas->wpa_s;
204
205
0
  dl_list_for_each(q, &gas->pending, struct gas_query_pending, list) {
206
0
    if (ether_addr_equal(q->addr, addr) &&
207
0
        q->dialog_token == dialog_token)
208
0
      return q;
209
0
    if (wpa_s->valid_links &&
210
0
        ether_addr_equal(wpa_s->ap_mld_addr, addr) &&
211
0
        wpas_ap_link_address(wpa_s, q->addr))
212
0
      return q;
213
0
  }
214
0
  return NULL;
215
0
}
216
217
218
static int gas_query_append(struct gas_query_pending *query, const u8 *data,
219
          size_t len)
220
0
{
221
0
  if (wpabuf_resize(&query->resp, len) < 0) {
222
0
    wpa_printf(MSG_DEBUG, "GAS: No memory to store the response");
223
0
    return -1;
224
0
  }
225
0
  wpabuf_put_data(query->resp, data, len);
226
0
  return 0;
227
0
}
228
229
230
static void gas_query_tx_status(struct wpa_supplicant *wpa_s,
231
        unsigned int freq, const u8 *dst,
232
        const u8 *src, const u8 *bssid,
233
        const u8 *data, size_t data_len,
234
        enum offchannel_send_action_result result)
235
0
{
236
0
  struct gas_query_pending *query;
237
0
  struct gas_query *gas = wpa_s->gas;
238
0
  int dur;
239
240
0
  if (gas->current == NULL) {
241
0
    wpa_printf(MSG_DEBUG, "GAS: Unexpected TX status: freq=%u dst="
242
0
         MACSTR " result=%d - no query in progress",
243
0
         freq, MAC2STR(dst), result);
244
0
    return;
245
0
  }
246
247
0
  query = gas->current;
248
249
0
  dur = ms_from_time(&query->last_oper);
250
0
  wpa_printf(MSG_DEBUG, "GAS: TX status: freq=%u dst=" MACSTR
251
0
       " result=%d query=%p dialog_token=%u dur=%d ms",
252
0
       freq, MAC2STR(dst), result, query, query->dialog_token, dur);
253
0
  if (!ether_addr_equal(dst, query->addr)) {
254
0
    wpa_printf(MSG_DEBUG, "GAS: TX status for unexpected destination");
255
0
    return;
256
0
  }
257
0
  os_get_reltime(&query->last_oper);
258
259
0
  if (result == OFFCHANNEL_SEND_ACTION_SUCCESS ||
260
0
      result == OFFCHANNEL_SEND_ACTION_NO_ACK) {
261
0
    eloop_cancel_timeout(gas_query_timeout, gas, query);
262
0
    if (result == OFFCHANNEL_SEND_ACTION_NO_ACK) {
263
0
      wpa_printf(MSG_DEBUG, "GAS: No ACK to GAS request");
264
0
      eloop_register_timeout(0, 250000,
265
0
                 gas_query_timeout, gas, query);
266
0
    } else {
267
0
      eloop_register_timeout(GAS_QUERY_TIMEOUT_PERIOD, 0,
268
0
                 gas_query_timeout, gas, query);
269
0
    }
270
0
    if (query->wait_comeback && !query->retry) {
271
0
      eloop_cancel_timeout(gas_query_rx_comeback_timeout,
272
0
               gas, query);
273
0
      eloop_register_timeout(
274
0
        0, (GAS_QUERY_WAIT_TIME_COMEBACK + 10) * 1000,
275
0
        gas_query_rx_comeback_timeout, gas, query);
276
0
    }
277
0
  }
278
0
  if (result == OFFCHANNEL_SEND_ACTION_FAILED) {
279
0
    eloop_cancel_timeout(gas_query_timeout, gas, query);
280
0
    eloop_register_timeout(0, 0, gas_query_timeout, gas, query);
281
0
  }
282
0
}
283
284
285
static int gas_query_tx(struct gas_query *gas, struct gas_query_pending *query,
286
      struct wpabuf *req, unsigned int wait_time)
287
0
{
288
0
  int res, prot = pmf_in_use(gas->wpa_s, query->addr);
289
0
  const u8 *bssid;
290
0
  const u8 wildcard_bssid[ETH_ALEN] = {
291
0
    0xff, 0xff, 0xff, 0xff, 0xff, 0xff
292
0
  };
293
294
0
  wpa_printf(MSG_DEBUG, "GAS: Send action frame to " MACSTR " len=%u "
295
0
       "freq=%d prot=%d using src addr " MACSTR,
296
0
       MAC2STR(query->addr), (unsigned int) wpabuf_len(req),
297
0
       query->freq, prot, MAC2STR(query->sa));
298
0
  if (prot) {
299
0
    u8 *categ = wpabuf_mhead_u8(req);
300
0
    *categ = WLAN_ACTION_PROTECTED_DUAL;
301
0
  }
302
0
  os_get_reltime(&query->last_oper);
303
0
  if (gas->wpa_s->max_remain_on_chan &&
304
0
      wait_time > gas->wpa_s->max_remain_on_chan)
305
0
    wait_time = gas->wpa_s->max_remain_on_chan;
306
0
  if (!query->wildcard_bssid &&
307
0
      (!gas->wpa_s->conf->gas_address3 ||
308
0
       (gas->wpa_s->current_ssid &&
309
0
        gas->wpa_s->wpa_state >= WPA_ASSOCIATED &&
310
0
        ether_addr_equal(query->addr, gas->wpa_s->bssid))))
311
0
    bssid = query->addr;
312
0
  else
313
0
    bssid = wildcard_bssid;
314
315
0
  res = offchannel_send_action(gas->wpa_s, query->freq, query->addr,
316
0
             query->sa, bssid, wpabuf_head(req),
317
0
             wpabuf_len(req), wait_time,
318
0
             gas_query_tx_status, 0);
319
320
0
  if (res == 0)
321
0
    query->offchannel_tx_started = 1;
322
0
  return res;
323
0
}
324
325
326
static void gas_query_tx_comeback_req(struct gas_query *gas,
327
              struct gas_query_pending *query)
328
0
{
329
0
  struct wpabuf *req;
330
0
  unsigned int wait_time;
331
332
0
  req = gas_build_comeback_req(query->dialog_token);
333
0
  if (req == NULL) {
334
0
    gas_query_done(gas, query, GAS_QUERY_INTERNAL_ERROR);
335
0
    return;
336
0
  }
337
338
0
  wait_time = (query->retry || !query->offchannel_tx_started) ?
339
0
    GAS_QUERY_WAIT_TIME_INITIAL : GAS_QUERY_WAIT_TIME_COMEBACK;
340
341
0
  if (gas_query_tx(gas, query, req, wait_time) < 0) {
342
0
    wpa_printf(MSG_DEBUG, "GAS: Failed to send Action frame to "
343
0
         MACSTR, MAC2STR(query->addr));
344
0
    gas_query_done(gas, query, GAS_QUERY_INTERNAL_ERROR);
345
0
  }
346
347
0
  wpabuf_free(req);
348
0
}
349
350
351
static void gas_query_rx_comeback_timeout(void *eloop_data, void *user_ctx)
352
0
{
353
0
  struct gas_query *gas = eloop_data;
354
0
  struct gas_query_pending *query = user_ctx;
355
0
  int dialog_token;
356
357
0
  wpa_printf(MSG_DEBUG,
358
0
       "GAS: No response to comeback request received (retry=%u)",
359
0
       query->retry);
360
0
  if (gas->current != query || query->retry)
361
0
    return;
362
0
  dialog_token = gas_query_new_dialog_token(gas, query->addr);
363
0
  if (dialog_token < 0)
364
0
    return;
365
0
  wpa_printf(MSG_DEBUG,
366
0
       "GAS: Retry GAS query due to comeback response timeout");
367
0
  query->retry = 1;
368
0
  query->dialog_token = dialog_token;
369
0
  *(wpabuf_mhead_u8(query->req) + 2) = dialog_token;
370
0
  query->wait_comeback = 0;
371
0
  query->next_frag_id = 0;
372
0
  wpabuf_free(query->adv_proto);
373
0
  query->adv_proto = NULL;
374
0
  eloop_cancel_timeout(gas_query_tx_comeback_timeout, gas, query);
375
0
  eloop_cancel_timeout(gas_query_timeout, gas, query);
376
0
  gas_query_tx_initial_req(gas, query);
377
0
}
378
379
380
static void gas_query_tx_comeback_timeout(void *eloop_data, void *user_ctx)
381
0
{
382
0
  struct gas_query *gas = eloop_data;
383
0
  struct gas_query_pending *query = user_ctx;
384
385
0
  wpa_printf(MSG_DEBUG, "GAS: Comeback timeout for request to " MACSTR,
386
0
       MAC2STR(query->addr));
387
0
  gas_query_tx_comeback_req(gas, query);
388
0
}
389
390
391
static void gas_query_tx_comeback_req_delay(struct gas_query *gas,
392
              struct gas_query_pending *query,
393
              u16 comeback_delay)
394
0
{
395
0
  unsigned int secs, usecs;
396
397
0
  if (comeback_delay > 1 && query->offchannel_tx_started) {
398
0
    offchannel_send_action_done(gas->wpa_s);
399
0
    query->offchannel_tx_started = 0;
400
0
  }
401
402
0
  secs = (comeback_delay * 1024) / 1000000;
403
0
  usecs = comeback_delay * 1024 - secs * 1000000;
404
0
  wpa_printf(MSG_DEBUG, "GAS: Send comeback request to " MACSTR
405
0
       " in %u secs %u usecs", MAC2STR(query->addr), secs, usecs);
406
0
  eloop_cancel_timeout(gas_query_tx_comeback_timeout, gas, query);
407
0
  eloop_register_timeout(secs, usecs, gas_query_tx_comeback_timeout,
408
0
             gas, query);
409
0
}
410
411
412
static void gas_query_rx_initial(struct gas_query *gas,
413
         struct gas_query_pending *query,
414
         const u8 *adv_proto, size_t adv_proto_len,
415
         const u8 *resp, size_t len, u16 comeback_delay)
416
0
{
417
0
  wpa_printf(MSG_DEBUG, "GAS: Received initial response from "
418
0
       MACSTR " (dialog_token=%u comeback_delay=%u)",
419
0
       MAC2STR(query->addr), query->dialog_token, comeback_delay);
420
421
0
  query->adv_proto = wpabuf_alloc_copy(adv_proto, adv_proto_len);
422
0
  if (query->adv_proto == NULL) {
423
0
    gas_query_done(gas, query, GAS_QUERY_INTERNAL_ERROR);
424
0
    return;
425
0
  }
426
427
0
  if (comeback_delay) {
428
0
    eloop_cancel_timeout(gas_query_timeout, gas, query);
429
0
    query->wait_comeback = 1;
430
0
    gas_query_tx_comeback_req_delay(gas, query, comeback_delay);
431
0
    return;
432
0
  }
433
434
  /* Query was completed without comeback mechanism */
435
0
  if (gas_query_append(query, resp, len) < 0) {
436
0
    gas_query_done(gas, query, GAS_QUERY_INTERNAL_ERROR);
437
0
    return;
438
0
  }
439
440
0
  gas_query_done(gas, query, GAS_QUERY_SUCCESS);
441
0
}
442
443
444
static void gas_query_rx_comeback(struct gas_query *gas,
445
          struct gas_query_pending *query,
446
          const u8 *adv_proto, size_t adv_proto_len,
447
          const u8 *resp, size_t len, u8 frag_id,
448
          u8 more_frags, u16 comeback_delay)
449
0
{
450
0
  wpa_printf(MSG_DEBUG, "GAS: Received comeback response from "
451
0
       MACSTR " (dialog_token=%u frag_id=%u more_frags=%u "
452
0
       "comeback_delay=%u)",
453
0
       MAC2STR(query->addr), query->dialog_token, frag_id,
454
0
       more_frags, comeback_delay);
455
0
  eloop_cancel_timeout(gas_query_rx_comeback_timeout, gas, query);
456
457
0
  if (adv_proto_len != wpabuf_len(query->adv_proto) ||
458
0
      os_memcmp(adv_proto, wpabuf_head(query->adv_proto),
459
0
          wpabuf_len(query->adv_proto)) != 0) {
460
0
    wpa_printf(MSG_DEBUG, "GAS: Advertisement Protocol changed "
461
0
         "between initial and comeback response from "
462
0
         MACSTR, MAC2STR(query->addr));
463
0
    gas_query_done(gas, query, GAS_QUERY_PEER_ERROR);
464
0
    return;
465
0
  }
466
467
0
  if (comeback_delay) {
468
0
    if (frag_id) {
469
0
      wpa_printf(MSG_DEBUG, "GAS: Invalid comeback response "
470
0
           "with non-zero frag_id and comeback_delay "
471
0
           "from " MACSTR, MAC2STR(query->addr));
472
0
      gas_query_done(gas, query, GAS_QUERY_PEER_ERROR);
473
0
      return;
474
0
    }
475
0
    gas_query_tx_comeback_req_delay(gas, query, comeback_delay);
476
0
    return;
477
0
  }
478
479
0
  if (frag_id != query->next_frag_id) {
480
0
    wpa_printf(MSG_DEBUG, "GAS: Unexpected frag_id in response "
481
0
         "from " MACSTR, MAC2STR(query->addr));
482
0
    if (frag_id + 1 == query->next_frag_id) {
483
0
      wpa_printf(MSG_DEBUG, "GAS: Drop frame as possible "
484
0
           "retry of previous fragment");
485
0
      return;
486
0
    }
487
0
    gas_query_done(gas, query, GAS_QUERY_PEER_ERROR);
488
0
    return;
489
0
  }
490
0
  query->next_frag_id++;
491
492
0
  if (gas_query_append(query, resp, len) < 0) {
493
0
    gas_query_done(gas, query, GAS_QUERY_INTERNAL_ERROR);
494
0
    return;
495
0
  }
496
497
0
  if (more_frags) {
498
0
    gas_query_tx_comeback_req(gas, query);
499
0
    return;
500
0
  }
501
502
0
  gas_query_done(gas, query, GAS_QUERY_SUCCESS);
503
0
}
504
505
506
/**
507
 * gas_query_rx - Indicate reception of a Public Action or Protected Dual frame
508
 * @gas: GAS query data from gas_query_init()
509
 * @da: Destination MAC address of the Action frame
510
 * @sa: Source MAC address of the Action frame
511
 * @bssid: BSSID of the Action frame
512
 * @categ: Category of the Action frame
513
 * @data: Payload of the Action frame
514
 * @len: Length of @data
515
 * @freq: Frequency (in MHz) on which the frame was received
516
 * Returns: 0 if the Public Action frame was a GAS frame or -1 if not
517
 */
518
int gas_query_rx(struct gas_query *gas, const u8 *da, const u8 *sa,
519
     const u8 *bssid, u8 categ, const u8 *data, size_t len,
520
     int freq)
521
0
{
522
0
  struct gas_query_pending *query;
523
0
  u8 action, dialog_token, frag_id = 0, more_frags = 0;
524
0
  u16 comeback_delay, resp_len;
525
0
  const u8 *pos, *adv_proto;
526
0
  size_t adv_proto_len;
527
0
  int prot, pmf;
528
0
  unsigned int left;
529
530
0
  if (gas == NULL || len < 4)
531
0
    return -1;
532
533
0
  pos = data;
534
0
  action = *pos++;
535
0
  dialog_token = *pos++;
536
537
0
  if (action != WLAN_PA_GAS_INITIAL_RESP &&
538
0
      action != WLAN_PA_GAS_COMEBACK_RESP)
539
0
    return -1; /* Not a GAS response */
540
541
0
  prot = categ == WLAN_ACTION_PROTECTED_DUAL;
542
0
  pmf = pmf_in_use(gas->wpa_s, sa);
543
0
  if (prot && !pmf) {
544
0
    wpa_printf(MSG_DEBUG, "GAS: Drop unexpected protected GAS frame when PMF is disabled");
545
0
    return 0;
546
0
  }
547
0
  if (!prot && pmf) {
548
0
    wpa_printf(MSG_DEBUG, "GAS: Drop unexpected unprotected GAS frame when PMF is enabled");
549
0
    return 0;
550
0
  }
551
552
0
  query = gas_query_get_pending(gas, sa, dialog_token);
553
0
  if (query == NULL) {
554
0
    wpa_printf(MSG_DEBUG, "GAS: No pending query found for " MACSTR
555
0
         " dialog token %u", MAC2STR(sa), dialog_token);
556
0
    return -1;
557
0
  }
558
559
0
  wpa_printf(MSG_DEBUG, "GAS: Response in %d ms from " MACSTR,
560
0
       ms_from_time(&query->last_oper), MAC2STR(sa));
561
562
0
  if (query->wait_comeback && action == WLAN_PA_GAS_INITIAL_RESP) {
563
0
    wpa_printf(MSG_DEBUG, "GAS: Unexpected initial response from "
564
0
         MACSTR " dialog token %u when waiting for comeback "
565
0
         "response", MAC2STR(sa), dialog_token);
566
0
    return 0;
567
0
  }
568
569
0
  if (!query->wait_comeback && action == WLAN_PA_GAS_COMEBACK_RESP) {
570
0
    wpa_printf(MSG_DEBUG, "GAS: Unexpected comeback response from "
571
0
         MACSTR " dialog token %u when waiting for initial "
572
0
         "response", MAC2STR(sa), dialog_token);
573
0
    return 0;
574
0
  }
575
576
0
  query->status_code = WPA_GET_LE16(pos);
577
0
  pos += 2;
578
579
0
  if (query->status_code == WLAN_STATUS_QUERY_RESP_OUTSTANDING &&
580
0
      action == WLAN_PA_GAS_COMEBACK_RESP) {
581
0
    wpa_printf(MSG_DEBUG, "GAS: Allow non-zero status for outstanding comeback response");
582
0
  } else if (query->status_code != WLAN_STATUS_SUCCESS) {
583
0
    wpa_printf(MSG_DEBUG, "GAS: Query to " MACSTR " dialog token "
584
0
         "%u failed - status code %u",
585
0
         MAC2STR(sa), dialog_token, query->status_code);
586
0
    gas_query_done(gas, query, GAS_QUERY_FAILURE);
587
0
    return 0;
588
0
  }
589
590
0
  if (action == WLAN_PA_GAS_COMEBACK_RESP) {
591
0
    if (pos + 1 > data + len)
592
0
      return 0;
593
0
    frag_id = *pos & 0x7f;
594
0
    more_frags = (*pos & 0x80) >> 7;
595
0
    pos++;
596
0
  }
597
598
  /* Comeback Delay */
599
0
  if (pos + 2 > data + len)
600
0
    return 0;
601
0
  comeback_delay = WPA_GET_LE16(pos);
602
0
  if (comeback_delay > GAS_QUERY_MAX_COMEBACK_DELAY)
603
0
    comeback_delay = GAS_QUERY_MAX_COMEBACK_DELAY;
604
0
  pos += 2;
605
606
  /* Advertisement Protocol element */
607
0
  adv_proto = pos;
608
0
  left = data + len - adv_proto;
609
0
  if (left < 2 || adv_proto[1] > left - 2) {
610
0
    wpa_printf(MSG_DEBUG, "GAS: No room for Advertisement "
611
0
         "Protocol element in the response from " MACSTR,
612
0
         MAC2STR(sa));
613
0
    return 0;
614
0
  }
615
616
0
  if (*adv_proto != WLAN_EID_ADV_PROTO) {
617
0
    wpa_printf(MSG_DEBUG, "GAS: Unexpected Advertisement "
618
0
         "Protocol element ID %u in response from " MACSTR,
619
0
         *adv_proto, MAC2STR(sa));
620
0
    return 0;
621
0
  }
622
0
  adv_proto_len = 2 + adv_proto[1];
623
0
  if (adv_proto_len > (size_t) (data + len - pos))
624
0
    return 0; /* unreachable due to an earlier check */
625
626
0
  pos += adv_proto_len;
627
628
  /* Query Response Length */
629
0
  if (pos + 2 > data + len) {
630
0
    wpa_printf(MSG_DEBUG, "GAS: No room for GAS Response Length");
631
0
    return 0;
632
0
  }
633
0
  resp_len = WPA_GET_LE16(pos);
634
0
  pos += 2;
635
636
0
  left = data + len - pos;
637
0
  if (resp_len > left) {
638
0
    wpa_printf(MSG_DEBUG, "GAS: Truncated Query Response in "
639
0
         "response from " MACSTR, MAC2STR(sa));
640
0
    return 0;
641
0
  }
642
643
0
  if (resp_len < left) {
644
0
    wpa_printf(MSG_DEBUG, "GAS: Ignore %u octets of extra data "
645
0
         "after Query Response from " MACSTR,
646
0
         left - resp_len, MAC2STR(sa));
647
0
  }
648
649
0
  if (action == WLAN_PA_GAS_COMEBACK_RESP)
650
0
    gas_query_rx_comeback(gas, query, adv_proto, adv_proto_len,
651
0
              pos, resp_len, frag_id, more_frags,
652
0
              comeback_delay);
653
0
  else
654
0
    gas_query_rx_initial(gas, query, adv_proto, adv_proto_len,
655
0
             pos, resp_len, comeback_delay);
656
657
0
  return 0;
658
0
}
659
660
661
static void gas_query_timeout(void *eloop_data, void *user_ctx)
662
0
{
663
0
  struct gas_query *gas = eloop_data;
664
0
  struct gas_query_pending *query = user_ctx;
665
666
0
  wpa_printf(MSG_DEBUG, "GAS: No response received for query to " MACSTR
667
0
       " dialog token %u",
668
0
       MAC2STR(query->addr), query->dialog_token);
669
0
  gas_query_done(gas, query, GAS_QUERY_TIMEOUT);
670
0
}
671
672
673
static int gas_query_dialog_token_available(struct gas_query *gas,
674
              const u8 *dst, u8 dialog_token)
675
0
{
676
0
  struct gas_query_pending *q;
677
0
  dl_list_for_each(q, &gas->pending, struct gas_query_pending, list) {
678
0
    if (ether_addr_equal(dst, q->addr) &&
679
0
        dialog_token == q->dialog_token)
680
0
      return 0;
681
0
  }
682
683
0
  return 1;
684
0
}
685
686
687
static void gas_query_start_cb(struct wpa_radio_work *work, int deinit)
688
0
{
689
0
  struct gas_query_pending *query = work->ctx;
690
0
  struct gas_query *gas = query->gas;
691
0
  struct wpa_supplicant *wpa_s = gas->wpa_s;
692
693
0
  if (deinit) {
694
0
    if (work->started) {
695
0
      gas->work = NULL;
696
0
      gas_query_done(gas, query, GAS_QUERY_DELETED_AT_DEINIT);
697
0
      return;
698
0
    }
699
700
0
    gas_query_free(query, 1);
701
0
    return;
702
0
  }
703
704
0
  if (!query->maintain_addr && !wpa_s->conf->gas_rand_mac_addr) {
705
0
    if (wpas_update_random_addr_disassoc(wpa_s) < 0) {
706
0
      wpa_msg(wpa_s, MSG_INFO,
707
0
        "Failed to assign random MAC address for GAS");
708
0
      gas_query_free(query, 1);
709
0
      radio_work_done(work);
710
0
      return;
711
0
    }
712
0
    os_memcpy(query->sa, wpa_s->own_addr, ETH_ALEN);
713
0
  }
714
715
0
  gas->work = work;
716
0
  gas_query_tx_initial_req(gas, query);
717
0
}
718
719
720
static void gas_query_tx_initial_req(struct gas_query *gas,
721
             struct gas_query_pending *query)
722
0
{
723
0
  if (gas_query_tx(gas, query, query->req,
724
0
       GAS_QUERY_WAIT_TIME_INITIAL) < 0) {
725
0
    wpa_printf(MSG_DEBUG, "GAS: Failed to send Action frame to "
726
0
         MACSTR, MAC2STR(query->addr));
727
0
    gas_query_done(gas, query, GAS_QUERY_INTERNAL_ERROR);
728
0
    return;
729
0
  }
730
0
  gas->current = query;
731
732
0
  wpa_printf(MSG_DEBUG, "GAS: Starting query timeout for dialog token %u",
733
0
       query->dialog_token);
734
0
  eloop_register_timeout(GAS_QUERY_TIMEOUT_PERIOD, 0,
735
0
             gas_query_timeout, gas, query);
736
0
}
737
738
739
static int gas_query_new_dialog_token(struct gas_query *gas, const u8 *dst)
740
0
{
741
0
  u8 dialog_token;
742
0
  int i;
743
744
  /* There should never be more than couple active GAS queries in
745
   * progress, so it should be very likely to find an available dialog
746
   * token by checking random values. Use a limit on the number of
747
   * iterations to handle the unexpected case of large number of pending
748
   * queries cleanly. */
749
0
  for (i = 0; i < 256; i++) {
750
    /* Get a random number and check if the slot is available */
751
0
    if (os_get_random(&dialog_token, sizeof(dialog_token)) < 0)
752
0
      break;
753
0
    if (gas_query_dialog_token_available(gas, dst, dialog_token))
754
0
      return dialog_token;
755
0
  }
756
757
  /* No dialog token value available */
758
0
  return -1;
759
0
}
760
761
762
static int gas_query_set_sa(struct gas_query *gas,
763
          struct gas_query_pending *query)
764
0
{
765
0
  struct wpa_supplicant *wpa_s = gas->wpa_s;
766
0
  struct os_reltime now;
767
768
0
  if (query->maintain_addr ||
769
0
      !wpa_s->conf->gas_rand_mac_addr ||
770
0
      !(wpa_s->current_bss ?
771
0
        (wpa_s->drv_flags &
772
0
         WPA_DRIVER_FLAGS_MGMT_TX_RANDOM_TA_CONNECTED) :
773
0
        (wpa_s->drv_flags & WPA_DRIVER_FLAGS_MGMT_TX_RANDOM_TA))) {
774
    /* Use own MAC address as the transmitter address */
775
0
    wpa_printf(MSG_DEBUG,
776
0
         "GAS: Use own MAC address as the transmitter address%s%s%s",
777
0
         query->maintain_addr ? " (maintain_addr)" : "",
778
0
         !wpa_s->conf->gas_rand_mac_addr ? " (no gas_rand_mac_adr set)" : "",
779
0
         !(wpa_s->current_bss ?
780
0
           (wpa_s->drv_flags &
781
0
            WPA_DRIVER_FLAGS_MGMT_TX_RANDOM_TA_CONNECTED) :
782
0
           (wpa_s->drv_flags &
783
0
            WPA_DRIVER_FLAGS_MGMT_TX_RANDOM_TA)) ?
784
0
         " (no driver rand capa" : "");
785
0
    os_memcpy(query->sa, wpa_s->own_addr, ETH_ALEN);
786
0
    return 0;
787
0
  }
788
789
0
  os_get_reltime(&now);
790
791
0
  if (wpa_s->conf->gas_rand_mac_addr == gas->last_rand_sa_type &&
792
0
      gas->last_mac_addr_rand.sec != 0 &&
793
0
      !os_reltime_expired(&now, &gas->last_mac_addr_rand,
794
0
        wpa_s->conf->gas_rand_addr_lifetime)) {
795
0
    wpa_printf(MSG_DEBUG,
796
0
         "GAS: Use the previously selected random transmitter address "
797
0
         MACSTR, MAC2STR(gas->rand_addr));
798
0
    os_memcpy(query->sa, gas->rand_addr, ETH_ALEN);
799
0
    return 0;
800
0
  }
801
802
0
  if (wpa_s->conf->gas_rand_mac_addr == 1 &&
803
0
      random_mac_addr(gas->rand_addr) < 0) {
804
0
    wpa_printf(MSG_ERROR, "GAS: Failed to get random address");
805
0
    return -1;
806
0
  }
807
808
0
  if (wpa_s->conf->gas_rand_mac_addr == 2 &&
809
0
      random_mac_addr_keep_oui(gas->rand_addr) < 0) {
810
0
    wpa_printf(MSG_ERROR,
811
0
         "GAS: Failed to get random address with same OUI");
812
0
    return -1;
813
0
  }
814
815
0
  wpa_printf(MSG_DEBUG, "GAS: Use a new random transmitter address "
816
0
       MACSTR, MAC2STR(gas->rand_addr));
817
0
  os_memcpy(query->sa, gas->rand_addr, ETH_ALEN);
818
0
  os_get_reltime(&gas->last_mac_addr_rand);
819
0
  gas->last_rand_sa_type = wpa_s->conf->gas_rand_mac_addr;
820
821
0
  return 0;
822
0
}
823
824
825
/**
826
 * gas_query_req - Request a GAS query
827
 * @gas: GAS query data from gas_query_init()
828
 * @dst: Destination MAC address for the query
829
 * @freq: Frequency (in MHz) for the channel on which to send the query
830
 * @wildcard_bssid: Force use of wildcard BSSID value
831
 * @maintain_addr: Maintain own MAC address for exchange (i.e., ignore MAC
832
 *  address randomization rules)
833
 * @req: GAS query payload (to be freed by gas_query module in case of success
834
 *  return)
835
 * @cb: Callback function for reporting GAS query result and response
836
 * @ctx: Context pointer to use with the @cb call
837
 * Returns: dialog token (>= 0) on success or -1 on failure
838
 */
839
int gas_query_req(struct gas_query *gas, const u8 *dst, int freq,
840
      int wildcard_bssid, int maintain_addr, struct wpabuf *req,
841
      void (*cb)(void *ctx, const u8 *dst, u8 dialog_token,
842
           enum gas_query_result result,
843
           const struct wpabuf *adv_proto,
844
           const struct wpabuf *resp, u16 status_code),
845
      void *ctx)
846
0
{
847
0
  struct gas_query_pending *query;
848
0
  int dialog_token;
849
850
0
  if (wpabuf_len(req) < 3)
851
0
    return -1;
852
853
0
  dialog_token = gas_query_new_dialog_token(gas, dst);
854
0
  if (dialog_token < 0)
855
0
    return -1;
856
857
0
  query = os_zalloc(sizeof(*query));
858
0
  if (query == NULL)
859
0
    return -1;
860
861
0
  query->gas = gas;
862
0
  query->maintain_addr = !!maintain_addr;
863
0
  if (gas_query_set_sa(gas, query)) {
864
0
    os_free(query);
865
0
    return -1;
866
0
  }
867
0
  os_memcpy(query->addr, dst, ETH_ALEN);
868
0
  query->dialog_token = dialog_token;
869
0
  query->wildcard_bssid = !!wildcard_bssid;
870
0
  query->freq = freq;
871
0
  query->cb = cb;
872
0
  query->ctx = ctx;
873
0
  query->req = req;
874
0
  dl_list_add(&gas->pending, &query->list);
875
876
0
  *(wpabuf_mhead_u8(req) + 2) = dialog_token;
877
878
0
  wpa_msg(gas->wpa_s, MSG_INFO, GAS_QUERY_START "addr=" MACSTR
879
0
    " dialog_token=%u freq=%d",
880
0
    MAC2STR(query->addr), query->dialog_token, query->freq);
881
882
0
  if (radio_add_work(gas->wpa_s, freq, "gas-query", 0, gas_query_start_cb,
883
0
         query) < 0) {
884
0
    query->req = NULL; /* caller will free this in error case */
885
0
    gas_query_free(query, 1);
886
0
    return -1;
887
0
  }
888
889
0
  return dialog_token;
890
0
}
891
892
893
int gas_query_stop(struct gas_query *gas, u8 dialog_token)
894
0
{
895
0
  struct gas_query_pending *query;
896
897
0
  dl_list_for_each(query, &gas->pending, struct gas_query_pending, list) {
898
0
    if (query->dialog_token == dialog_token) {
899
0
      if (!gas->work) {
900
        /* The pending radio work has not yet been
901
         * started, but the pending entry has a
902
         * reference to the soon to be freed query.
903
         * Need to remove that radio work now to avoid
904
         * leaving behind a reference to freed memory.
905
         */
906
0
        radio_remove_pending_work(gas->wpa_s, query);
907
0
      }
908
0
      gas_query_done(gas, query, GAS_QUERY_STOPPED);
909
0
      return 0;
910
0
    }
911
0
  }
912
913
0
  return -1;
914
0
}