blob: fd1041eac11392d81b7753a928628b224a74420d [file] [log] [blame]
Dmitry Shmidt04949592012-07-19 12:16:46 -07001/*
2 * Generic advertisement service (GAS) server
Dmitry Shmidt18463232014-01-24 12:29:41 -08003 * Copyright (c) 2011-2014, Qualcomm Atheros, Inc.
Dmitry Shmidt04949592012-07-19 12:16:46 -07004 *
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
Dmitry Shmidt18463232014-01-24 12:29:41 -080022static void convert_to_protected_dual(struct wpabuf *msg)
23{
24 u8 *categ = wpabuf_mhead_u8(msg);
25 *categ = WLAN_ACTION_PROTECTED_DUAL;
26}
27
28
Dmitry Shmidt04949592012-07-19 12:16:46 -070029static struct gas_dialog_info *
30gas_dialog_create(struct hostapd_data *hapd, const u8 *addr, u8 dialog_token)
31{
32 struct sta_info *sta;
33 struct gas_dialog_info *dia = NULL;
34 int i, j;
35
36 sta = ap_get_sta(hapd, addr);
37 if (!sta) {
38 /*
39 * We need a STA entry to be able to maintain state for
40 * the GAS query.
41 */
42 wpa_printf(MSG_DEBUG, "ANQP: Add a temporary STA entry for "
43 "GAS query");
44 sta = ap_sta_add(hapd, addr);
45 if (!sta) {
46 wpa_printf(MSG_DEBUG, "Failed to add STA " MACSTR
47 " for GAS query", MAC2STR(addr));
48 return NULL;
49 }
50 sta->flags |= WLAN_STA_GAS;
51 /*
52 * The default inactivity is 300 seconds. We don't need
53 * it to be that long.
54 */
55 ap_sta_session_timeout(hapd, sta, 5);
Dmitry Shmidt54605472013-11-08 11:10:19 -080056 } else {
57 ap_sta_replenish_timeout(hapd, sta, 5);
Dmitry Shmidt04949592012-07-19 12:16:46 -070058 }
59
60 if (sta->gas_dialog == NULL) {
61 sta->gas_dialog = os_zalloc(GAS_DIALOG_MAX *
62 sizeof(struct gas_dialog_info));
63 if (sta->gas_dialog == NULL)
64 return NULL;
65 }
66
67 for (i = sta->gas_dialog_next, j = 0; j < GAS_DIALOG_MAX; i++, j++) {
68 if (i == GAS_DIALOG_MAX)
69 i = 0;
70 if (sta->gas_dialog[i].valid)
71 continue;
72 dia = &sta->gas_dialog[i];
73 dia->valid = 1;
74 dia->index = i;
75 dia->dialog_token = dialog_token;
76 sta->gas_dialog_next = (++i == GAS_DIALOG_MAX) ? 0 : i;
77 return dia;
78 }
79
80 wpa_msg(hapd->msg_ctx, MSG_ERROR, "ANQP: Could not create dialog for "
81 MACSTR " dialog_token %u. Consider increasing "
82 "GAS_DIALOG_MAX.", MAC2STR(addr), dialog_token);
83
84 return NULL;
85}
86
87
88struct gas_dialog_info *
89gas_serv_dialog_find(struct hostapd_data *hapd, const u8 *addr,
90 u8 dialog_token)
91{
92 struct sta_info *sta;
93 int i;
94
95 sta = ap_get_sta(hapd, addr);
96 if (!sta) {
97 wpa_printf(MSG_DEBUG, "ANQP: could not find STA " MACSTR,
98 MAC2STR(addr));
99 return NULL;
100 }
101 for (i = 0; sta->gas_dialog && i < GAS_DIALOG_MAX; i++) {
102 if (sta->gas_dialog[i].dialog_token != dialog_token ||
103 !sta->gas_dialog[i].valid)
104 continue;
105 return &sta->gas_dialog[i];
106 }
107 wpa_printf(MSG_DEBUG, "ANQP: Could not find dialog for "
108 MACSTR " dialog_token %u", MAC2STR(addr), dialog_token);
109 return NULL;
110}
111
112
113void gas_serv_dialog_clear(struct gas_dialog_info *dia)
114{
115 wpabuf_free(dia->sd_resp);
116 os_memset(dia, 0, sizeof(*dia));
117}
118
119
120static void gas_serv_free_dialogs(struct hostapd_data *hapd,
121 const u8 *sta_addr)
122{
123 struct sta_info *sta;
124 int i;
125
126 sta = ap_get_sta(hapd, sta_addr);
127 if (sta == NULL || sta->gas_dialog == NULL)
128 return;
129
130 for (i = 0; i < GAS_DIALOG_MAX; i++) {
131 if (sta->gas_dialog[i].valid)
132 return;
133 }
134
135 os_free(sta->gas_dialog);
136 sta->gas_dialog = NULL;
137}
138
139
Dmitry Shmidtaa532512012-09-24 10:35:31 -0700140#ifdef CONFIG_HS20
Dmitry Shmidt61d9df32012-08-29 16:22:06 -0700141static void anqp_add_hs_capab_list(struct hostapd_data *hapd,
142 struct wpabuf *buf)
143{
144 u8 *len;
145
146 len = gas_anqp_add_element(buf, ANQP_VENDOR_SPECIFIC);
147 wpabuf_put_be24(buf, OUI_WFA);
148 wpabuf_put_u8(buf, HS20_ANQP_OUI_TYPE);
149 wpabuf_put_u8(buf, HS20_STYPE_CAPABILITY_LIST);
150 wpabuf_put_u8(buf, 0); /* Reserved */
151 wpabuf_put_u8(buf, HS20_STYPE_CAPABILITY_LIST);
152 if (hapd->conf->hs20_oper_friendly_name)
153 wpabuf_put_u8(buf, HS20_STYPE_OPERATOR_FRIENDLY_NAME);
154 if (hapd->conf->hs20_wan_metrics)
155 wpabuf_put_u8(buf, HS20_STYPE_WAN_METRICS);
156 if (hapd->conf->hs20_connection_capability)
157 wpabuf_put_u8(buf, HS20_STYPE_CONNECTION_CAPABILITY);
158 if (hapd->conf->nai_realm_data)
159 wpabuf_put_u8(buf, HS20_STYPE_NAI_HOME_REALM_QUERY);
160 if (hapd->conf->hs20_operating_class)
161 wpabuf_put_u8(buf, HS20_STYPE_OPERATING_CLASS);
Dmitry Shmidtf21452a2014-02-26 10:55:25 -0800162 if (hapd->conf->hs20_osu_providers_count)
163 wpabuf_put_u8(buf, HS20_STYPE_OSU_PROVIDERS_LIST);
164 if (hapd->conf->hs20_icons_count)
165 wpabuf_put_u8(buf, HS20_STYPE_ICON_REQUEST);
Dmitry Shmidt61d9df32012-08-29 16:22:06 -0700166 gas_anqp_set_element_len(buf, len);
167}
Dmitry Shmidtaa532512012-09-24 10:35:31 -0700168#endif /* CONFIG_HS20 */
Dmitry Shmidt61d9df32012-08-29 16:22:06 -0700169
170
Dmitry Shmidt04949592012-07-19 12:16:46 -0700171static void anqp_add_capab_list(struct hostapd_data *hapd,
172 struct wpabuf *buf)
173{
174 u8 *len;
175
176 len = gas_anqp_add_element(buf, ANQP_CAPABILITY_LIST);
177 wpabuf_put_le16(buf, ANQP_CAPABILITY_LIST);
178 if (hapd->conf->venue_name)
179 wpabuf_put_le16(buf, ANQP_VENUE_NAME);
Dmitry Shmidt61d9df32012-08-29 16:22:06 -0700180 if (hapd->conf->network_auth_type)
181 wpabuf_put_le16(buf, ANQP_NETWORK_AUTH_TYPE);
Dmitry Shmidt04949592012-07-19 12:16:46 -0700182 if (hapd->conf->roaming_consortium)
183 wpabuf_put_le16(buf, ANQP_ROAMING_CONSORTIUM);
Dmitry Shmidt61d9df32012-08-29 16:22:06 -0700184 if (hapd->conf->ipaddr_type_configured)
185 wpabuf_put_le16(buf, ANQP_IP_ADDR_TYPE_AVAILABILITY);
186 if (hapd->conf->nai_realm_data)
187 wpabuf_put_le16(buf, ANQP_NAI_REALM);
188 if (hapd->conf->anqp_3gpp_cell_net)
189 wpabuf_put_le16(buf, ANQP_3GPP_CELLULAR_NETWORK);
190 if (hapd->conf->domain_name)
191 wpabuf_put_le16(buf, ANQP_DOMAIN_NAME);
Dmitry Shmidtaa532512012-09-24 10:35:31 -0700192#ifdef CONFIG_HS20
Dmitry Shmidt61d9df32012-08-29 16:22:06 -0700193 anqp_add_hs_capab_list(hapd, buf);
Dmitry Shmidtaa532512012-09-24 10:35:31 -0700194#endif /* CONFIG_HS20 */
Dmitry Shmidt04949592012-07-19 12:16:46 -0700195 gas_anqp_set_element_len(buf, len);
196}
197
198
199static void anqp_add_venue_name(struct hostapd_data *hapd, struct wpabuf *buf)
200{
201 if (hapd->conf->venue_name) {
202 u8 *len;
203 unsigned int i;
204 len = gas_anqp_add_element(buf, ANQP_VENUE_NAME);
205 wpabuf_put_u8(buf, hapd->conf->venue_group);
206 wpabuf_put_u8(buf, hapd->conf->venue_type);
207 for (i = 0; i < hapd->conf->venue_name_count; i++) {
Dmitry Shmidt61d9df32012-08-29 16:22:06 -0700208 struct hostapd_lang_string *vn;
Dmitry Shmidt04949592012-07-19 12:16:46 -0700209 vn = &hapd->conf->venue_name[i];
210 wpabuf_put_u8(buf, 3 + vn->name_len);
211 wpabuf_put_data(buf, vn->lang, 3);
212 wpabuf_put_data(buf, vn->name, vn->name_len);
213 }
214 gas_anqp_set_element_len(buf, len);
215 }
216}
217
218
Dmitry Shmidt61d9df32012-08-29 16:22:06 -0700219static void anqp_add_network_auth_type(struct hostapd_data *hapd,
220 struct wpabuf *buf)
221{
222 if (hapd->conf->network_auth_type) {
223 wpabuf_put_le16(buf, ANQP_NETWORK_AUTH_TYPE);
224 wpabuf_put_le16(buf, hapd->conf->network_auth_type_len);
225 wpabuf_put_data(buf, hapd->conf->network_auth_type,
226 hapd->conf->network_auth_type_len);
227 }
228}
229
230
Dmitry Shmidt04949592012-07-19 12:16:46 -0700231static void anqp_add_roaming_consortium(struct hostapd_data *hapd,
232 struct wpabuf *buf)
233{
234 unsigned int i;
235 u8 *len;
236
237 len = gas_anqp_add_element(buf, ANQP_ROAMING_CONSORTIUM);
238 for (i = 0; i < hapd->conf->roaming_consortium_count; i++) {
239 struct hostapd_roaming_consortium *rc;
240 rc = &hapd->conf->roaming_consortium[i];
241 wpabuf_put_u8(buf, rc->len);
242 wpabuf_put_data(buf, rc->oi, rc->len);
243 }
244 gas_anqp_set_element_len(buf, len);
245}
246
247
Dmitry Shmidt61d9df32012-08-29 16:22:06 -0700248static void anqp_add_ip_addr_type_availability(struct hostapd_data *hapd,
249 struct wpabuf *buf)
250{
251 if (hapd->conf->ipaddr_type_configured) {
252 wpabuf_put_le16(buf, ANQP_IP_ADDR_TYPE_AVAILABILITY);
253 wpabuf_put_le16(buf, 1);
254 wpabuf_put_u8(buf, hapd->conf->ipaddr_type_availability);
255 }
256}
257
258
259static void anqp_add_nai_realm_eap(struct wpabuf *buf,
260 struct hostapd_nai_realm_data *realm)
261{
262 unsigned int i, j;
263
264 wpabuf_put_u8(buf, realm->eap_method_count);
265
266 for (i = 0; i < realm->eap_method_count; i++) {
267 struct hostapd_nai_realm_eap *eap = &realm->eap_method[i];
268 wpabuf_put_u8(buf, 2 + (3 * eap->num_auths));
269 wpabuf_put_u8(buf, eap->eap_method);
270 wpabuf_put_u8(buf, eap->num_auths);
271 for (j = 0; j < eap->num_auths; j++) {
272 wpabuf_put_u8(buf, eap->auth_id[j]);
273 wpabuf_put_u8(buf, 1);
274 wpabuf_put_u8(buf, eap->auth_val[j]);
275 }
276 }
277}
278
279
280static void anqp_add_nai_realm_data(struct wpabuf *buf,
281 struct hostapd_nai_realm_data *realm,
282 unsigned int realm_idx)
283{
284 u8 *realm_data_len;
285
286 wpa_printf(MSG_DEBUG, "realm=%s, len=%d", realm->realm[realm_idx],
287 (int) os_strlen(realm->realm[realm_idx]));
288 realm_data_len = wpabuf_put(buf, 2);
289 wpabuf_put_u8(buf, realm->encoding);
290 wpabuf_put_u8(buf, os_strlen(realm->realm[realm_idx]));
291 wpabuf_put_str(buf, realm->realm[realm_idx]);
292 anqp_add_nai_realm_eap(buf, realm);
293 gas_anqp_set_element_len(buf, realm_data_len);
294}
295
296
297static int hs20_add_nai_home_realm_matches(struct hostapd_data *hapd,
298 struct wpabuf *buf,
299 const u8 *home_realm,
300 size_t home_realm_len)
301{
302 unsigned int i, j, k;
303 u8 num_realms, num_matching = 0, encoding, realm_len, *realm_list_len;
304 struct hostapd_nai_realm_data *realm;
305 const u8 *pos, *realm_name, *end;
306 struct {
307 unsigned int realm_data_idx;
308 unsigned int realm_idx;
309 } matches[10];
310
311 pos = home_realm;
312 end = pos + home_realm_len;
313 if (pos + 1 > end) {
314 wpa_hexdump(MSG_DEBUG, "Too short NAI Home Realm Query",
315 home_realm, home_realm_len);
316 return -1;
317 }
318 num_realms = *pos++;
319
320 for (i = 0; i < num_realms && num_matching < 10; i++) {
321 if (pos + 2 > end) {
322 wpa_hexdump(MSG_DEBUG,
323 "Truncated NAI Home Realm Query",
324 home_realm, home_realm_len);
325 return -1;
326 }
327 encoding = *pos++;
328 realm_len = *pos++;
329 if (pos + realm_len > end) {
330 wpa_hexdump(MSG_DEBUG,
331 "Truncated NAI Home Realm Query",
332 home_realm, home_realm_len);
333 return -1;
334 }
335 realm_name = pos;
336 for (j = 0; j < hapd->conf->nai_realm_count &&
337 num_matching < 10; j++) {
338 const u8 *rpos, *rend;
339 realm = &hapd->conf->nai_realm_data[j];
340 if (encoding != realm->encoding)
341 continue;
342
343 rpos = realm_name;
344 while (rpos < realm_name + realm_len &&
345 num_matching < 10) {
346 for (rend = rpos;
347 rend < realm_name + realm_len; rend++) {
348 if (*rend == ';')
349 break;
350 }
351 for (k = 0; k < MAX_NAI_REALMS &&
352 realm->realm[k] &&
353 num_matching < 10; k++) {
354 if ((int) os_strlen(realm->realm[k]) !=
355 rend - rpos ||
356 os_strncmp((char *) rpos,
357 realm->realm[k],
358 rend - rpos) != 0)
359 continue;
360 matches[num_matching].realm_data_idx =
361 j;
362 matches[num_matching].realm_idx = k;
363 num_matching++;
364 }
365 rpos = rend + 1;
366 }
367 }
368 pos += realm_len;
369 }
370
371 realm_list_len = gas_anqp_add_element(buf, ANQP_NAI_REALM);
372 wpabuf_put_le16(buf, num_matching);
373
374 /*
375 * There are two ways to format. 1. each realm in a NAI Realm Data unit
376 * 2. all realms that share the same EAP methods in a NAI Realm Data
377 * unit. The first format is likely to be bigger in size than the
378 * second, but may be easier to parse and process by the receiver.
379 */
380 for (i = 0; i < num_matching; i++) {
381 wpa_printf(MSG_DEBUG, "realm_idx %d, realm_data_idx %d",
382 matches[i].realm_data_idx, matches[i].realm_idx);
383 realm = &hapd->conf->nai_realm_data[matches[i].realm_data_idx];
384 anqp_add_nai_realm_data(buf, realm, matches[i].realm_idx);
385 }
386 gas_anqp_set_element_len(buf, realm_list_len);
387 return 0;
388}
389
390
391static void anqp_add_nai_realm(struct hostapd_data *hapd, struct wpabuf *buf,
392 const u8 *home_realm, size_t home_realm_len,
393 int nai_realm, int nai_home_realm)
394{
395 if (nai_realm && hapd->conf->nai_realm_data) {
396 u8 *len;
397 unsigned int i, j;
398 len = gas_anqp_add_element(buf, ANQP_NAI_REALM);
399 wpabuf_put_le16(buf, hapd->conf->nai_realm_count);
400 for (i = 0; i < hapd->conf->nai_realm_count; i++) {
401 u8 *realm_data_len, *realm_len;
402 struct hostapd_nai_realm_data *realm;
403
404 realm = &hapd->conf->nai_realm_data[i];
405 realm_data_len = wpabuf_put(buf, 2);
406 wpabuf_put_u8(buf, realm->encoding);
407 realm_len = wpabuf_put(buf, 1);
408 for (j = 0; realm->realm[j]; j++) {
409 if (j > 0)
410 wpabuf_put_u8(buf, ';');
411 wpabuf_put_str(buf, realm->realm[j]);
412 }
413 *realm_len = (u8 *) wpabuf_put(buf, 0) - realm_len - 1;
414 anqp_add_nai_realm_eap(buf, realm);
415 gas_anqp_set_element_len(buf, realm_data_len);
416 }
417 gas_anqp_set_element_len(buf, len);
418 } else if (nai_home_realm && hapd->conf->nai_realm_data) {
419 hs20_add_nai_home_realm_matches(hapd, buf, home_realm,
420 home_realm_len);
421 }
422}
423
424
425static void anqp_add_3gpp_cellular_network(struct hostapd_data *hapd,
426 struct wpabuf *buf)
427{
428 if (hapd->conf->anqp_3gpp_cell_net) {
429 wpabuf_put_le16(buf, ANQP_3GPP_CELLULAR_NETWORK);
430 wpabuf_put_le16(buf,
431 hapd->conf->anqp_3gpp_cell_net_len);
432 wpabuf_put_data(buf, hapd->conf->anqp_3gpp_cell_net,
433 hapd->conf->anqp_3gpp_cell_net_len);
434 }
435}
436
437
438static void anqp_add_domain_name(struct hostapd_data *hapd, struct wpabuf *buf)
439{
440 if (hapd->conf->domain_name) {
441 wpabuf_put_le16(buf, ANQP_DOMAIN_NAME);
442 wpabuf_put_le16(buf, hapd->conf->domain_name_len);
443 wpabuf_put_data(buf, hapd->conf->domain_name,
444 hapd->conf->domain_name_len);
445 }
446}
447
448
Dmitry Shmidtaa532512012-09-24 10:35:31 -0700449#ifdef CONFIG_HS20
450
Dmitry Shmidt61d9df32012-08-29 16:22:06 -0700451static void anqp_add_operator_friendly_name(struct hostapd_data *hapd,
452 struct wpabuf *buf)
453{
454 if (hapd->conf->hs20_oper_friendly_name) {
455 u8 *len;
456 unsigned int i;
457 len = gas_anqp_add_element(buf, ANQP_VENDOR_SPECIFIC);
458 wpabuf_put_be24(buf, OUI_WFA);
459 wpabuf_put_u8(buf, HS20_ANQP_OUI_TYPE);
460 wpabuf_put_u8(buf, HS20_STYPE_OPERATOR_FRIENDLY_NAME);
461 wpabuf_put_u8(buf, 0); /* Reserved */
462 for (i = 0; i < hapd->conf->hs20_oper_friendly_name_count; i++)
463 {
464 struct hostapd_lang_string *vn;
465 vn = &hapd->conf->hs20_oper_friendly_name[i];
466 wpabuf_put_u8(buf, 3 + vn->name_len);
467 wpabuf_put_data(buf, vn->lang, 3);
468 wpabuf_put_data(buf, vn->name, vn->name_len);
469 }
470 gas_anqp_set_element_len(buf, len);
471 }
472}
473
474
475static void anqp_add_wan_metrics(struct hostapd_data *hapd,
476 struct wpabuf *buf)
477{
478 if (hapd->conf->hs20_wan_metrics) {
479 u8 *len = gas_anqp_add_element(buf, ANQP_VENDOR_SPECIFIC);
480 wpabuf_put_be24(buf, OUI_WFA);
481 wpabuf_put_u8(buf, HS20_ANQP_OUI_TYPE);
482 wpabuf_put_u8(buf, HS20_STYPE_WAN_METRICS);
483 wpabuf_put_u8(buf, 0); /* Reserved */
484 wpabuf_put_data(buf, hapd->conf->hs20_wan_metrics, 13);
485 gas_anqp_set_element_len(buf, len);
486 }
487}
488
489
490static void anqp_add_connection_capability(struct hostapd_data *hapd,
491 struct wpabuf *buf)
492{
493 if (hapd->conf->hs20_connection_capability) {
494 u8 *len = gas_anqp_add_element(buf, ANQP_VENDOR_SPECIFIC);
495 wpabuf_put_be24(buf, OUI_WFA);
496 wpabuf_put_u8(buf, HS20_ANQP_OUI_TYPE);
497 wpabuf_put_u8(buf, HS20_STYPE_CONNECTION_CAPABILITY);
498 wpabuf_put_u8(buf, 0); /* Reserved */
499 wpabuf_put_data(buf, hapd->conf->hs20_connection_capability,
500 hapd->conf->hs20_connection_capability_len);
501 gas_anqp_set_element_len(buf, len);
502 }
503}
504
505
506static void anqp_add_operating_class(struct hostapd_data *hapd,
507 struct wpabuf *buf)
508{
509 if (hapd->conf->hs20_operating_class) {
510 u8 *len = gas_anqp_add_element(buf, ANQP_VENDOR_SPECIFIC);
511 wpabuf_put_be24(buf, OUI_WFA);
512 wpabuf_put_u8(buf, HS20_ANQP_OUI_TYPE);
513 wpabuf_put_u8(buf, HS20_STYPE_OPERATING_CLASS);
514 wpabuf_put_u8(buf, 0); /* Reserved */
515 wpabuf_put_data(buf, hapd->conf->hs20_operating_class,
516 hapd->conf->hs20_operating_class_len);
517 gas_anqp_set_element_len(buf, len);
518 }
519}
520
Dmitry Shmidtf21452a2014-02-26 10:55:25 -0800521
522static void anqp_add_osu_provider(struct wpabuf *buf,
523 struct hostapd_bss_config *bss,
524 struct hs20_osu_provider *p)
525{
526 u8 *len, *len2, *count;
527 unsigned int i;
528
529 len = wpabuf_put(buf, 2); /* OSU Provider Length to be filled */
530
531 /* OSU Friendly Name Duples */
532 len2 = wpabuf_put(buf, 2);
533 for (i = 0; i < p->friendly_name_count; i++) {
534 struct hostapd_lang_string *s = &p->friendly_name[i];
535 wpabuf_put_u8(buf, 3 + s->name_len);
536 wpabuf_put_data(buf, s->lang, 3);
537 wpabuf_put_data(buf, s->name, s->name_len);
538 }
539 WPA_PUT_LE16(len2, (u8 *) wpabuf_put(buf, 0) - len2 - 2);
540
541 /* OSU Server URI */
542 if (p->server_uri) {
543 wpabuf_put_u8(buf, os_strlen(p->server_uri));
544 wpabuf_put_str(buf, p->server_uri);
545 } else
546 wpabuf_put_u8(buf, 0);
547
548 /* OSU Method List */
549 count = wpabuf_put(buf, 1);
550 for (i = 0; p->method_list[i] >= 0; i++)
551 wpabuf_put_u8(buf, p->method_list[i]);
552 *count = i;
553
554 /* Icons Available */
555 len2 = wpabuf_put(buf, 2);
556 for (i = 0; i < p->icons_count; i++) {
557 size_t j;
558 struct hs20_icon *icon = NULL;
559
560 for (j = 0; j < bss->hs20_icons_count && !icon; j++) {
561 if (os_strcmp(p->icons[i], bss->hs20_icons[j].name) ==
562 0)
563 icon = &bss->hs20_icons[j];
564 }
565 if (!icon)
566 continue; /* icon info not found */
567
568 wpabuf_put_le16(buf, icon->width);
569 wpabuf_put_le16(buf, icon->height);
570 wpabuf_put_data(buf, icon->language, 3);
571 wpabuf_put_u8(buf, os_strlen(icon->type));
572 wpabuf_put_str(buf, icon->type);
573 wpabuf_put_u8(buf, os_strlen(icon->name));
574 wpabuf_put_str(buf, icon->name);
575 }
576 WPA_PUT_LE16(len2, (u8 *) wpabuf_put(buf, 0) - len2 - 2);
577
578 /* OSU_NAI */
579 if (p->osu_nai) {
580 wpabuf_put_u8(buf, os_strlen(p->osu_nai));
581 wpabuf_put_str(buf, p->osu_nai);
582 } else
583 wpabuf_put_u8(buf, 0);
584
585 /* OSU Service Description Duples */
586 len2 = wpabuf_put(buf, 2);
587 for (i = 0; i < p->service_desc_count; i++) {
588 struct hostapd_lang_string *s = &p->service_desc[i];
589 wpabuf_put_u8(buf, 3 + s->name_len);
590 wpabuf_put_data(buf, s->lang, 3);
591 wpabuf_put_data(buf, s->name, s->name_len);
592 }
593 WPA_PUT_LE16(len2, (u8 *) wpabuf_put(buf, 0) - len2 - 2);
594
595 WPA_PUT_LE16(len, (u8 *) wpabuf_put(buf, 0) - len - 2);
596}
597
598
599static void anqp_add_osu_providers_list(struct hostapd_data *hapd,
600 struct wpabuf *buf)
601{
602 if (hapd->conf->hs20_osu_providers_count) {
603 size_t i;
604 u8 *len = gas_anqp_add_element(buf, ANQP_VENDOR_SPECIFIC);
605 wpabuf_put_be24(buf, OUI_WFA);
606 wpabuf_put_u8(buf, HS20_ANQP_OUI_TYPE);
607 wpabuf_put_u8(buf, HS20_STYPE_OSU_PROVIDERS_LIST);
608 wpabuf_put_u8(buf, 0); /* Reserved */
609
610 /* OSU SSID */
611 wpabuf_put_u8(buf, hapd->conf->osu_ssid_len);
612 wpabuf_put_data(buf, hapd->conf->osu_ssid,
613 hapd->conf->osu_ssid_len);
614
615 /* Number of OSU Providers */
616 wpabuf_put_u8(buf, hapd->conf->hs20_osu_providers_count);
617
618 for (i = 0; i < hapd->conf->hs20_osu_providers_count; i++) {
619 anqp_add_osu_provider(
620 buf, hapd->conf,
621 &hapd->conf->hs20_osu_providers[i]);
622 }
623
624 gas_anqp_set_element_len(buf, len);
625 }
626}
627
628
629static void anqp_add_icon_binary_file(struct hostapd_data *hapd,
630 struct wpabuf *buf,
631 const u8 *name, size_t name_len)
632{
633 struct hs20_icon *icon;
634 size_t i;
635 u8 *len;
636
637 wpa_hexdump_ascii(MSG_DEBUG, "HS 2.0: Requested Icon Filename",
638 name, name_len);
639 for (i = 0; i < hapd->conf->hs20_icons_count; i++) {
640 icon = &hapd->conf->hs20_icons[i];
641 if (name_len == os_strlen(icon->name) &&
642 os_memcmp(name, icon->name, name_len) == 0)
643 break;
644 }
645
646 if (i < hapd->conf->hs20_icons_count)
647 icon = &hapd->conf->hs20_icons[i];
648 else
649 icon = NULL;
650
651 len = gas_anqp_add_element(buf, ANQP_VENDOR_SPECIFIC);
652 wpabuf_put_be24(buf, OUI_WFA);
653 wpabuf_put_u8(buf, HS20_ANQP_OUI_TYPE);
654 wpabuf_put_u8(buf, HS20_STYPE_ICON_BINARY_FILE);
655 wpabuf_put_u8(buf, 0); /* Reserved */
656
657 if (icon) {
658 char *data;
659 size_t data_len;
660
661 data = os_readfile(icon->file, &data_len);
662 if (data == NULL || data_len > 65535) {
663 wpabuf_put_u8(buf, 2); /* Download Status:
664 * Unspecified file error */
665 wpabuf_put_u8(buf, 0);
666 wpabuf_put_le16(buf, 0);
667 } else {
668 wpabuf_put_u8(buf, 0); /* Download Status: Success */
669 wpabuf_put_u8(buf, os_strlen(icon->type));
670 wpabuf_put_str(buf, icon->type);
671 wpabuf_put_le16(buf, data_len);
672 wpabuf_put_data(buf, data, data_len);
673 }
674 os_free(data);
675 } else {
676 wpabuf_put_u8(buf, 1); /* Download Status: File not found */
677 wpabuf_put_u8(buf, 0);
678 wpabuf_put_le16(buf, 0);
679 }
680
681 gas_anqp_set_element_len(buf, len);
682}
683
Dmitry Shmidtaa532512012-09-24 10:35:31 -0700684#endif /* CONFIG_HS20 */
685
Dmitry Shmidt61d9df32012-08-29 16:22:06 -0700686
Dmitry Shmidt04949592012-07-19 12:16:46 -0700687static struct wpabuf *
688gas_serv_build_gas_resp_payload(struct hostapd_data *hapd,
689 unsigned int request,
Dmitry Shmidt61d9df32012-08-29 16:22:06 -0700690 struct gas_dialog_info *di,
Dmitry Shmidtf21452a2014-02-26 10:55:25 -0800691 const u8 *home_realm, size_t home_realm_len,
692 const u8 *icon_name, size_t icon_name_len)
Dmitry Shmidt04949592012-07-19 12:16:46 -0700693{
694 struct wpabuf *buf;
Dmitry Shmidtf21452a2014-02-26 10:55:25 -0800695 size_t len;
Dmitry Shmidt04949592012-07-19 12:16:46 -0700696
Dmitry Shmidtf21452a2014-02-26 10:55:25 -0800697 len = 1400;
698 if (request & (ANQP_REQ_NAI_REALM | ANQP_REQ_NAI_HOME_REALM))
699 len += 1000;
700 if (request & ANQP_REQ_ICON_REQUEST)
701 len += 65536;
702
703 buf = wpabuf_alloc(len);
Dmitry Shmidt04949592012-07-19 12:16:46 -0700704 if (buf == NULL)
705 return NULL;
706
707 if (request & ANQP_REQ_CAPABILITY_LIST)
708 anqp_add_capab_list(hapd, buf);
709 if (request & ANQP_REQ_VENUE_NAME)
710 anqp_add_venue_name(hapd, buf);
Dmitry Shmidt61d9df32012-08-29 16:22:06 -0700711 if (request & ANQP_REQ_NETWORK_AUTH_TYPE)
712 anqp_add_network_auth_type(hapd, buf);
Dmitry Shmidt04949592012-07-19 12:16:46 -0700713 if (request & ANQP_REQ_ROAMING_CONSORTIUM)
714 anqp_add_roaming_consortium(hapd, buf);
Dmitry Shmidt61d9df32012-08-29 16:22:06 -0700715 if (request & ANQP_REQ_IP_ADDR_TYPE_AVAILABILITY)
716 anqp_add_ip_addr_type_availability(hapd, buf);
717 if (request & (ANQP_REQ_NAI_REALM | ANQP_REQ_NAI_HOME_REALM))
718 anqp_add_nai_realm(hapd, buf, home_realm, home_realm_len,
719 request & ANQP_REQ_NAI_REALM,
720 request & ANQP_REQ_NAI_HOME_REALM);
721 if (request & ANQP_REQ_3GPP_CELLULAR_NETWORK)
722 anqp_add_3gpp_cellular_network(hapd, buf);
723 if (request & ANQP_REQ_DOMAIN_NAME)
724 anqp_add_domain_name(hapd, buf);
725
Dmitry Shmidtaa532512012-09-24 10:35:31 -0700726#ifdef CONFIG_HS20
Dmitry Shmidt61d9df32012-08-29 16:22:06 -0700727 if (request & ANQP_REQ_HS_CAPABILITY_LIST)
728 anqp_add_hs_capab_list(hapd, buf);
729 if (request & ANQP_REQ_OPERATOR_FRIENDLY_NAME)
730 anqp_add_operator_friendly_name(hapd, buf);
731 if (request & ANQP_REQ_WAN_METRICS)
732 anqp_add_wan_metrics(hapd, buf);
733 if (request & ANQP_REQ_CONNECTION_CAPABILITY)
734 anqp_add_connection_capability(hapd, buf);
735 if (request & ANQP_REQ_OPERATING_CLASS)
736 anqp_add_operating_class(hapd, buf);
Dmitry Shmidtf21452a2014-02-26 10:55:25 -0800737 if (request & ANQP_REQ_OSU_PROVIDERS_LIST)
738 anqp_add_osu_providers_list(hapd, buf);
739 if (request & ANQP_REQ_ICON_REQUEST)
740 anqp_add_icon_binary_file(hapd, buf, icon_name, icon_name_len);
Dmitry Shmidtaa532512012-09-24 10:35:31 -0700741#endif /* CONFIG_HS20 */
Dmitry Shmidt04949592012-07-19 12:16:46 -0700742
743 return buf;
744}
745
746
747static void gas_serv_clear_cached_ies(void *eloop_data, void *user_ctx)
748{
749 struct gas_dialog_info *dia = eloop_data;
750
751 wpa_printf(MSG_DEBUG, "GAS: Timeout triggered, clearing dialog for "
752 "dialog token %d", dia->dialog_token);
753
754 gas_serv_dialog_clear(dia);
755}
756
757
758struct anqp_query_info {
759 unsigned int request;
760 unsigned int remote_request;
Dmitry Shmidt61d9df32012-08-29 16:22:06 -0700761 const u8 *home_realm_query;
762 size_t home_realm_query_len;
Dmitry Shmidtf21452a2014-02-26 10:55:25 -0800763 const u8 *icon_name;
764 size_t icon_name_len;
Dmitry Shmidt04949592012-07-19 12:16:46 -0700765 u16 remote_delay;
766};
767
768
769static void set_anqp_req(unsigned int bit, const char *name, int local,
770 unsigned int remote, u16 remote_delay,
771 struct anqp_query_info *qi)
772{
773 qi->request |= bit;
774 if (local) {
775 wpa_printf(MSG_DEBUG, "ANQP: %s (local)", name);
776 } else if (bit & remote) {
777 wpa_printf(MSG_DEBUG, "ANQP: %s (remote)", name);
778 qi->remote_request |= bit;
779 if (remote_delay > qi->remote_delay)
780 qi->remote_delay = remote_delay;
781 } else {
782 wpa_printf(MSG_DEBUG, "ANQP: %s not available", name);
783 }
784}
785
786
787static void rx_anqp_query_list_id(struct hostapd_data *hapd, u16 info_id,
788 struct anqp_query_info *qi)
789{
790 switch (info_id) {
791 case ANQP_CAPABILITY_LIST:
792 set_anqp_req(ANQP_REQ_CAPABILITY_LIST, "Capability List", 1, 0,
793 0, qi);
794 break;
795 case ANQP_VENUE_NAME:
796 set_anqp_req(ANQP_REQ_VENUE_NAME, "Venue Name",
797 hapd->conf->venue_name != NULL, 0, 0, qi);
798 break;
Dmitry Shmidt61d9df32012-08-29 16:22:06 -0700799 case ANQP_NETWORK_AUTH_TYPE:
800 set_anqp_req(ANQP_REQ_NETWORK_AUTH_TYPE, "Network Auth Type",
801 hapd->conf->network_auth_type != NULL,
802 0, 0, qi);
803 break;
Dmitry Shmidt04949592012-07-19 12:16:46 -0700804 case ANQP_ROAMING_CONSORTIUM:
805 set_anqp_req(ANQP_REQ_ROAMING_CONSORTIUM, "Roaming Consortium",
806 hapd->conf->roaming_consortium != NULL, 0, 0, qi);
807 break;
Dmitry Shmidt61d9df32012-08-29 16:22:06 -0700808 case ANQP_IP_ADDR_TYPE_AVAILABILITY:
809 set_anqp_req(ANQP_REQ_IP_ADDR_TYPE_AVAILABILITY,
810 "IP Addr Type Availability",
811 hapd->conf->ipaddr_type_configured,
812 0, 0, qi);
813 break;
814 case ANQP_NAI_REALM:
815 set_anqp_req(ANQP_REQ_NAI_REALM, "NAI Realm",
816 hapd->conf->nai_realm_data != NULL,
817 0, 0, qi);
818 break;
819 case ANQP_3GPP_CELLULAR_NETWORK:
820 set_anqp_req(ANQP_REQ_3GPP_CELLULAR_NETWORK,
821 "3GPP Cellular Network",
822 hapd->conf->anqp_3gpp_cell_net != NULL,
823 0, 0, qi);
824 break;
825 case ANQP_DOMAIN_NAME:
826 set_anqp_req(ANQP_REQ_DOMAIN_NAME, "Domain Name",
827 hapd->conf->domain_name != NULL,
828 0, 0, qi);
829 break;
Dmitry Shmidt04949592012-07-19 12:16:46 -0700830 default:
831 wpa_printf(MSG_DEBUG, "ANQP: Unsupported Info Id %u",
832 info_id);
833 break;
834 }
835}
836
837
838static void rx_anqp_query_list(struct hostapd_data *hapd,
839 const u8 *pos, const u8 *end,
840 struct anqp_query_info *qi)
841{
842 wpa_printf(MSG_DEBUG, "ANQP: %u Info IDs requested in Query list",
843 (unsigned int) (end - pos) / 2);
844
845 while (pos + 2 <= end) {
846 rx_anqp_query_list_id(hapd, WPA_GET_LE16(pos), qi);
847 pos += 2;
848 }
849}
850
851
Dmitry Shmidtaa532512012-09-24 10:35:31 -0700852#ifdef CONFIG_HS20
853
Dmitry Shmidt61d9df32012-08-29 16:22:06 -0700854static void rx_anqp_hs_query_list(struct hostapd_data *hapd, u8 subtype,
855 struct anqp_query_info *qi)
856{
857 switch (subtype) {
858 case HS20_STYPE_CAPABILITY_LIST:
859 set_anqp_req(ANQP_REQ_HS_CAPABILITY_LIST, "HS Capability List",
860 1, 0, 0, qi);
861 break;
862 case HS20_STYPE_OPERATOR_FRIENDLY_NAME:
863 set_anqp_req(ANQP_REQ_OPERATOR_FRIENDLY_NAME,
864 "Operator Friendly Name",
865 hapd->conf->hs20_oper_friendly_name != NULL,
866 0, 0, qi);
867 break;
868 case HS20_STYPE_WAN_METRICS:
869 set_anqp_req(ANQP_REQ_WAN_METRICS, "WAN Metrics",
870 hapd->conf->hs20_wan_metrics != NULL,
871 0, 0, qi);
872 break;
873 case HS20_STYPE_CONNECTION_CAPABILITY:
874 set_anqp_req(ANQP_REQ_CONNECTION_CAPABILITY,
875 "Connection Capability",
876 hapd->conf->hs20_connection_capability != NULL,
877 0, 0, qi);
878 break;
879 case HS20_STYPE_OPERATING_CLASS:
880 set_anqp_req(ANQP_REQ_OPERATING_CLASS, "Operating Class",
881 hapd->conf->hs20_operating_class != NULL,
882 0, 0, qi);
883 break;
Dmitry Shmidtf21452a2014-02-26 10:55:25 -0800884 case HS20_STYPE_OSU_PROVIDERS_LIST:
885 set_anqp_req(ANQP_REQ_OSU_PROVIDERS_LIST, "OSU Providers list",
886 hapd->conf->hs20_osu_providers_count, 0, 0, qi);
887 break;
Dmitry Shmidt61d9df32012-08-29 16:22:06 -0700888 default:
889 wpa_printf(MSG_DEBUG, "ANQP: Unsupported HS 2.0 subtype %u",
890 subtype);
891 break;
892 }
893}
894
895
896static void rx_anqp_hs_nai_home_realm(struct hostapd_data *hapd,
897 const u8 *pos, const u8 *end,
898 struct anqp_query_info *qi)
899{
900 qi->request |= ANQP_REQ_NAI_HOME_REALM;
901 qi->home_realm_query = pos;
902 qi->home_realm_query_len = end - pos;
903 if (hapd->conf->nai_realm_data != NULL) {
904 wpa_printf(MSG_DEBUG, "ANQP: HS 2.0 NAI Home Realm Query "
905 "(local)");
906 } else {
907 wpa_printf(MSG_DEBUG, "ANQP: HS 2.0 NAI Home Realm Query not "
908 "available");
909 }
910}
911
912
Dmitry Shmidtf21452a2014-02-26 10:55:25 -0800913static void rx_anqp_hs_icon_request(struct hostapd_data *hapd,
914 const u8 *pos, const u8 *end,
915 struct anqp_query_info *qi)
916{
917 qi->request |= ANQP_REQ_ICON_REQUEST;
918 qi->icon_name = pos;
919 qi->icon_name_len = end - pos;
920 if (hapd->conf->hs20_icons_count) {
921 wpa_printf(MSG_DEBUG, "ANQP: HS 2.0 Icon Request Query "
922 "(local)");
923 } else {
924 wpa_printf(MSG_DEBUG, "ANQP: HS 2.0 Icon Request Query not "
925 "available");
926 }
927}
928
929
Dmitry Shmidt61d9df32012-08-29 16:22:06 -0700930static void rx_anqp_vendor_specific(struct hostapd_data *hapd,
931 const u8 *pos, const u8 *end,
932 struct anqp_query_info *qi)
933{
934 u32 oui;
935 u8 subtype;
936
937 if (pos + 4 > end) {
938 wpa_printf(MSG_DEBUG, "ANQP: Too short vendor specific ANQP "
939 "Query element");
940 return;
941 }
942
943 oui = WPA_GET_BE24(pos);
944 pos += 3;
945 if (oui != OUI_WFA) {
946 wpa_printf(MSG_DEBUG, "ANQP: Unsupported vendor OUI %06x",
947 oui);
948 return;
949 }
950
951 if (*pos != HS20_ANQP_OUI_TYPE) {
952 wpa_printf(MSG_DEBUG, "ANQP: Unsupported WFA vendor type %u",
953 *pos);
954 return;
955 }
956 pos++;
957
958 if (pos + 1 >= end)
959 return;
960
961 subtype = *pos++;
962 pos++; /* Reserved */
963 switch (subtype) {
964 case HS20_STYPE_QUERY_LIST:
965 wpa_printf(MSG_DEBUG, "ANQP: HS 2.0 Query List");
966 while (pos < end) {
967 rx_anqp_hs_query_list(hapd, *pos, qi);
968 pos++;
969 }
970 break;
971 case HS20_STYPE_NAI_HOME_REALM_QUERY:
972 rx_anqp_hs_nai_home_realm(hapd, pos, end, qi);
973 break;
Dmitry Shmidtf21452a2014-02-26 10:55:25 -0800974 case HS20_STYPE_ICON_REQUEST:
975 rx_anqp_hs_icon_request(hapd, pos, end, qi);
976 break;
Dmitry Shmidt61d9df32012-08-29 16:22:06 -0700977 default:
978 wpa_printf(MSG_DEBUG, "ANQP: Unsupported HS 2.0 query subtype "
979 "%u", subtype);
980 break;
981 }
982}
983
Dmitry Shmidtaa532512012-09-24 10:35:31 -0700984#endif /* CONFIG_HS20 */
985
Dmitry Shmidt61d9df32012-08-29 16:22:06 -0700986
Dmitry Shmidt04949592012-07-19 12:16:46 -0700987static void gas_serv_req_local_processing(struct hostapd_data *hapd,
988 const u8 *sa, u8 dialog_token,
Dmitry Shmidt18463232014-01-24 12:29:41 -0800989 struct anqp_query_info *qi, int prot)
Dmitry Shmidt04949592012-07-19 12:16:46 -0700990{
991 struct wpabuf *buf, *tx_buf;
992
Dmitry Shmidt61d9df32012-08-29 16:22:06 -0700993 buf = gas_serv_build_gas_resp_payload(hapd, qi->request, NULL,
994 qi->home_realm_query,
Dmitry Shmidtf21452a2014-02-26 10:55:25 -0800995 qi->home_realm_query_len,
996 qi->icon_name, qi->icon_name_len);
Dmitry Shmidt04949592012-07-19 12:16:46 -0700997 wpa_hexdump_buf(MSG_MSGDUMP, "ANQP: Locally generated ANQP responses",
998 buf);
999 if (!buf)
1000 return;
1001
1002 if (wpabuf_len(buf) > hapd->gas_frag_limit ||
1003 hapd->conf->gas_comeback_delay) {
1004 struct gas_dialog_info *di;
1005 u16 comeback_delay = 1;
1006
1007 if (hapd->conf->gas_comeback_delay) {
1008 /* Testing - allow overriding of the delay value */
1009 comeback_delay = hapd->conf->gas_comeback_delay;
1010 }
1011
1012 wpa_printf(MSG_DEBUG, "ANQP: Too long response to fit in "
1013 "initial response - use GAS comeback");
1014 di = gas_dialog_create(hapd, sa, dialog_token);
1015 if (!di) {
1016 wpa_printf(MSG_INFO, "ANQP: Could not create dialog "
1017 "for " MACSTR " (dialog token %u)",
1018 MAC2STR(sa), dialog_token);
1019 wpabuf_free(buf);
1020 return;
1021 }
Dmitry Shmidt18463232014-01-24 12:29:41 -08001022 di->prot = prot;
Dmitry Shmidt04949592012-07-19 12:16:46 -07001023 di->sd_resp = buf;
1024 di->sd_resp_pos = 0;
1025 tx_buf = gas_anqp_build_initial_resp_buf(
1026 dialog_token, WLAN_STATUS_SUCCESS, comeback_delay,
1027 NULL);
1028 } else {
1029 wpa_printf(MSG_DEBUG, "ANQP: Initial response (no comeback)");
1030 tx_buf = gas_anqp_build_initial_resp_buf(
1031 dialog_token, WLAN_STATUS_SUCCESS, 0, buf);
1032 wpabuf_free(buf);
1033 }
1034 if (!tx_buf)
1035 return;
Dmitry Shmidt18463232014-01-24 12:29:41 -08001036 if (prot)
1037 convert_to_protected_dual(tx_buf);
Dmitry Shmidt04949592012-07-19 12:16:46 -07001038 hostapd_drv_send_action(hapd, hapd->iface->freq, 0, sa,
1039 wpabuf_head(tx_buf), wpabuf_len(tx_buf));
1040 wpabuf_free(tx_buf);
1041}
1042
1043
1044static void gas_serv_rx_gas_initial_req(struct hostapd_data *hapd,
1045 const u8 *sa,
Dmitry Shmidt18463232014-01-24 12:29:41 -08001046 const u8 *data, size_t len, int prot)
Dmitry Shmidt04949592012-07-19 12:16:46 -07001047{
1048 const u8 *pos = data;
1049 const u8 *end = data + len;
1050 const u8 *next;
1051 u8 dialog_token;
1052 u16 slen;
1053 struct anqp_query_info qi;
1054 const u8 *adv_proto;
1055
1056 if (len < 1 + 2)
1057 return;
1058
1059 os_memset(&qi, 0, sizeof(qi));
1060
1061 dialog_token = *pos++;
1062 wpa_msg(hapd->msg_ctx, MSG_DEBUG,
1063 "GAS: GAS Initial Request from " MACSTR " (dialog token %u) ",
1064 MAC2STR(sa), dialog_token);
1065
1066 if (*pos != WLAN_EID_ADV_PROTO) {
1067 wpa_msg(hapd->msg_ctx, MSG_DEBUG,
1068 "GAS: Unexpected IE in GAS Initial Request: %u", *pos);
1069 return;
1070 }
1071 adv_proto = pos++;
1072
1073 slen = *pos++;
1074 next = pos + slen;
1075 if (next > end || slen < 2) {
1076 wpa_msg(hapd->msg_ctx, MSG_DEBUG,
1077 "GAS: Invalid IE in GAS Initial Request");
1078 return;
1079 }
1080 pos++; /* skip QueryRespLenLimit and PAME-BI */
1081
1082 if (*pos != ACCESS_NETWORK_QUERY_PROTOCOL) {
1083 struct wpabuf *buf;
1084 wpa_msg(hapd->msg_ctx, MSG_DEBUG,
1085 "GAS: Unsupported GAS advertisement protocol id %u",
1086 *pos);
1087 if (sa[0] & 0x01)
1088 return; /* Invalid source address - drop silently */
1089 buf = gas_build_initial_resp(
1090 dialog_token, WLAN_STATUS_GAS_ADV_PROTO_NOT_SUPPORTED,
1091 0, 2 + slen + 2);
1092 if (buf == NULL)
1093 return;
1094 wpabuf_put_data(buf, adv_proto, 2 + slen);
1095 wpabuf_put_le16(buf, 0); /* Query Response Length */
Dmitry Shmidt18463232014-01-24 12:29:41 -08001096 if (prot)
1097 convert_to_protected_dual(buf);
Dmitry Shmidt04949592012-07-19 12:16:46 -07001098 hostapd_drv_send_action(hapd, hapd->iface->freq, 0, sa,
1099 wpabuf_head(buf), wpabuf_len(buf));
1100 wpabuf_free(buf);
1101 return;
1102 }
1103
1104 pos = next;
1105 /* Query Request */
1106 if (pos + 2 > end)
1107 return;
1108 slen = WPA_GET_LE16(pos);
1109 pos += 2;
1110 if (pos + slen > end)
1111 return;
1112 end = pos + slen;
1113
1114 /* ANQP Query Request */
1115 while (pos < end) {
1116 u16 info_id, elen;
1117
1118 if (pos + 4 > end)
1119 return;
1120
1121 info_id = WPA_GET_LE16(pos);
1122 pos += 2;
1123 elen = WPA_GET_LE16(pos);
1124 pos += 2;
1125
1126 if (pos + elen > end) {
1127 wpa_printf(MSG_DEBUG, "ANQP: Invalid Query Request");
1128 return;
1129 }
1130
1131 switch (info_id) {
1132 case ANQP_QUERY_LIST:
1133 rx_anqp_query_list(hapd, pos, pos + elen, &qi);
1134 break;
Dmitry Shmidtaa532512012-09-24 10:35:31 -07001135#ifdef CONFIG_HS20
Dmitry Shmidt61d9df32012-08-29 16:22:06 -07001136 case ANQP_VENDOR_SPECIFIC:
1137 rx_anqp_vendor_specific(hapd, pos, pos + elen, &qi);
1138 break;
Dmitry Shmidtaa532512012-09-24 10:35:31 -07001139#endif /* CONFIG_HS20 */
Dmitry Shmidt04949592012-07-19 12:16:46 -07001140 default:
1141 wpa_printf(MSG_DEBUG, "ANQP: Unsupported Query "
1142 "Request element %u", info_id);
1143 break;
1144 }
1145
1146 pos += elen;
1147 }
1148
Dmitry Shmidt18463232014-01-24 12:29:41 -08001149 gas_serv_req_local_processing(hapd, sa, dialog_token, &qi, prot);
Dmitry Shmidt04949592012-07-19 12:16:46 -07001150}
1151
1152
1153void gas_serv_tx_gas_response(struct hostapd_data *hapd, const u8 *dst,
1154 struct gas_dialog_info *dialog)
1155{
1156 struct wpabuf *buf, *tx_buf;
1157 u8 dialog_token = dialog->dialog_token;
1158 size_t frag_len;
1159
1160 if (dialog->sd_resp == NULL) {
1161 buf = gas_serv_build_gas_resp_payload(hapd,
1162 dialog->all_requested,
Dmitry Shmidtf21452a2014-02-26 10:55:25 -08001163 dialog, NULL, 0, NULL, 0);
Dmitry Shmidt04949592012-07-19 12:16:46 -07001164 wpa_hexdump_buf(MSG_MSGDUMP, "ANQP: Generated ANQP responses",
1165 buf);
1166 if (!buf)
1167 goto tx_gas_response_done;
1168 dialog->sd_resp = buf;
1169 dialog->sd_resp_pos = 0;
1170 }
1171 frag_len = wpabuf_len(dialog->sd_resp) - dialog->sd_resp_pos;
1172 if (frag_len > hapd->gas_frag_limit || dialog->comeback_delay ||
1173 hapd->conf->gas_comeback_delay) {
1174 u16 comeback_delay_tus = dialog->comeback_delay +
1175 GAS_SERV_COMEBACK_DELAY_FUDGE;
1176 u32 comeback_delay_secs, comeback_delay_usecs;
1177
1178 if (hapd->conf->gas_comeback_delay) {
1179 /* Testing - allow overriding of the delay value */
1180 comeback_delay_tus = hapd->conf->gas_comeback_delay;
1181 }
1182
1183 wpa_printf(MSG_DEBUG, "GAS: Response frag_len %u (frag limit "
1184 "%u) and comeback delay %u, "
1185 "requesting comebacks", (unsigned int) frag_len,
1186 (unsigned int) hapd->gas_frag_limit,
1187 dialog->comeback_delay);
1188 tx_buf = gas_anqp_build_initial_resp_buf(dialog_token,
1189 WLAN_STATUS_SUCCESS,
1190 comeback_delay_tus,
1191 NULL);
1192 if (tx_buf) {
1193 wpa_msg(hapd->msg_ctx, MSG_DEBUG,
1194 "GAS: Tx GAS Initial Resp (comeback = 10TU)");
Dmitry Shmidt18463232014-01-24 12:29:41 -08001195 if (dialog->prot)
1196 convert_to_protected_dual(tx_buf);
Dmitry Shmidt04949592012-07-19 12:16:46 -07001197 hostapd_drv_send_action(hapd, hapd->iface->freq, 0,
1198 dst,
1199 wpabuf_head(tx_buf),
1200 wpabuf_len(tx_buf));
1201 }
1202 wpabuf_free(tx_buf);
1203
1204 /* start a timer of 1.5 * comeback-delay */
1205 comeback_delay_tus = comeback_delay_tus +
1206 (comeback_delay_tus / 2);
1207 comeback_delay_secs = (comeback_delay_tus * 1024) / 1000000;
1208 comeback_delay_usecs = (comeback_delay_tus * 1024) -
1209 (comeback_delay_secs * 1000000);
1210 eloop_register_timeout(comeback_delay_secs,
1211 comeback_delay_usecs,
1212 gas_serv_clear_cached_ies, dialog,
1213 NULL);
1214 goto tx_gas_response_done;
1215 }
1216
1217 buf = wpabuf_alloc_copy(wpabuf_head_u8(dialog->sd_resp) +
1218 dialog->sd_resp_pos, frag_len);
1219 if (buf == NULL) {
1220 wpa_msg(hapd->msg_ctx, MSG_DEBUG, "GAS: Buffer allocation "
1221 "failed");
1222 goto tx_gas_response_done;
1223 }
1224 tx_buf = gas_anqp_build_initial_resp_buf(dialog_token,
1225 WLAN_STATUS_SUCCESS, 0, buf);
1226 wpabuf_free(buf);
1227 if (tx_buf == NULL)
1228 goto tx_gas_response_done;
1229 wpa_msg(hapd->msg_ctx, MSG_DEBUG, "GAS: Tx GAS Initial "
1230 "Response (frag_id %d frag_len %d)",
1231 dialog->sd_frag_id, (int) frag_len);
1232 dialog->sd_frag_id++;
1233
Dmitry Shmidt18463232014-01-24 12:29:41 -08001234 if (dialog->prot)
1235 convert_to_protected_dual(tx_buf);
Dmitry Shmidt04949592012-07-19 12:16:46 -07001236 hostapd_drv_send_action(hapd, hapd->iface->freq, 0, dst,
1237 wpabuf_head(tx_buf), wpabuf_len(tx_buf));
1238 wpabuf_free(tx_buf);
1239tx_gas_response_done:
1240 gas_serv_clear_cached_ies(dialog, NULL);
1241}
1242
1243
1244static void gas_serv_rx_gas_comeback_req(struct hostapd_data *hapd,
1245 const u8 *sa,
Dmitry Shmidt18463232014-01-24 12:29:41 -08001246 const u8 *data, size_t len, int prot)
Dmitry Shmidt04949592012-07-19 12:16:46 -07001247{
1248 struct gas_dialog_info *dialog;
1249 struct wpabuf *buf, *tx_buf;
1250 u8 dialog_token;
1251 size_t frag_len;
1252 int more = 0;
1253
1254 wpa_hexdump(MSG_DEBUG, "GAS: RX GAS Comeback Request", data, len);
1255 if (len < 1)
1256 return;
1257 dialog_token = *data;
1258 wpa_msg(hapd->msg_ctx, MSG_DEBUG, "GAS: Dialog Token: %u",
1259 dialog_token);
1260
1261 dialog = gas_serv_dialog_find(hapd, sa, dialog_token);
1262 if (!dialog) {
1263 wpa_msg(hapd->msg_ctx, MSG_DEBUG, "GAS: No pending SD "
1264 "response fragment for " MACSTR " dialog token %u",
1265 MAC2STR(sa), dialog_token);
1266
1267 if (sa[0] & 0x01)
1268 return; /* Invalid source address - drop silently */
1269 tx_buf = gas_anqp_build_comeback_resp_buf(
1270 dialog_token, WLAN_STATUS_NO_OUTSTANDING_GAS_REQ, 0, 0,
1271 0, NULL);
1272 if (tx_buf == NULL)
1273 return;
1274 goto send_resp;
1275 }
1276
1277 if (dialog->sd_resp == NULL) {
1278 wpa_printf(MSG_DEBUG, "GAS: Remote request 0x%x received 0x%x",
1279 dialog->requested, dialog->received);
1280 if ((dialog->requested & dialog->received) !=
1281 dialog->requested) {
1282 wpa_printf(MSG_DEBUG, "GAS: Did not receive response "
1283 "from remote processing");
1284 gas_serv_dialog_clear(dialog);
1285 tx_buf = gas_anqp_build_comeback_resp_buf(
1286 dialog_token,
1287 WLAN_STATUS_GAS_RESP_NOT_RECEIVED, 0, 0, 0,
1288 NULL);
1289 if (tx_buf == NULL)
1290 return;
1291 goto send_resp;
1292 }
1293
1294 buf = gas_serv_build_gas_resp_payload(hapd,
1295 dialog->all_requested,
Dmitry Shmidtf21452a2014-02-26 10:55:25 -08001296 dialog, NULL, 0, NULL, 0);
Dmitry Shmidt04949592012-07-19 12:16:46 -07001297 wpa_hexdump_buf(MSG_MSGDUMP, "ANQP: Generated ANQP responses",
1298 buf);
1299 if (!buf)
1300 goto rx_gas_comeback_req_done;
1301 dialog->sd_resp = buf;
1302 dialog->sd_resp_pos = 0;
1303 }
1304 frag_len = wpabuf_len(dialog->sd_resp) - dialog->sd_resp_pos;
1305 if (frag_len > hapd->gas_frag_limit) {
1306 frag_len = hapd->gas_frag_limit;
1307 more = 1;
1308 }
1309 wpa_msg(hapd->msg_ctx, MSG_DEBUG, "GAS: resp frag_len %u",
1310 (unsigned int) frag_len);
1311 buf = wpabuf_alloc_copy(wpabuf_head_u8(dialog->sd_resp) +
1312 dialog->sd_resp_pos, frag_len);
1313 if (buf == NULL) {
1314 wpa_msg(hapd->msg_ctx, MSG_DEBUG, "GAS: Failed to allocate "
1315 "buffer");
1316 goto rx_gas_comeback_req_done;
1317 }
1318 tx_buf = gas_anqp_build_comeback_resp_buf(dialog_token,
1319 WLAN_STATUS_SUCCESS,
1320 dialog->sd_frag_id,
1321 more, 0, buf);
1322 wpabuf_free(buf);
1323 if (tx_buf == NULL)
1324 goto rx_gas_comeback_req_done;
1325 wpa_msg(hapd->msg_ctx, MSG_DEBUG, "GAS: Tx GAS Comeback Response "
1326 "(frag_id %d more=%d frag_len=%d)",
1327 dialog->sd_frag_id, more, (int) frag_len);
1328 dialog->sd_frag_id++;
1329 dialog->sd_resp_pos += frag_len;
1330
1331 if (more) {
1332 wpa_msg(hapd->msg_ctx, MSG_DEBUG, "GAS: %d more bytes remain "
1333 "to be sent",
1334 (int) (wpabuf_len(dialog->sd_resp) -
1335 dialog->sd_resp_pos));
1336 } else {
1337 wpa_msg(hapd->msg_ctx, MSG_DEBUG, "GAS: All fragments of "
1338 "SD response sent");
1339 gas_serv_dialog_clear(dialog);
1340 gas_serv_free_dialogs(hapd, sa);
1341 }
1342
1343send_resp:
Dmitry Shmidt18463232014-01-24 12:29:41 -08001344 if (prot)
1345 convert_to_protected_dual(tx_buf);
Dmitry Shmidt04949592012-07-19 12:16:46 -07001346 hostapd_drv_send_action(hapd, hapd->iface->freq, 0, sa,
1347 wpabuf_head(tx_buf), wpabuf_len(tx_buf));
1348 wpabuf_free(tx_buf);
1349 return;
1350
1351rx_gas_comeback_req_done:
1352 gas_serv_clear_cached_ies(dialog, NULL);
1353}
1354
1355
1356static void gas_serv_rx_public_action(void *ctx, const u8 *buf, size_t len,
1357 int freq)
1358{
1359 struct hostapd_data *hapd = ctx;
1360 const struct ieee80211_mgmt *mgmt;
1361 size_t hdr_len;
1362 const u8 *sa, *data;
Dmitry Shmidt18463232014-01-24 12:29:41 -08001363 int prot;
Dmitry Shmidt04949592012-07-19 12:16:46 -07001364
1365 mgmt = (const struct ieee80211_mgmt *) buf;
1366 hdr_len = (const u8 *) &mgmt->u.action.u.vs_public_action.action - buf;
1367 if (hdr_len > len)
1368 return;
Dmitry Shmidt18463232014-01-24 12:29:41 -08001369 if (mgmt->u.action.category != WLAN_ACTION_PUBLIC &&
1370 mgmt->u.action.category != WLAN_ACTION_PROTECTED_DUAL)
Dmitry Shmidt04949592012-07-19 12:16:46 -07001371 return;
Dmitry Shmidt18463232014-01-24 12:29:41 -08001372 /*
1373 * Note: Public Action and Protected Dual of Public Action frames share
1374 * the same payload structure, so it is fine to use definitions of
1375 * Public Action frames to process both.
1376 */
1377 prot = mgmt->u.action.category == WLAN_ACTION_PROTECTED_DUAL;
Dmitry Shmidt04949592012-07-19 12:16:46 -07001378 sa = mgmt->sa;
1379 len -= hdr_len;
1380 data = &mgmt->u.action.u.public_action.action;
1381 switch (data[0]) {
1382 case WLAN_PA_GAS_INITIAL_REQ:
Dmitry Shmidt18463232014-01-24 12:29:41 -08001383 gas_serv_rx_gas_initial_req(hapd, sa, data + 1, len - 1, prot);
Dmitry Shmidt04949592012-07-19 12:16:46 -07001384 break;
1385 case WLAN_PA_GAS_COMEBACK_REQ:
Dmitry Shmidt18463232014-01-24 12:29:41 -08001386 gas_serv_rx_gas_comeback_req(hapd, sa, data + 1, len - 1, prot);
Dmitry Shmidt04949592012-07-19 12:16:46 -07001387 break;
1388 }
1389}
1390
1391
1392int gas_serv_init(struct hostapd_data *hapd)
1393{
Dmitry Shmidt4b9d52f2013-02-05 17:44:43 -08001394 hapd->public_action_cb2 = gas_serv_rx_public_action;
1395 hapd->public_action_cb2_ctx = hapd;
Dmitry Shmidt04949592012-07-19 12:16:46 -07001396 hapd->gas_frag_limit = 1400;
1397 if (hapd->conf->gas_frag_limit > 0)
1398 hapd->gas_frag_limit = hapd->conf->gas_frag_limit;
1399 return 0;
1400}
1401
1402
1403void gas_serv_deinit(struct hostapd_data *hapd)
1404{
1405}