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