blob: 53e6cbbe405da19f1d3b4c39fdc7db703a33b60e [file] [log] [blame]
Dmitry Shmidt04949592012-07-19 12:16:46 -07001/*
2 * Generic advertisement service (GAS) server
3 * Copyright (c) 2011-2012, Qualcomm Atheros, Inc.
4 *
5 * This software may be distributed under the terms of the BSD license.
6 * See README for more details.
7 */
8
9#include "includes.h"
10
11#include "common.h"
12#include "common/ieee802_11_defs.h"
13#include "common/gas.h"
14#include "utils/eloop.h"
15#include "hostapd.h"
16#include "ap_config.h"
17#include "ap_drv_ops.h"
18#include "sta_info.h"
19#include "gas_serv.h"
20
21
22static struct gas_dialog_info *
23gas_dialog_create(struct hostapd_data *hapd, const u8 *addr, u8 dialog_token)
24{
25 struct sta_info *sta;
26 struct gas_dialog_info *dia = NULL;
27 int i, j;
28
29 sta = ap_get_sta(hapd, addr);
30 if (!sta) {
31 /*
32 * We need a STA entry to be able to maintain state for
33 * the GAS query.
34 */
35 wpa_printf(MSG_DEBUG, "ANQP: Add a temporary STA entry for "
36 "GAS query");
37 sta = ap_sta_add(hapd, addr);
38 if (!sta) {
39 wpa_printf(MSG_DEBUG, "Failed to add STA " MACSTR
40 " for GAS query", MAC2STR(addr));
41 return NULL;
42 }
43 sta->flags |= WLAN_STA_GAS;
44 /*
45 * The default inactivity is 300 seconds. We don't need
46 * it to be that long.
47 */
48 ap_sta_session_timeout(hapd, sta, 5);
49 }
50
51 if (sta->gas_dialog == NULL) {
52 sta->gas_dialog = os_zalloc(GAS_DIALOG_MAX *
53 sizeof(struct gas_dialog_info));
54 if (sta->gas_dialog == NULL)
55 return NULL;
56 }
57
58 for (i = sta->gas_dialog_next, j = 0; j < GAS_DIALOG_MAX; i++, j++) {
59 if (i == GAS_DIALOG_MAX)
60 i = 0;
61 if (sta->gas_dialog[i].valid)
62 continue;
63 dia = &sta->gas_dialog[i];
64 dia->valid = 1;
65 dia->index = i;
66 dia->dialog_token = dialog_token;
67 sta->gas_dialog_next = (++i == GAS_DIALOG_MAX) ? 0 : i;
68 return dia;
69 }
70
71 wpa_msg(hapd->msg_ctx, MSG_ERROR, "ANQP: Could not create dialog for "
72 MACSTR " dialog_token %u. Consider increasing "
73 "GAS_DIALOG_MAX.", MAC2STR(addr), dialog_token);
74
75 return NULL;
76}
77
78
79struct gas_dialog_info *
80gas_serv_dialog_find(struct hostapd_data *hapd, const u8 *addr,
81 u8 dialog_token)
82{
83 struct sta_info *sta;
84 int i;
85
86 sta = ap_get_sta(hapd, addr);
87 if (!sta) {
88 wpa_printf(MSG_DEBUG, "ANQP: could not find STA " MACSTR,
89 MAC2STR(addr));
90 return NULL;
91 }
92 for (i = 0; sta->gas_dialog && i < GAS_DIALOG_MAX; i++) {
93 if (sta->gas_dialog[i].dialog_token != dialog_token ||
94 !sta->gas_dialog[i].valid)
95 continue;
96 return &sta->gas_dialog[i];
97 }
98 wpa_printf(MSG_DEBUG, "ANQP: Could not find dialog for "
99 MACSTR " dialog_token %u", MAC2STR(addr), dialog_token);
100 return NULL;
101}
102
103
104void gas_serv_dialog_clear(struct gas_dialog_info *dia)
105{
106 wpabuf_free(dia->sd_resp);
107 os_memset(dia, 0, sizeof(*dia));
108}
109
110
111static void gas_serv_free_dialogs(struct hostapd_data *hapd,
112 const u8 *sta_addr)
113{
114 struct sta_info *sta;
115 int i;
116
117 sta = ap_get_sta(hapd, sta_addr);
118 if (sta == NULL || sta->gas_dialog == NULL)
119 return;
120
121 for (i = 0; i < GAS_DIALOG_MAX; i++) {
122 if (sta->gas_dialog[i].valid)
123 return;
124 }
125
126 os_free(sta->gas_dialog);
127 sta->gas_dialog = NULL;
128}
129
130
Dmitry Shmidt61d9df32012-08-29 16:22:06 -0700131static void anqp_add_hs_capab_list(struct hostapd_data *hapd,
132 struct wpabuf *buf)
133{
134 u8 *len;
135
136 len = gas_anqp_add_element(buf, ANQP_VENDOR_SPECIFIC);
137 wpabuf_put_be24(buf, OUI_WFA);
138 wpabuf_put_u8(buf, HS20_ANQP_OUI_TYPE);
139 wpabuf_put_u8(buf, HS20_STYPE_CAPABILITY_LIST);
140 wpabuf_put_u8(buf, 0); /* Reserved */
141 wpabuf_put_u8(buf, HS20_STYPE_CAPABILITY_LIST);
142 if (hapd->conf->hs20_oper_friendly_name)
143 wpabuf_put_u8(buf, HS20_STYPE_OPERATOR_FRIENDLY_NAME);
144 if (hapd->conf->hs20_wan_metrics)
145 wpabuf_put_u8(buf, HS20_STYPE_WAN_METRICS);
146 if (hapd->conf->hs20_connection_capability)
147 wpabuf_put_u8(buf, HS20_STYPE_CONNECTION_CAPABILITY);
148 if (hapd->conf->nai_realm_data)
149 wpabuf_put_u8(buf, HS20_STYPE_NAI_HOME_REALM_QUERY);
150 if (hapd->conf->hs20_operating_class)
151 wpabuf_put_u8(buf, HS20_STYPE_OPERATING_CLASS);
152 gas_anqp_set_element_len(buf, len);
153}
154
155
Dmitry Shmidt04949592012-07-19 12:16:46 -0700156static void anqp_add_capab_list(struct hostapd_data *hapd,
157 struct wpabuf *buf)
158{
159 u8 *len;
160
161 len = gas_anqp_add_element(buf, ANQP_CAPABILITY_LIST);
162 wpabuf_put_le16(buf, ANQP_CAPABILITY_LIST);
163 if (hapd->conf->venue_name)
164 wpabuf_put_le16(buf, ANQP_VENUE_NAME);
Dmitry Shmidt61d9df32012-08-29 16:22:06 -0700165 if (hapd->conf->network_auth_type)
166 wpabuf_put_le16(buf, ANQP_NETWORK_AUTH_TYPE);
Dmitry Shmidt04949592012-07-19 12:16:46 -0700167 if (hapd->conf->roaming_consortium)
168 wpabuf_put_le16(buf, ANQP_ROAMING_CONSORTIUM);
Dmitry Shmidt61d9df32012-08-29 16:22:06 -0700169 if (hapd->conf->ipaddr_type_configured)
170 wpabuf_put_le16(buf, ANQP_IP_ADDR_TYPE_AVAILABILITY);
171 if (hapd->conf->nai_realm_data)
172 wpabuf_put_le16(buf, ANQP_NAI_REALM);
173 if (hapd->conf->anqp_3gpp_cell_net)
174 wpabuf_put_le16(buf, ANQP_3GPP_CELLULAR_NETWORK);
175 if (hapd->conf->domain_name)
176 wpabuf_put_le16(buf, ANQP_DOMAIN_NAME);
177 anqp_add_hs_capab_list(hapd, buf);
Dmitry Shmidt04949592012-07-19 12:16:46 -0700178 gas_anqp_set_element_len(buf, len);
179}
180
181
182static void anqp_add_venue_name(struct hostapd_data *hapd, struct wpabuf *buf)
183{
184 if (hapd->conf->venue_name) {
185 u8 *len;
186 unsigned int i;
187 len = gas_anqp_add_element(buf, ANQP_VENUE_NAME);
188 wpabuf_put_u8(buf, hapd->conf->venue_group);
189 wpabuf_put_u8(buf, hapd->conf->venue_type);
190 for (i = 0; i < hapd->conf->venue_name_count; i++) {
Dmitry Shmidt61d9df32012-08-29 16:22:06 -0700191 struct hostapd_lang_string *vn;
Dmitry Shmidt04949592012-07-19 12:16:46 -0700192 vn = &hapd->conf->venue_name[i];
193 wpabuf_put_u8(buf, 3 + vn->name_len);
194 wpabuf_put_data(buf, vn->lang, 3);
195 wpabuf_put_data(buf, vn->name, vn->name_len);
196 }
197 gas_anqp_set_element_len(buf, len);
198 }
199}
200
201
Dmitry Shmidt61d9df32012-08-29 16:22:06 -0700202static void anqp_add_network_auth_type(struct hostapd_data *hapd,
203 struct wpabuf *buf)
204{
205 if (hapd->conf->network_auth_type) {
206 wpabuf_put_le16(buf, ANQP_NETWORK_AUTH_TYPE);
207 wpabuf_put_le16(buf, hapd->conf->network_auth_type_len);
208 wpabuf_put_data(buf, hapd->conf->network_auth_type,
209 hapd->conf->network_auth_type_len);
210 }
211}
212
213
Dmitry Shmidt04949592012-07-19 12:16:46 -0700214static void anqp_add_roaming_consortium(struct hostapd_data *hapd,
215 struct wpabuf *buf)
216{
217 unsigned int i;
218 u8 *len;
219
220 len = gas_anqp_add_element(buf, ANQP_ROAMING_CONSORTIUM);
221 for (i = 0; i < hapd->conf->roaming_consortium_count; i++) {
222 struct hostapd_roaming_consortium *rc;
223 rc = &hapd->conf->roaming_consortium[i];
224 wpabuf_put_u8(buf, rc->len);
225 wpabuf_put_data(buf, rc->oi, rc->len);
226 }
227 gas_anqp_set_element_len(buf, len);
228}
229
230
Dmitry Shmidt61d9df32012-08-29 16:22:06 -0700231static void anqp_add_ip_addr_type_availability(struct hostapd_data *hapd,
232 struct wpabuf *buf)
233{
234 if (hapd->conf->ipaddr_type_configured) {
235 wpabuf_put_le16(buf, ANQP_IP_ADDR_TYPE_AVAILABILITY);
236 wpabuf_put_le16(buf, 1);
237 wpabuf_put_u8(buf, hapd->conf->ipaddr_type_availability);
238 }
239}
240
241
242static void anqp_add_nai_realm_eap(struct wpabuf *buf,
243 struct hostapd_nai_realm_data *realm)
244{
245 unsigned int i, j;
246
247 wpabuf_put_u8(buf, realm->eap_method_count);
248
249 for (i = 0; i < realm->eap_method_count; i++) {
250 struct hostapd_nai_realm_eap *eap = &realm->eap_method[i];
251 wpabuf_put_u8(buf, 2 + (3 * eap->num_auths));
252 wpabuf_put_u8(buf, eap->eap_method);
253 wpabuf_put_u8(buf, eap->num_auths);
254 for (j = 0; j < eap->num_auths; j++) {
255 wpabuf_put_u8(buf, eap->auth_id[j]);
256 wpabuf_put_u8(buf, 1);
257 wpabuf_put_u8(buf, eap->auth_val[j]);
258 }
259 }
260}
261
262
263static void anqp_add_nai_realm_data(struct wpabuf *buf,
264 struct hostapd_nai_realm_data *realm,
265 unsigned int realm_idx)
266{
267 u8 *realm_data_len;
268
269 wpa_printf(MSG_DEBUG, "realm=%s, len=%d", realm->realm[realm_idx],
270 (int) os_strlen(realm->realm[realm_idx]));
271 realm_data_len = wpabuf_put(buf, 2);
272 wpabuf_put_u8(buf, realm->encoding);
273 wpabuf_put_u8(buf, os_strlen(realm->realm[realm_idx]));
274 wpabuf_put_str(buf, realm->realm[realm_idx]);
275 anqp_add_nai_realm_eap(buf, realm);
276 gas_anqp_set_element_len(buf, realm_data_len);
277}
278
279
280static int hs20_add_nai_home_realm_matches(struct hostapd_data *hapd,
281 struct wpabuf *buf,
282 const u8 *home_realm,
283 size_t home_realm_len)
284{
285 unsigned int i, j, k;
286 u8 num_realms, num_matching = 0, encoding, realm_len, *realm_list_len;
287 struct hostapd_nai_realm_data *realm;
288 const u8 *pos, *realm_name, *end;
289 struct {
290 unsigned int realm_data_idx;
291 unsigned int realm_idx;
292 } matches[10];
293
294 pos = home_realm;
295 end = pos + home_realm_len;
296 if (pos + 1 > end) {
297 wpa_hexdump(MSG_DEBUG, "Too short NAI Home Realm Query",
298 home_realm, home_realm_len);
299 return -1;
300 }
301 num_realms = *pos++;
302
303 for (i = 0; i < num_realms && num_matching < 10; i++) {
304 if (pos + 2 > end) {
305 wpa_hexdump(MSG_DEBUG,
306 "Truncated NAI Home Realm Query",
307 home_realm, home_realm_len);
308 return -1;
309 }
310 encoding = *pos++;
311 realm_len = *pos++;
312 if (pos + realm_len > end) {
313 wpa_hexdump(MSG_DEBUG,
314 "Truncated NAI Home Realm Query",
315 home_realm, home_realm_len);
316 return -1;
317 }
318 realm_name = pos;
319 for (j = 0; j < hapd->conf->nai_realm_count &&
320 num_matching < 10; j++) {
321 const u8 *rpos, *rend;
322 realm = &hapd->conf->nai_realm_data[j];
323 if (encoding != realm->encoding)
324 continue;
325
326 rpos = realm_name;
327 while (rpos < realm_name + realm_len &&
328 num_matching < 10) {
329 for (rend = rpos;
330 rend < realm_name + realm_len; rend++) {
331 if (*rend == ';')
332 break;
333 }
334 for (k = 0; k < MAX_NAI_REALMS &&
335 realm->realm[k] &&
336 num_matching < 10; k++) {
337 if ((int) os_strlen(realm->realm[k]) !=
338 rend - rpos ||
339 os_strncmp((char *) rpos,
340 realm->realm[k],
341 rend - rpos) != 0)
342 continue;
343 matches[num_matching].realm_data_idx =
344 j;
345 matches[num_matching].realm_idx = k;
346 num_matching++;
347 }
348 rpos = rend + 1;
349 }
350 }
351 pos += realm_len;
352 }
353
354 realm_list_len = gas_anqp_add_element(buf, ANQP_NAI_REALM);
355 wpabuf_put_le16(buf, num_matching);
356
357 /*
358 * There are two ways to format. 1. each realm in a NAI Realm Data unit
359 * 2. all realms that share the same EAP methods in a NAI Realm Data
360 * unit. The first format is likely to be bigger in size than the
361 * second, but may be easier to parse and process by the receiver.
362 */
363 for (i = 0; i < num_matching; i++) {
364 wpa_printf(MSG_DEBUG, "realm_idx %d, realm_data_idx %d",
365 matches[i].realm_data_idx, matches[i].realm_idx);
366 realm = &hapd->conf->nai_realm_data[matches[i].realm_data_idx];
367 anqp_add_nai_realm_data(buf, realm, matches[i].realm_idx);
368 }
369 gas_anqp_set_element_len(buf, realm_list_len);
370 return 0;
371}
372
373
374static void anqp_add_nai_realm(struct hostapd_data *hapd, struct wpabuf *buf,
375 const u8 *home_realm, size_t home_realm_len,
376 int nai_realm, int nai_home_realm)
377{
378 if (nai_realm && hapd->conf->nai_realm_data) {
379 u8 *len;
380 unsigned int i, j;
381 len = gas_anqp_add_element(buf, ANQP_NAI_REALM);
382 wpabuf_put_le16(buf, hapd->conf->nai_realm_count);
383 for (i = 0; i < hapd->conf->nai_realm_count; i++) {
384 u8 *realm_data_len, *realm_len;
385 struct hostapd_nai_realm_data *realm;
386
387 realm = &hapd->conf->nai_realm_data[i];
388 realm_data_len = wpabuf_put(buf, 2);
389 wpabuf_put_u8(buf, realm->encoding);
390 realm_len = wpabuf_put(buf, 1);
391 for (j = 0; realm->realm[j]; j++) {
392 if (j > 0)
393 wpabuf_put_u8(buf, ';');
394 wpabuf_put_str(buf, realm->realm[j]);
395 }
396 *realm_len = (u8 *) wpabuf_put(buf, 0) - realm_len - 1;
397 anqp_add_nai_realm_eap(buf, realm);
398 gas_anqp_set_element_len(buf, realm_data_len);
399 }
400 gas_anqp_set_element_len(buf, len);
401 } else if (nai_home_realm && hapd->conf->nai_realm_data) {
402 hs20_add_nai_home_realm_matches(hapd, buf, home_realm,
403 home_realm_len);
404 }
405}
406
407
408static void anqp_add_3gpp_cellular_network(struct hostapd_data *hapd,
409 struct wpabuf *buf)
410{
411 if (hapd->conf->anqp_3gpp_cell_net) {
412 wpabuf_put_le16(buf, ANQP_3GPP_CELLULAR_NETWORK);
413 wpabuf_put_le16(buf,
414 hapd->conf->anqp_3gpp_cell_net_len);
415 wpabuf_put_data(buf, hapd->conf->anqp_3gpp_cell_net,
416 hapd->conf->anqp_3gpp_cell_net_len);
417 }
418}
419
420
421static void anqp_add_domain_name(struct hostapd_data *hapd, struct wpabuf *buf)
422{
423 if (hapd->conf->domain_name) {
424 wpabuf_put_le16(buf, ANQP_DOMAIN_NAME);
425 wpabuf_put_le16(buf, hapd->conf->domain_name_len);
426 wpabuf_put_data(buf, hapd->conf->domain_name,
427 hapd->conf->domain_name_len);
428 }
429}
430
431
432static void anqp_add_operator_friendly_name(struct hostapd_data *hapd,
433 struct wpabuf *buf)
434{
435 if (hapd->conf->hs20_oper_friendly_name) {
436 u8 *len;
437 unsigned int i;
438 len = gas_anqp_add_element(buf, ANQP_VENDOR_SPECIFIC);
439 wpabuf_put_be24(buf, OUI_WFA);
440 wpabuf_put_u8(buf, HS20_ANQP_OUI_TYPE);
441 wpabuf_put_u8(buf, HS20_STYPE_OPERATOR_FRIENDLY_NAME);
442 wpabuf_put_u8(buf, 0); /* Reserved */
443 for (i = 0; i < hapd->conf->hs20_oper_friendly_name_count; i++)
444 {
445 struct hostapd_lang_string *vn;
446 vn = &hapd->conf->hs20_oper_friendly_name[i];
447 wpabuf_put_u8(buf, 3 + vn->name_len);
448 wpabuf_put_data(buf, vn->lang, 3);
449 wpabuf_put_data(buf, vn->name, vn->name_len);
450 }
451 gas_anqp_set_element_len(buf, len);
452 }
453}
454
455
456static void anqp_add_wan_metrics(struct hostapd_data *hapd,
457 struct wpabuf *buf)
458{
459 if (hapd->conf->hs20_wan_metrics) {
460 u8 *len = gas_anqp_add_element(buf, ANQP_VENDOR_SPECIFIC);
461 wpabuf_put_be24(buf, OUI_WFA);
462 wpabuf_put_u8(buf, HS20_ANQP_OUI_TYPE);
463 wpabuf_put_u8(buf, HS20_STYPE_WAN_METRICS);
464 wpabuf_put_u8(buf, 0); /* Reserved */
465 wpabuf_put_data(buf, hapd->conf->hs20_wan_metrics, 13);
466 gas_anqp_set_element_len(buf, len);
467 }
468}
469
470
471static void anqp_add_connection_capability(struct hostapd_data *hapd,
472 struct wpabuf *buf)
473{
474 if (hapd->conf->hs20_connection_capability) {
475 u8 *len = gas_anqp_add_element(buf, ANQP_VENDOR_SPECIFIC);
476 wpabuf_put_be24(buf, OUI_WFA);
477 wpabuf_put_u8(buf, HS20_ANQP_OUI_TYPE);
478 wpabuf_put_u8(buf, HS20_STYPE_CONNECTION_CAPABILITY);
479 wpabuf_put_u8(buf, 0); /* Reserved */
480 wpabuf_put_data(buf, hapd->conf->hs20_connection_capability,
481 hapd->conf->hs20_connection_capability_len);
482 gas_anqp_set_element_len(buf, len);
483 }
484}
485
486
487static void anqp_add_operating_class(struct hostapd_data *hapd,
488 struct wpabuf *buf)
489{
490 if (hapd->conf->hs20_operating_class) {
491 u8 *len = gas_anqp_add_element(buf, ANQP_VENDOR_SPECIFIC);
492 wpabuf_put_be24(buf, OUI_WFA);
493 wpabuf_put_u8(buf, HS20_ANQP_OUI_TYPE);
494 wpabuf_put_u8(buf, HS20_STYPE_OPERATING_CLASS);
495 wpabuf_put_u8(buf, 0); /* Reserved */
496 wpabuf_put_data(buf, hapd->conf->hs20_operating_class,
497 hapd->conf->hs20_operating_class_len);
498 gas_anqp_set_element_len(buf, len);
499 }
500}
501
502
Dmitry Shmidt04949592012-07-19 12:16:46 -0700503static struct wpabuf *
504gas_serv_build_gas_resp_payload(struct hostapd_data *hapd,
505 unsigned int request,
Dmitry Shmidt61d9df32012-08-29 16:22:06 -0700506 struct gas_dialog_info *di,
507 const u8 *home_realm, size_t home_realm_len)
Dmitry Shmidt04949592012-07-19 12:16:46 -0700508{
509 struct wpabuf *buf;
510
511 buf = wpabuf_alloc(1400);
512 if (buf == NULL)
513 return NULL;
514
515 if (request & ANQP_REQ_CAPABILITY_LIST)
516 anqp_add_capab_list(hapd, buf);
517 if (request & ANQP_REQ_VENUE_NAME)
518 anqp_add_venue_name(hapd, buf);
Dmitry Shmidt61d9df32012-08-29 16:22:06 -0700519 if (request & ANQP_REQ_NETWORK_AUTH_TYPE)
520 anqp_add_network_auth_type(hapd, buf);
Dmitry Shmidt04949592012-07-19 12:16:46 -0700521 if (request & ANQP_REQ_ROAMING_CONSORTIUM)
522 anqp_add_roaming_consortium(hapd, buf);
Dmitry Shmidt61d9df32012-08-29 16:22:06 -0700523 if (request & ANQP_REQ_IP_ADDR_TYPE_AVAILABILITY)
524 anqp_add_ip_addr_type_availability(hapd, buf);
525 if (request & (ANQP_REQ_NAI_REALM | ANQP_REQ_NAI_HOME_REALM))
526 anqp_add_nai_realm(hapd, buf, home_realm, home_realm_len,
527 request & ANQP_REQ_NAI_REALM,
528 request & ANQP_REQ_NAI_HOME_REALM);
529 if (request & ANQP_REQ_3GPP_CELLULAR_NETWORK)
530 anqp_add_3gpp_cellular_network(hapd, buf);
531 if (request & ANQP_REQ_DOMAIN_NAME)
532 anqp_add_domain_name(hapd, buf);
533
534 if (request & ANQP_REQ_HS_CAPABILITY_LIST)
535 anqp_add_hs_capab_list(hapd, buf);
536 if (request & ANQP_REQ_OPERATOR_FRIENDLY_NAME)
537 anqp_add_operator_friendly_name(hapd, buf);
538 if (request & ANQP_REQ_WAN_METRICS)
539 anqp_add_wan_metrics(hapd, buf);
540 if (request & ANQP_REQ_CONNECTION_CAPABILITY)
541 anqp_add_connection_capability(hapd, buf);
542 if (request & ANQP_REQ_OPERATING_CLASS)
543 anqp_add_operating_class(hapd, buf);
Dmitry Shmidt04949592012-07-19 12:16:46 -0700544
545 return buf;
546}
547
548
549static void gas_serv_clear_cached_ies(void *eloop_data, void *user_ctx)
550{
551 struct gas_dialog_info *dia = eloop_data;
552
553 wpa_printf(MSG_DEBUG, "GAS: Timeout triggered, clearing dialog for "
554 "dialog token %d", dia->dialog_token);
555
556 gas_serv_dialog_clear(dia);
557}
558
559
560struct anqp_query_info {
561 unsigned int request;
562 unsigned int remote_request;
Dmitry Shmidt61d9df32012-08-29 16:22:06 -0700563 const u8 *home_realm_query;
564 size_t home_realm_query_len;
Dmitry Shmidt04949592012-07-19 12:16:46 -0700565 u16 remote_delay;
566};
567
568
569static void set_anqp_req(unsigned int bit, const char *name, int local,
570 unsigned int remote, u16 remote_delay,
571 struct anqp_query_info *qi)
572{
573 qi->request |= bit;
574 if (local) {
575 wpa_printf(MSG_DEBUG, "ANQP: %s (local)", name);
576 } else if (bit & remote) {
577 wpa_printf(MSG_DEBUG, "ANQP: %s (remote)", name);
578 qi->remote_request |= bit;
579 if (remote_delay > qi->remote_delay)
580 qi->remote_delay = remote_delay;
581 } else {
582 wpa_printf(MSG_DEBUG, "ANQP: %s not available", name);
583 }
584}
585
586
587static void rx_anqp_query_list_id(struct hostapd_data *hapd, u16 info_id,
588 struct anqp_query_info *qi)
589{
590 switch (info_id) {
591 case ANQP_CAPABILITY_LIST:
592 set_anqp_req(ANQP_REQ_CAPABILITY_LIST, "Capability List", 1, 0,
593 0, qi);
594 break;
595 case ANQP_VENUE_NAME:
596 set_anqp_req(ANQP_REQ_VENUE_NAME, "Venue Name",
597 hapd->conf->venue_name != NULL, 0, 0, qi);
598 break;
Dmitry Shmidt61d9df32012-08-29 16:22:06 -0700599 case ANQP_NETWORK_AUTH_TYPE:
600 set_anqp_req(ANQP_REQ_NETWORK_AUTH_TYPE, "Network Auth Type",
601 hapd->conf->network_auth_type != NULL,
602 0, 0, qi);
603 break;
Dmitry Shmidt04949592012-07-19 12:16:46 -0700604 case ANQP_ROAMING_CONSORTIUM:
605 set_anqp_req(ANQP_REQ_ROAMING_CONSORTIUM, "Roaming Consortium",
606 hapd->conf->roaming_consortium != NULL, 0, 0, qi);
607 break;
Dmitry Shmidt61d9df32012-08-29 16:22:06 -0700608 case ANQP_IP_ADDR_TYPE_AVAILABILITY:
609 set_anqp_req(ANQP_REQ_IP_ADDR_TYPE_AVAILABILITY,
610 "IP Addr Type Availability",
611 hapd->conf->ipaddr_type_configured,
612 0, 0, qi);
613 break;
614 case ANQP_NAI_REALM:
615 set_anqp_req(ANQP_REQ_NAI_REALM, "NAI Realm",
616 hapd->conf->nai_realm_data != NULL,
617 0, 0, qi);
618 break;
619 case ANQP_3GPP_CELLULAR_NETWORK:
620 set_anqp_req(ANQP_REQ_3GPP_CELLULAR_NETWORK,
621 "3GPP Cellular Network",
622 hapd->conf->anqp_3gpp_cell_net != NULL,
623 0, 0, qi);
624 break;
625 case ANQP_DOMAIN_NAME:
626 set_anqp_req(ANQP_REQ_DOMAIN_NAME, "Domain Name",
627 hapd->conf->domain_name != NULL,
628 0, 0, qi);
629 break;
Dmitry Shmidt04949592012-07-19 12:16:46 -0700630 default:
631 wpa_printf(MSG_DEBUG, "ANQP: Unsupported Info Id %u",
632 info_id);
633 break;
634 }
635}
636
637
638static void rx_anqp_query_list(struct hostapd_data *hapd,
639 const u8 *pos, const u8 *end,
640 struct anqp_query_info *qi)
641{
642 wpa_printf(MSG_DEBUG, "ANQP: %u Info IDs requested in Query list",
643 (unsigned int) (end - pos) / 2);
644
645 while (pos + 2 <= end) {
646 rx_anqp_query_list_id(hapd, WPA_GET_LE16(pos), qi);
647 pos += 2;
648 }
649}
650
651
Dmitry Shmidt61d9df32012-08-29 16:22:06 -0700652static void rx_anqp_hs_query_list(struct hostapd_data *hapd, u8 subtype,
653 struct anqp_query_info *qi)
654{
655 switch (subtype) {
656 case HS20_STYPE_CAPABILITY_LIST:
657 set_anqp_req(ANQP_REQ_HS_CAPABILITY_LIST, "HS Capability List",
658 1, 0, 0, qi);
659 break;
660 case HS20_STYPE_OPERATOR_FRIENDLY_NAME:
661 set_anqp_req(ANQP_REQ_OPERATOR_FRIENDLY_NAME,
662 "Operator Friendly Name",
663 hapd->conf->hs20_oper_friendly_name != NULL,
664 0, 0, qi);
665 break;
666 case HS20_STYPE_WAN_METRICS:
667 set_anqp_req(ANQP_REQ_WAN_METRICS, "WAN Metrics",
668 hapd->conf->hs20_wan_metrics != NULL,
669 0, 0, qi);
670 break;
671 case HS20_STYPE_CONNECTION_CAPABILITY:
672 set_anqp_req(ANQP_REQ_CONNECTION_CAPABILITY,
673 "Connection Capability",
674 hapd->conf->hs20_connection_capability != NULL,
675 0, 0, qi);
676 break;
677 case HS20_STYPE_OPERATING_CLASS:
678 set_anqp_req(ANQP_REQ_OPERATING_CLASS, "Operating Class",
679 hapd->conf->hs20_operating_class != NULL,
680 0, 0, qi);
681 break;
682 default:
683 wpa_printf(MSG_DEBUG, "ANQP: Unsupported HS 2.0 subtype %u",
684 subtype);
685 break;
686 }
687}
688
689
690static void rx_anqp_hs_nai_home_realm(struct hostapd_data *hapd,
691 const u8 *pos, const u8 *end,
692 struct anqp_query_info *qi)
693{
694 qi->request |= ANQP_REQ_NAI_HOME_REALM;
695 qi->home_realm_query = pos;
696 qi->home_realm_query_len = end - pos;
697 if (hapd->conf->nai_realm_data != NULL) {
698 wpa_printf(MSG_DEBUG, "ANQP: HS 2.0 NAI Home Realm Query "
699 "(local)");
700 } else {
701 wpa_printf(MSG_DEBUG, "ANQP: HS 2.0 NAI Home Realm Query not "
702 "available");
703 }
704}
705
706
707static void rx_anqp_vendor_specific(struct hostapd_data *hapd,
708 const u8 *pos, const u8 *end,
709 struct anqp_query_info *qi)
710{
711 u32 oui;
712 u8 subtype;
713
714 if (pos + 4 > end) {
715 wpa_printf(MSG_DEBUG, "ANQP: Too short vendor specific ANQP "
716 "Query element");
717 return;
718 }
719
720 oui = WPA_GET_BE24(pos);
721 pos += 3;
722 if (oui != OUI_WFA) {
723 wpa_printf(MSG_DEBUG, "ANQP: Unsupported vendor OUI %06x",
724 oui);
725 return;
726 }
727
728 if (*pos != HS20_ANQP_OUI_TYPE) {
729 wpa_printf(MSG_DEBUG, "ANQP: Unsupported WFA vendor type %u",
730 *pos);
731 return;
732 }
733 pos++;
734
735 if (pos + 1 >= end)
736 return;
737
738 subtype = *pos++;
739 pos++; /* Reserved */
740 switch (subtype) {
741 case HS20_STYPE_QUERY_LIST:
742 wpa_printf(MSG_DEBUG, "ANQP: HS 2.0 Query List");
743 while (pos < end) {
744 rx_anqp_hs_query_list(hapd, *pos, qi);
745 pos++;
746 }
747 break;
748 case HS20_STYPE_NAI_HOME_REALM_QUERY:
749 rx_anqp_hs_nai_home_realm(hapd, pos, end, qi);
750 break;
751 default:
752 wpa_printf(MSG_DEBUG, "ANQP: Unsupported HS 2.0 query subtype "
753 "%u", subtype);
754 break;
755 }
756}
757
758
Dmitry Shmidt04949592012-07-19 12:16:46 -0700759static void gas_serv_req_local_processing(struct hostapd_data *hapd,
760 const u8 *sa, u8 dialog_token,
761 struct anqp_query_info *qi)
762{
763 struct wpabuf *buf, *tx_buf;
764
Dmitry Shmidt61d9df32012-08-29 16:22:06 -0700765 buf = gas_serv_build_gas_resp_payload(hapd, qi->request, NULL,
766 qi->home_realm_query,
767 qi->home_realm_query_len);
Dmitry Shmidt04949592012-07-19 12:16:46 -0700768 wpa_hexdump_buf(MSG_MSGDUMP, "ANQP: Locally generated ANQP responses",
769 buf);
770 if (!buf)
771 return;
772
773 if (wpabuf_len(buf) > hapd->gas_frag_limit ||
774 hapd->conf->gas_comeback_delay) {
775 struct gas_dialog_info *di;
776 u16 comeback_delay = 1;
777
778 if (hapd->conf->gas_comeback_delay) {
779 /* Testing - allow overriding of the delay value */
780 comeback_delay = hapd->conf->gas_comeback_delay;
781 }
782
783 wpa_printf(MSG_DEBUG, "ANQP: Too long response to fit in "
784 "initial response - use GAS comeback");
785 di = gas_dialog_create(hapd, sa, dialog_token);
786 if (!di) {
787 wpa_printf(MSG_INFO, "ANQP: Could not create dialog "
788 "for " MACSTR " (dialog token %u)",
789 MAC2STR(sa), dialog_token);
790 wpabuf_free(buf);
791 return;
792 }
793 di->sd_resp = buf;
794 di->sd_resp_pos = 0;
795 tx_buf = gas_anqp_build_initial_resp_buf(
796 dialog_token, WLAN_STATUS_SUCCESS, comeback_delay,
797 NULL);
798 } else {
799 wpa_printf(MSG_DEBUG, "ANQP: Initial response (no comeback)");
800 tx_buf = gas_anqp_build_initial_resp_buf(
801 dialog_token, WLAN_STATUS_SUCCESS, 0, buf);
802 wpabuf_free(buf);
803 }
804 if (!tx_buf)
805 return;
806
807 hostapd_drv_send_action(hapd, hapd->iface->freq, 0, sa,
808 wpabuf_head(tx_buf), wpabuf_len(tx_buf));
809 wpabuf_free(tx_buf);
810}
811
812
813static void gas_serv_rx_gas_initial_req(struct hostapd_data *hapd,
814 const u8 *sa,
815 const u8 *data, size_t len)
816{
817 const u8 *pos = data;
818 const u8 *end = data + len;
819 const u8 *next;
820 u8 dialog_token;
821 u16 slen;
822 struct anqp_query_info qi;
823 const u8 *adv_proto;
824
825 if (len < 1 + 2)
826 return;
827
828 os_memset(&qi, 0, sizeof(qi));
829
830 dialog_token = *pos++;
831 wpa_msg(hapd->msg_ctx, MSG_DEBUG,
832 "GAS: GAS Initial Request from " MACSTR " (dialog token %u) ",
833 MAC2STR(sa), dialog_token);
834
835 if (*pos != WLAN_EID_ADV_PROTO) {
836 wpa_msg(hapd->msg_ctx, MSG_DEBUG,
837 "GAS: Unexpected IE in GAS Initial Request: %u", *pos);
838 return;
839 }
840 adv_proto = pos++;
841
842 slen = *pos++;
843 next = pos + slen;
844 if (next > end || slen < 2) {
845 wpa_msg(hapd->msg_ctx, MSG_DEBUG,
846 "GAS: Invalid IE in GAS Initial Request");
847 return;
848 }
849 pos++; /* skip QueryRespLenLimit and PAME-BI */
850
851 if (*pos != ACCESS_NETWORK_QUERY_PROTOCOL) {
852 struct wpabuf *buf;
853 wpa_msg(hapd->msg_ctx, MSG_DEBUG,
854 "GAS: Unsupported GAS advertisement protocol id %u",
855 *pos);
856 if (sa[0] & 0x01)
857 return; /* Invalid source address - drop silently */
858 buf = gas_build_initial_resp(
859 dialog_token, WLAN_STATUS_GAS_ADV_PROTO_NOT_SUPPORTED,
860 0, 2 + slen + 2);
861 if (buf == NULL)
862 return;
863 wpabuf_put_data(buf, adv_proto, 2 + slen);
864 wpabuf_put_le16(buf, 0); /* Query Response Length */
865 hostapd_drv_send_action(hapd, hapd->iface->freq, 0, sa,
866 wpabuf_head(buf), wpabuf_len(buf));
867 wpabuf_free(buf);
868 return;
869 }
870
871 pos = next;
872 /* Query Request */
873 if (pos + 2 > end)
874 return;
875 slen = WPA_GET_LE16(pos);
876 pos += 2;
877 if (pos + slen > end)
878 return;
879 end = pos + slen;
880
881 /* ANQP Query Request */
882 while (pos < end) {
883 u16 info_id, elen;
884
885 if (pos + 4 > end)
886 return;
887
888 info_id = WPA_GET_LE16(pos);
889 pos += 2;
890 elen = WPA_GET_LE16(pos);
891 pos += 2;
892
893 if (pos + elen > end) {
894 wpa_printf(MSG_DEBUG, "ANQP: Invalid Query Request");
895 return;
896 }
897
898 switch (info_id) {
899 case ANQP_QUERY_LIST:
900 rx_anqp_query_list(hapd, pos, pos + elen, &qi);
901 break;
Dmitry Shmidt61d9df32012-08-29 16:22:06 -0700902 case ANQP_VENDOR_SPECIFIC:
903 rx_anqp_vendor_specific(hapd, pos, pos + elen, &qi);
904 break;
Dmitry Shmidt04949592012-07-19 12:16:46 -0700905 default:
906 wpa_printf(MSG_DEBUG, "ANQP: Unsupported Query "
907 "Request element %u", info_id);
908 break;
909 }
910
911 pos += elen;
912 }
913
914 gas_serv_req_local_processing(hapd, sa, dialog_token, &qi);
915}
916
917
918void gas_serv_tx_gas_response(struct hostapd_data *hapd, const u8 *dst,
919 struct gas_dialog_info *dialog)
920{
921 struct wpabuf *buf, *tx_buf;
922 u8 dialog_token = dialog->dialog_token;
923 size_t frag_len;
924
925 if (dialog->sd_resp == NULL) {
926 buf = gas_serv_build_gas_resp_payload(hapd,
927 dialog->all_requested,
Dmitry Shmidt61d9df32012-08-29 16:22:06 -0700928 dialog, NULL, 0);
Dmitry Shmidt04949592012-07-19 12:16:46 -0700929 wpa_hexdump_buf(MSG_MSGDUMP, "ANQP: Generated ANQP responses",
930 buf);
931 if (!buf)
932 goto tx_gas_response_done;
933 dialog->sd_resp = buf;
934 dialog->sd_resp_pos = 0;
935 }
936 frag_len = wpabuf_len(dialog->sd_resp) - dialog->sd_resp_pos;
937 if (frag_len > hapd->gas_frag_limit || dialog->comeback_delay ||
938 hapd->conf->gas_comeback_delay) {
939 u16 comeback_delay_tus = dialog->comeback_delay +
940 GAS_SERV_COMEBACK_DELAY_FUDGE;
941 u32 comeback_delay_secs, comeback_delay_usecs;
942
943 if (hapd->conf->gas_comeback_delay) {
944 /* Testing - allow overriding of the delay value */
945 comeback_delay_tus = hapd->conf->gas_comeback_delay;
946 }
947
948 wpa_printf(MSG_DEBUG, "GAS: Response frag_len %u (frag limit "
949 "%u) and comeback delay %u, "
950 "requesting comebacks", (unsigned int) frag_len,
951 (unsigned int) hapd->gas_frag_limit,
952 dialog->comeback_delay);
953 tx_buf = gas_anqp_build_initial_resp_buf(dialog_token,
954 WLAN_STATUS_SUCCESS,
955 comeback_delay_tus,
956 NULL);
957 if (tx_buf) {
958 wpa_msg(hapd->msg_ctx, MSG_DEBUG,
959 "GAS: Tx GAS Initial Resp (comeback = 10TU)");
960 hostapd_drv_send_action(hapd, hapd->iface->freq, 0,
961 dst,
962 wpabuf_head(tx_buf),
963 wpabuf_len(tx_buf));
964 }
965 wpabuf_free(tx_buf);
966
967 /* start a timer of 1.5 * comeback-delay */
968 comeback_delay_tus = comeback_delay_tus +
969 (comeback_delay_tus / 2);
970 comeback_delay_secs = (comeback_delay_tus * 1024) / 1000000;
971 comeback_delay_usecs = (comeback_delay_tus * 1024) -
972 (comeback_delay_secs * 1000000);
973 eloop_register_timeout(comeback_delay_secs,
974 comeback_delay_usecs,
975 gas_serv_clear_cached_ies, dialog,
976 NULL);
977 goto tx_gas_response_done;
978 }
979
980 buf = wpabuf_alloc_copy(wpabuf_head_u8(dialog->sd_resp) +
981 dialog->sd_resp_pos, frag_len);
982 if (buf == NULL) {
983 wpa_msg(hapd->msg_ctx, MSG_DEBUG, "GAS: Buffer allocation "
984 "failed");
985 goto tx_gas_response_done;
986 }
987 tx_buf = gas_anqp_build_initial_resp_buf(dialog_token,
988 WLAN_STATUS_SUCCESS, 0, buf);
989 wpabuf_free(buf);
990 if (tx_buf == NULL)
991 goto tx_gas_response_done;
992 wpa_msg(hapd->msg_ctx, MSG_DEBUG, "GAS: Tx GAS Initial "
993 "Response (frag_id %d frag_len %d)",
994 dialog->sd_frag_id, (int) frag_len);
995 dialog->sd_frag_id++;
996
997 hostapd_drv_send_action(hapd, hapd->iface->freq, 0, dst,
998 wpabuf_head(tx_buf), wpabuf_len(tx_buf));
999 wpabuf_free(tx_buf);
1000tx_gas_response_done:
1001 gas_serv_clear_cached_ies(dialog, NULL);
1002}
1003
1004
1005static void gas_serv_rx_gas_comeback_req(struct hostapd_data *hapd,
1006 const u8 *sa,
1007 const u8 *data, size_t len)
1008{
1009 struct gas_dialog_info *dialog;
1010 struct wpabuf *buf, *tx_buf;
1011 u8 dialog_token;
1012 size_t frag_len;
1013 int more = 0;
1014
1015 wpa_hexdump(MSG_DEBUG, "GAS: RX GAS Comeback Request", data, len);
1016 if (len < 1)
1017 return;
1018 dialog_token = *data;
1019 wpa_msg(hapd->msg_ctx, MSG_DEBUG, "GAS: Dialog Token: %u",
1020 dialog_token);
1021
1022 dialog = gas_serv_dialog_find(hapd, sa, dialog_token);
1023 if (!dialog) {
1024 wpa_msg(hapd->msg_ctx, MSG_DEBUG, "GAS: No pending SD "
1025 "response fragment for " MACSTR " dialog token %u",
1026 MAC2STR(sa), dialog_token);
1027
1028 if (sa[0] & 0x01)
1029 return; /* Invalid source address - drop silently */
1030 tx_buf = gas_anqp_build_comeback_resp_buf(
1031 dialog_token, WLAN_STATUS_NO_OUTSTANDING_GAS_REQ, 0, 0,
1032 0, NULL);
1033 if (tx_buf == NULL)
1034 return;
1035 goto send_resp;
1036 }
1037
1038 if (dialog->sd_resp == NULL) {
1039 wpa_printf(MSG_DEBUG, "GAS: Remote request 0x%x received 0x%x",
1040 dialog->requested, dialog->received);
1041 if ((dialog->requested & dialog->received) !=
1042 dialog->requested) {
1043 wpa_printf(MSG_DEBUG, "GAS: Did not receive response "
1044 "from remote processing");
1045 gas_serv_dialog_clear(dialog);
1046 tx_buf = gas_anqp_build_comeback_resp_buf(
1047 dialog_token,
1048 WLAN_STATUS_GAS_RESP_NOT_RECEIVED, 0, 0, 0,
1049 NULL);
1050 if (tx_buf == NULL)
1051 return;
1052 goto send_resp;
1053 }
1054
1055 buf = gas_serv_build_gas_resp_payload(hapd,
1056 dialog->all_requested,
Dmitry Shmidt61d9df32012-08-29 16:22:06 -07001057 dialog, NULL, 0);
Dmitry Shmidt04949592012-07-19 12:16:46 -07001058 wpa_hexdump_buf(MSG_MSGDUMP, "ANQP: Generated ANQP responses",
1059 buf);
1060 if (!buf)
1061 goto rx_gas_comeback_req_done;
1062 dialog->sd_resp = buf;
1063 dialog->sd_resp_pos = 0;
1064 }
1065 frag_len = wpabuf_len(dialog->sd_resp) - dialog->sd_resp_pos;
1066 if (frag_len > hapd->gas_frag_limit) {
1067 frag_len = hapd->gas_frag_limit;
1068 more = 1;
1069 }
1070 wpa_msg(hapd->msg_ctx, MSG_DEBUG, "GAS: resp frag_len %u",
1071 (unsigned int) frag_len);
1072 buf = wpabuf_alloc_copy(wpabuf_head_u8(dialog->sd_resp) +
1073 dialog->sd_resp_pos, frag_len);
1074 if (buf == NULL) {
1075 wpa_msg(hapd->msg_ctx, MSG_DEBUG, "GAS: Failed to allocate "
1076 "buffer");
1077 goto rx_gas_comeback_req_done;
1078 }
1079 tx_buf = gas_anqp_build_comeback_resp_buf(dialog_token,
1080 WLAN_STATUS_SUCCESS,
1081 dialog->sd_frag_id,
1082 more, 0, buf);
1083 wpabuf_free(buf);
1084 if (tx_buf == NULL)
1085 goto rx_gas_comeback_req_done;
1086 wpa_msg(hapd->msg_ctx, MSG_DEBUG, "GAS: Tx GAS Comeback Response "
1087 "(frag_id %d more=%d frag_len=%d)",
1088 dialog->sd_frag_id, more, (int) frag_len);
1089 dialog->sd_frag_id++;
1090 dialog->sd_resp_pos += frag_len;
1091
1092 if (more) {
1093 wpa_msg(hapd->msg_ctx, MSG_DEBUG, "GAS: %d more bytes remain "
1094 "to be sent",
1095 (int) (wpabuf_len(dialog->sd_resp) -
1096 dialog->sd_resp_pos));
1097 } else {
1098 wpa_msg(hapd->msg_ctx, MSG_DEBUG, "GAS: All fragments of "
1099 "SD response sent");
1100 gas_serv_dialog_clear(dialog);
1101 gas_serv_free_dialogs(hapd, sa);
1102 }
1103
1104send_resp:
1105 hostapd_drv_send_action(hapd, hapd->iface->freq, 0, sa,
1106 wpabuf_head(tx_buf), wpabuf_len(tx_buf));
1107 wpabuf_free(tx_buf);
1108 return;
1109
1110rx_gas_comeback_req_done:
1111 gas_serv_clear_cached_ies(dialog, NULL);
1112}
1113
1114
1115static void gas_serv_rx_public_action(void *ctx, const u8 *buf, size_t len,
1116 int freq)
1117{
1118 struct hostapd_data *hapd = ctx;
1119 const struct ieee80211_mgmt *mgmt;
1120 size_t hdr_len;
1121 const u8 *sa, *data;
1122
1123 mgmt = (const struct ieee80211_mgmt *) buf;
1124 hdr_len = (const u8 *) &mgmt->u.action.u.vs_public_action.action - buf;
1125 if (hdr_len > len)
1126 return;
1127 if (mgmt->u.action.category != WLAN_ACTION_PUBLIC)
1128 return;
1129 sa = mgmt->sa;
1130 len -= hdr_len;
1131 data = &mgmt->u.action.u.public_action.action;
1132 switch (data[0]) {
1133 case WLAN_PA_GAS_INITIAL_REQ:
1134 gas_serv_rx_gas_initial_req(hapd, sa, data + 1, len - 1);
1135 break;
1136 case WLAN_PA_GAS_COMEBACK_REQ:
1137 gas_serv_rx_gas_comeback_req(hapd, sa, data + 1, len - 1);
1138 break;
1139 }
1140}
1141
1142
1143int gas_serv_init(struct hostapd_data *hapd)
1144{
1145 hapd->public_action_cb = gas_serv_rx_public_action;
1146 hapd->public_action_cb_ctx = hapd;
1147 hapd->gas_frag_limit = 1400;
1148 if (hapd->conf->gas_frag_limit > 0)
1149 hapd->gas_frag_limit = hapd->conf->gas_frag_limit;
1150 return 0;
1151}
1152
1153
1154void gas_serv_deinit(struct hostapd_data *hapd)
1155{
1156}