2 * Generic advertisement service (GAS) query
3 * Copyright (c) 2009, Atheros Communications
4 * Copyright (c) 2011, Qualcomm Atheros
6 * This software may be distributed under the terms of the BSD license.
7 * See README for more details.
13 #include "utils/eloop.h"
14 #include "common/ieee802_11_defs.h"
15 #include "common/gas.h"
16 #include "wpa_supplicant_i.h"
18 #include "offchannel.h"
19 #include "gas_query.h"
22 #define GAS_QUERY_TIMEOUT 5
25 struct gas_query_pending
{
30 unsigned int wait_comeback
:1;
31 unsigned int offchannel_tx_started
:1;
34 struct wpabuf
*adv_proto
;
36 void (*cb
)(void *ctx
, const u8
*dst
, u8 dialog_token
,
37 enum gas_query_result result
,
38 const struct wpabuf
*adv_proto
,
39 const struct wpabuf
*resp
, u16 status_code
);
44 struct wpa_supplicant
*wpa_s
;
45 struct dl_list pending
; /* struct gas_query_pending */
49 static void gas_query_tx_comeback_timeout(void *eloop_data
, void *user_ctx
);
50 static void gas_query_timeout(void *eloop_data
, void *user_ctx
);
53 struct gas_query
* gas_query_init(struct wpa_supplicant
*wpa_s
)
55 struct gas_query
*gas
;
57 gas
= os_zalloc(sizeof(*gas
));
62 dl_list_init(&gas
->pending
);
68 static void gas_query_done(struct gas_query
*gas
,
69 struct gas_query_pending
*query
,
70 enum gas_query_result result
)
72 if (query
->offchannel_tx_started
)
73 offchannel_send_action_done(gas
->wpa_s
);
74 eloop_cancel_timeout(gas_query_tx_comeback_timeout
, gas
, query
);
75 eloop_cancel_timeout(gas_query_timeout
, gas
, query
);
76 dl_list_del(&query
->list
);
77 query
->cb(query
->ctx
, query
->addr
, query
->dialog_token
, result
,
78 query
->adv_proto
, query
->resp
, query
->status_code
);
79 wpabuf_free(query
->adv_proto
);
80 wpabuf_free(query
->resp
);
85 void gas_query_deinit(struct gas_query
*gas
)
87 struct gas_query_pending
*query
, *next
;
92 dl_list_for_each_safe(query
, next
, &gas
->pending
,
93 struct gas_query_pending
, list
)
94 gas_query_done(gas
, query
, GAS_QUERY_DELETED_AT_DEINIT
);
100 static struct gas_query_pending
*
101 gas_query_get_pending(struct gas_query
*gas
, const u8
*addr
, u8 dialog_token
)
103 struct gas_query_pending
*q
;
104 dl_list_for_each(q
, &gas
->pending
, struct gas_query_pending
, list
) {
105 if (os_memcmp(q
->addr
, addr
, ETH_ALEN
) == 0 &&
106 q
->dialog_token
== dialog_token
)
113 static int gas_query_append(struct gas_query_pending
*query
, const u8
*data
,
116 if (wpabuf_resize(&query
->resp
, len
) < 0) {
117 wpa_printf(MSG_DEBUG
, "GAS: No memory to store the response");
120 wpabuf_put_data(query
->resp
, data
, len
);
125 static int gas_query_tx(struct gas_query
*gas
, struct gas_query_pending
*query
,
129 wpa_printf(MSG_DEBUG
, "GAS: Send action frame to " MACSTR
" len=%u "
130 "freq=%d", MAC2STR(query
->addr
),
131 (unsigned int) wpabuf_len(req
), query
->freq
);
132 res
= offchannel_send_action(gas
->wpa_s
, query
->freq
, query
->addr
,
133 gas
->wpa_s
->own_addr
, query
->addr
,
134 wpabuf_head(req
), wpabuf_len(req
), 1000,
137 query
->offchannel_tx_started
= 1;
142 static void gas_query_tx_comeback_req(struct gas_query
*gas
,
143 struct gas_query_pending
*query
)
147 req
= gas_build_comeback_req(query
->dialog_token
);
149 gas_query_done(gas
, query
, GAS_QUERY_INTERNAL_ERROR
);
153 if (gas_query_tx(gas
, query
, req
) < 0) {
154 wpa_printf(MSG_DEBUG
, "GAS: Failed to send Action frame to "
155 MACSTR
, MAC2STR(query
->addr
));
156 gas_query_done(gas
, query
, GAS_QUERY_INTERNAL_ERROR
);
163 static void gas_query_tx_comeback_timeout(void *eloop_data
, void *user_ctx
)
165 struct gas_query
*gas
= eloop_data
;
166 struct gas_query_pending
*query
= user_ctx
;
168 wpa_printf(MSG_DEBUG
, "GAS: Comeback timeout for request to " MACSTR
,
169 MAC2STR(query
->addr
));
170 gas_query_tx_comeback_req(gas
, query
);
174 static void gas_query_tx_comeback_req_delay(struct gas_query
*gas
,
175 struct gas_query_pending
*query
,
178 unsigned int secs
, usecs
;
180 secs
= (comeback_delay
* 1024) / 1000000;
181 usecs
= comeback_delay
* 1024 - secs
* 1000000;
182 wpa_printf(MSG_DEBUG
, "GAS: Send comeback request to " MACSTR
183 " in %u secs %u usecs", MAC2STR(query
->addr
), secs
, usecs
);
184 eloop_cancel_timeout(gas_query_tx_comeback_timeout
, gas
, query
);
185 eloop_register_timeout(secs
, usecs
, gas_query_tx_comeback_timeout
,
190 static void gas_query_rx_initial(struct gas_query
*gas
,
191 struct gas_query_pending
*query
,
192 const u8
*adv_proto
, const u8
*resp
,
193 size_t len
, u16 comeback_delay
)
195 wpa_printf(MSG_DEBUG
, "GAS: Received initial response from "
196 MACSTR
" (dialog_token=%u comeback_delay=%u)",
197 MAC2STR(query
->addr
), query
->dialog_token
, comeback_delay
);
199 query
->adv_proto
= wpabuf_alloc_copy(adv_proto
, 2 + adv_proto
[1]);
200 if (query
->adv_proto
== NULL
) {
201 gas_query_done(gas
, query
, GAS_QUERY_INTERNAL_ERROR
);
205 if (comeback_delay
) {
206 query
->wait_comeback
= 1;
207 gas_query_tx_comeback_req_delay(gas
, query
, comeback_delay
);
211 /* Query was completed without comeback mechanism */
212 if (gas_query_append(query
, resp
, len
) < 0) {
213 gas_query_done(gas
, query
, GAS_QUERY_INTERNAL_ERROR
);
217 gas_query_done(gas
, query
, GAS_QUERY_SUCCESS
);
221 static void gas_query_rx_comeback(struct gas_query
*gas
,
222 struct gas_query_pending
*query
,
223 const u8
*adv_proto
, const u8
*resp
,
224 size_t len
, u8 frag_id
, u8 more_frags
,
227 wpa_printf(MSG_DEBUG
, "GAS: Received comeback response from "
228 MACSTR
" (dialog_token=%u frag_id=%u more_frags=%u "
229 "comeback_delay=%u)",
230 MAC2STR(query
->addr
), query
->dialog_token
, frag_id
,
231 more_frags
, comeback_delay
);
233 if ((size_t) 2 + adv_proto
[1] != wpabuf_len(query
->adv_proto
) ||
234 os_memcmp(adv_proto
, wpabuf_head(query
->adv_proto
),
235 wpabuf_len(query
->adv_proto
)) != 0) {
236 wpa_printf(MSG_DEBUG
, "GAS: Advertisement Protocol changed "
237 "between initial and comeback response from "
238 MACSTR
, MAC2STR(query
->addr
));
239 gas_query_done(gas
, query
, GAS_QUERY_PEER_ERROR
);
243 if (comeback_delay
) {
245 wpa_printf(MSG_DEBUG
, "GAS: Invalid comeback response "
246 "with non-zero frag_id and comeback_delay "
247 "from " MACSTR
, MAC2STR(query
->addr
));
248 gas_query_done(gas
, query
, GAS_QUERY_PEER_ERROR
);
251 gas_query_tx_comeback_req_delay(gas
, query
, comeback_delay
);
255 if (frag_id
!= query
->next_frag_id
) {
256 wpa_printf(MSG_DEBUG
, "GAS: Unexpected frag_id in response "
257 "from " MACSTR
, MAC2STR(query
->addr
));
258 gas_query_done(gas
, query
, GAS_QUERY_PEER_ERROR
);
261 query
->next_frag_id
++;
263 if (gas_query_append(query
, resp
, len
) < 0) {
264 gas_query_done(gas
, query
, GAS_QUERY_INTERNAL_ERROR
);
269 gas_query_tx_comeback_req(gas
, query
);
273 gas_query_done(gas
, query
, GAS_QUERY_SUCCESS
);
277 int gas_query_rx(struct gas_query
*gas
, const u8
*da
, const u8
*sa
,
278 const u8
*bssid
, const u8
*data
, size_t len
, int freq
)
280 struct gas_query_pending
*query
;
281 u8 action
, dialog_token
, frag_id
= 0, more_frags
= 0;
282 u16 comeback_delay
, resp_len
;
283 const u8
*pos
, *adv_proto
;
285 if (gas
== NULL
|| len
< 4)
290 dialog_token
= *pos
++;
292 if (action
!= WLAN_PA_GAS_INITIAL_RESP
&&
293 action
!= WLAN_PA_GAS_COMEBACK_RESP
)
294 return -1; /* Not a GAS response */
296 query
= gas_query_get_pending(gas
, sa
, dialog_token
);
298 wpa_printf(MSG_DEBUG
, "GAS: No pending query found for " MACSTR
299 " dialog token %u", MAC2STR(sa
), dialog_token
);
303 if (query
->wait_comeback
&& action
== WLAN_PA_GAS_INITIAL_RESP
) {
304 wpa_printf(MSG_DEBUG
, "GAS: Unexpected initial response from "
305 MACSTR
" dialog token %u when waiting for comeback "
306 "response", MAC2STR(sa
), dialog_token
);
310 if (!query
->wait_comeback
&& action
== WLAN_PA_GAS_COMEBACK_RESP
) {
311 wpa_printf(MSG_DEBUG
, "GAS: Unexpected comeback response from "
312 MACSTR
" dialog token %u when waiting for initial "
313 "response", MAC2STR(sa
), dialog_token
);
317 query
->status_code
= WPA_GET_LE16(pos
);
320 if (query
->status_code
!= WLAN_STATUS_SUCCESS
) {
321 wpa_printf(MSG_DEBUG
, "GAS: Query to " MACSTR
" dialog token "
322 "%u failed - status code %u",
323 MAC2STR(sa
), dialog_token
, query
->status_code
);
324 gas_query_done(gas
, query
, GAS_QUERY_FAILURE
);
328 if (action
== WLAN_PA_GAS_COMEBACK_RESP
) {
329 if (pos
+ 1 > data
+ len
)
331 frag_id
= *pos
& 0x7f;
332 more_frags
= (*pos
& 0x80) >> 7;
337 if (pos
+ 2 > data
+ len
)
339 comeback_delay
= WPA_GET_LE16(pos
);
342 /* Advertisement Protocol element */
343 if (pos
+ 2 > data
+ len
|| pos
+ 2 + pos
[1] > data
+ len
) {
344 wpa_printf(MSG_DEBUG
, "GAS: No room for Advertisement "
345 "Protocol element in the response from " MACSTR
,
350 if (*pos
!= WLAN_EID_ADV_PROTO
) {
351 wpa_printf(MSG_DEBUG
, "GAS: Unexpected Advertisement "
352 "Protocol element ID %u in response from " MACSTR
,
360 /* Query Response Length */
361 if (pos
+ 2 > data
+ len
) {
362 wpa_printf(MSG_DEBUG
, "GAS: No room for GAS Response Length");
365 resp_len
= WPA_GET_LE16(pos
);
368 if (pos
+ resp_len
> data
+ len
) {
369 wpa_printf(MSG_DEBUG
, "GAS: Truncated Query Response in "
370 "response from " MACSTR
, MAC2STR(sa
));
374 if (pos
+ resp_len
< data
+ len
) {
375 wpa_printf(MSG_DEBUG
, "GAS: Ignore %u octets of extra data "
376 "after Query Response from " MACSTR
,
377 (unsigned int) (data
+ len
- pos
- resp_len
),
381 if (action
== WLAN_PA_GAS_COMEBACK_RESP
)
382 gas_query_rx_comeback(gas
, query
, adv_proto
, pos
, resp_len
,
383 frag_id
, more_frags
, comeback_delay
);
385 gas_query_rx_initial(gas
, query
, adv_proto
, pos
, resp_len
,
392 static void gas_query_timeout(void *eloop_data
, void *user_ctx
)
394 struct gas_query
*gas
= eloop_data
;
395 struct gas_query_pending
*query
= user_ctx
;
397 wpa_printf(MSG_DEBUG
, "GAS: No response received for query to " MACSTR
,
398 MAC2STR(query
->addr
));
399 gas_query_done(gas
, query
, GAS_QUERY_TIMEOUT
);
403 static int gas_query_dialog_token_available(struct gas_query
*gas
,
404 const u8
*dst
, u8 dialog_token
)
406 struct gas_query_pending
*q
;
407 dl_list_for_each(q
, &gas
->pending
, struct gas_query_pending
, list
) {
408 if (os_memcmp(dst
, q
->addr
, ETH_ALEN
) == 0 &&
409 dialog_token
== q
->dialog_token
)
417 int gas_query_req(struct gas_query
*gas
, const u8
*dst
, int freq
,
419 void (*cb
)(void *ctx
, const u8
*dst
, u8 dialog_token
,
420 enum gas_query_result result
,
421 const struct wpabuf
*adv_proto
,
422 const struct wpabuf
*resp
, u16 status_code
),
425 struct gas_query_pending
*query
;
428 if (wpabuf_len(req
) < 3)
431 for (dialog_token
= 0; dialog_token
< 256; dialog_token
++) {
432 if (gas_query_dialog_token_available(gas
, dst
, dialog_token
))
435 if (dialog_token
== 256)
436 return -1; /* Too many pending queries */
438 query
= os_zalloc(sizeof(*query
));
442 os_memcpy(query
->addr
, dst
, ETH_ALEN
);
443 query
->dialog_token
= dialog_token
;
447 dl_list_add(&gas
->pending
, &query
->list
);
449 *(wpabuf_mhead_u8(req
) + 2) = dialog_token
;
451 wpa_printf(MSG_DEBUG
, "GAS: Starting request for " MACSTR
452 " dialog_token %u", MAC2STR(dst
), dialog_token
);
453 if (gas_query_tx(gas
, query
, req
) < 0) {
454 wpa_printf(MSG_DEBUG
, "GAS: Failed to send Action frame to "
455 MACSTR
, MAC2STR(query
->addr
));
460 eloop_register_timeout(GAS_QUERY_TIMEOUT
, 0, gas_query_timeout
,
467 void gas_query_cancel(struct gas_query
*gas
, const u8
*dst
, u8 dialog_token
)
469 struct gas_query_pending
*query
;
471 query
= gas_query_get_pending(gas
, dst
, dialog_token
);
473 gas_query_done(gas
, query
, GAS_QUERY_CANCELLED
);