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