GAS: Reduce query timeout to two seconds
[mech_eap.git] / wpa_supplicant / gas_query.c
1 /*
2  * Generic advertisement service (GAS) query
3  * Copyright (c) 2009, Atheros Communications
4  * Copyright (c) 2011, Qualcomm Atheros
5  *
6  * This software may be distributed under the terms of the BSD license.
7  * See README for more details.
8  */
9
10 #include "includes.h"
11
12 #include "common.h"
13 #include "utils/eloop.h"
14 #include "common/ieee802_11_defs.h"
15 #include "common/gas.h"
16 #include "wpa_supplicant_i.h"
17 #include "driver_i.h"
18 #include "offchannel.h"
19 #include "gas_query.h"
20
21
22 /** GAS query timeout in seconds */
23 #define GAS_QUERY_TIMEOUT_PERIOD 2
24
25
26 /**
27  * struct gas_query_pending - Pending GAS query
28  */
29 struct gas_query_pending {
30         struct dl_list list;
31         u8 addr[ETH_ALEN];
32         u8 dialog_token;
33         u8 next_frag_id;
34         unsigned int wait_comeback:1;
35         unsigned int offchannel_tx_started:1;
36         int freq;
37         u16 status_code;
38         struct wpabuf *adv_proto;
39         struct wpabuf *resp;
40         void (*cb)(void *ctx, const u8 *dst, u8 dialog_token,
41                    enum gas_query_result result,
42                    const struct wpabuf *adv_proto,
43                    const struct wpabuf *resp, u16 status_code);
44         void *ctx;
45 };
46
47 /**
48  * struct gas_query - Internal GAS query data
49  */
50 struct gas_query {
51         struct wpa_supplicant *wpa_s;
52         struct dl_list pending; /* struct gas_query_pending */
53 };
54
55
56 static void gas_query_tx_comeback_timeout(void *eloop_data, void *user_ctx);
57 static void gas_query_timeout(void *eloop_data, void *user_ctx);
58
59
60 /**
61  * gas_query_init - Initialize GAS query component
62  * @wpa_s: Pointer to wpa_supplicant data
63  * Returns: Pointer to GAS query data or %NULL on failure
64  */
65 struct gas_query * gas_query_init(struct wpa_supplicant *wpa_s)
66 {
67         struct gas_query *gas;
68
69         gas = os_zalloc(sizeof(*gas));
70         if (gas == NULL)
71                 return NULL;
72
73         gas->wpa_s = wpa_s;
74         dl_list_init(&gas->pending);
75
76         return gas;
77 }
78
79
80 static void gas_query_done(struct gas_query *gas,
81                            struct gas_query_pending *query,
82                            enum gas_query_result result)
83 {
84         if (query->offchannel_tx_started)
85                 offchannel_send_action_done(gas->wpa_s);
86         eloop_cancel_timeout(gas_query_tx_comeback_timeout, gas, query);
87         eloop_cancel_timeout(gas_query_timeout, gas, query);
88         dl_list_del(&query->list);
89         query->cb(query->ctx, query->addr, query->dialog_token, result,
90                   query->adv_proto, query->resp, query->status_code);
91         wpabuf_free(query->adv_proto);
92         wpabuf_free(query->resp);
93         os_free(query);
94 }
95
96
97 /**
98  * gas_query_deinit - Deinitialize GAS query component
99  * @gas: GAS query data from gas_query_init()
100  */
101 void gas_query_deinit(struct gas_query *gas)
102 {
103         struct gas_query_pending *query, *next;
104
105         if (gas == NULL)
106                 return;
107
108         dl_list_for_each_safe(query, next, &gas->pending,
109                               struct gas_query_pending, list)
110                 gas_query_done(gas, query, GAS_QUERY_DELETED_AT_DEINIT);
111
112         os_free(gas);
113 }
114
115
116 static struct gas_query_pending *
117 gas_query_get_pending(struct gas_query *gas, const u8 *addr, u8 dialog_token)
118 {
119         struct gas_query_pending *q;
120         dl_list_for_each(q, &gas->pending, struct gas_query_pending, list) {
121                 if (os_memcmp(q->addr, addr, ETH_ALEN) == 0 &&
122                     q->dialog_token == dialog_token)
123                         return q;
124         }
125         return NULL;
126 }
127
128
129 static int gas_query_append(struct gas_query_pending *query, const u8 *data,
130                             size_t len)
131 {
132         if (wpabuf_resize(&query->resp, len) < 0) {
133                 wpa_printf(MSG_DEBUG, "GAS: No memory to store the response");
134                 return -1;
135         }
136         wpabuf_put_data(query->resp, data, len);
137         return 0;
138 }
139
140
141 static int gas_query_tx(struct gas_query *gas, struct gas_query_pending *query,
142                         struct wpabuf *req)
143 {
144         int res;
145         wpa_printf(MSG_DEBUG, "GAS: Send action frame to " MACSTR " len=%u "
146                    "freq=%d", MAC2STR(query->addr),
147                    (unsigned int) wpabuf_len(req), query->freq);
148         res = offchannel_send_action(gas->wpa_s, query->freq, query->addr,
149                                      gas->wpa_s->own_addr, query->addr,
150                                      wpabuf_head(req), wpabuf_len(req), 1000,
151                                      NULL, 0);
152         if (res == 0)
153                 query->offchannel_tx_started = 1;
154         return res;
155 }
156
157
158 static void gas_query_tx_comeback_req(struct gas_query *gas,
159                                       struct gas_query_pending *query)
160 {
161         struct wpabuf *req;
162
163         req = gas_build_comeback_req(query->dialog_token);
164         if (req == NULL) {
165                 gas_query_done(gas, query, GAS_QUERY_INTERNAL_ERROR);
166                 return;
167         }
168
169         if (gas_query_tx(gas, query, req) < 0) {
170                 wpa_printf(MSG_DEBUG, "GAS: Failed to send Action frame to "
171                            MACSTR, MAC2STR(query->addr));
172                 gas_query_done(gas, query, GAS_QUERY_INTERNAL_ERROR);
173         }
174
175         wpabuf_free(req);
176 }
177
178
179 static void gas_query_tx_comeback_timeout(void *eloop_data, void *user_ctx)
180 {
181         struct gas_query *gas = eloop_data;
182         struct gas_query_pending *query = user_ctx;
183
184         wpa_printf(MSG_DEBUG, "GAS: Comeback timeout for request to " MACSTR,
185                    MAC2STR(query->addr));
186         gas_query_tx_comeback_req(gas, query);
187 }
188
189
190 static void gas_query_tx_comeback_req_delay(struct gas_query *gas,
191                                             struct gas_query_pending *query,
192                                             u16 comeback_delay)
193 {
194         unsigned int secs, usecs;
195
196         secs = (comeback_delay * 1024) / 1000000;
197         usecs = comeback_delay * 1024 - secs * 1000000;
198         wpa_printf(MSG_DEBUG, "GAS: Send comeback request to " MACSTR
199                    " in %u secs %u usecs", MAC2STR(query->addr), secs, usecs);
200         eloop_cancel_timeout(gas_query_tx_comeback_timeout, gas, query);
201         eloop_register_timeout(secs, usecs, gas_query_tx_comeback_timeout,
202                                gas, query);
203 }
204
205
206 static void gas_query_rx_initial(struct gas_query *gas,
207                                  struct gas_query_pending *query,
208                                  const u8 *adv_proto, const u8 *resp,
209                                  size_t len, u16 comeback_delay)
210 {
211         wpa_printf(MSG_DEBUG, "GAS: Received initial response from "
212                    MACSTR " (dialog_token=%u comeback_delay=%u)",
213                    MAC2STR(query->addr), query->dialog_token, comeback_delay);
214
215         query->adv_proto = wpabuf_alloc_copy(adv_proto, 2 + adv_proto[1]);
216         if (query->adv_proto == NULL) {
217                 gas_query_done(gas, query, GAS_QUERY_INTERNAL_ERROR);
218                 return;
219         }
220
221         if (comeback_delay) {
222                 query->wait_comeback = 1;
223                 gas_query_tx_comeback_req_delay(gas, query, comeback_delay);
224                 return;
225         }
226
227         /* Query was completed without comeback mechanism */
228         if (gas_query_append(query, resp, len) < 0) {
229                 gas_query_done(gas, query, GAS_QUERY_INTERNAL_ERROR);
230                 return;
231         }
232
233         gas_query_done(gas, query, GAS_QUERY_SUCCESS);
234 }
235
236
237 static void gas_query_rx_comeback(struct gas_query *gas,
238                                   struct gas_query_pending *query,
239                                   const u8 *adv_proto, const u8 *resp,
240                                   size_t len, u8 frag_id, u8 more_frags,
241                                   u16 comeback_delay)
242 {
243         wpa_printf(MSG_DEBUG, "GAS: Received comeback response from "
244                    MACSTR " (dialog_token=%u frag_id=%u more_frags=%u "
245                    "comeback_delay=%u)",
246                    MAC2STR(query->addr), query->dialog_token, frag_id,
247                    more_frags, comeback_delay);
248
249         if ((size_t) 2 + adv_proto[1] != wpabuf_len(query->adv_proto) ||
250             os_memcmp(adv_proto, wpabuf_head(query->adv_proto),
251                       wpabuf_len(query->adv_proto)) != 0) {
252                 wpa_printf(MSG_DEBUG, "GAS: Advertisement Protocol changed "
253                            "between initial and comeback response from "
254                            MACSTR, MAC2STR(query->addr));
255                 gas_query_done(gas, query, GAS_QUERY_PEER_ERROR);
256                 return;
257         }
258
259         if (comeback_delay) {
260                 if (frag_id) {
261                         wpa_printf(MSG_DEBUG, "GAS: Invalid comeback response "
262                                    "with non-zero frag_id and comeback_delay "
263                                    "from " MACSTR, MAC2STR(query->addr));
264                         gas_query_done(gas, query, GAS_QUERY_PEER_ERROR);
265                         return;
266                 }
267                 gas_query_tx_comeback_req_delay(gas, query, comeback_delay);
268                 return;
269         }
270
271         if (frag_id != query->next_frag_id) {
272                 wpa_printf(MSG_DEBUG, "GAS: Unexpected frag_id in response "
273                            "from " MACSTR, MAC2STR(query->addr));
274                 if (frag_id + 1 == query->next_frag_id) {
275                         wpa_printf(MSG_DEBUG, "GAS: Drop frame as possible "
276                                    "retry of previous fragment");
277                         return;
278                 }
279                 gas_query_done(gas, query, GAS_QUERY_PEER_ERROR);
280                 return;
281         }
282         query->next_frag_id++;
283
284         if (gas_query_append(query, resp, len) < 0) {
285                 gas_query_done(gas, query, GAS_QUERY_INTERNAL_ERROR);
286                 return;
287         }
288
289         if (more_frags) {
290                 gas_query_tx_comeback_req(gas, query);
291                 return;
292         }
293
294         gas_query_done(gas, query, GAS_QUERY_SUCCESS);
295 }
296
297
298 /**
299  * gas_query_rx - Indicate reception of a Public Action frame
300  * @gas: GAS query data from gas_query_init()
301  * @da: Destination MAC address of the Action frame
302  * @sa: Source MAC address of the Action frame
303  * @bssid: BSSID of the Action frame
304  * @data: Payload of the Action frame
305  * @len: Length of @data
306  * @freq: Frequency (in MHz) on which the frame was received
307  * Returns: 0 if the Public Action frame was a GAS frame or -1 if not
308  */
309 int gas_query_rx(struct gas_query *gas, const u8 *da, const u8 *sa,
310                  const u8 *bssid, const u8 *data, size_t len, int freq)
311 {
312         struct gas_query_pending *query;
313         u8 action, dialog_token, frag_id = 0, more_frags = 0;
314         u16 comeback_delay, resp_len;
315         const u8 *pos, *adv_proto;
316
317         if (gas == NULL || len < 4)
318                 return -1;
319
320         pos = data;
321         action = *pos++;
322         dialog_token = *pos++;
323
324         if (action != WLAN_PA_GAS_INITIAL_RESP &&
325             action != WLAN_PA_GAS_COMEBACK_RESP)
326                 return -1; /* Not a GAS response */
327
328         query = gas_query_get_pending(gas, sa, dialog_token);
329         if (query == NULL) {
330                 wpa_printf(MSG_DEBUG, "GAS: No pending query found for " MACSTR
331                            " dialog token %u", MAC2STR(sa), dialog_token);
332                 return -1;
333         }
334
335         if (query->wait_comeback && action == WLAN_PA_GAS_INITIAL_RESP) {
336                 wpa_printf(MSG_DEBUG, "GAS: Unexpected initial response from "
337                            MACSTR " dialog token %u when waiting for comeback "
338                            "response", MAC2STR(sa), dialog_token);
339                 return 0;
340         }
341
342         if (!query->wait_comeback && action == WLAN_PA_GAS_COMEBACK_RESP) {
343                 wpa_printf(MSG_DEBUG, "GAS: Unexpected comeback response from "
344                            MACSTR " dialog token %u when waiting for initial "
345                            "response", MAC2STR(sa), dialog_token);
346                 return 0;
347         }
348
349         query->status_code = WPA_GET_LE16(pos);
350         pos += 2;
351
352         if (query->status_code != WLAN_STATUS_SUCCESS) {
353                 wpa_printf(MSG_DEBUG, "GAS: Query to " MACSTR " dialog token "
354                            "%u failed - status code %u",
355                            MAC2STR(sa), dialog_token, query->status_code);
356                 gas_query_done(gas, query, GAS_QUERY_FAILURE);
357                 return 0;
358         }
359
360         if (action == WLAN_PA_GAS_COMEBACK_RESP) {
361                 if (pos + 1 > data + len)
362                         return 0;
363                 frag_id = *pos & 0x7f;
364                 more_frags = (*pos & 0x80) >> 7;
365                 pos++;
366         }
367
368         /* Comeback Delay */
369         if (pos + 2 > data + len)
370                 return 0;
371         comeback_delay = WPA_GET_LE16(pos);
372         pos += 2;
373
374         /* Advertisement Protocol element */
375         if (pos + 2 > data + len || pos + 2 + pos[1] > data + len) {
376                 wpa_printf(MSG_DEBUG, "GAS: No room for Advertisement "
377                            "Protocol element in the response from " MACSTR,
378                            MAC2STR(sa));
379                 return 0;
380         }
381
382         if (*pos != WLAN_EID_ADV_PROTO) {
383                 wpa_printf(MSG_DEBUG, "GAS: Unexpected Advertisement "
384                            "Protocol element ID %u in response from " MACSTR,
385                            *pos, MAC2STR(sa));
386                 return 0;
387         }
388
389         adv_proto = pos;
390         pos += 2 + pos[1];
391
392         /* Query Response Length */
393         if (pos + 2 > data + len) {
394                 wpa_printf(MSG_DEBUG, "GAS: No room for GAS Response Length");
395                 return 0;
396         }
397         resp_len = WPA_GET_LE16(pos);
398         pos += 2;
399
400         if (pos + resp_len > data + len) {
401                 wpa_printf(MSG_DEBUG, "GAS: Truncated Query Response in "
402                            "response from " MACSTR, MAC2STR(sa));
403                 return 0;
404         }
405
406         if (pos + resp_len < data + len) {
407                 wpa_printf(MSG_DEBUG, "GAS: Ignore %u octets of extra data "
408                            "after Query Response from " MACSTR,
409                            (unsigned int) (data + len - pos - resp_len),
410                            MAC2STR(sa));
411         }
412
413         if (action == WLAN_PA_GAS_COMEBACK_RESP)
414                 gas_query_rx_comeback(gas, query, adv_proto, pos, resp_len,
415                                       frag_id, more_frags, comeback_delay);
416         else
417                 gas_query_rx_initial(gas, query, adv_proto, pos, resp_len,
418                                      comeback_delay);
419
420         return 0;
421 }
422
423
424 static void gas_query_timeout(void *eloop_data, void *user_ctx)
425 {
426         struct gas_query *gas = eloop_data;
427         struct gas_query_pending *query = user_ctx;
428
429         wpa_printf(MSG_DEBUG, "GAS: No response received for query to " MACSTR,
430                    MAC2STR(query->addr));
431         gas_query_done(gas, query, GAS_QUERY_TIMEOUT);
432 }
433
434
435 static int gas_query_dialog_token_available(struct gas_query *gas,
436                                             const u8 *dst, u8 dialog_token)
437 {
438         struct gas_query_pending *q;
439         dl_list_for_each(q, &gas->pending, struct gas_query_pending, list) {
440                 if (os_memcmp(dst, q->addr, ETH_ALEN) == 0 &&
441                     dialog_token == q->dialog_token)
442                         return 0;
443         }
444
445         return 1;
446 }
447
448
449 /**
450  * gas_query_req - Request a GAS query
451  * @gas: GAS query data from gas_query_init()
452  * @dst: Destination MAC address for the query
453  * @freq: Frequency (in MHz) for the channel on which to send the query
454  * @req: GAS query payload
455  * @cb: Callback function for reporting GAS query result and response
456  * @ctx: Context pointer to use with the @cb call
457  * Returns: dialog token (>= 0) on success or -1 on failure
458  */
459 int gas_query_req(struct gas_query *gas, const u8 *dst, int freq,
460                   struct wpabuf *req,
461                   void (*cb)(void *ctx, const u8 *dst, u8 dialog_token,
462                              enum gas_query_result result,
463                              const struct wpabuf *adv_proto,
464                              const struct wpabuf *resp, u16 status_code),
465                   void *ctx)
466 {
467         struct gas_query_pending *query;
468         int dialog_token;
469         static int next_start = 0;
470
471         if (wpabuf_len(req) < 3)
472                 return -1;
473
474         for (dialog_token = 0; dialog_token < 256; dialog_token++) {
475                 if (gas_query_dialog_token_available(
476                             gas, dst, (next_start + dialog_token) % 256))
477                         break;
478         }
479         if (dialog_token == 256)
480                 return -1; /* Too many pending queries */
481         dialog_token = (next_start + dialog_token) % 256;
482         next_start = (dialog_token + 1) % 256;
483
484         query = os_zalloc(sizeof(*query));
485         if (query == NULL)
486                 return -1;
487
488         os_memcpy(query->addr, dst, ETH_ALEN);
489         query->dialog_token = dialog_token;
490         query->freq = freq;
491         query->cb = cb;
492         query->ctx = ctx;
493         dl_list_add(&gas->pending, &query->list);
494
495         *(wpabuf_mhead_u8(req) + 2) = dialog_token;
496
497         wpa_printf(MSG_DEBUG, "GAS: Starting request for " MACSTR
498                    " dialog_token %u", MAC2STR(dst), dialog_token);
499         if (gas_query_tx(gas, query, req) < 0) {
500                 wpa_printf(MSG_DEBUG, "GAS: Failed to send Action frame to "
501                            MACSTR, MAC2STR(query->addr));
502                 dl_list_del(&query->list);
503                 os_free(query);
504                 return -1;
505         }
506
507         eloop_register_timeout(GAS_QUERY_TIMEOUT_PERIOD, 0, gas_query_timeout,
508                                gas, query);
509
510         return dialog_token;
511 }
512
513
514 /**
515  * gas_query_cancel - Cancel a pending GAS query
516  * @gas: GAS query data from gas_query_init()
517  * @dst: Destination MAC address for the query
518  * @dialog_token: Dialog token from gas_query_req()
519  */
520 void gas_query_cancel(struct gas_query *gas, const u8 *dst, u8 dialog_token)
521 {
522         struct gas_query_pending *query;
523
524         query = gas_query_get_pending(gas, dst, dialog_token);
525         if (query)
526                 gas_query_done(gas, query, GAS_QUERY_CANCELLED);
527
528 }