]> CyberLeo.Net >> Repos - FreeBSD/releng/10.0.git/blob - contrib/wpa/wpa_supplicant/gas_query.c
- Copy stable/10 (r259064) to releng/10.0 as part of the
[FreeBSD/releng/10.0.git] / contrib / wpa / 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 5
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                 gas_query_done(gas, query, GAS_QUERY_PEER_ERROR);
275                 return;
276         }
277         query->next_frag_id++;
278
279         if (gas_query_append(query, resp, len) < 0) {
280                 gas_query_done(gas, query, GAS_QUERY_INTERNAL_ERROR);
281                 return;
282         }
283
284         if (more_frags) {
285                 gas_query_tx_comeback_req(gas, query);
286                 return;
287         }
288
289         gas_query_done(gas, query, GAS_QUERY_SUCCESS);
290 }
291
292
293 /**
294  * gas_query_rx - Indicate reception of a Public Action frame
295  * @gas: GAS query data from gas_query_init()
296  * @da: Destination MAC address of the Action frame
297  * @sa: Source MAC address of the Action frame
298  * @bssid: BSSID of the Action frame
299  * @data: Payload of the Action frame
300  * @len: Length of @data
301  * @freq: Frequency (in MHz) on which the frame was received
302  * Returns: 0 if the Public Action frame was a GAS frame or -1 if not
303  */
304 int gas_query_rx(struct gas_query *gas, const u8 *da, const u8 *sa,
305                  const u8 *bssid, const u8 *data, size_t len, int freq)
306 {
307         struct gas_query_pending *query;
308         u8 action, dialog_token, frag_id = 0, more_frags = 0;
309         u16 comeback_delay, resp_len;
310         const u8 *pos, *adv_proto;
311
312         if (gas == NULL || len < 4)
313                 return -1;
314
315         pos = data;
316         action = *pos++;
317         dialog_token = *pos++;
318
319         if (action != WLAN_PA_GAS_INITIAL_RESP &&
320             action != WLAN_PA_GAS_COMEBACK_RESP)
321                 return -1; /* Not a GAS response */
322
323         query = gas_query_get_pending(gas, sa, dialog_token);
324         if (query == NULL) {
325                 wpa_printf(MSG_DEBUG, "GAS: No pending query found for " MACSTR
326                            " dialog token %u", MAC2STR(sa), dialog_token);
327                 return -1;
328         }
329
330         if (query->wait_comeback && action == WLAN_PA_GAS_INITIAL_RESP) {
331                 wpa_printf(MSG_DEBUG, "GAS: Unexpected initial response from "
332                            MACSTR " dialog token %u when waiting for comeback "
333                            "response", MAC2STR(sa), dialog_token);
334                 return 0;
335         }
336
337         if (!query->wait_comeback && action == WLAN_PA_GAS_COMEBACK_RESP) {
338                 wpa_printf(MSG_DEBUG, "GAS: Unexpected comeback response from "
339                            MACSTR " dialog token %u when waiting for initial "
340                            "response", MAC2STR(sa), dialog_token);
341                 return 0;
342         }
343
344         query->status_code = WPA_GET_LE16(pos);
345         pos += 2;
346
347         if (query->status_code != WLAN_STATUS_SUCCESS) {
348                 wpa_printf(MSG_DEBUG, "GAS: Query to " MACSTR " dialog token "
349                            "%u failed - status code %u",
350                            MAC2STR(sa), dialog_token, query->status_code);
351                 gas_query_done(gas, query, GAS_QUERY_FAILURE);
352                 return 0;
353         }
354
355         if (action == WLAN_PA_GAS_COMEBACK_RESP) {
356                 if (pos + 1 > data + len)
357                         return 0;
358                 frag_id = *pos & 0x7f;
359                 more_frags = (*pos & 0x80) >> 7;
360                 pos++;
361         }
362
363         /* Comeback Delay */
364         if (pos + 2 > data + len)
365                 return 0;
366         comeback_delay = WPA_GET_LE16(pos);
367         pos += 2;
368
369         /* Advertisement Protocol element */
370         if (pos + 2 > data + len || pos + 2 + pos[1] > data + len) {
371                 wpa_printf(MSG_DEBUG, "GAS: No room for Advertisement "
372                            "Protocol element in the response from " MACSTR,
373                            MAC2STR(sa));
374                 return 0;
375         }
376
377         if (*pos != WLAN_EID_ADV_PROTO) {
378                 wpa_printf(MSG_DEBUG, "GAS: Unexpected Advertisement "
379                            "Protocol element ID %u in response from " MACSTR,
380                            *pos, MAC2STR(sa));
381                 return 0;
382         }
383
384         adv_proto = pos;
385         pos += 2 + pos[1];
386
387         /* Query Response Length */
388         if (pos + 2 > data + len) {
389                 wpa_printf(MSG_DEBUG, "GAS: No room for GAS Response Length");
390                 return 0;
391         }
392         resp_len = WPA_GET_LE16(pos);
393         pos += 2;
394
395         if (pos + resp_len > data + len) {
396                 wpa_printf(MSG_DEBUG, "GAS: Truncated Query Response in "
397                            "response from " MACSTR, MAC2STR(sa));
398                 return 0;
399         }
400
401         if (pos + resp_len < data + len) {
402                 wpa_printf(MSG_DEBUG, "GAS: Ignore %u octets of extra data "
403                            "after Query Response from " MACSTR,
404                            (unsigned int) (data + len - pos - resp_len),
405                            MAC2STR(sa));
406         }
407
408         if (action == WLAN_PA_GAS_COMEBACK_RESP)
409                 gas_query_rx_comeback(gas, query, adv_proto, pos, resp_len,
410                                       frag_id, more_frags, comeback_delay);
411         else
412                 gas_query_rx_initial(gas, query, adv_proto, pos, resp_len,
413                                      comeback_delay);
414
415         return 0;
416 }
417
418
419 static void gas_query_timeout(void *eloop_data, void *user_ctx)
420 {
421         struct gas_query *gas = eloop_data;
422         struct gas_query_pending *query = user_ctx;
423
424         wpa_printf(MSG_DEBUG, "GAS: No response received for query to " MACSTR,
425                    MAC2STR(query->addr));
426         gas_query_done(gas, query, GAS_QUERY_TIMEOUT);
427 }
428
429
430 static int gas_query_dialog_token_available(struct gas_query *gas,
431                                             const u8 *dst, u8 dialog_token)
432 {
433         struct gas_query_pending *q;
434         dl_list_for_each(q, &gas->pending, struct gas_query_pending, list) {
435                 if (os_memcmp(dst, q->addr, ETH_ALEN) == 0 &&
436                     dialog_token == q->dialog_token)
437                         return 0;
438         }
439
440         return 1;
441 }
442
443
444 /**
445  * gas_query_req - Request a GAS query
446  * @gas: GAS query data from gas_query_init()
447  * @dst: Destination MAC address for the query
448  * @freq: Frequency (in MHz) for the channel on which to send the query
449  * @req: GAS query payload
450  * @cb: Callback function for reporting GAS query result and response
451  * @ctx: Context pointer to use with the @cb call
452  * Returns: dialog token (>= 0) on success or -1 on failure
453  */
454 int gas_query_req(struct gas_query *gas, const u8 *dst, int freq,
455                   struct wpabuf *req,
456                   void (*cb)(void *ctx, const u8 *dst, u8 dialog_token,
457                              enum gas_query_result result,
458                              const struct wpabuf *adv_proto,
459                              const struct wpabuf *resp, u16 status_code),
460                   void *ctx)
461 {
462         struct gas_query_pending *query;
463         int dialog_token;
464
465         if (wpabuf_len(req) < 3)
466                 return -1;
467
468         for (dialog_token = 0; dialog_token < 256; dialog_token++) {
469                 if (gas_query_dialog_token_available(gas, dst, dialog_token))
470                         break;
471         }
472         if (dialog_token == 256)
473                 return -1; /* Too many pending queries */
474
475         query = os_zalloc(sizeof(*query));
476         if (query == NULL)
477                 return -1;
478
479         os_memcpy(query->addr, dst, ETH_ALEN);
480         query->dialog_token = dialog_token;
481         query->freq = freq;
482         query->cb = cb;
483         query->ctx = ctx;
484         dl_list_add(&gas->pending, &query->list);
485
486         *(wpabuf_mhead_u8(req) + 2) = dialog_token;
487
488         wpa_printf(MSG_DEBUG, "GAS: Starting request for " MACSTR
489                    " dialog_token %u", MAC2STR(dst), dialog_token);
490         if (gas_query_tx(gas, query, req) < 0) {
491                 wpa_printf(MSG_DEBUG, "GAS: Failed to send Action frame to "
492                            MACSTR, MAC2STR(query->addr));
493                 dl_list_del(&query->list);
494                 os_free(query);
495                 return -1;
496         }
497
498         eloop_register_timeout(GAS_QUERY_TIMEOUT_PERIOD, 0, gas_query_timeout,
499                                gas, query);
500
501         return dialog_token;
502 }
503
504
505 /**
506  * gas_query_cancel - Cancel a pending GAS query
507  * @gas: GAS query data from gas_query_init()
508  * @dst: Destination MAC address for the query
509  * @dialog_token: Dialog token from gas_query_req()
510  */
511 void gas_query_cancel(struct gas_query *gas, const u8 *dst, u8 dialog_token)
512 {
513         struct gas_query_pending *query;
514
515         query = gas_query_get_pending(gas, dst, dialog_token);
516         if (query)
517                 gas_query_done(gas, query, GAS_QUERY_CANCELLED);
518
519 }