GAS: Fix reporting of GAS query timeouts
[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 #define GAS_QUERY_TIMEOUT_PERIOD 5
23
24
25 struct gas_query_pending {
26         struct dl_list list;
27         u8 addr[ETH_ALEN];
28         u8 dialog_token;
29         u8 next_frag_id;
30         unsigned int wait_comeback:1;
31         unsigned int offchannel_tx_started:1;
32         int freq;
33         u16 status_code;
34         struct wpabuf *adv_proto;
35         struct wpabuf *resp;
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);
40         void *ctx;
41 };
42
43 struct gas_query {
44         struct wpa_supplicant *wpa_s;
45         struct dl_list pending; /* struct gas_query_pending */
46 };
47
48
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);
51
52
53 struct gas_query * gas_query_init(struct wpa_supplicant *wpa_s)
54 {
55         struct gas_query *gas;
56
57         gas = os_zalloc(sizeof(*gas));
58         if (gas == NULL)
59                 return NULL;
60
61         gas->wpa_s = wpa_s;
62         dl_list_init(&gas->pending);
63
64         return gas;
65 }
66
67
68 static void gas_query_done(struct gas_query *gas,
69                            struct gas_query_pending *query,
70                            enum gas_query_result result)
71 {
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);
81         os_free(query);
82 }
83
84
85 void gas_query_deinit(struct gas_query *gas)
86 {
87         struct gas_query_pending *query, *next;
88
89         if (gas == NULL)
90                 return;
91
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);
95
96         os_free(gas);
97 }
98
99
100 static struct gas_query_pending *
101 gas_query_get_pending(struct gas_query *gas, const u8 *addr, u8 dialog_token)
102 {
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)
107                         return q;
108         }
109         return NULL;
110 }
111
112
113 static int gas_query_append(struct gas_query_pending *query, const u8 *data,
114                             size_t len)
115 {
116         if (wpabuf_resize(&query->resp, len) < 0) {
117                 wpa_printf(MSG_DEBUG, "GAS: No memory to store the response");
118                 return -1;
119         }
120         wpabuf_put_data(query->resp, data, len);
121         return 0;
122 }
123
124
125 static int gas_query_tx(struct gas_query *gas, struct gas_query_pending *query,
126                         struct wpabuf *req)
127 {
128         int res;
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,
135                                      NULL, 0);
136         if (res == 0)
137                 query->offchannel_tx_started = 1;
138         return res;
139 }
140
141
142 static void gas_query_tx_comeback_req(struct gas_query *gas,
143                                       struct gas_query_pending *query)
144 {
145         struct wpabuf *req;
146
147         req = gas_build_comeback_req(query->dialog_token);
148         if (req == NULL) {
149                 gas_query_done(gas, query, GAS_QUERY_INTERNAL_ERROR);
150                 return;
151         }
152
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);
157         }
158
159         wpabuf_free(req);
160 }
161
162
163 static void gas_query_tx_comeback_timeout(void *eloop_data, void *user_ctx)
164 {
165         struct gas_query *gas = eloop_data;
166         struct gas_query_pending *query = user_ctx;
167
168         wpa_printf(MSG_DEBUG, "GAS: Comeback timeout for request to " MACSTR,
169                    MAC2STR(query->addr));
170         gas_query_tx_comeback_req(gas, query);
171 }
172
173
174 static void gas_query_tx_comeback_req_delay(struct gas_query *gas,
175                                             struct gas_query_pending *query,
176                                             u16 comeback_delay)
177 {
178         unsigned int secs, usecs;
179
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,
186                                gas, query);
187 }
188
189
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)
194 {
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);
198
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);
202                 return;
203         }
204
205         if (comeback_delay) {
206                 query->wait_comeback = 1;
207                 gas_query_tx_comeback_req_delay(gas, query, comeback_delay);
208                 return;
209         }
210
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);
214                 return;
215         }
216
217         gas_query_done(gas, query, GAS_QUERY_SUCCESS);
218 }
219
220
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,
225                                   u16 comeback_delay)
226 {
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);
232
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);
240                 return;
241         }
242
243         if (comeback_delay) {
244                 if (frag_id) {
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);
249                         return;
250                 }
251                 gas_query_tx_comeback_req_delay(gas, query, comeback_delay);
252                 return;
253         }
254
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);
259                 return;
260         }
261         query->next_frag_id++;
262
263         if (gas_query_append(query, resp, len) < 0) {
264                 gas_query_done(gas, query, GAS_QUERY_INTERNAL_ERROR);
265                 return;
266         }
267
268         if (more_frags) {
269                 gas_query_tx_comeback_req(gas, query);
270                 return;
271         }
272
273         gas_query_done(gas, query, GAS_QUERY_SUCCESS);
274 }
275
276
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)
279 {
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;
284
285         if (gas == NULL || len < 4)
286                 return -1;
287
288         pos = data;
289         action = *pos++;
290         dialog_token = *pos++;
291
292         if (action != WLAN_PA_GAS_INITIAL_RESP &&
293             action != WLAN_PA_GAS_COMEBACK_RESP)
294                 return -1; /* Not a GAS response */
295
296         query = gas_query_get_pending(gas, sa, dialog_token);
297         if (query == NULL) {
298                 wpa_printf(MSG_DEBUG, "GAS: No pending query found for " MACSTR
299                            " dialog token %u", MAC2STR(sa), dialog_token);
300                 return -1;
301         }
302
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);
307                 return 0;
308         }
309
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);
314                 return 0;
315         }
316
317         query->status_code = WPA_GET_LE16(pos);
318         pos += 2;
319
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);
325                 return 0;
326         }
327
328         if (action == WLAN_PA_GAS_COMEBACK_RESP) {
329                 if (pos + 1 > data + len)
330                         return 0;
331                 frag_id = *pos & 0x7f;
332                 more_frags = (*pos & 0x80) >> 7;
333                 pos++;
334         }
335
336         /* Comeback Delay */
337         if (pos + 2 > data + len)
338                 return 0;
339         comeback_delay = WPA_GET_LE16(pos);
340         pos += 2;
341
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,
346                            MAC2STR(sa));
347                 return 0;
348         }
349
350         if (*pos != WLAN_EID_ADV_PROTO) {
351                 wpa_printf(MSG_DEBUG, "GAS: Unexpected Advertisement "
352                            "Protocol element ID %u in response from " MACSTR,
353                            *pos, MAC2STR(sa));
354                 return 0;
355         }
356
357         adv_proto = pos;
358         pos += 2 + pos[1];
359
360         /* Query Response Length */
361         if (pos + 2 > data + len) {
362                 wpa_printf(MSG_DEBUG, "GAS: No room for GAS Response Length");
363                 return 0;
364         }
365         resp_len = WPA_GET_LE16(pos);
366         pos += 2;
367
368         if (pos + resp_len > data + len) {
369                 wpa_printf(MSG_DEBUG, "GAS: Truncated Query Response in "
370                            "response from " MACSTR, MAC2STR(sa));
371                 return 0;
372         }
373
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),
378                            MAC2STR(sa));
379         }
380
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);
384         else
385                 gas_query_rx_initial(gas, query, adv_proto, pos, resp_len,
386                                      comeback_delay);
387
388         return 0;
389 }
390
391
392 static void gas_query_timeout(void *eloop_data, void *user_ctx)
393 {
394         struct gas_query *gas = eloop_data;
395         struct gas_query_pending *query = user_ctx;
396
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);
400 }
401
402
403 static int gas_query_dialog_token_available(struct gas_query *gas,
404                                             const u8 *dst, u8 dialog_token)
405 {
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)
410                         return 0;
411         }
412
413         return 1;
414 }
415
416
417 int gas_query_req(struct gas_query *gas, const u8 *dst, int freq,
418                   struct wpabuf *req,
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),
423                   void *ctx)
424 {
425         struct gas_query_pending *query;
426         int dialog_token;
427
428         if (wpabuf_len(req) < 3)
429                 return -1;
430
431         for (dialog_token = 0; dialog_token < 256; dialog_token++) {
432                 if (gas_query_dialog_token_available(gas, dst, dialog_token))
433                         break;
434         }
435         if (dialog_token == 256)
436                 return -1; /* Too many pending queries */
437
438         query = os_zalloc(sizeof(*query));
439         if (query == NULL)
440                 return -1;
441
442         os_memcpy(query->addr, dst, ETH_ALEN);
443         query->dialog_token = dialog_token;
444         query->freq = freq;
445         query->cb = cb;
446         query->ctx = ctx;
447         dl_list_add(&gas->pending, &query->list);
448
449         *(wpabuf_mhead_u8(req) + 2) = dialog_token;
450
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));
456                 os_free(query);
457                 return -1;
458         }
459
460         eloop_register_timeout(GAS_QUERY_TIMEOUT_PERIOD, 0, gas_query_timeout,
461                                gas, query);
462
463         return dialog_token;
464 }
465
466
467 void gas_query_cancel(struct gas_query *gas, const u8 *dst, u8 dialog_token)
468 {
469         struct gas_query_pending *query;
470
471         query = gas_query_get_pending(gas, dst, dialog_token);
472         if (query)
473                 gas_query_done(gas, query, GAS_QUERY_CANCELLED);
474
475 }