blob: 7d29931796604925422eff1434f4bbead7f34926 [file] [log] [blame]
Dmitry Shmidt1f69aa52012-01-24 16:10:04 -08001/*
2 * Generic advertisement service (GAS) query
3 * Copyright (c) 2009, Atheros Communications
Dmitry Shmidt18463232014-01-24 12:29:41 -08004 * Copyright (c) 2011-2014, Qualcomm Atheros, Inc.
Dmitry Shmidtfb79edc2014-01-10 10:45:54 -08005 * Copyright (c) 2011-2014, Jouni Malinen <j@w1.fi>
Dmitry Shmidt1f69aa52012-01-24 16:10:04 -08006 *
Dmitry Shmidtc5ec7f52012-03-06 16:33:24 -08007 * This software may be distributed under the terms of the BSD license.
8 * See README for more details.
Dmitry Shmidt1f69aa52012-01-24 16:10:04 -08009 */
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"
Dmitry Shmidtfb79edc2014-01-10 10:45:54 -080017#include "common/wpa_ctrl.h"
Dmitry Shmidt18463232014-01-24 12:29:41 -080018#include "rsn_supp/wpa.h"
Dmitry Shmidt1f69aa52012-01-24 16:10:04 -080019#include "wpa_supplicant_i.h"
Dmitry Shmidtd5ab1b52016-06-21 12:38:41 -070020#include "config.h"
Dmitry Shmidt1f69aa52012-01-24 16:10:04 -080021#include "driver_i.h"
22#include "offchannel.h"
23#include "gas_query.h"
24
25
Dmitry Shmidta54fa5f2013-01-15 13:53:35 -080026/** GAS query timeout in seconds */
Dmitry Shmidtb6e9aaf2013-05-20 14:49:44 -070027#define GAS_QUERY_TIMEOUT_PERIOD 2
Dmitry Shmidt1f69aa52012-01-24 16:10:04 -080028
Dmitry Shmidt7d56b752015-12-22 10:59:44 -080029/* GAS query wait-time / duration in ms */
30#define GAS_QUERY_WAIT_TIME_INITIAL 1000
31#define GAS_QUERY_WAIT_TIME_COMEBACK 150
Dmitry Shmidt1f69aa52012-01-24 16:10:04 -080032
Sunil8cd6f4d2022-06-28 18:40:46 +000033#define GAS_QUERY_MAX_COMEBACK_DELAY 60000
34
Dmitry Shmidta54fa5f2013-01-15 13:53:35 -080035/**
36 * struct gas_query_pending - Pending GAS query
37 */
Dmitry Shmidt1f69aa52012-01-24 16:10:04 -080038struct gas_query_pending {
39 struct dl_list list;
Dmitry Shmidtfb79edc2014-01-10 10:45:54 -080040 struct gas_query *gas;
Dmitry Shmidt1f69aa52012-01-24 16:10:04 -080041 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;
Dmitry Shmidt7d56b752015-12-22 10:59:44 -080046 unsigned int retry:1;
Roshan Pius3a1667e2018-07-03 15:17:14 -070047 unsigned int wildcard_bssid:1;
Hai Shalomb755a2a2020-04-23 21:49:02 -070048 unsigned int maintain_addr:1;
Dmitry Shmidt1f69aa52012-01-24 16:10:04 -080049 int freq;
50 u16 status_code;
Dmitry Shmidt051af732013-10-22 13:52:46 -070051 struct wpabuf *req;
Dmitry Shmidt1f69aa52012-01-24 16:10:04 -080052 struct wpabuf *adv_proto;
53 struct wpabuf *resp;
Dmitry Shmidtf21452a2014-02-26 10:55:25 -080054 struct os_reltime last_oper;
Dmitry Shmidt1f69aa52012-01-24 16:10:04 -080055 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;
Dmitry Shmidtebd93af2017-02-21 13:40:44 -080060 u8 sa[ETH_ALEN];
Dmitry Shmidt1f69aa52012-01-24 16:10:04 -080061};
62
Dmitry Shmidta54fa5f2013-01-15 13:53:35 -080063/**
64 * struct gas_query - Internal GAS query data
65 */
Dmitry Shmidt1f69aa52012-01-24 16:10:04 -080066struct gas_query {
67 struct wpa_supplicant *wpa_s;
68 struct dl_list pending; /* struct gas_query_pending */
Dmitry Shmidt051af732013-10-22 13:52:46 -070069 struct gas_query_pending *current;
Dmitry Shmidtfb79edc2014-01-10 10:45:54 -080070 struct wpa_radio_work *work;
Dmitry Shmidtebd93af2017-02-21 13:40:44 -080071 struct os_reltime last_mac_addr_rand;
72 int last_rand_sa_type;
73 u8 rand_addr[ETH_ALEN];
Dmitry Shmidt1f69aa52012-01-24 16:10:04 -080074};
75
76
77static void gas_query_tx_comeback_timeout(void *eloop_data, void *user_ctx);
78static void gas_query_timeout(void *eloop_data, void *user_ctx);
Dmitry Shmidt7d56b752015-12-22 10:59:44 -080079static void gas_query_rx_comeback_timeout(void *eloop_data, void *user_ctx);
80static void gas_query_tx_initial_req(struct gas_query *gas,
81 struct gas_query_pending *query);
82static int gas_query_new_dialog_token(struct gas_query *gas, const u8 *dst);
Dmitry Shmidt1f69aa52012-01-24 16:10:04 -080083
84
Dmitry Shmidtf21452a2014-02-26 10:55:25 -080085static int ms_from_time(struct os_reltime *last)
86{
87 struct os_reltime now, res;
88
89 os_get_reltime(&now);
90 os_reltime_sub(&now, last, &res);
91 return res.sec * 1000 + res.usec / 1000;
92}
93
94
Dmitry Shmidta54fa5f2013-01-15 13:53:35 -080095/**
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 */
Dmitry Shmidt1f69aa52012-01-24 16:10:04 -0800100struct gas_query * gas_query_init(struct wpa_supplicant *wpa_s)
101{
102 struct gas_query *gas;
103
104 gas = os_zalloc(sizeof(*gas));
105 if (gas == NULL)
106 return NULL;
107
108 gas->wpa_s = wpa_s;
109 dl_list_init(&gas->pending);
110
111 return gas;
112}
113
114
Dmitry Shmidtfb79edc2014-01-10 10:45:54 -0800115static const char * gas_result_txt(enum gas_query_result result)
116{
117 switch (result) {
118 case GAS_QUERY_SUCCESS:
119 return "SUCCESS";
120 case GAS_QUERY_FAILURE:
121 return "FAILURE";
122 case GAS_QUERY_TIMEOUT:
123 return "TIMEOUT";
124 case GAS_QUERY_PEER_ERROR:
125 return "PEER_ERROR";
126 case GAS_QUERY_INTERNAL_ERROR:
127 return "INTERNAL_ERROR";
Roshan Pius3a1667e2018-07-03 15:17:14 -0700128 case GAS_QUERY_STOPPED:
129 return "STOPPED";
Dmitry Shmidtfb79edc2014-01-10 10:45:54 -0800130 case GAS_QUERY_DELETED_AT_DEINIT:
131 return "DELETED_AT_DEINIT";
132 }
133
134 return "N/A";
135}
136
137
138static void gas_query_free(struct gas_query_pending *query, int del_list)
139{
140 struct gas_query *gas = query->gas;
141
142 if (del_list)
143 dl_list_del(&query->list);
144
145 if (gas->work && gas->work->ctx == query) {
146 radio_work_done(gas->work);
147 gas->work = NULL;
148 }
149
150 wpabuf_free(query->req);
151 wpabuf_free(query->adv_proto);
152 wpabuf_free(query->resp);
153 os_free(query);
154}
155
156
Dmitry Shmidt1f69aa52012-01-24 16:10:04 -0800157static void gas_query_done(struct gas_query *gas,
158 struct gas_query_pending *query,
159 enum gas_query_result result)
160{
Dmitry Shmidtfb79edc2014-01-10 10:45:54 -0800161 wpa_msg(gas->wpa_s, MSG_INFO, GAS_QUERY_DONE "addr=" MACSTR
162 " dialog_token=%u freq=%d status_code=%u result=%s",
163 MAC2STR(query->addr), query->dialog_token, query->freq,
164 query->status_code, gas_result_txt(result));
Dmitry Shmidt051af732013-10-22 13:52:46 -0700165 if (gas->current == query)
166 gas->current = NULL;
Dmitry Shmidt1f69aa52012-01-24 16:10:04 -0800167 if (query->offchannel_tx_started)
168 offchannel_send_action_done(gas->wpa_s);
169 eloop_cancel_timeout(gas_query_tx_comeback_timeout, gas, query);
170 eloop_cancel_timeout(gas_query_timeout, gas, query);
Dmitry Shmidt7d56b752015-12-22 10:59:44 -0800171 eloop_cancel_timeout(gas_query_rx_comeback_timeout, gas, query);
Dmitry Shmidt1f69aa52012-01-24 16:10:04 -0800172 dl_list_del(&query->list);
173 query->cb(query->ctx, query->addr, query->dialog_token, result,
174 query->adv_proto, query->resp, query->status_code);
Dmitry Shmidtfb79edc2014-01-10 10:45:54 -0800175 gas_query_free(query, 0);
Dmitry Shmidt1f69aa52012-01-24 16:10:04 -0800176}
177
178
Dmitry Shmidta54fa5f2013-01-15 13:53:35 -0800179/**
180 * gas_query_deinit - Deinitialize GAS query component
181 * @gas: GAS query data from gas_query_init()
182 */
Dmitry Shmidt1f69aa52012-01-24 16:10:04 -0800183void gas_query_deinit(struct gas_query *gas)
184{
185 struct gas_query_pending *query, *next;
186
187 if (gas == NULL)
188 return;
189
190 dl_list_for_each_safe(query, next, &gas->pending,
191 struct gas_query_pending, list)
192 gas_query_done(gas, query, GAS_QUERY_DELETED_AT_DEINIT);
193
194 os_free(gas);
195}
196
197
198static struct gas_query_pending *
199gas_query_get_pending(struct gas_query *gas, const u8 *addr, u8 dialog_token)
200{
201 struct gas_query_pending *q;
Sunil Ravib0ac25f2024-07-12 01:42:03 +0000202 struct wpa_supplicant *wpa_s = gas->wpa_s;
203
Dmitry Shmidt1f69aa52012-01-24 16:10:04 -0800204 dl_list_for_each(q, &gas->pending, struct gas_query_pending, list) {
Sunil Ravib0ac25f2024-07-12 01:42:03 +0000205 if (ether_addr_equal(q->addr, addr) &&
Dmitry Shmidt1f69aa52012-01-24 16:10:04 -0800206 q->dialog_token == dialog_token)
207 return q;
Sunil Ravib0ac25f2024-07-12 01:42:03 +0000208 if (wpa_s->valid_links &&
209 ether_addr_equal(wpa_s->ap_mld_addr, addr) &&
210 wpas_ap_link_address(wpa_s, q->addr))
211 return q;
Dmitry Shmidt1f69aa52012-01-24 16:10:04 -0800212 }
213 return NULL;
214}
215
216
217static int gas_query_append(struct gas_query_pending *query, const u8 *data,
218 size_t len)
219{
220 if (wpabuf_resize(&query->resp, len) < 0) {
221 wpa_printf(MSG_DEBUG, "GAS: No memory to store the response");
222 return -1;
223 }
224 wpabuf_put_data(query->resp, data, len);
225 return 0;
226}
227
228
Dmitry Shmidt051af732013-10-22 13:52:46 -0700229static void gas_query_tx_status(struct wpa_supplicant *wpa_s,
230 unsigned int freq, const u8 *dst,
231 const u8 *src, const u8 *bssid,
232 const u8 *data, size_t data_len,
233 enum offchannel_send_action_result result)
234{
235 struct gas_query_pending *query;
236 struct gas_query *gas = wpa_s->gas;
Dmitry Shmidtf21452a2014-02-26 10:55:25 -0800237 int dur;
Dmitry Shmidt051af732013-10-22 13:52:46 -0700238
239 if (gas->current == NULL) {
240 wpa_printf(MSG_DEBUG, "GAS: Unexpected TX status: freq=%u dst="
241 MACSTR " result=%d - no query in progress",
242 freq, MAC2STR(dst), result);
243 return;
244 }
245
246 query = gas->current;
247
Dmitry Shmidtf21452a2014-02-26 10:55:25 -0800248 dur = ms_from_time(&query->last_oper);
Dmitry Shmidt051af732013-10-22 13:52:46 -0700249 wpa_printf(MSG_DEBUG, "GAS: TX status: freq=%u dst=" MACSTR
Dmitry Shmidtf21452a2014-02-26 10:55:25 -0800250 " result=%d query=%p dialog_token=%u dur=%d ms",
251 freq, MAC2STR(dst), result, query, query->dialog_token, dur);
Sunil Ravib0ac25f2024-07-12 01:42:03 +0000252 if (!ether_addr_equal(dst, query->addr)) {
Dmitry Shmidt051af732013-10-22 13:52:46 -0700253 wpa_printf(MSG_DEBUG, "GAS: TX status for unexpected destination");
254 return;
255 }
Dmitry Shmidtf21452a2014-02-26 10:55:25 -0800256 os_get_reltime(&query->last_oper);
Dmitry Shmidt051af732013-10-22 13:52:46 -0700257
Dmitry Shmidtd2986c22017-10-23 14:22:09 -0700258 if (result == OFFCHANNEL_SEND_ACTION_SUCCESS ||
259 result == OFFCHANNEL_SEND_ACTION_NO_ACK) {
Dmitry Shmidt051af732013-10-22 13:52:46 -0700260 eloop_cancel_timeout(gas_query_timeout, gas, query);
Dmitry Shmidtd2986c22017-10-23 14:22:09 -0700261 if (result == OFFCHANNEL_SEND_ACTION_NO_ACK) {
262 wpa_printf(MSG_DEBUG, "GAS: No ACK to GAS request");
263 eloop_register_timeout(0, 250000,
264 gas_query_timeout, gas, query);
265 } else {
266 eloop_register_timeout(GAS_QUERY_TIMEOUT_PERIOD, 0,
267 gas_query_timeout, gas, query);
268 }
Dmitry Shmidt7d56b752015-12-22 10:59:44 -0800269 if (query->wait_comeback && !query->retry) {
270 eloop_cancel_timeout(gas_query_rx_comeback_timeout,
271 gas, query);
272 eloop_register_timeout(
273 0, (GAS_QUERY_WAIT_TIME_COMEBACK + 10) * 1000,
274 gas_query_rx_comeback_timeout, gas, query);
275 }
Dmitry Shmidt051af732013-10-22 13:52:46 -0700276 }
277 if (result == OFFCHANNEL_SEND_ACTION_FAILED) {
278 eloop_cancel_timeout(gas_query_timeout, gas, query);
279 eloop_register_timeout(0, 0, gas_query_timeout, gas, query);
280 }
281}
282
283
Dmitry Shmidt1f69aa52012-01-24 16:10:04 -0800284static int gas_query_tx(struct gas_query *gas, struct gas_query_pending *query,
Dmitry Shmidt7d56b752015-12-22 10:59:44 -0800285 struct wpabuf *req, unsigned int wait_time)
Dmitry Shmidt1f69aa52012-01-24 16:10:04 -0800286{
Dmitry Shmidt18463232014-01-24 12:29:41 -0800287 int res, prot = pmf_in_use(gas->wpa_s, query->addr);
Dmitry Shmidtd5ab1b52016-06-21 12:38:41 -0700288 const u8 *bssid;
289 const u8 wildcard_bssid[ETH_ALEN] = {
290 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
291 };
Dmitry Shmidt18463232014-01-24 12:29:41 -0800292
Dmitry Shmidt1f69aa52012-01-24 16:10:04 -0800293 wpa_printf(MSG_DEBUG, "GAS: Send action frame to " MACSTR " len=%u "
Dmitry Shmidtebd93af2017-02-21 13:40:44 -0800294 "freq=%d prot=%d using src addr " MACSTR,
295 MAC2STR(query->addr), (unsigned int) wpabuf_len(req),
296 query->freq, prot, MAC2STR(query->sa));
Dmitry Shmidt18463232014-01-24 12:29:41 -0800297 if (prot) {
298 u8 *categ = wpabuf_mhead_u8(req);
299 *categ = WLAN_ACTION_PROTECTED_DUAL;
300 }
Dmitry Shmidtf21452a2014-02-26 10:55:25 -0800301 os_get_reltime(&query->last_oper);
Dmitry Shmidt623d63a2014-06-13 11:05:14 -0700302 if (gas->wpa_s->max_remain_on_chan &&
303 wait_time > gas->wpa_s->max_remain_on_chan)
304 wait_time = gas->wpa_s->max_remain_on_chan;
Roshan Pius3a1667e2018-07-03 15:17:14 -0700305 if (!query->wildcard_bssid &&
306 (!gas->wpa_s->conf->gas_address3 ||
307 (gas->wpa_s->current_ssid &&
308 gas->wpa_s->wpa_state >= WPA_ASSOCIATED &&
Sunil Ravib0ac25f2024-07-12 01:42:03 +0000309 ether_addr_equal(query->addr, gas->wpa_s->bssid))))
Dmitry Shmidtd5ab1b52016-06-21 12:38:41 -0700310 bssid = query->addr;
311 else
312 bssid = wildcard_bssid;
Dmitry Shmidtebd93af2017-02-21 13:40:44 -0800313
Dmitry Shmidt1f69aa52012-01-24 16:10:04 -0800314 res = offchannel_send_action(gas->wpa_s, query->freq, query->addr,
Dmitry Shmidtebd93af2017-02-21 13:40:44 -0800315 query->sa, bssid, wpabuf_head(req),
316 wpabuf_len(req), wait_time,
317 gas_query_tx_status, 0);
318
Dmitry Shmidt1f69aa52012-01-24 16:10:04 -0800319 if (res == 0)
320 query->offchannel_tx_started = 1;
321 return res;
322}
323
324
325static void gas_query_tx_comeback_req(struct gas_query *gas,
326 struct gas_query_pending *query)
327{
328 struct wpabuf *req;
Dmitry Shmidt7d56b752015-12-22 10:59:44 -0800329 unsigned int wait_time;
Dmitry Shmidt1f69aa52012-01-24 16:10:04 -0800330
331 req = gas_build_comeback_req(query->dialog_token);
332 if (req == NULL) {
333 gas_query_done(gas, query, GAS_QUERY_INTERNAL_ERROR);
334 return;
335 }
336
Dmitry Shmidt7d56b752015-12-22 10:59:44 -0800337 wait_time = (query->retry || !query->offchannel_tx_started) ?
338 GAS_QUERY_WAIT_TIME_INITIAL : GAS_QUERY_WAIT_TIME_COMEBACK;
339
340 if (gas_query_tx(gas, query, req, wait_time) < 0) {
Dmitry Shmidt1f69aa52012-01-24 16:10:04 -0800341 wpa_printf(MSG_DEBUG, "GAS: Failed to send Action frame to "
342 MACSTR, MAC2STR(query->addr));
343 gas_query_done(gas, query, GAS_QUERY_INTERNAL_ERROR);
344 }
345
346 wpabuf_free(req);
347}
348
349
Dmitry Shmidt7d56b752015-12-22 10:59:44 -0800350static void gas_query_rx_comeback_timeout(void *eloop_data, void *user_ctx)
351{
352 struct gas_query *gas = eloop_data;
353 struct gas_query_pending *query = user_ctx;
354 int dialog_token;
355
356 wpa_printf(MSG_DEBUG,
357 "GAS: No response to comeback request received (retry=%u)",
358 query->retry);
359 if (gas->current != query || query->retry)
360 return;
361 dialog_token = gas_query_new_dialog_token(gas, query->addr);
362 if (dialog_token < 0)
363 return;
364 wpa_printf(MSG_DEBUG,
365 "GAS: Retry GAS query due to comeback response timeout");
366 query->retry = 1;
367 query->dialog_token = dialog_token;
368 *(wpabuf_mhead_u8(query->req) + 2) = dialog_token;
369 query->wait_comeback = 0;
370 query->next_frag_id = 0;
371 wpabuf_free(query->adv_proto);
372 query->adv_proto = NULL;
373 eloop_cancel_timeout(gas_query_tx_comeback_timeout, gas, query);
374 eloop_cancel_timeout(gas_query_timeout, gas, query);
375 gas_query_tx_initial_req(gas, query);
376}
377
378
Dmitry Shmidt1f69aa52012-01-24 16:10:04 -0800379static void gas_query_tx_comeback_timeout(void *eloop_data, void *user_ctx)
380{
381 struct gas_query *gas = eloop_data;
382 struct gas_query_pending *query = user_ctx;
383
384 wpa_printf(MSG_DEBUG, "GAS: Comeback timeout for request to " MACSTR,
385 MAC2STR(query->addr));
386 gas_query_tx_comeback_req(gas, query);
387}
388
389
390static void gas_query_tx_comeback_req_delay(struct gas_query *gas,
391 struct gas_query_pending *query,
392 u16 comeback_delay)
393{
394 unsigned int secs, usecs;
395
Dmitry Shmidt7d56b752015-12-22 10:59:44 -0800396 if (comeback_delay > 1 && query->offchannel_tx_started) {
397 offchannel_send_action_done(gas->wpa_s);
398 query->offchannel_tx_started = 0;
399 }
400
Dmitry Shmidt1f69aa52012-01-24 16:10:04 -0800401 secs = (comeback_delay * 1024) / 1000000;
402 usecs = comeback_delay * 1024 - secs * 1000000;
403 wpa_printf(MSG_DEBUG, "GAS: Send comeback request to " MACSTR
404 " in %u secs %u usecs", MAC2STR(query->addr), secs, usecs);
405 eloop_cancel_timeout(gas_query_tx_comeback_timeout, gas, query);
406 eloop_register_timeout(secs, usecs, gas_query_tx_comeback_timeout,
407 gas, query);
408}
409
410
411static void gas_query_rx_initial(struct gas_query *gas,
412 struct gas_query_pending *query,
Sunil Ravi38ad1ed2023-01-17 23:58:31 +0000413 const u8 *adv_proto, size_t adv_proto_len,
414 const u8 *resp, size_t len, u16 comeback_delay)
Dmitry Shmidt1f69aa52012-01-24 16:10:04 -0800415{
416 wpa_printf(MSG_DEBUG, "GAS: Received initial response from "
417 MACSTR " (dialog_token=%u comeback_delay=%u)",
418 MAC2STR(query->addr), query->dialog_token, comeback_delay);
419
Sunil Ravi38ad1ed2023-01-17 23:58:31 +0000420 query->adv_proto = wpabuf_alloc_copy(adv_proto, adv_proto_len);
Dmitry Shmidt1f69aa52012-01-24 16:10:04 -0800421 if (query->adv_proto == NULL) {
422 gas_query_done(gas, query, GAS_QUERY_INTERNAL_ERROR);
423 return;
424 }
425
426 if (comeback_delay) {
Paul Stewart092955c2017-02-06 09:13:09 -0800427 eloop_cancel_timeout(gas_query_timeout, gas, query);
Dmitry Shmidt1f69aa52012-01-24 16:10:04 -0800428 query->wait_comeback = 1;
429 gas_query_tx_comeback_req_delay(gas, query, comeback_delay);
430 return;
431 }
432
433 /* Query was completed without comeback mechanism */
434 if (gas_query_append(query, resp, len) < 0) {
435 gas_query_done(gas, query, GAS_QUERY_INTERNAL_ERROR);
436 return;
437 }
438
439 gas_query_done(gas, query, GAS_QUERY_SUCCESS);
440}
441
442
443static void gas_query_rx_comeback(struct gas_query *gas,
444 struct gas_query_pending *query,
Sunil Ravi38ad1ed2023-01-17 23:58:31 +0000445 const u8 *adv_proto, size_t adv_proto_len,
446 const u8 *resp, size_t len, u8 frag_id,
447 u8 more_frags, u16 comeback_delay)
Dmitry Shmidt1f69aa52012-01-24 16:10:04 -0800448{
449 wpa_printf(MSG_DEBUG, "GAS: Received comeback response from "
450 MACSTR " (dialog_token=%u frag_id=%u more_frags=%u "
451 "comeback_delay=%u)",
452 MAC2STR(query->addr), query->dialog_token, frag_id,
453 more_frags, comeback_delay);
Dmitry Shmidt7d56b752015-12-22 10:59:44 -0800454 eloop_cancel_timeout(gas_query_rx_comeback_timeout, gas, query);
Dmitry Shmidt1f69aa52012-01-24 16:10:04 -0800455
Sunil Ravi38ad1ed2023-01-17 23:58:31 +0000456 if (adv_proto_len != wpabuf_len(query->adv_proto) ||
Dmitry Shmidt1f69aa52012-01-24 16:10:04 -0800457 os_memcmp(adv_proto, wpabuf_head(query->adv_proto),
458 wpabuf_len(query->adv_proto)) != 0) {
459 wpa_printf(MSG_DEBUG, "GAS: Advertisement Protocol changed "
460 "between initial and comeback response from "
461 MACSTR, MAC2STR(query->addr));
462 gas_query_done(gas, query, GAS_QUERY_PEER_ERROR);
463 return;
464 }
465
466 if (comeback_delay) {
467 if (frag_id) {
468 wpa_printf(MSG_DEBUG, "GAS: Invalid comeback response "
469 "with non-zero frag_id and comeback_delay "
470 "from " MACSTR, MAC2STR(query->addr));
471 gas_query_done(gas, query, GAS_QUERY_PEER_ERROR);
472 return;
473 }
474 gas_query_tx_comeback_req_delay(gas, query, comeback_delay);
475 return;
476 }
477
478 if (frag_id != query->next_frag_id) {
479 wpa_printf(MSG_DEBUG, "GAS: Unexpected frag_id in response "
480 "from " MACSTR, MAC2STR(query->addr));
Dmitry Shmidtb6e9aaf2013-05-20 14:49:44 -0700481 if (frag_id + 1 == query->next_frag_id) {
482 wpa_printf(MSG_DEBUG, "GAS: Drop frame as possible "
483 "retry of previous fragment");
484 return;
485 }
Dmitry Shmidt1f69aa52012-01-24 16:10:04 -0800486 gas_query_done(gas, query, GAS_QUERY_PEER_ERROR);
487 return;
488 }
489 query->next_frag_id++;
490
491 if (gas_query_append(query, resp, len) < 0) {
492 gas_query_done(gas, query, GAS_QUERY_INTERNAL_ERROR);
493 return;
494 }
495
496 if (more_frags) {
497 gas_query_tx_comeback_req(gas, query);
498 return;
499 }
500
501 gas_query_done(gas, query, GAS_QUERY_SUCCESS);
502}
503
504
Dmitry Shmidta54fa5f2013-01-15 13:53:35 -0800505/**
Dmitry Shmidt18463232014-01-24 12:29:41 -0800506 * gas_query_rx - Indicate reception of a Public Action or Protected Dual frame
Dmitry Shmidta54fa5f2013-01-15 13:53:35 -0800507 * @gas: GAS query data from gas_query_init()
508 * @da: Destination MAC address of the Action frame
509 * @sa: Source MAC address of the Action frame
510 * @bssid: BSSID of the Action frame
Dmitry Shmidt18463232014-01-24 12:29:41 -0800511 * @categ: Category of the Action frame
Dmitry Shmidta54fa5f2013-01-15 13:53:35 -0800512 * @data: Payload of the Action frame
513 * @len: Length of @data
514 * @freq: Frequency (in MHz) on which the frame was received
515 * Returns: 0 if the Public Action frame was a GAS frame or -1 if not
516 */
Dmitry Shmidt1f69aa52012-01-24 16:10:04 -0800517int gas_query_rx(struct gas_query *gas, const u8 *da, const u8 *sa,
Dmitry Shmidt18463232014-01-24 12:29:41 -0800518 const u8 *bssid, u8 categ, const u8 *data, size_t len,
519 int freq)
Dmitry Shmidt1f69aa52012-01-24 16:10:04 -0800520{
521 struct gas_query_pending *query;
522 u8 action, dialog_token, frag_id = 0, more_frags = 0;
523 u16 comeback_delay, resp_len;
524 const u8 *pos, *adv_proto;
Sunil Ravi38ad1ed2023-01-17 23:58:31 +0000525 size_t adv_proto_len;
Dmitry Shmidt18463232014-01-24 12:29:41 -0800526 int prot, pmf;
Dmitry Shmidtfb45fd52015-01-05 13:08:17 -0800527 unsigned int left;
Dmitry Shmidt1f69aa52012-01-24 16:10:04 -0800528
529 if (gas == NULL || len < 4)
530 return -1;
531
Dmitry Shmidt4ae50e62016-06-27 13:48:39 -0700532 pos = data;
533 action = *pos++;
534 dialog_token = *pos++;
535
536 if (action != WLAN_PA_GAS_INITIAL_RESP &&
537 action != WLAN_PA_GAS_COMEBACK_RESP)
538 return -1; /* Not a GAS response */
539
Dmitry Shmidt18463232014-01-24 12:29:41 -0800540 prot = categ == WLAN_ACTION_PROTECTED_DUAL;
Dmitry Shmidt9c175262016-03-03 10:20:07 -0800541 pmf = pmf_in_use(gas->wpa_s, sa);
Dmitry Shmidt18463232014-01-24 12:29:41 -0800542 if (prot && !pmf) {
543 wpa_printf(MSG_DEBUG, "GAS: Drop unexpected protected GAS frame when PMF is disabled");
544 return 0;
545 }
546 if (!prot && pmf) {
547 wpa_printf(MSG_DEBUG, "GAS: Drop unexpected unprotected GAS frame when PMF is enabled");
548 return 0;
549 }
550
Dmitry Shmidt1f69aa52012-01-24 16:10:04 -0800551 query = gas_query_get_pending(gas, sa, dialog_token);
552 if (query == NULL) {
553 wpa_printf(MSG_DEBUG, "GAS: No pending query found for " MACSTR
554 " dialog token %u", MAC2STR(sa), dialog_token);
555 return -1;
556 }
557
Dmitry Shmidtf21452a2014-02-26 10:55:25 -0800558 wpa_printf(MSG_DEBUG, "GAS: Response in %d ms from " MACSTR,
559 ms_from_time(&query->last_oper), MAC2STR(sa));
560
Dmitry Shmidt1f69aa52012-01-24 16:10:04 -0800561 if (query->wait_comeback && action == WLAN_PA_GAS_INITIAL_RESP) {
562 wpa_printf(MSG_DEBUG, "GAS: Unexpected initial response from "
563 MACSTR " dialog token %u when waiting for comeback "
564 "response", MAC2STR(sa), dialog_token);
565 return 0;
566 }
567
568 if (!query->wait_comeback && action == WLAN_PA_GAS_COMEBACK_RESP) {
569 wpa_printf(MSG_DEBUG, "GAS: Unexpected comeback response from "
570 MACSTR " dialog token %u when waiting for initial "
571 "response", MAC2STR(sa), dialog_token);
572 return 0;
573 }
574
575 query->status_code = WPA_GET_LE16(pos);
576 pos += 2;
577
Dmitry Shmidt7d5c8f22014-03-03 13:53:28 -0800578 if (query->status_code == WLAN_STATUS_QUERY_RESP_OUTSTANDING &&
579 action == WLAN_PA_GAS_COMEBACK_RESP) {
580 wpa_printf(MSG_DEBUG, "GAS: Allow non-zero status for outstanding comeback response");
581 } else if (query->status_code != WLAN_STATUS_SUCCESS) {
Dmitry Shmidt1f69aa52012-01-24 16:10:04 -0800582 wpa_printf(MSG_DEBUG, "GAS: Query to " MACSTR " dialog token "
583 "%u failed - status code %u",
584 MAC2STR(sa), dialog_token, query->status_code);
585 gas_query_done(gas, query, GAS_QUERY_FAILURE);
586 return 0;
587 }
588
589 if (action == WLAN_PA_GAS_COMEBACK_RESP) {
590 if (pos + 1 > data + len)
591 return 0;
592 frag_id = *pos & 0x7f;
593 more_frags = (*pos & 0x80) >> 7;
594 pos++;
595 }
596
597 /* Comeback Delay */
598 if (pos + 2 > data + len)
599 return 0;
600 comeback_delay = WPA_GET_LE16(pos);
Sunil8cd6f4d2022-06-28 18:40:46 +0000601 if (comeback_delay > GAS_QUERY_MAX_COMEBACK_DELAY)
602 comeback_delay = GAS_QUERY_MAX_COMEBACK_DELAY;
Dmitry Shmidt1f69aa52012-01-24 16:10:04 -0800603 pos += 2;
604
605 /* Advertisement Protocol element */
Sunil Ravi38ad1ed2023-01-17 23:58:31 +0000606 adv_proto = pos;
607 left = data + len - adv_proto;
608 if (left < 2 || adv_proto[1] > left - 2) {
Dmitry Shmidt1f69aa52012-01-24 16:10:04 -0800609 wpa_printf(MSG_DEBUG, "GAS: No room for Advertisement "
610 "Protocol element in the response from " MACSTR,
611 MAC2STR(sa));
612 return 0;
613 }
614
Sunil Ravi38ad1ed2023-01-17 23:58:31 +0000615 if (*adv_proto != WLAN_EID_ADV_PROTO) {
Dmitry Shmidt1f69aa52012-01-24 16:10:04 -0800616 wpa_printf(MSG_DEBUG, "GAS: Unexpected Advertisement "
617 "Protocol element ID %u in response from " MACSTR,
Sunil Ravi38ad1ed2023-01-17 23:58:31 +0000618 *adv_proto, MAC2STR(sa));
Dmitry Shmidt1f69aa52012-01-24 16:10:04 -0800619 return 0;
620 }
Sunil Ravi38ad1ed2023-01-17 23:58:31 +0000621 adv_proto_len = 2 + adv_proto[1];
622 if (adv_proto_len > (size_t) (data + len - pos))
623 return 0; /* unreachable due to an earlier check */
Dmitry Shmidt1f69aa52012-01-24 16:10:04 -0800624
Sunil Ravi38ad1ed2023-01-17 23:58:31 +0000625 pos += adv_proto_len;
Dmitry Shmidt1f69aa52012-01-24 16:10:04 -0800626
627 /* Query Response Length */
628 if (pos + 2 > data + len) {
629 wpa_printf(MSG_DEBUG, "GAS: No room for GAS Response Length");
630 return 0;
631 }
632 resp_len = WPA_GET_LE16(pos);
633 pos += 2;
634
Dmitry Shmidtfb45fd52015-01-05 13:08:17 -0800635 left = data + len - pos;
636 if (resp_len > left) {
Dmitry Shmidt1f69aa52012-01-24 16:10:04 -0800637 wpa_printf(MSG_DEBUG, "GAS: Truncated Query Response in "
638 "response from " MACSTR, MAC2STR(sa));
639 return 0;
640 }
641
Dmitry Shmidtfb45fd52015-01-05 13:08:17 -0800642 if (resp_len < left) {
Dmitry Shmidt1f69aa52012-01-24 16:10:04 -0800643 wpa_printf(MSG_DEBUG, "GAS: Ignore %u octets of extra data "
644 "after Query Response from " MACSTR,
Dmitry Shmidtfb45fd52015-01-05 13:08:17 -0800645 left - resp_len, MAC2STR(sa));
Dmitry Shmidt1f69aa52012-01-24 16:10:04 -0800646 }
647
648 if (action == WLAN_PA_GAS_COMEBACK_RESP)
Sunil Ravi38ad1ed2023-01-17 23:58:31 +0000649 gas_query_rx_comeback(gas, query, adv_proto, adv_proto_len,
650 pos, resp_len, frag_id, more_frags,
651 comeback_delay);
Dmitry Shmidt1f69aa52012-01-24 16:10:04 -0800652 else
Sunil Ravi38ad1ed2023-01-17 23:58:31 +0000653 gas_query_rx_initial(gas, query, adv_proto, adv_proto_len,
654 pos, resp_len, comeback_delay);
Dmitry Shmidt1f69aa52012-01-24 16:10:04 -0800655
656 return 0;
657}
658
659
660static void gas_query_timeout(void *eloop_data, void *user_ctx)
661{
662 struct gas_query *gas = eloop_data;
663 struct gas_query_pending *query = user_ctx;
664
Dmitry Shmidt051af732013-10-22 13:52:46 -0700665 wpa_printf(MSG_DEBUG, "GAS: No response received for query to " MACSTR
666 " dialog token %u",
667 MAC2STR(query->addr), query->dialog_token);
Dmitry Shmidt1f69aa52012-01-24 16:10:04 -0800668 gas_query_done(gas, query, GAS_QUERY_TIMEOUT);
669}
670
671
672static int gas_query_dialog_token_available(struct gas_query *gas,
673 const u8 *dst, u8 dialog_token)
674{
675 struct gas_query_pending *q;
676 dl_list_for_each(q, &gas->pending, struct gas_query_pending, list) {
Sunil Ravib0ac25f2024-07-12 01:42:03 +0000677 if (ether_addr_equal(dst, q->addr) &&
Dmitry Shmidt1f69aa52012-01-24 16:10:04 -0800678 dialog_token == q->dialog_token)
679 return 0;
680 }
681
682 return 1;
683}
684
685
Dmitry Shmidtfb79edc2014-01-10 10:45:54 -0800686static void gas_query_start_cb(struct wpa_radio_work *work, int deinit)
687{
688 struct gas_query_pending *query = work->ctx;
689 struct gas_query *gas = query->gas;
Dmitry Shmidt661b4f72014-09-29 14:58:27 -0700690 struct wpa_supplicant *wpa_s = gas->wpa_s;
Dmitry Shmidtfb79edc2014-01-10 10:45:54 -0800691
692 if (deinit) {
Dmitry Shmidtbd14a572014-02-18 10:33:49 -0800693 if (work->started) {
694 gas->work = NULL;
695 gas_query_done(gas, query, GAS_QUERY_DELETED_AT_DEINIT);
696 return;
697 }
698
Dmitry Shmidtfb79edc2014-01-10 10:45:54 -0800699 gas_query_free(query, 1);
700 return;
701 }
702
Hai Shalom899fcc72020-10-19 14:38:18 -0700703 if (!query->maintain_addr && !wpa_s->conf->gas_rand_mac_addr) {
704 if (wpas_update_random_addr_disassoc(wpa_s) < 0) {
705 wpa_msg(wpa_s, MSG_INFO,
706 "Failed to assign random MAC address for GAS");
707 gas_query_free(query, 1);
708 radio_work_done(work);
709 return;
710 }
711 os_memcpy(query->sa, wpa_s->own_addr, ETH_ALEN);
Dmitry Shmidt661b4f72014-09-29 14:58:27 -0700712 }
713
Dmitry Shmidtfb79edc2014-01-10 10:45:54 -0800714 gas->work = work;
Dmitry Shmidt7d56b752015-12-22 10:59:44 -0800715 gas_query_tx_initial_req(gas, query);
716}
Dmitry Shmidtfb79edc2014-01-10 10:45:54 -0800717
Dmitry Shmidt7d56b752015-12-22 10:59:44 -0800718
719static void gas_query_tx_initial_req(struct gas_query *gas,
720 struct gas_query_pending *query)
721{
722 if (gas_query_tx(gas, query, query->req,
723 GAS_QUERY_WAIT_TIME_INITIAL) < 0) {
Dmitry Shmidtfb79edc2014-01-10 10:45:54 -0800724 wpa_printf(MSG_DEBUG, "GAS: Failed to send Action frame to "
725 MACSTR, MAC2STR(query->addr));
Dmitry Shmidt4ae50e62016-06-27 13:48:39 -0700726 gas_query_done(gas, query, GAS_QUERY_INTERNAL_ERROR);
Dmitry Shmidtfb79edc2014-01-10 10:45:54 -0800727 return;
728 }
729 gas->current = query;
730
731 wpa_printf(MSG_DEBUG, "GAS: Starting query timeout for dialog token %u",
732 query->dialog_token);
733 eloop_register_timeout(GAS_QUERY_TIMEOUT_PERIOD, 0,
734 gas_query_timeout, gas, query);
Dmitry Shmidt7d56b752015-12-22 10:59:44 -0800735}
Dmitry Shmidtfb79edc2014-01-10 10:45:54 -0800736
Dmitry Shmidt7d56b752015-12-22 10:59:44 -0800737
738static int gas_query_new_dialog_token(struct gas_query *gas, const u8 *dst)
739{
Hai Shalome21d4e82020-04-29 16:34:06 -0700740 u8 dialog_token;
741 int i;
Dmitry Shmidt7d56b752015-12-22 10:59:44 -0800742
Hai Shalome21d4e82020-04-29 16:34:06 -0700743 /* There should never be more than couple active GAS queries in
744 * progress, so it should be very likely to find an available dialog
745 * token by checking random values. Use a limit on the number of
746 * iterations to handle the unexpected case of large number of pending
747 * queries cleanly. */
748 for (i = 0; i < 256; i++) {
749 /* Get a random number and check if the slot is available */
750 if (os_get_random(&dialog_token, sizeof(dialog_token)) < 0)
Dmitry Shmidt7d56b752015-12-22 10:59:44 -0800751 break;
Hai Shalome21d4e82020-04-29 16:34:06 -0700752 if (gas_query_dialog_token_available(gas, dst, dialog_token))
753 return dialog_token;
Dmitry Shmidt7d56b752015-12-22 10:59:44 -0800754 }
Hai Shalome21d4e82020-04-29 16:34:06 -0700755
756 /* No dialog token value available */
757 return -1;
Dmitry Shmidtfb79edc2014-01-10 10:45:54 -0800758}
759
760
Dmitry Shmidtebd93af2017-02-21 13:40:44 -0800761static int gas_query_set_sa(struct gas_query *gas,
762 struct gas_query_pending *query)
763{
764 struct wpa_supplicant *wpa_s = gas->wpa_s;
765 struct os_reltime now;
766
Hai Shalomb755a2a2020-04-23 21:49:02 -0700767 if (query->maintain_addr ||
768 !wpa_s->conf->gas_rand_mac_addr ||
Dmitry Shmidtebd93af2017-02-21 13:40:44 -0800769 !(wpa_s->current_bss ?
770 (wpa_s->drv_flags &
771 WPA_DRIVER_FLAGS_MGMT_TX_RANDOM_TA_CONNECTED) :
772 (wpa_s->drv_flags & WPA_DRIVER_FLAGS_MGMT_TX_RANDOM_TA))) {
773 /* Use own MAC address as the transmitter address */
Hai Shalomb755a2a2020-04-23 21:49:02 -0700774 wpa_printf(MSG_DEBUG,
775 "GAS: Use own MAC address as the transmitter address%s%s%s",
776 query->maintain_addr ? " (maintain_addr)" : "",
777 !wpa_s->conf->gas_rand_mac_addr ? " (no gas_rand_mac_adr set)" : "",
778 !(wpa_s->current_bss ?
779 (wpa_s->drv_flags &
780 WPA_DRIVER_FLAGS_MGMT_TX_RANDOM_TA_CONNECTED) :
781 (wpa_s->drv_flags &
782 WPA_DRIVER_FLAGS_MGMT_TX_RANDOM_TA)) ?
783 " (no driver rand capa" : "");
Dmitry Shmidtebd93af2017-02-21 13:40:44 -0800784 os_memcpy(query->sa, wpa_s->own_addr, ETH_ALEN);
785 return 0;
786 }
787
788 os_get_reltime(&now);
789
790 if (wpa_s->conf->gas_rand_mac_addr == gas->last_rand_sa_type &&
791 gas->last_mac_addr_rand.sec != 0 &&
792 !os_reltime_expired(&now, &gas->last_mac_addr_rand,
793 wpa_s->conf->gas_rand_addr_lifetime)) {
794 wpa_printf(MSG_DEBUG,
795 "GAS: Use the previously selected random transmitter address "
796 MACSTR, MAC2STR(gas->rand_addr));
797 os_memcpy(query->sa, gas->rand_addr, ETH_ALEN);
798 return 0;
799 }
800
801 if (wpa_s->conf->gas_rand_mac_addr == 1 &&
802 random_mac_addr(gas->rand_addr) < 0) {
803 wpa_printf(MSG_ERROR, "GAS: Failed to get random address");
804 return -1;
805 }
806
807 if (wpa_s->conf->gas_rand_mac_addr == 2 &&
808 random_mac_addr_keep_oui(gas->rand_addr) < 0) {
809 wpa_printf(MSG_ERROR,
810 "GAS: Failed to get random address with same OUI");
811 return -1;
812 }
813
814 wpa_printf(MSG_DEBUG, "GAS: Use a new random transmitter address "
815 MACSTR, MAC2STR(gas->rand_addr));
816 os_memcpy(query->sa, gas->rand_addr, ETH_ALEN);
817 os_get_reltime(&gas->last_mac_addr_rand);
818 gas->last_rand_sa_type = wpa_s->conf->gas_rand_mac_addr;
819
820 return 0;
821}
822
823
Dmitry Shmidta54fa5f2013-01-15 13:53:35 -0800824/**
825 * gas_query_req - Request a GAS query
826 * @gas: GAS query data from gas_query_init()
827 * @dst: Destination MAC address for the query
828 * @freq: Frequency (in MHz) for the channel on which to send the query
Hai Shalomb755a2a2020-04-23 21:49:02 -0700829 * @wildcard_bssid: Force use of wildcard BSSID value
830 * @maintain_addr: Maintain own MAC address for exchange (i.e., ignore MAC
831 * address randomization rules)
Dmitry Shmidt051af732013-10-22 13:52:46 -0700832 * @req: GAS query payload (to be freed by gas_query module in case of success
833 * return)
Dmitry Shmidta54fa5f2013-01-15 13:53:35 -0800834 * @cb: Callback function for reporting GAS query result and response
835 * @ctx: Context pointer to use with the @cb call
836 * Returns: dialog token (>= 0) on success or -1 on failure
837 */
Dmitry Shmidt1f69aa52012-01-24 16:10:04 -0800838int gas_query_req(struct gas_query *gas, const u8 *dst, int freq,
Hai Shalomb755a2a2020-04-23 21:49:02 -0700839 int wildcard_bssid, int maintain_addr, struct wpabuf *req,
Dmitry Shmidt1f69aa52012-01-24 16:10:04 -0800840 void (*cb)(void *ctx, const u8 *dst, u8 dialog_token,
841 enum gas_query_result result,
842 const struct wpabuf *adv_proto,
843 const struct wpabuf *resp, u16 status_code),
844 void *ctx)
845{
846 struct gas_query_pending *query;
847 int dialog_token;
848
849 if (wpabuf_len(req) < 3)
850 return -1;
851
Dmitry Shmidt7d56b752015-12-22 10:59:44 -0800852 dialog_token = gas_query_new_dialog_token(gas, dst);
853 if (dialog_token < 0)
854 return -1;
Dmitry Shmidt1f69aa52012-01-24 16:10:04 -0800855
856 query = os_zalloc(sizeof(*query));
857 if (query == NULL)
858 return -1;
859
Dmitry Shmidtfb79edc2014-01-10 10:45:54 -0800860 query->gas = gas;
Hai Shalomb755a2a2020-04-23 21:49:02 -0700861 query->maintain_addr = !!maintain_addr;
Dmitry Shmidtebd93af2017-02-21 13:40:44 -0800862 if (gas_query_set_sa(gas, query)) {
863 os_free(query);
864 return -1;
865 }
Dmitry Shmidt1f69aa52012-01-24 16:10:04 -0800866 os_memcpy(query->addr, dst, ETH_ALEN);
867 query->dialog_token = dialog_token;
Roshan Pius3a1667e2018-07-03 15:17:14 -0700868 query->wildcard_bssid = !!wildcard_bssid;
Dmitry Shmidt1f69aa52012-01-24 16:10:04 -0800869 query->freq = freq;
870 query->cb = cb;
871 query->ctx = ctx;
Dmitry Shmidt051af732013-10-22 13:52:46 -0700872 query->req = req;
Dmitry Shmidt1f69aa52012-01-24 16:10:04 -0800873 dl_list_add(&gas->pending, &query->list);
874
875 *(wpabuf_mhead_u8(req) + 2) = dialog_token;
876
Dmitry Shmidtfb79edc2014-01-10 10:45:54 -0800877 wpa_msg(gas->wpa_s, MSG_INFO, GAS_QUERY_START "addr=" MACSTR
878 " dialog_token=%u freq=%d",
879 MAC2STR(query->addr), query->dialog_token, query->freq);
Dmitry Shmidt1f69aa52012-01-24 16:10:04 -0800880
Dmitry Shmidtfb79edc2014-01-10 10:45:54 -0800881 if (radio_add_work(gas->wpa_s, freq, "gas-query", 0, gas_query_start_cb,
882 query) < 0) {
Dmitry Shmidt4ae50e62016-06-27 13:48:39 -0700883 query->req = NULL; /* caller will free this in error case */
Dmitry Shmidtfb79edc2014-01-10 10:45:54 -0800884 gas_query_free(query, 1);
885 return -1;
886 }
Dmitry Shmidt1f69aa52012-01-24 16:10:04 -0800887
888 return dialog_token;
889}
Roshan Pius3a1667e2018-07-03 15:17:14 -0700890
891
892int gas_query_stop(struct gas_query *gas, u8 dialog_token)
893{
894 struct gas_query_pending *query;
895
896 dl_list_for_each(query, &gas->pending, struct gas_query_pending, list) {
897 if (query->dialog_token == dialog_token) {
898 if (!gas->work) {
899 /* The pending radio work has not yet been
900 * started, but the pending entry has a
901 * reference to the soon to be freed query.
902 * Need to remove that radio work now to avoid
903 * leaving behind a reference to freed memory.
904 */
905 radio_remove_pending_work(gas->wpa_s, query);
906 }
907 gas_query_done(gas, query, GAS_QUERY_STOPPED);
908 return 0;
909 }
910 }
911
912 return -1;
913}