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