]> git.ipfire.org Git - thirdparty/hostap.git/blame - src/ap/gas_serv.c
HS 2.0: Allow configuration of operator icons
[thirdparty/hostap.git] / src / ap / gas_serv.c
CommitLineData
dca30c3f
JK
1/*
2 * Generic advertisement service (GAS) server
5ce00d09 3 * Copyright (c) 2011-2014, Qualcomm Atheros, Inc.
dca30c3f
JK
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"
2605405a 14#include "common/wpa_ctrl.h"
dca30c3f
JK
15#include "utils/eloop.h"
16#include "hostapd.h"
17#include "ap_config.h"
18#include "ap_drv_ops.h"
2605405a 19#include "dpp_hostapd.h"
dca30c3f
JK
20#include "sta_info.h"
21#include "gas_serv.h"
22
23
2605405a
JM
24#ifdef CONFIG_DPP
25static void gas_serv_write_dpp_adv_proto(struct wpabuf *buf)
26{
27 wpabuf_put_u8(buf, WLAN_EID_ADV_PROTO);
28 wpabuf_put_u8(buf, 8); /* Length */
29 wpabuf_put_u8(buf, 0x7f);
30 wpabuf_put_u8(buf, WLAN_EID_VENDOR_SPECIFIC);
31 wpabuf_put_u8(buf, 5);
32 wpabuf_put_be24(buf, OUI_WFA);
33 wpabuf_put_u8(buf, DPP_OUI_TYPE);
34 wpabuf_put_u8(buf, 0x01);
35}
36#endif /* CONFIG_DPP */
37
38
5ce00d09
JM
39static void convert_to_protected_dual(struct wpabuf *msg)
40{
41 u8 *categ = wpabuf_mhead_u8(msg);
42 *categ = WLAN_ACTION_PROTECTED_DUAL;
43}
44
45
dca30c3f
JK
46static struct gas_dialog_info *
47gas_dialog_create(struct hostapd_data *hapd, const u8 *addr, u8 dialog_token)
48{
49 struct sta_info *sta;
50 struct gas_dialog_info *dia = NULL;
51 int i, j;
52
53 sta = ap_get_sta(hapd, addr);
54 if (!sta) {
55 /*
56 * We need a STA entry to be able to maintain state for
57 * the GAS query.
58 */
59 wpa_printf(MSG_DEBUG, "ANQP: Add a temporary STA entry for "
60 "GAS query");
61 sta = ap_sta_add(hapd, addr);
62 if (!sta) {
63 wpa_printf(MSG_DEBUG, "Failed to add STA " MACSTR
64 " for GAS query", MAC2STR(addr));
65 return NULL;
66 }
67 sta->flags |= WLAN_STA_GAS;
68 /*
69 * The default inactivity is 300 seconds. We don't need
4cc61c38
DK
70 * it to be that long. Use five second timeout and increase this
71 * with the comeback_delay for testing cases.
dca30c3f 72 */
4cc61c38
DK
73 ap_sta_session_timeout(hapd, sta,
74 hapd->conf->gas_comeback_delay / 1024 +
75 5);
91f9e607
KP
76 } else {
77 ap_sta_replenish_timeout(hapd, sta, 5);
dca30c3f
JK
78 }
79
80 if (sta->gas_dialog == NULL) {
faebdeaa 81 sta->gas_dialog = os_calloc(GAS_DIALOG_MAX,
dca30c3f
JK
82 sizeof(struct gas_dialog_info));
83 if (sta->gas_dialog == NULL)
84 return NULL;
85 }
86
87 for (i = sta->gas_dialog_next, j = 0; j < GAS_DIALOG_MAX; i++, j++) {
88 if (i == GAS_DIALOG_MAX)
89 i = 0;
90 if (sta->gas_dialog[i].valid)
91 continue;
92 dia = &sta->gas_dialog[i];
93 dia->valid = 1;
dca30c3f
JK
94 dia->dialog_token = dialog_token;
95 sta->gas_dialog_next = (++i == GAS_DIALOG_MAX) ? 0 : i;
96 return dia;
97 }
98
99 wpa_msg(hapd->msg_ctx, MSG_ERROR, "ANQP: Could not create dialog for "
100 MACSTR " dialog_token %u. Consider increasing "
101 "GAS_DIALOG_MAX.", MAC2STR(addr), dialog_token);
102
103 return NULL;
104}
105
106
107struct gas_dialog_info *
108gas_serv_dialog_find(struct hostapd_data *hapd, const u8 *addr,
109 u8 dialog_token)
110{
111 struct sta_info *sta;
112 int i;
113
114 sta = ap_get_sta(hapd, addr);
115 if (!sta) {
116 wpa_printf(MSG_DEBUG, "ANQP: could not find STA " MACSTR,
117 MAC2STR(addr));
118 return NULL;
119 }
120 for (i = 0; sta->gas_dialog && i < GAS_DIALOG_MAX; i++) {
121 if (sta->gas_dialog[i].dialog_token != dialog_token ||
122 !sta->gas_dialog[i].valid)
123 continue;
a587666c 124 ap_sta_replenish_timeout(hapd, sta, 5);
dca30c3f
JK
125 return &sta->gas_dialog[i];
126 }
127 wpa_printf(MSG_DEBUG, "ANQP: Could not find dialog for "
128 MACSTR " dialog_token %u", MAC2STR(addr), dialog_token);
129 return NULL;
130}
131
132
133void gas_serv_dialog_clear(struct gas_dialog_info *dia)
134{
135 wpabuf_free(dia->sd_resp);
136 os_memset(dia, 0, sizeof(*dia));
137}
138
139
140static void gas_serv_free_dialogs(struct hostapd_data *hapd,
141 const u8 *sta_addr)
142{
143 struct sta_info *sta;
144 int i;
145
146 sta = ap_get_sta(hapd, sta_addr);
147 if (sta == NULL || sta->gas_dialog == NULL)
148 return;
149
150 for (i = 0; i < GAS_DIALOG_MAX; i++) {
151 if (sta->gas_dialog[i].valid)
152 return;
153 }
154
155 os_free(sta->gas_dialog);
156 sta->gas_dialog = NULL;
157}
158
159
ce6ce7fb 160#ifdef CONFIG_HS20
45ac307a
JK
161static void anqp_add_hs_capab_list(struct hostapd_data *hapd,
162 struct wpabuf *buf)
163{
164 u8 *len;
165
166 len = gas_anqp_add_element(buf, ANQP_VENDOR_SPECIFIC);
167 wpabuf_put_be24(buf, OUI_WFA);
168 wpabuf_put_u8(buf, HS20_ANQP_OUI_TYPE);
169 wpabuf_put_u8(buf, HS20_STYPE_CAPABILITY_LIST);
170 wpabuf_put_u8(buf, 0); /* Reserved */
171 wpabuf_put_u8(buf, HS20_STYPE_CAPABILITY_LIST);
a9277e85
JK
172 if (hapd->conf->hs20_oper_friendly_name)
173 wpabuf_put_u8(buf, HS20_STYPE_OPERATOR_FRIENDLY_NAME);
4065a309
JK
174 if (hapd->conf->hs20_wan_metrics)
175 wpabuf_put_u8(buf, HS20_STYPE_WAN_METRICS);
5ccc54aa
JK
176 if (hapd->conf->hs20_connection_capability)
177 wpabuf_put_u8(buf, HS20_STYPE_CONNECTION_CAPABILITY);
43f51e2a
JK
178 if (hapd->conf->nai_realm_data)
179 wpabuf_put_u8(buf, HS20_STYPE_NAI_HOME_REALM_QUERY);
df5934f1
JK
180 if (hapd->conf->hs20_operating_class)
181 wpabuf_put_u8(buf, HS20_STYPE_OPERATING_CLASS);
ae6d15c7
JM
182 if (hapd->conf->hs20_osu_providers_count)
183 wpabuf_put_u8(buf, HS20_STYPE_OSU_PROVIDERS_LIST);
f7bd7a01
JM
184 if (hapd->conf->hs20_icons_count)
185 wpabuf_put_u8(buf, HS20_STYPE_ICON_REQUEST);
0e450db2
JM
186 if (hapd->conf->hs20_operator_icon_count)
187 wpabuf_put_u8(buf, HS20_STYPE_OPERATOR_ICON_METADATA);
45ac307a
JK
188 gas_anqp_set_element_len(buf, len);
189}
ce6ce7fb 190#endif /* CONFIG_HS20 */
45ac307a
JK
191
192
695dbbea
JM
193static struct anqp_element * get_anqp_elem(struct hostapd_data *hapd,
194 u16 infoid)
195{
196 struct anqp_element *elem;
197
198 dl_list_for_each(elem, &hapd->conf->anqp_elem, struct anqp_element,
199 list) {
200 if (elem->infoid == infoid)
201 return elem;
202 }
203
204 return NULL;
205}
206
207
208static void anqp_add_elem(struct hostapd_data *hapd, struct wpabuf *buf,
209 u16 infoid)
210{
211 struct anqp_element *elem;
212
213 elem = get_anqp_elem(hapd, infoid);
214 if (!elem)
215 return;
216 if (wpabuf_tailroom(buf) < 2 + 2 + wpabuf_len(elem->payload)) {
217 wpa_printf(MSG_DEBUG, "ANQP: No room for InfoID %u payload",
218 infoid);
219 return;
220 }
221
222 wpabuf_put_le16(buf, infoid);
223 wpabuf_put_le16(buf, wpabuf_len(elem->payload));
224 wpabuf_put_buf(buf, elem->payload);
225}
226
227
228static int anqp_add_override(struct hostapd_data *hapd, struct wpabuf *buf,
229 u16 infoid)
230{
231 if (get_anqp_elem(hapd, infoid)) {
232 anqp_add_elem(hapd, buf, infoid);
233 return 1;
234 }
235
236 return 0;
237}
238
239
dca30c3f
JK
240static void anqp_add_capab_list(struct hostapd_data *hapd,
241 struct wpabuf *buf)
242{
243 u8 *len;
695dbbea
JM
244 u16 id;
245
246 if (anqp_add_override(hapd, buf, ANQP_CAPABILITY_LIST))
247 return;
dca30c3f
JK
248
249 len = gas_anqp_add_element(buf, ANQP_CAPABILITY_LIST);
250 wpabuf_put_le16(buf, ANQP_CAPABILITY_LIST);
695dbbea 251 if (hapd->conf->venue_name || get_anqp_elem(hapd, ANQP_VENUE_NAME))
648cc711 252 wpabuf_put_le16(buf, ANQP_VENUE_NAME);
695dbbea
JM
253 if (get_anqp_elem(hapd, ANQP_EMERGENCY_CALL_NUMBER))
254 wpabuf_put_le16(buf, ANQP_EMERGENCY_CALL_NUMBER);
255 if (hapd->conf->network_auth_type ||
256 get_anqp_elem(hapd, ANQP_NETWORK_AUTH_TYPE))
550a3958 257 wpabuf_put_le16(buf, ANQP_NETWORK_AUTH_TYPE);
695dbbea
JM
258 if (hapd->conf->roaming_consortium ||
259 get_anqp_elem(hapd, ANQP_ROAMING_CONSORTIUM))
3eaee4bf 260 wpabuf_put_le16(buf, ANQP_ROAMING_CONSORTIUM);
695dbbea
JM
261 if (hapd->conf->ipaddr_type_configured ||
262 get_anqp_elem(hapd, ANQP_IP_ADDR_TYPE_AVAILABILITY))
78bda93e 263 wpabuf_put_le16(buf, ANQP_IP_ADDR_TYPE_AVAILABILITY);
695dbbea
JM
264 if (hapd->conf->nai_realm_data ||
265 get_anqp_elem(hapd, ANQP_NAI_REALM))
8047b186 266 wpabuf_put_le16(buf, ANQP_NAI_REALM);
695dbbea
JM
267 if (hapd->conf->anqp_3gpp_cell_net ||
268 get_anqp_elem(hapd, ANQP_3GPP_CELLULAR_NETWORK))
7515adb2 269 wpabuf_put_le16(buf, ANQP_3GPP_CELLULAR_NETWORK);
695dbbea
JM
270 if (get_anqp_elem(hapd, ANQP_AP_GEOSPATIAL_LOCATION))
271 wpabuf_put_le16(buf, ANQP_AP_GEOSPATIAL_LOCATION);
272 if (get_anqp_elem(hapd, ANQP_AP_CIVIC_LOCATION))
273 wpabuf_put_le16(buf, ANQP_AP_CIVIC_LOCATION);
274 if (get_anqp_elem(hapd, ANQP_AP_LOCATION_PUBLIC_URI))
275 wpabuf_put_le16(buf, ANQP_AP_LOCATION_PUBLIC_URI);
276 if (hapd->conf->domain_name || get_anqp_elem(hapd, ANQP_DOMAIN_NAME))
26fac8b6 277 wpabuf_put_le16(buf, ANQP_DOMAIN_NAME);
695dbbea
JM
278 if (get_anqp_elem(hapd, ANQP_EMERGENCY_ALERT_URI))
279 wpabuf_put_le16(buf, ANQP_EMERGENCY_ALERT_URI);
4051dd86
JM
280 if (get_anqp_elem(hapd, ANQP_TDLS_CAPABILITY))
281 wpabuf_put_le16(buf, ANQP_TDLS_CAPABILITY);
695dbbea
JM
282 if (get_anqp_elem(hapd, ANQP_EMERGENCY_NAI))
283 wpabuf_put_le16(buf, ANQP_EMERGENCY_NAI);
284 if (get_anqp_elem(hapd, ANQP_NEIGHBOR_REPORT))
285 wpabuf_put_le16(buf, ANQP_NEIGHBOR_REPORT);
8183aee6
JM
286#ifdef CONFIG_FILS
287 if (!dl_list_empty(&hapd->conf->fils_realms) ||
288 get_anqp_elem(hapd, ANQP_FILS_REALM_INFO))
289 wpabuf_put_le16(buf, ANQP_FILS_REALM_INFO);
290#endif /* CONFIG_FILS */
291 if (get_anqp_elem(hapd, ANQP_CAG))
292 wpabuf_put_le16(buf, ANQP_CAG);
7e1d3ee9 293 if (hapd->conf->venue_url || get_anqp_elem(hapd, ANQP_VENUE_URL))
695dbbea
JM
294 wpabuf_put_le16(buf, ANQP_VENUE_URL);
295 if (get_anqp_elem(hapd, ANQP_ADVICE_OF_CHARGE))
296 wpabuf_put_le16(buf, ANQP_ADVICE_OF_CHARGE);
297 if (get_anqp_elem(hapd, ANQP_LOCAL_CONTENT))
298 wpabuf_put_le16(buf, ANQP_LOCAL_CONTENT);
4051dd86
JM
299 for (id = 280; id < 300; id++) {
300 if (get_anqp_elem(hapd, id))
301 wpabuf_put_le16(buf, id);
302 }
ce6ce7fb 303#ifdef CONFIG_HS20
45ac307a 304 anqp_add_hs_capab_list(hapd, buf);
ce6ce7fb 305#endif /* CONFIG_HS20 */
3eaee4bf
JM
306 gas_anqp_set_element_len(buf, len);
307}
308
309
648cc711
JM
310static void anqp_add_venue_name(struct hostapd_data *hapd, struct wpabuf *buf)
311{
695dbbea
JM
312 if (anqp_add_override(hapd, buf, ANQP_VENUE_NAME))
313 return;
314
648cc711
JM
315 if (hapd->conf->venue_name) {
316 u8 *len;
317 unsigned int i;
318 len = gas_anqp_add_element(buf, ANQP_VENUE_NAME);
319 wpabuf_put_u8(buf, hapd->conf->venue_group);
320 wpabuf_put_u8(buf, hapd->conf->venue_type);
321 for (i = 0; i < hapd->conf->venue_name_count; i++) {
1792e58d 322 struct hostapd_lang_string *vn;
648cc711
JM
323 vn = &hapd->conf->venue_name[i];
324 wpabuf_put_u8(buf, 3 + vn->name_len);
325 wpabuf_put_data(buf, vn->lang, 3);
326 wpabuf_put_data(buf, vn->name, vn->name_len);
327 }
328 gas_anqp_set_element_len(buf, len);
329 }
330}
331
332
7e1d3ee9
JM
333static void anqp_add_venue_url(struct hostapd_data *hapd, struct wpabuf *buf)
334{
335 if (anqp_add_override(hapd, buf, ANQP_VENUE_URL))
336 return;
337
338 if (hapd->conf->venue_url) {
339 u8 *len;
340 unsigned int i;
341
342 len = gas_anqp_add_element(buf, ANQP_VENUE_URL);
343 for (i = 0; i < hapd->conf->venue_url_count; i++) {
344 struct hostapd_venue_url *url;
345
346 url = &hapd->conf->venue_url[i];
347 wpabuf_put_u8(buf, 1 + url->url_len);
348 wpabuf_put_u8(buf, url->venue_number);
349 wpabuf_put_data(buf, url->url, url->url_len);
350 }
351 gas_anqp_set_element_len(buf, len);
352 }
353}
354
355
550a3958
JK
356static void anqp_add_network_auth_type(struct hostapd_data *hapd,
357 struct wpabuf *buf)
358{
695dbbea
JM
359 if (anqp_add_override(hapd, buf, ANQP_NETWORK_AUTH_TYPE))
360 return;
361
550a3958
JK
362 if (hapd->conf->network_auth_type) {
363 wpabuf_put_le16(buf, ANQP_NETWORK_AUTH_TYPE);
364 wpabuf_put_le16(buf, hapd->conf->network_auth_type_len);
365 wpabuf_put_data(buf, hapd->conf->network_auth_type,
366 hapd->conf->network_auth_type_len);
367 }
368}
369
370
3eaee4bf
JM
371static void anqp_add_roaming_consortium(struct hostapd_data *hapd,
372 struct wpabuf *buf)
373{
374 unsigned int i;
375 u8 *len;
376
695dbbea
JM
377 if (anqp_add_override(hapd, buf, ANQP_ROAMING_CONSORTIUM))
378 return;
379
3eaee4bf
JM
380 len = gas_anqp_add_element(buf, ANQP_ROAMING_CONSORTIUM);
381 for (i = 0; i < hapd->conf->roaming_consortium_count; i++) {
382 struct hostapd_roaming_consortium *rc;
383 rc = &hapd->conf->roaming_consortium[i];
384 wpabuf_put_u8(buf, rc->len);
385 wpabuf_put_data(buf, rc->oi, rc->len);
386 }
dca30c3f
JK
387 gas_anqp_set_element_len(buf, len);
388}
389
390
78bda93e
JK
391static void anqp_add_ip_addr_type_availability(struct hostapd_data *hapd,
392 struct wpabuf *buf)
393{
695dbbea
JM
394 if (anqp_add_override(hapd, buf, ANQP_IP_ADDR_TYPE_AVAILABILITY))
395 return;
396
78bda93e
JK
397 if (hapd->conf->ipaddr_type_configured) {
398 wpabuf_put_le16(buf, ANQP_IP_ADDR_TYPE_AVAILABILITY);
399 wpabuf_put_le16(buf, 1);
400 wpabuf_put_u8(buf, hapd->conf->ipaddr_type_availability);
401 }
402}
403
404
8047b186
JK
405static void anqp_add_nai_realm_eap(struct wpabuf *buf,
406 struct hostapd_nai_realm_data *realm)
407{
408 unsigned int i, j;
409
410 wpabuf_put_u8(buf, realm->eap_method_count);
411
412 for (i = 0; i < realm->eap_method_count; i++) {
413 struct hostapd_nai_realm_eap *eap = &realm->eap_method[i];
414 wpabuf_put_u8(buf, 2 + (3 * eap->num_auths));
415 wpabuf_put_u8(buf, eap->eap_method);
416 wpabuf_put_u8(buf, eap->num_auths);
417 for (j = 0; j < eap->num_auths; j++) {
418 wpabuf_put_u8(buf, eap->auth_id[j]);
419 wpabuf_put_u8(buf, 1);
420 wpabuf_put_u8(buf, eap->auth_val[j]);
421 }
422 }
423}
424
425
43f51e2a
JK
426static void anqp_add_nai_realm_data(struct wpabuf *buf,
427 struct hostapd_nai_realm_data *realm,
428 unsigned int realm_idx)
429{
430 u8 *realm_data_len;
431
432 wpa_printf(MSG_DEBUG, "realm=%s, len=%d", realm->realm[realm_idx],
433 (int) os_strlen(realm->realm[realm_idx]));
434 realm_data_len = wpabuf_put(buf, 2);
435 wpabuf_put_u8(buf, realm->encoding);
436 wpabuf_put_u8(buf, os_strlen(realm->realm[realm_idx]));
437 wpabuf_put_str(buf, realm->realm[realm_idx]);
438 anqp_add_nai_realm_eap(buf, realm);
439 gas_anqp_set_element_len(buf, realm_data_len);
440}
441
442
443static int hs20_add_nai_home_realm_matches(struct hostapd_data *hapd,
444 struct wpabuf *buf,
445 const u8 *home_realm,
446 size_t home_realm_len)
447{
448 unsigned int i, j, k;
449 u8 num_realms, num_matching = 0, encoding, realm_len, *realm_list_len;
450 struct hostapd_nai_realm_data *realm;
451 const u8 *pos, *realm_name, *end;
452 struct {
453 unsigned int realm_data_idx;
454 unsigned int realm_idx;
455 } matches[10];
456
457 pos = home_realm;
458 end = pos + home_realm_len;
f88bcad0 459 if (end - pos < 1) {
43f51e2a
JK
460 wpa_hexdump(MSG_DEBUG, "Too short NAI Home Realm Query",
461 home_realm, home_realm_len);
462 return -1;
463 }
464 num_realms = *pos++;
465
466 for (i = 0; i < num_realms && num_matching < 10; i++) {
f88bcad0 467 if (end - pos < 2) {
43f51e2a
JK
468 wpa_hexdump(MSG_DEBUG,
469 "Truncated NAI Home Realm Query",
470 home_realm, home_realm_len);
471 return -1;
472 }
473 encoding = *pos++;
474 realm_len = *pos++;
f88bcad0 475 if (realm_len > end - pos) {
43f51e2a
JK
476 wpa_hexdump(MSG_DEBUG,
477 "Truncated NAI Home Realm Query",
478 home_realm, home_realm_len);
479 return -1;
480 }
481 realm_name = pos;
482 for (j = 0; j < hapd->conf->nai_realm_count &&
483 num_matching < 10; j++) {
484 const u8 *rpos, *rend;
485 realm = &hapd->conf->nai_realm_data[j];
486 if (encoding != realm->encoding)
487 continue;
488
489 rpos = realm_name;
490 while (rpos < realm_name + realm_len &&
491 num_matching < 10) {
492 for (rend = rpos;
493 rend < realm_name + realm_len; rend++) {
494 if (*rend == ';')
495 break;
496 }
497 for (k = 0; k < MAX_NAI_REALMS &&
498 realm->realm[k] &&
499 num_matching < 10; k++) {
500 if ((int) os_strlen(realm->realm[k]) !=
501 rend - rpos ||
502 os_strncmp((char *) rpos,
503 realm->realm[k],
504 rend - rpos) != 0)
505 continue;
506 matches[num_matching].realm_data_idx =
507 j;
508 matches[num_matching].realm_idx = k;
509 num_matching++;
510 }
511 rpos = rend + 1;
512 }
513 }
514 pos += realm_len;
515 }
516
517 realm_list_len = gas_anqp_add_element(buf, ANQP_NAI_REALM);
518 wpabuf_put_le16(buf, num_matching);
519
520 /*
521 * There are two ways to format. 1. each realm in a NAI Realm Data unit
522 * 2. all realms that share the same EAP methods in a NAI Realm Data
523 * unit. The first format is likely to be bigger in size than the
524 * second, but may be easier to parse and process by the receiver.
525 */
526 for (i = 0; i < num_matching; i++) {
527 wpa_printf(MSG_DEBUG, "realm_idx %d, realm_data_idx %d",
528 matches[i].realm_data_idx, matches[i].realm_idx);
529 realm = &hapd->conf->nai_realm_data[matches[i].realm_data_idx];
530 anqp_add_nai_realm_data(buf, realm, matches[i].realm_idx);
531 }
532 gas_anqp_set_element_len(buf, realm_list_len);
533 return 0;
534}
535
536
537static void anqp_add_nai_realm(struct hostapd_data *hapd, struct wpabuf *buf,
538 const u8 *home_realm, size_t home_realm_len,
539 int nai_realm, int nai_home_realm)
8047b186 540{
695dbbea
JM
541 if (nai_realm && !nai_home_realm &&
542 anqp_add_override(hapd, buf, ANQP_NAI_REALM))
543 return;
544
43f51e2a 545 if (nai_realm && hapd->conf->nai_realm_data) {
8047b186
JK
546 u8 *len;
547 unsigned int i, j;
548 len = gas_anqp_add_element(buf, ANQP_NAI_REALM);
549 wpabuf_put_le16(buf, hapd->conf->nai_realm_count);
550 for (i = 0; i < hapd->conf->nai_realm_count; i++) {
551 u8 *realm_data_len, *realm_len;
552 struct hostapd_nai_realm_data *realm;
553
554 realm = &hapd->conf->nai_realm_data[i];
555 realm_data_len = wpabuf_put(buf, 2);
556 wpabuf_put_u8(buf, realm->encoding);
557 realm_len = wpabuf_put(buf, 1);
558 for (j = 0; realm->realm[j]; j++) {
559 if (j > 0)
560 wpabuf_put_u8(buf, ';');
561 wpabuf_put_str(buf, realm->realm[j]);
562 }
563 *realm_len = (u8 *) wpabuf_put(buf, 0) - realm_len - 1;
564 anqp_add_nai_realm_eap(buf, realm);
565 gas_anqp_set_element_len(buf, realm_data_len);
566 }
567 gas_anqp_set_element_len(buf, len);
1fde15a2 568 } else if (nai_home_realm && hapd->conf->nai_realm_data && home_realm) {
43f51e2a
JK
569 hs20_add_nai_home_realm_matches(hapd, buf, home_realm,
570 home_realm_len);
8047b186
JK
571 }
572}
573
574
7515adb2
JK
575static void anqp_add_3gpp_cellular_network(struct hostapd_data *hapd,
576 struct wpabuf *buf)
577{
695dbbea
JM
578 if (anqp_add_override(hapd, buf, ANQP_3GPP_CELLULAR_NETWORK))
579 return;
580
7515adb2
JK
581 if (hapd->conf->anqp_3gpp_cell_net) {
582 wpabuf_put_le16(buf, ANQP_3GPP_CELLULAR_NETWORK);
583 wpabuf_put_le16(buf,
584 hapd->conf->anqp_3gpp_cell_net_len);
585 wpabuf_put_data(buf, hapd->conf->anqp_3gpp_cell_net,
586 hapd->conf->anqp_3gpp_cell_net_len);
587 }
588}
589
590
26fac8b6
JK
591static void anqp_add_domain_name(struct hostapd_data *hapd, struct wpabuf *buf)
592{
695dbbea
JM
593 if (anqp_add_override(hapd, buf, ANQP_DOMAIN_NAME))
594 return;
595
26fac8b6
JK
596 if (hapd->conf->domain_name) {
597 wpabuf_put_le16(buf, ANQP_DOMAIN_NAME);
598 wpabuf_put_le16(buf, hapd->conf->domain_name_len);
599 wpabuf_put_data(buf, hapd->conf->domain_name,
600 hapd->conf->domain_name_len);
601 }
602}
603
604
8183aee6
JM
605#ifdef CONFIG_FILS
606static void anqp_add_fils_realm_info(struct hostapd_data *hapd,
607 struct wpabuf *buf)
608{
609 size_t count;
610
611 if (anqp_add_override(hapd, buf, ANQP_FILS_REALM_INFO))
612 return;
613
614 count = dl_list_len(&hapd->conf->fils_realms);
615 if (count > 10000)
616 count = 10000;
617 if (count) {
618 struct fils_realm *realm;
619
620 wpabuf_put_le16(buf, ANQP_FILS_REALM_INFO);
621 wpabuf_put_le16(buf, 2 * count);
622
623 dl_list_for_each(realm, &hapd->conf->fils_realms,
624 struct fils_realm, list) {
625 if (count == 0)
626 break;
627 wpabuf_put_data(buf, realm->hash, 2);
aeff0645 628 count--;
8183aee6
JM
629 }
630 }
631}
632#endif /* CONFIG_FILS */
633
634
ce6ce7fb
JM
635#ifdef CONFIG_HS20
636
a9277e85
JK
637static void anqp_add_operator_friendly_name(struct hostapd_data *hapd,
638 struct wpabuf *buf)
639{
640 if (hapd->conf->hs20_oper_friendly_name) {
641 u8 *len;
642 unsigned int i;
643 len = gas_anqp_add_element(buf, ANQP_VENDOR_SPECIFIC);
644 wpabuf_put_be24(buf, OUI_WFA);
645 wpabuf_put_u8(buf, HS20_ANQP_OUI_TYPE);
646 wpabuf_put_u8(buf, HS20_STYPE_OPERATOR_FRIENDLY_NAME);
647 wpabuf_put_u8(buf, 0); /* Reserved */
648 for (i = 0; i < hapd->conf->hs20_oper_friendly_name_count; i++)
649 {
650 struct hostapd_lang_string *vn;
651 vn = &hapd->conf->hs20_oper_friendly_name[i];
652 wpabuf_put_u8(buf, 3 + vn->name_len);
653 wpabuf_put_data(buf, vn->lang, 3);
654 wpabuf_put_data(buf, vn->name, vn->name_len);
655 }
656 gas_anqp_set_element_len(buf, len);
657 }
658}
659
660
4065a309
JK
661static void anqp_add_wan_metrics(struct hostapd_data *hapd,
662 struct wpabuf *buf)
663{
664 if (hapd->conf->hs20_wan_metrics) {
665 u8 *len = gas_anqp_add_element(buf, ANQP_VENDOR_SPECIFIC);
666 wpabuf_put_be24(buf, OUI_WFA);
667 wpabuf_put_u8(buf, HS20_ANQP_OUI_TYPE);
668 wpabuf_put_u8(buf, HS20_STYPE_WAN_METRICS);
669 wpabuf_put_u8(buf, 0); /* Reserved */
670 wpabuf_put_data(buf, hapd->conf->hs20_wan_metrics, 13);
671 gas_anqp_set_element_len(buf, len);
672 }
673}
674
675
5ccc54aa
JK
676static void anqp_add_connection_capability(struct hostapd_data *hapd,
677 struct wpabuf *buf)
678{
679 if (hapd->conf->hs20_connection_capability) {
680 u8 *len = gas_anqp_add_element(buf, ANQP_VENDOR_SPECIFIC);
681 wpabuf_put_be24(buf, OUI_WFA);
682 wpabuf_put_u8(buf, HS20_ANQP_OUI_TYPE);
683 wpabuf_put_u8(buf, HS20_STYPE_CONNECTION_CAPABILITY);
684 wpabuf_put_u8(buf, 0); /* Reserved */
685 wpabuf_put_data(buf, hapd->conf->hs20_connection_capability,
686 hapd->conf->hs20_connection_capability_len);
687 gas_anqp_set_element_len(buf, len);
688 }
689}
690
691
df5934f1
JK
692static void anqp_add_operating_class(struct hostapd_data *hapd,
693 struct wpabuf *buf)
694{
695 if (hapd->conf->hs20_operating_class) {
696 u8 *len = gas_anqp_add_element(buf, ANQP_VENDOR_SPECIFIC);
697 wpabuf_put_be24(buf, OUI_WFA);
698 wpabuf_put_u8(buf, HS20_ANQP_OUI_TYPE);
699 wpabuf_put_u8(buf, HS20_STYPE_OPERATING_CLASS);
700 wpabuf_put_u8(buf, 0); /* Reserved */
701 wpabuf_put_data(buf, hapd->conf->hs20_operating_class,
702 hapd->conf->hs20_operating_class_len);
703 gas_anqp_set_element_len(buf, len);
704 }
705}
706
f7bd7a01 707
0e450db2
JM
708static void anqp_add_icon(struct wpabuf *buf, struct hostapd_bss_config *bss,
709 const char *name)
710{
711 size_t j;
712 struct hs20_icon *icon = NULL;
713
714 for (j = 0; j < bss->hs20_icons_count && !icon; j++) {
715 if (os_strcmp(name, bss->hs20_icons[j].name) == 0)
716 icon = &bss->hs20_icons[j];
717 }
718 if (!icon)
719 return; /* icon info not found */
720
721 wpabuf_put_le16(buf, icon->width);
722 wpabuf_put_le16(buf, icon->height);
723 wpabuf_put_data(buf, icon->language, 3);
724 wpabuf_put_u8(buf, os_strlen(icon->type));
725 wpabuf_put_str(buf, icon->type);
726 wpabuf_put_u8(buf, os_strlen(icon->name));
727 wpabuf_put_str(buf, icon->name);
728}
729
730
ae6d15c7
JM
731static void anqp_add_osu_provider(struct wpabuf *buf,
732 struct hostapd_bss_config *bss,
733 struct hs20_osu_provider *p)
734{
735 u8 *len, *len2, *count;
736 unsigned int i;
737
738 len = wpabuf_put(buf, 2); /* OSU Provider Length to be filled */
739
740 /* OSU Friendly Name Duples */
741 len2 = wpabuf_put(buf, 2);
742 for (i = 0; i < p->friendly_name_count; i++) {
743 struct hostapd_lang_string *s = &p->friendly_name[i];
744 wpabuf_put_u8(buf, 3 + s->name_len);
745 wpabuf_put_data(buf, s->lang, 3);
746 wpabuf_put_data(buf, s->name, s->name_len);
747 }
748 WPA_PUT_LE16(len2, (u8 *) wpabuf_put(buf, 0) - len2 - 2);
749
750 /* OSU Server URI */
751 if (p->server_uri) {
752 wpabuf_put_u8(buf, os_strlen(p->server_uri));
753 wpabuf_put_str(buf, p->server_uri);
754 } else
755 wpabuf_put_u8(buf, 0);
756
757 /* OSU Method List */
758 count = wpabuf_put(buf, 1);
65ab7eb1 759 for (i = 0; p->method_list && p->method_list[i] >= 0; i++)
ae6d15c7
JM
760 wpabuf_put_u8(buf, p->method_list[i]);
761 *count = i;
762
763 /* Icons Available */
764 len2 = wpabuf_put(buf, 2);
0e450db2
JM
765 for (i = 0; i < p->icons_count; i++)
766 anqp_add_icon(buf, bss, p->icons[i]);
ae6d15c7
JM
767 WPA_PUT_LE16(len2, (u8 *) wpabuf_put(buf, 0) - len2 - 2);
768
769 /* OSU_NAI */
770 if (p->osu_nai) {
771 wpabuf_put_u8(buf, os_strlen(p->osu_nai));
772 wpabuf_put_str(buf, p->osu_nai);
773 } else
774 wpabuf_put_u8(buf, 0);
775
776 /* OSU Service Description Duples */
777 len2 = wpabuf_put(buf, 2);
778 for (i = 0; i < p->service_desc_count; i++) {
779 struct hostapd_lang_string *s = &p->service_desc[i];
780 wpabuf_put_u8(buf, 3 + s->name_len);
781 wpabuf_put_data(buf, s->lang, 3);
782 wpabuf_put_data(buf, s->name, s->name_len);
783 }
784 WPA_PUT_LE16(len2, (u8 *) wpabuf_put(buf, 0) - len2 - 2);
785
786 WPA_PUT_LE16(len, (u8 *) wpabuf_put(buf, 0) - len - 2);
787}
788
789
790static void anqp_add_osu_providers_list(struct hostapd_data *hapd,
791 struct wpabuf *buf)
792{
793 if (hapd->conf->hs20_osu_providers_count) {
794 size_t i;
795 u8 *len = gas_anqp_add_element(buf, ANQP_VENDOR_SPECIFIC);
796 wpabuf_put_be24(buf, OUI_WFA);
797 wpabuf_put_u8(buf, HS20_ANQP_OUI_TYPE);
798 wpabuf_put_u8(buf, HS20_STYPE_OSU_PROVIDERS_LIST);
799 wpabuf_put_u8(buf, 0); /* Reserved */
800
801 /* OSU SSID */
802 wpabuf_put_u8(buf, hapd->conf->osu_ssid_len);
803 wpabuf_put_data(buf, hapd->conf->osu_ssid,
804 hapd->conf->osu_ssid_len);
805
806 /* Number of OSU Providers */
807 wpabuf_put_u8(buf, hapd->conf->hs20_osu_providers_count);
808
809 for (i = 0; i < hapd->conf->hs20_osu_providers_count; i++) {
810 anqp_add_osu_provider(
811 buf, hapd->conf,
812 &hapd->conf->hs20_osu_providers[i]);
813 }
814
815 gas_anqp_set_element_len(buf, len);
816 }
817}
818
819
f7bd7a01
JM
820static void anqp_add_icon_binary_file(struct hostapd_data *hapd,
821 struct wpabuf *buf,
822 const u8 *name, size_t name_len)
823{
824 struct hs20_icon *icon;
825 size_t i;
826 u8 *len;
827
828 wpa_hexdump_ascii(MSG_DEBUG, "HS 2.0: Requested Icon Filename",
829 name, name_len);
830 for (i = 0; i < hapd->conf->hs20_icons_count; i++) {
831 icon = &hapd->conf->hs20_icons[i];
832 if (name_len == os_strlen(icon->name) &&
833 os_memcmp(name, icon->name, name_len) == 0)
834 break;
835 }
836
837 if (i < hapd->conf->hs20_icons_count)
838 icon = &hapd->conf->hs20_icons[i];
839 else
840 icon = NULL;
841
842 len = gas_anqp_add_element(buf, ANQP_VENDOR_SPECIFIC);
843 wpabuf_put_be24(buf, OUI_WFA);
844 wpabuf_put_u8(buf, HS20_ANQP_OUI_TYPE);
845 wpabuf_put_u8(buf, HS20_STYPE_ICON_BINARY_FILE);
846 wpabuf_put_u8(buf, 0); /* Reserved */
847
848 if (icon) {
849 char *data;
850 size_t data_len;
851
852 data = os_readfile(icon->file, &data_len);
853 if (data == NULL || data_len > 65535) {
854 wpabuf_put_u8(buf, 2); /* Download Status:
855 * Unspecified file error */
856 wpabuf_put_u8(buf, 0);
857 wpabuf_put_le16(buf, 0);
858 } else {
859 wpabuf_put_u8(buf, 0); /* Download Status: Success */
860 wpabuf_put_u8(buf, os_strlen(icon->type));
861 wpabuf_put_str(buf, icon->type);
862 wpabuf_put_le16(buf, data_len);
863 wpabuf_put_data(buf, data, data_len);
864 }
865 os_free(data);
866 } else {
867 wpabuf_put_u8(buf, 1); /* Download Status: File not found */
868 wpabuf_put_u8(buf, 0);
869 wpabuf_put_le16(buf, 0);
870 }
871
872 gas_anqp_set_element_len(buf, len);
873}
874
0e450db2
JM
875
876static void anqp_add_operator_icon_metadata(struct hostapd_data *hapd,
877 struct wpabuf *buf)
878{
879 struct hostapd_bss_config *bss = hapd->conf;
880 size_t i;
881 u8 *len;
882
883 if (!bss->hs20_operator_icon_count)
884 return;
885
886 len = gas_anqp_add_element(buf, ANQP_VENDOR_SPECIFIC);
887
888 wpabuf_put_be24(buf, OUI_WFA);
889 wpabuf_put_u8(buf, HS20_ANQP_OUI_TYPE);
890 wpabuf_put_u8(buf, HS20_STYPE_OPERATOR_ICON_METADATA);
891 wpabuf_put_u8(buf, 0); /* Reserved */
892
893 for (i = 0; i < bss->hs20_operator_icon_count; i++)
894 anqp_add_icon(buf, bss, bss->hs20_operator_icon[i]);
895
896 gas_anqp_set_element_len(buf, len);
897}
898
ce6ce7fb
JM
899#endif /* CONFIG_HS20 */
900
df5934f1 901
941caed9
JM
902#ifdef CONFIG_MBO
903static void anqp_add_mbo_cell_data_conn_pref(struct hostapd_data *hapd,
904 struct wpabuf *buf)
905{
906 if (hapd->conf->mbo_cell_data_conn_pref >= 0) {
907 u8 *len = gas_anqp_add_element(buf, ANQP_VENDOR_SPECIFIC);
908 wpabuf_put_be24(buf, OUI_WFA);
909 wpabuf_put_u8(buf, MBO_ANQP_OUI_TYPE);
910 wpabuf_put_u8(buf, MBO_ANQP_SUBTYPE_CELL_CONN_PREF);
911 wpabuf_put_u8(buf, hapd->conf->mbo_cell_data_conn_pref);
912 gas_anqp_set_element_len(buf, len);
913 }
914}
915#endif /* CONFIG_MBO */
916
917
d179089b
MS
918static size_t anqp_get_required_len(struct hostapd_data *hapd,
919 const u16 *infoid,
920 unsigned int num_infoid)
921{
922 size_t len = 0;
923 unsigned int i;
924
925 for (i = 0; i < num_infoid; i++) {
926 struct anqp_element *elem = get_anqp_elem(hapd, infoid[i]);
927
928 if (elem)
929 len += 2 + 2 + wpabuf_len(elem->payload);
930 }
931
932 return len;
933}
934
935
dca30c3f
JK
936static struct wpabuf *
937gas_serv_build_gas_resp_payload(struct hostapd_data *hapd,
938 unsigned int request,
f7bd7a01 939 const u8 *home_realm, size_t home_realm_len,
695dbbea
JM
940 const u8 *icon_name, size_t icon_name_len,
941 const u16 *extra_req,
942 unsigned int num_extra_req)
dca30c3f
JK
943{
944 struct wpabuf *buf;
f7bd7a01 945 size_t len;
695dbbea 946 unsigned int i;
f7bd7a01
JM
947
948 len = 1400;
949 if (request & (ANQP_REQ_NAI_REALM | ANQP_REQ_NAI_HOME_REALM))
950 len += 1000;
951 if (request & ANQP_REQ_ICON_REQUEST)
952 len += 65536;
8183aee6
JM
953#ifdef CONFIG_FILS
954 if (request & ANQP_FILS_REALM_INFO)
955 len += 2 * dl_list_len(&hapd->conf->fils_realms);
956#endif /* CONFIG_FILS */
d179089b 957 len += anqp_get_required_len(hapd, extra_req, num_extra_req);
dca30c3f 958
f7bd7a01 959 buf = wpabuf_alloc(len);
dca30c3f
JK
960 if (buf == NULL)
961 return NULL;
962
963 if (request & ANQP_REQ_CAPABILITY_LIST)
964 anqp_add_capab_list(hapd, buf);
648cc711
JM
965 if (request & ANQP_REQ_VENUE_NAME)
966 anqp_add_venue_name(hapd, buf);
695dbbea
JM
967 if (request & ANQP_REQ_EMERGENCY_CALL_NUMBER)
968 anqp_add_elem(hapd, buf, ANQP_EMERGENCY_CALL_NUMBER);
550a3958
JK
969 if (request & ANQP_REQ_NETWORK_AUTH_TYPE)
970 anqp_add_network_auth_type(hapd, buf);
3eaee4bf
JM
971 if (request & ANQP_REQ_ROAMING_CONSORTIUM)
972 anqp_add_roaming_consortium(hapd, buf);
78bda93e
JK
973 if (request & ANQP_REQ_IP_ADDR_TYPE_AVAILABILITY)
974 anqp_add_ip_addr_type_availability(hapd, buf);
43f51e2a
JK
975 if (request & (ANQP_REQ_NAI_REALM | ANQP_REQ_NAI_HOME_REALM))
976 anqp_add_nai_realm(hapd, buf, home_realm, home_realm_len,
977 request & ANQP_REQ_NAI_REALM,
978 request & ANQP_REQ_NAI_HOME_REALM);
7515adb2
JK
979 if (request & ANQP_REQ_3GPP_CELLULAR_NETWORK)
980 anqp_add_3gpp_cellular_network(hapd, buf);
695dbbea
JM
981 if (request & ANQP_REQ_AP_GEOSPATIAL_LOCATION)
982 anqp_add_elem(hapd, buf, ANQP_AP_GEOSPATIAL_LOCATION);
983 if (request & ANQP_REQ_AP_CIVIC_LOCATION)
984 anqp_add_elem(hapd, buf, ANQP_AP_CIVIC_LOCATION);
985 if (request & ANQP_REQ_AP_LOCATION_PUBLIC_URI)
986 anqp_add_elem(hapd, buf, ANQP_AP_LOCATION_PUBLIC_URI);
26fac8b6
JK
987 if (request & ANQP_REQ_DOMAIN_NAME)
988 anqp_add_domain_name(hapd, buf);
695dbbea
JM
989 if (request & ANQP_REQ_EMERGENCY_ALERT_URI)
990 anqp_add_elem(hapd, buf, ANQP_EMERGENCY_ALERT_URI);
991 if (request & ANQP_REQ_TDLS_CAPABILITY)
992 anqp_add_elem(hapd, buf, ANQP_TDLS_CAPABILITY);
993 if (request & ANQP_REQ_EMERGENCY_NAI)
994 anqp_add_elem(hapd, buf, ANQP_EMERGENCY_NAI);
995
8183aee6
JM
996 for (i = 0; i < num_extra_req; i++) {
997#ifdef CONFIG_FILS
998 if (extra_req[i] == ANQP_FILS_REALM_INFO) {
999 anqp_add_fils_realm_info(hapd, buf);
1000 continue;
1001 }
1002#endif /* CONFIG_FILS */
7e1d3ee9
JM
1003 if (extra_req[i] == ANQP_VENUE_URL) {
1004 anqp_add_venue_url(hapd, buf);
1005 continue;
1006 }
695dbbea 1007 anqp_add_elem(hapd, buf, extra_req[i]);
8183aee6 1008 }
dca30c3f 1009
ce6ce7fb 1010#ifdef CONFIG_HS20
45ac307a
JK
1011 if (request & ANQP_REQ_HS_CAPABILITY_LIST)
1012 anqp_add_hs_capab_list(hapd, buf);
a9277e85
JK
1013 if (request & ANQP_REQ_OPERATOR_FRIENDLY_NAME)
1014 anqp_add_operator_friendly_name(hapd, buf);
4065a309
JK
1015 if (request & ANQP_REQ_WAN_METRICS)
1016 anqp_add_wan_metrics(hapd, buf);
5ccc54aa
JK
1017 if (request & ANQP_REQ_CONNECTION_CAPABILITY)
1018 anqp_add_connection_capability(hapd, buf);
df5934f1
JK
1019 if (request & ANQP_REQ_OPERATING_CLASS)
1020 anqp_add_operating_class(hapd, buf);
ae6d15c7
JM
1021 if (request & ANQP_REQ_OSU_PROVIDERS_LIST)
1022 anqp_add_osu_providers_list(hapd, buf);
f7bd7a01
JM
1023 if (request & ANQP_REQ_ICON_REQUEST)
1024 anqp_add_icon_binary_file(hapd, buf, icon_name, icon_name_len);
0e450db2
JM
1025 if (request & ANQP_REQ_OPERATOR_ICON_METADATA)
1026 anqp_add_operator_icon_metadata(hapd, buf);
ce6ce7fb 1027#endif /* CONFIG_HS20 */
45ac307a 1028
941caed9
JM
1029#ifdef CONFIG_MBO
1030 if (request & ANQP_REQ_MBO_CELL_DATA_CONN_PREF)
1031 anqp_add_mbo_cell_data_conn_pref(hapd, buf);
1032#endif /* CONFIG_MBO */
1033
dca30c3f
JK
1034 return buf;
1035}
1036
1037
695dbbea
JM
1038#define ANQP_MAX_EXTRA_REQ 20
1039
dca30c3f
JK
1040struct anqp_query_info {
1041 unsigned int request;
43f51e2a
JK
1042 const u8 *home_realm_query;
1043 size_t home_realm_query_len;
f7bd7a01
JM
1044 const u8 *icon_name;
1045 size_t icon_name_len;
a6b12157 1046 int p2p_sd;
695dbbea
JM
1047 u16 extra_req[ANQP_MAX_EXTRA_REQ];
1048 unsigned int num_extra_req;
dca30c3f
JK
1049};
1050
1051
1052static void set_anqp_req(unsigned int bit, const char *name, int local,
dca30c3f
JK
1053 struct anqp_query_info *qi)
1054{
1055 qi->request |= bit;
1056 if (local) {
1057 wpa_printf(MSG_DEBUG, "ANQP: %s (local)", name);
dca30c3f
JK
1058 } else {
1059 wpa_printf(MSG_DEBUG, "ANQP: %s not available", name);
1060 }
1061}
1062
1063
1064static void rx_anqp_query_list_id(struct hostapd_data *hapd, u16 info_id,
1065 struct anqp_query_info *qi)
1066{
1067 switch (info_id) {
1068 case ANQP_CAPABILITY_LIST:
d0bf06f2
JM
1069 set_anqp_req(ANQP_REQ_CAPABILITY_LIST, "Capability List", 1,
1070 qi);
dca30c3f 1071 break;
648cc711
JM
1072 case ANQP_VENUE_NAME:
1073 set_anqp_req(ANQP_REQ_VENUE_NAME, "Venue Name",
d0bf06f2 1074 hapd->conf->venue_name != NULL, qi);
648cc711 1075 break;
695dbbea
JM
1076 case ANQP_EMERGENCY_CALL_NUMBER:
1077 set_anqp_req(ANQP_REQ_EMERGENCY_CALL_NUMBER,
1078 "Emergency Call Number",
1079 get_anqp_elem(hapd, info_id) != NULL, qi);
1080 break;
550a3958
JK
1081 case ANQP_NETWORK_AUTH_TYPE:
1082 set_anqp_req(ANQP_REQ_NETWORK_AUTH_TYPE, "Network Auth Type",
d0bf06f2 1083 hapd->conf->network_auth_type != NULL, qi);
550a3958 1084 break;
3eaee4bf
JM
1085 case ANQP_ROAMING_CONSORTIUM:
1086 set_anqp_req(ANQP_REQ_ROAMING_CONSORTIUM, "Roaming Consortium",
d0bf06f2 1087 hapd->conf->roaming_consortium != NULL, qi);
3eaee4bf 1088 break;
78bda93e
JK
1089 case ANQP_IP_ADDR_TYPE_AVAILABILITY:
1090 set_anqp_req(ANQP_REQ_IP_ADDR_TYPE_AVAILABILITY,
1091 "IP Addr Type Availability",
d0bf06f2 1092 hapd->conf->ipaddr_type_configured, qi);
771e2f7b 1093 break;
8047b186
JK
1094 case ANQP_NAI_REALM:
1095 set_anqp_req(ANQP_REQ_NAI_REALM, "NAI Realm",
d0bf06f2 1096 hapd->conf->nai_realm_data != NULL, qi);
8047b186 1097 break;
7515adb2
JK
1098 case ANQP_3GPP_CELLULAR_NETWORK:
1099 set_anqp_req(ANQP_REQ_3GPP_CELLULAR_NETWORK,
1100 "3GPP Cellular Network",
d0bf06f2 1101 hapd->conf->anqp_3gpp_cell_net != NULL, qi);
7515adb2 1102 break;
695dbbea
JM
1103 case ANQP_AP_GEOSPATIAL_LOCATION:
1104 set_anqp_req(ANQP_REQ_AP_GEOSPATIAL_LOCATION,
1105 "AP Geospatial Location",
1106 get_anqp_elem(hapd, info_id) != NULL, qi);
1107 break;
1108 case ANQP_AP_CIVIC_LOCATION:
1109 set_anqp_req(ANQP_REQ_AP_CIVIC_LOCATION,
1110 "AP Civic Location",
1111 get_anqp_elem(hapd, info_id) != NULL, qi);
1112 break;
1113 case ANQP_AP_LOCATION_PUBLIC_URI:
1114 set_anqp_req(ANQP_REQ_AP_LOCATION_PUBLIC_URI,
1115 "AP Location Public URI",
1116 get_anqp_elem(hapd, info_id) != NULL, qi);
1117 break;
26fac8b6
JK
1118 case ANQP_DOMAIN_NAME:
1119 set_anqp_req(ANQP_REQ_DOMAIN_NAME, "Domain Name",
d0bf06f2 1120 hapd->conf->domain_name != NULL, qi);
26fac8b6 1121 break;
695dbbea
JM
1122 case ANQP_EMERGENCY_ALERT_URI:
1123 set_anqp_req(ANQP_REQ_EMERGENCY_ALERT_URI,
1124 "Emergency Alert URI",
1125 get_anqp_elem(hapd, info_id) != NULL, qi);
1126 break;
1127 case ANQP_TDLS_CAPABILITY:
1128 set_anqp_req(ANQP_REQ_TDLS_CAPABILITY,
1129 "TDLS Capability",
1130 get_anqp_elem(hapd, info_id) != NULL, qi);
1131 break;
1132 case ANQP_EMERGENCY_NAI:
1133 set_anqp_req(ANQP_REQ_EMERGENCY_NAI,
1134 "Emergency NAI",
1135 get_anqp_elem(hapd, info_id) != NULL, qi);
1136 break;
dca30c3f 1137 default:
8183aee6
JM
1138#ifdef CONFIG_FILS
1139 if (info_id == ANQP_FILS_REALM_INFO &&
1140 !dl_list_empty(&hapd->conf->fils_realms)) {
1141 wpa_printf(MSG_DEBUG,
1142 "ANQP: FILS Realm Information (local)");
1143 } else
1144#endif /* CONFIG_FILS */
7e1d3ee9
JM
1145 if (info_id == ANQP_VENUE_URL && hapd->conf->venue_url) {
1146 wpa_printf(MSG_DEBUG,
1147 "ANQP: Venue URL (local)");
1148 } else if (!get_anqp_elem(hapd, info_id)) {
695dbbea
JM
1149 wpa_printf(MSG_DEBUG, "ANQP: Unsupported Info Id %u",
1150 info_id);
1151 break;
1152 }
1153 if (qi->num_extra_req == ANQP_MAX_EXTRA_REQ) {
1154 wpa_printf(MSG_DEBUG,
1155 "ANQP: No more room for extra requests - ignore Info Id %u",
1156 info_id);
1157 break;
1158 }
1159 wpa_printf(MSG_DEBUG, "ANQP: Info Id %u (local)", info_id);
1160 qi->extra_req[qi->num_extra_req] = info_id;
1161 qi->num_extra_req++;
dca30c3f
JK
1162 break;
1163 }
1164}
1165
1166
1167static void rx_anqp_query_list(struct hostapd_data *hapd,
1168 const u8 *pos, const u8 *end,
1169 struct anqp_query_info *qi)
1170{
1171 wpa_printf(MSG_DEBUG, "ANQP: %u Info IDs requested in Query list",
1172 (unsigned int) (end - pos) / 2);
1173
f88bcad0 1174 while (end - pos >= 2) {
dca30c3f
JK
1175 rx_anqp_query_list_id(hapd, WPA_GET_LE16(pos), qi);
1176 pos += 2;
1177 }
1178}
1179
1180
ce6ce7fb
JM
1181#ifdef CONFIG_HS20
1182
45ac307a
JK
1183static void rx_anqp_hs_query_list(struct hostapd_data *hapd, u8 subtype,
1184 struct anqp_query_info *qi)
1185{
1186 switch (subtype) {
1187 case HS20_STYPE_CAPABILITY_LIST:
1188 set_anqp_req(ANQP_REQ_HS_CAPABILITY_LIST, "HS Capability List",
d0bf06f2 1189 1, qi);
45ac307a 1190 break;
a9277e85
JK
1191 case HS20_STYPE_OPERATOR_FRIENDLY_NAME:
1192 set_anqp_req(ANQP_REQ_OPERATOR_FRIENDLY_NAME,
1193 "Operator Friendly Name",
d0bf06f2 1194 hapd->conf->hs20_oper_friendly_name != NULL, qi);
a9277e85 1195 break;
4065a309
JK
1196 case HS20_STYPE_WAN_METRICS:
1197 set_anqp_req(ANQP_REQ_WAN_METRICS, "WAN Metrics",
d0bf06f2 1198 hapd->conf->hs20_wan_metrics != NULL, qi);
4065a309 1199 break;
5ccc54aa
JK
1200 case HS20_STYPE_CONNECTION_CAPABILITY:
1201 set_anqp_req(ANQP_REQ_CONNECTION_CAPABILITY,
1202 "Connection Capability",
1203 hapd->conf->hs20_connection_capability != NULL,
d0bf06f2 1204 qi);
5ccc54aa 1205 break;
df5934f1
JK
1206 case HS20_STYPE_OPERATING_CLASS:
1207 set_anqp_req(ANQP_REQ_OPERATING_CLASS, "Operating Class",
d0bf06f2 1208 hapd->conf->hs20_operating_class != NULL, qi);
df5934f1 1209 break;
ae6d15c7
JM
1210 case HS20_STYPE_OSU_PROVIDERS_LIST:
1211 set_anqp_req(ANQP_REQ_OSU_PROVIDERS_LIST, "OSU Providers list",
d0bf06f2 1212 hapd->conf->hs20_osu_providers_count, qi);
ae6d15c7 1213 break;
0e450db2
JM
1214 case HS20_STYPE_OPERATOR_ICON_METADATA:
1215 set_anqp_req(ANQP_REQ_OPERATOR_ICON_METADATA,
1216 "Operator Icon Metadata",
1217 hapd->conf->hs20_operator_icon_count, qi);
1218 break;
45ac307a
JK
1219 default:
1220 wpa_printf(MSG_DEBUG, "ANQP: Unsupported HS 2.0 subtype %u",
1221 subtype);
1222 break;
1223 }
1224}
1225
1226
43f51e2a
JK
1227static void rx_anqp_hs_nai_home_realm(struct hostapd_data *hapd,
1228 const u8 *pos, const u8 *end,
1229 struct anqp_query_info *qi)
1230{
1231 qi->request |= ANQP_REQ_NAI_HOME_REALM;
1232 qi->home_realm_query = pos;
1233 qi->home_realm_query_len = end - pos;
1234 if (hapd->conf->nai_realm_data != NULL) {
1235 wpa_printf(MSG_DEBUG, "ANQP: HS 2.0 NAI Home Realm Query "
1236 "(local)");
1237 } else {
1238 wpa_printf(MSG_DEBUG, "ANQP: HS 2.0 NAI Home Realm Query not "
1239 "available");
1240 }
1241}
1242
1243
f7bd7a01
JM
1244static void rx_anqp_hs_icon_request(struct hostapd_data *hapd,
1245 const u8 *pos, const u8 *end,
1246 struct anqp_query_info *qi)
1247{
1248 qi->request |= ANQP_REQ_ICON_REQUEST;
1249 qi->icon_name = pos;
1250 qi->icon_name_len = end - pos;
1251 if (hapd->conf->hs20_icons_count) {
1252 wpa_printf(MSG_DEBUG, "ANQP: HS 2.0 Icon Request Query "
1253 "(local)");
1254 } else {
1255 wpa_printf(MSG_DEBUG, "ANQP: HS 2.0 Icon Request Query not "
1256 "available");
1257 }
1258}
1259
1260
941caed9
JM
1261static void rx_anqp_vendor_specific_hs20(struct hostapd_data *hapd,
1262 const u8 *pos, const u8 *end,
1263 struct anqp_query_info *qi)
45ac307a 1264{
45ac307a
JK
1265 u8 subtype;
1266
f88bcad0 1267 if (end - pos <= 1)
45ac307a
JK
1268 return;
1269
1270 subtype = *pos++;
1271 pos++; /* Reserved */
1272 switch (subtype) {
1273 case HS20_STYPE_QUERY_LIST:
1274 wpa_printf(MSG_DEBUG, "ANQP: HS 2.0 Query List");
1275 while (pos < end) {
1276 rx_anqp_hs_query_list(hapd, *pos, qi);
1277 pos++;
1278 }
1279 break;
43f51e2a
JK
1280 case HS20_STYPE_NAI_HOME_REALM_QUERY:
1281 rx_anqp_hs_nai_home_realm(hapd, pos, end, qi);
1282 break;
f7bd7a01
JM
1283 case HS20_STYPE_ICON_REQUEST:
1284 rx_anqp_hs_icon_request(hapd, pos, end, qi);
1285 break;
45ac307a
JK
1286 default:
1287 wpa_printf(MSG_DEBUG, "ANQP: Unsupported HS 2.0 query subtype "
1288 "%u", subtype);
1289 break;
1290 }
1291}
1292
ce6ce7fb
JM
1293#endif /* CONFIG_HS20 */
1294
45ac307a 1295
941caed9
JM
1296#ifdef CONFIG_P2P
1297static void rx_anqp_vendor_specific_p2p(struct hostapd_data *hapd,
1298 struct anqp_query_info *qi)
1299{
1300 /*
1301 * This is for P2P SD and will be taken care of by the P2P
1302 * implementation. This query needs to be ignored in the generic
1303 * GAS server to avoid duplicated response.
1304 */
1305 wpa_printf(MSG_DEBUG,
1306 "ANQP: Ignore WFA vendor type %u (P2P SD) in generic GAS server",
1307 P2P_OUI_TYPE);
1308 qi->p2p_sd = 1;
1309 return;
1310}
1311#endif /* CONFIG_P2P */
1312
1313
1314#ifdef CONFIG_MBO
1315
1316static void rx_anqp_mbo_query_list(struct hostapd_data *hapd, u8 subtype,
1317 struct anqp_query_info *qi)
1318{
1319 switch (subtype) {
1320 case MBO_ANQP_SUBTYPE_CELL_CONN_PREF:
1321 set_anqp_req(ANQP_REQ_MBO_CELL_DATA_CONN_PREF,
1322 "Cellular Data Connection Preference",
1323 hapd->conf->mbo_cell_data_conn_pref >= 0, qi);
1324 break;
1325 default:
1326 wpa_printf(MSG_DEBUG, "ANQP: Unsupported MBO subtype %u",
1327 subtype);
1328 break;
1329 }
1330}
1331
1332
1333static void rx_anqp_vendor_specific_mbo(struct hostapd_data *hapd,
1334 const u8 *pos, const u8 *end,
1335 struct anqp_query_info *qi)
1336{
1337 u8 subtype;
1338
1339 if (end - pos < 1)
1340 return;
1341
1342 subtype = *pos++;
1343 switch (subtype) {
1344 case MBO_ANQP_SUBTYPE_QUERY_LIST:
1345 wpa_printf(MSG_DEBUG, "ANQP: MBO Query List");
1346 while (pos < end) {
1347 rx_anqp_mbo_query_list(hapd, *pos, qi);
1348 pos++;
1349 }
1350 break;
1351 default:
1352 wpa_printf(MSG_DEBUG, "ANQP: Unsupported MBO query subtype %u",
1353 subtype);
1354 break;
1355 }
1356}
1357
1358#endif /* CONFIG_MBO */
1359
1360
1361static void rx_anqp_vendor_specific(struct hostapd_data *hapd,
1362 const u8 *pos, const u8 *end,
1363 struct anqp_query_info *qi)
1364{
1365 u32 oui;
1366
1367 if (end - pos < 4) {
1368 wpa_printf(MSG_DEBUG, "ANQP: Too short vendor specific ANQP "
1369 "Query element");
1370 return;
1371 }
1372
1373 oui = WPA_GET_BE24(pos);
1374 pos += 3;
1375 if (oui != OUI_WFA) {
1376 wpa_printf(MSG_DEBUG, "ANQP: Unsupported vendor OUI %06x",
1377 oui);
1378 return;
1379 }
1380
1381 switch (*pos) {
1382#ifdef CONFIG_P2P
1383 case P2P_OUI_TYPE:
1384 rx_anqp_vendor_specific_p2p(hapd, qi);
1385 break;
1386#endif /* CONFIG_P2P */
1387#ifdef CONFIG_HS20
1388 case HS20_ANQP_OUI_TYPE:
1389 rx_anqp_vendor_specific_hs20(hapd, pos + 1, end, qi);
1390 break;
1391#endif /* CONFIG_HS20 */
1392#ifdef CONFIG_MBO
1393 case MBO_ANQP_OUI_TYPE:
1394 rx_anqp_vendor_specific_mbo(hapd, pos + 1, end, qi);
1395 break;
1396#endif /* CONFIG_MBO */
1397 default:
1398 wpa_printf(MSG_DEBUG, "ANQP: Unsupported WFA vendor type %u",
1399 *pos);
1400 break;
1401 }
1402}
1403
1404
dca30c3f
JK
1405static void gas_serv_req_local_processing(struct hostapd_data *hapd,
1406 const u8 *sa, u8 dialog_token,
78a36327
JM
1407 struct anqp_query_info *qi, int prot,
1408 int std_addr3)
dca30c3f
JK
1409{
1410 struct wpabuf *buf, *tx_buf;
1411
aff0bee7 1412 buf = gas_serv_build_gas_resp_payload(hapd, qi->request,
43f51e2a 1413 qi->home_realm_query,
f7bd7a01 1414 qi->home_realm_query_len,
695dbbea
JM
1415 qi->icon_name, qi->icon_name_len,
1416 qi->extra_req, qi->num_extra_req);
dca30c3f
JK
1417 wpa_hexdump_buf(MSG_MSGDUMP, "ANQP: Locally generated ANQP responses",
1418 buf);
1419 if (!buf)
1420 return;
a6b12157
JM
1421#ifdef CONFIG_P2P
1422 if (wpabuf_len(buf) == 0 && qi->p2p_sd) {
1423 wpa_printf(MSG_DEBUG,
1424 "ANQP: Do not send response to P2P SD from generic GAS service (P2P SD implementation will process this)");
1425 wpabuf_free(buf);
1426 return;
1427 }
1428#endif /* CONFIG_P2P */
dca30c3f 1429
2977f519 1430 if (wpabuf_len(buf) > hapd->conf->gas_frag_limit ||
dca30c3f
JK
1431 hapd->conf->gas_comeback_delay) {
1432 struct gas_dialog_info *di;
1433 u16 comeback_delay = 1;
1434
1435 if (hapd->conf->gas_comeback_delay) {
1436 /* Testing - allow overriding of the delay value */
1437 comeback_delay = hapd->conf->gas_comeback_delay;
1438 }
1439
1440 wpa_printf(MSG_DEBUG, "ANQP: Too long response to fit in "
1441 "initial response - use GAS comeback");
1442 di = gas_dialog_create(hapd, sa, dialog_token);
1443 if (!di) {
1444 wpa_printf(MSG_INFO, "ANQP: Could not create dialog "
1445 "for " MACSTR " (dialog token %u)",
1446 MAC2STR(sa), dialog_token);
1447 wpabuf_free(buf);
d73c7b96
JM
1448 tx_buf = gas_anqp_build_initial_resp_buf(
1449 dialog_token, WLAN_STATUS_UNSPECIFIED_FAILURE,
1450 0, NULL);
1451 } else {
1452 di->prot = prot;
1453 di->sd_resp = buf;
1454 di->sd_resp_pos = 0;
1455 tx_buf = gas_anqp_build_initial_resp_buf(
1456 dialog_token, WLAN_STATUS_SUCCESS,
1457 comeback_delay, NULL);
dca30c3f 1458 }
dca30c3f
JK
1459 } else {
1460 wpa_printf(MSG_DEBUG, "ANQP: Initial response (no comeback)");
1461 tx_buf = gas_anqp_build_initial_resp_buf(
1462 dialog_token, WLAN_STATUS_SUCCESS, 0, buf);
1463 wpabuf_free(buf);
1464 }
1465 if (!tx_buf)
1466 return;
5ce00d09
JM
1467 if (prot)
1468 convert_to_protected_dual(tx_buf);
78a36327
JM
1469 if (std_addr3)
1470 hostapd_drv_send_action(hapd, hapd->iface->freq, 0, sa,
1471 wpabuf_head(tx_buf),
1472 wpabuf_len(tx_buf));
1473 else
1474 hostapd_drv_send_action_addr3_ap(hapd, hapd->iface->freq, 0, sa,
1475 wpabuf_head(tx_buf),
1476 wpabuf_len(tx_buf));
dca30c3f
JK
1477 wpabuf_free(tx_buf);
1478}
1479
1480
2605405a
JM
1481#ifdef CONFIG_DPP
1482static void gas_serv_req_dpp_processing(struct hostapd_data *hapd,
1483 const u8 *sa, u8 dialog_token,
1484 int prot, struct wpabuf *buf)
1485{
1486 struct wpabuf *tx_buf;
1487
1488 if (wpabuf_len(buf) > hapd->conf->gas_frag_limit ||
1489 hapd->conf->gas_comeback_delay) {
1490 struct gas_dialog_info *di;
1491 u16 comeback_delay = 1;
1492
1493 if (hapd->conf->gas_comeback_delay) {
1494 /* Testing - allow overriding of the delay value */
1495 comeback_delay = hapd->conf->gas_comeback_delay;
1496 }
1497
1498 wpa_printf(MSG_DEBUG,
1499 "DPP: Too long response to fit in initial response - use GAS comeback");
1500 di = gas_dialog_create(hapd, sa, dialog_token);
1501 if (!di) {
1502 wpa_printf(MSG_INFO, "DPP: Could not create dialog for "
1503 MACSTR " (dialog token %u)",
1504 MAC2STR(sa), dialog_token);
1505 wpabuf_free(buf);
1506 tx_buf = gas_build_initial_resp(
1507 dialog_token, WLAN_STATUS_UNSPECIFIED_FAILURE,
1508 0, 10);
1509 if (tx_buf)
1510 gas_serv_write_dpp_adv_proto(tx_buf);
1511 } else {
1512 di->prot = prot;
1513 di->sd_resp = buf;
1514 di->sd_resp_pos = 0;
1515 tx_buf = gas_build_initial_resp(
1516 dialog_token, WLAN_STATUS_SUCCESS,
1517 comeback_delay, 10);
1518 if (tx_buf)
1519 gas_serv_write_dpp_adv_proto(tx_buf);
1520 }
1521 } else {
1522 wpa_printf(MSG_DEBUG,
1523 "DPP: GAS Initial response (no comeback)");
1524 tx_buf = gas_build_initial_resp(
1525 dialog_token, WLAN_STATUS_SUCCESS, 0,
1526 10 + 2 + wpabuf_len(buf));
1527 if (tx_buf) {
1528 gas_serv_write_dpp_adv_proto(tx_buf);
1529 wpabuf_put_le16(tx_buf, wpabuf_len(buf));
1530 wpabuf_put_buf(tx_buf, buf);
634a130a 1531 hostapd_dpp_gas_status_handler(hapd, 1);
2605405a
JM
1532 }
1533 wpabuf_free(buf);
1534 }
1535 if (!tx_buf)
1536 return;
1537 if (prot)
1538 convert_to_protected_dual(tx_buf);
1539 hostapd_drv_send_action(hapd, hapd->iface->freq, 0, sa,
1540 wpabuf_head(tx_buf),
1541 wpabuf_len(tx_buf));
1542 wpabuf_free(tx_buf);
1543}
1544#endif /* CONFIG_DPP */
1545
1546
dca30c3f
JK
1547static void gas_serv_rx_gas_initial_req(struct hostapd_data *hapd,
1548 const u8 *sa,
78a36327
JM
1549 const u8 *data, size_t len, int prot,
1550 int std_addr3)
dca30c3f
JK
1551{
1552 const u8 *pos = data;
1553 const u8 *end = data + len;
1554 const u8 *next;
1555 u8 dialog_token;
1556 u16 slen;
1557 struct anqp_query_info qi;
1558 const u8 *adv_proto;
2605405a
JM
1559#ifdef CONFIG_DPP
1560 int dpp = 0;
1561#endif /* CONFIG_DPP */
dca30c3f
JK
1562
1563 if (len < 1 + 2)
1564 return;
1565
1566 os_memset(&qi, 0, sizeof(qi));
1567
1568 dialog_token = *pos++;
1569 wpa_msg(hapd->msg_ctx, MSG_DEBUG,
1570 "GAS: GAS Initial Request from " MACSTR " (dialog token %u) ",
1571 MAC2STR(sa), dialog_token);
1572
1573 if (*pos != WLAN_EID_ADV_PROTO) {
1574 wpa_msg(hapd->msg_ctx, MSG_DEBUG,
1575 "GAS: Unexpected IE in GAS Initial Request: %u", *pos);
1576 return;
1577 }
1578 adv_proto = pos++;
1579
1580 slen = *pos++;
f88bcad0 1581 if (slen > end - pos || slen < 2) {
dca30c3f
JK
1582 wpa_msg(hapd->msg_ctx, MSG_DEBUG,
1583 "GAS: Invalid IE in GAS Initial Request");
1584 return;
1585 }
f88bcad0 1586 next = pos + slen;
dca30c3f
JK
1587 pos++; /* skip QueryRespLenLimit and PAME-BI */
1588
2605405a
JM
1589#ifdef CONFIG_DPP
1590 if (slen == 8 && *pos == WLAN_EID_VENDOR_SPECIFIC &&
1591 pos[1] == 5 && WPA_GET_BE24(&pos[2]) == OUI_WFA &&
1592 pos[5] == DPP_OUI_TYPE && pos[6] == 0x01) {
1593 wpa_printf(MSG_DEBUG, "DPP: Configuration Request");
1594 dpp = 1;
1595 } else
1596#endif /* CONFIG_DPP */
1597
dca30c3f
JK
1598 if (*pos != ACCESS_NETWORK_QUERY_PROTOCOL) {
1599 struct wpabuf *buf;
1600 wpa_msg(hapd->msg_ctx, MSG_DEBUG,
1601 "GAS: Unsupported GAS advertisement protocol id %u",
1602 *pos);
1603 if (sa[0] & 0x01)
1604 return; /* Invalid source address - drop silently */
1605 buf = gas_build_initial_resp(
1606 dialog_token, WLAN_STATUS_GAS_ADV_PROTO_NOT_SUPPORTED,
1607 0, 2 + slen + 2);
1608 if (buf == NULL)
1609 return;
1610 wpabuf_put_data(buf, adv_proto, 2 + slen);
1611 wpabuf_put_le16(buf, 0); /* Query Response Length */
5ce00d09
JM
1612 if (prot)
1613 convert_to_protected_dual(buf);
78a36327
JM
1614 if (std_addr3)
1615 hostapd_drv_send_action(hapd, hapd->iface->freq, 0, sa,
1616 wpabuf_head(buf),
1617 wpabuf_len(buf));
1618 else
1619 hostapd_drv_send_action_addr3_ap(hapd,
1620 hapd->iface->freq, 0,
1621 sa, wpabuf_head(buf),
1622 wpabuf_len(buf));
dca30c3f
JK
1623 wpabuf_free(buf);
1624 return;
1625 }
1626
1627 pos = next;
1628 /* Query Request */
f88bcad0 1629 if (end - pos < 2)
dca30c3f
JK
1630 return;
1631 slen = WPA_GET_LE16(pos);
1632 pos += 2;
f88bcad0 1633 if (slen > end - pos)
dca30c3f
JK
1634 return;
1635 end = pos + slen;
1636
2605405a
JM
1637#ifdef CONFIG_DPP
1638 if (dpp) {
1639 struct wpabuf *msg;
1640
1641 msg = hostapd_dpp_gas_req_handler(hapd, sa, pos, slen);
1642 if (!msg)
1643 return;
1644 gas_serv_req_dpp_processing(hapd, sa, dialog_token, prot, msg);
1645 return;
1646 }
1647#endif /* CONFIG_DPP */
1648
dca30c3f
JK
1649 /* ANQP Query Request */
1650 while (pos < end) {
1651 u16 info_id, elen;
1652
f88bcad0 1653 if (end - pos < 4)
dca30c3f
JK
1654 return;
1655
1656 info_id = WPA_GET_LE16(pos);
1657 pos += 2;
1658 elen = WPA_GET_LE16(pos);
1659 pos += 2;
1660
f88bcad0 1661 if (elen > end - pos) {
dca30c3f
JK
1662 wpa_printf(MSG_DEBUG, "ANQP: Invalid Query Request");
1663 return;
1664 }
1665
1666 switch (info_id) {
1667 case ANQP_QUERY_LIST:
1668 rx_anqp_query_list(hapd, pos, pos + elen, &qi);
1669 break;
45ac307a
JK
1670 case ANQP_VENDOR_SPECIFIC:
1671 rx_anqp_vendor_specific(hapd, pos, pos + elen, &qi);
1672 break;
dca30c3f
JK
1673 default:
1674 wpa_printf(MSG_DEBUG, "ANQP: Unsupported Query "
1675 "Request element %u", info_id);
1676 break;
1677 }
1678
1679 pos += elen;
1680 }
1681
78a36327
JM
1682 gas_serv_req_local_processing(hapd, sa, dialog_token, &qi, prot,
1683 std_addr3);
dca30c3f
JK
1684}
1685
1686
dca30c3f
JK
1687static void gas_serv_rx_gas_comeback_req(struct hostapd_data *hapd,
1688 const u8 *sa,
78a36327
JM
1689 const u8 *data, size_t len, int prot,
1690 int std_addr3)
dca30c3f
JK
1691{
1692 struct gas_dialog_info *dialog;
1693 struct wpabuf *buf, *tx_buf;
1694 u8 dialog_token;
1695 size_t frag_len;
1696 int more = 0;
1697
1698 wpa_hexdump(MSG_DEBUG, "GAS: RX GAS Comeback Request", data, len);
1699 if (len < 1)
1700 return;
1701 dialog_token = *data;
1702 wpa_msg(hapd->msg_ctx, MSG_DEBUG, "GAS: Dialog Token: %u",
1703 dialog_token);
1704
1705 dialog = gas_serv_dialog_find(hapd, sa, dialog_token);
1706 if (!dialog) {
1707 wpa_msg(hapd->msg_ctx, MSG_DEBUG, "GAS: No pending SD "
1708 "response fragment for " MACSTR " dialog token %u",
1709 MAC2STR(sa), dialog_token);
1710
1711 if (sa[0] & 0x01)
1712 return; /* Invalid source address - drop silently */
1713 tx_buf = gas_anqp_build_comeback_resp_buf(
1714 dialog_token, WLAN_STATUS_NO_OUTSTANDING_GAS_REQ, 0, 0,
1715 0, NULL);
1716 if (tx_buf == NULL)
1717 return;
1718 goto send_resp;
1719 }
1720
dca30c3f 1721 frag_len = wpabuf_len(dialog->sd_resp) - dialog->sd_resp_pos;
2977f519
JM
1722 if (frag_len > hapd->conf->gas_frag_limit) {
1723 frag_len = hapd->conf->gas_frag_limit;
dca30c3f
JK
1724 more = 1;
1725 }
1726 wpa_msg(hapd->msg_ctx, MSG_DEBUG, "GAS: resp frag_len %u",
1727 (unsigned int) frag_len);
1728 buf = wpabuf_alloc_copy(wpabuf_head_u8(dialog->sd_resp) +
1729 dialog->sd_resp_pos, frag_len);
1730 if (buf == NULL) {
1731 wpa_msg(hapd->msg_ctx, MSG_DEBUG, "GAS: Failed to allocate "
1732 "buffer");
d0bf06f2
JM
1733 gas_serv_dialog_clear(dialog);
1734 return;
dca30c3f 1735 }
2605405a
JM
1736#ifdef CONFIG_DPP
1737 if (dialog->dpp) {
1738 tx_buf = gas_build_comeback_resp(dialog_token,
1739 WLAN_STATUS_SUCCESS,
1740 dialog->sd_frag_id, more, 0,
1741 10 + frag_len);
1742 if (tx_buf) {
1743 gas_serv_write_dpp_adv_proto(tx_buf);
1744 wpabuf_put_buf(tx_buf, buf);
1745 }
1746 } else
1747#endif /* CONFIG_DPP */
dca30c3f
JK
1748 tx_buf = gas_anqp_build_comeback_resp_buf(dialog_token,
1749 WLAN_STATUS_SUCCESS,
1750 dialog->sd_frag_id,
1751 more, 0, buf);
1752 wpabuf_free(buf);
d0bf06f2
JM
1753 if (tx_buf == NULL) {
1754 gas_serv_dialog_clear(dialog);
1755 return;
1756 }
dca30c3f
JK
1757 wpa_msg(hapd->msg_ctx, MSG_DEBUG, "GAS: Tx GAS Comeback Response "
1758 "(frag_id %d more=%d frag_len=%d)",
1759 dialog->sd_frag_id, more, (int) frag_len);
1760 dialog->sd_frag_id++;
1761 dialog->sd_resp_pos += frag_len;
1762
1763 if (more) {
1764 wpa_msg(hapd->msg_ctx, MSG_DEBUG, "GAS: %d more bytes remain "
1765 "to be sent",
1766 (int) (wpabuf_len(dialog->sd_resp) -
1767 dialog->sd_resp_pos));
1768 } else {
1769 wpa_msg(hapd->msg_ctx, MSG_DEBUG, "GAS: All fragments of "
1770 "SD response sent");
2605405a
JM
1771#ifdef CONFIG_DPP
1772 if (dialog->dpp)
634a130a 1773 hostapd_dpp_gas_status_handler(hapd, 1);
2605405a 1774#endif /* CONFIG_DPP */
dca30c3f
JK
1775 gas_serv_dialog_clear(dialog);
1776 gas_serv_free_dialogs(hapd, sa);
1777 }
1778
1779send_resp:
5ce00d09
JM
1780 if (prot)
1781 convert_to_protected_dual(tx_buf);
78a36327
JM
1782 if (std_addr3)
1783 hostapd_drv_send_action(hapd, hapd->iface->freq, 0, sa,
1784 wpabuf_head(tx_buf),
1785 wpabuf_len(tx_buf));
1786 else
1787 hostapd_drv_send_action_addr3_ap(hapd, hapd->iface->freq, 0, sa,
1788 wpabuf_head(tx_buf),
1789 wpabuf_len(tx_buf));
dca30c3f 1790 wpabuf_free(tx_buf);
dca30c3f
JK
1791}
1792
1793
1794static void gas_serv_rx_public_action(void *ctx, const u8 *buf, size_t len,
1795 int freq)
1796{
1797 struct hostapd_data *hapd = ctx;
1798 const struct ieee80211_mgmt *mgmt;
d84b9139 1799 const u8 *sa, *data;
78a36327 1800 int prot, std_addr3;
dca30c3f
JK
1801
1802 mgmt = (const struct ieee80211_mgmt *) buf;
062833c6 1803 if (len < IEEE80211_HDRLEN + 2)
dca30c3f 1804 return;
5ce00d09
JM
1805 if (mgmt->u.action.category != WLAN_ACTION_PUBLIC &&
1806 mgmt->u.action.category != WLAN_ACTION_PROTECTED_DUAL)
dca30c3f 1807 return;
5ce00d09
JM
1808 /*
1809 * Note: Public Action and Protected Dual of Public Action frames share
1810 * the same payload structure, so it is fine to use definitions of
1811 * Public Action frames to process both.
1812 */
1813 prot = mgmt->u.action.category == WLAN_ACTION_PROTECTED_DUAL;
dca30c3f 1814 sa = mgmt->sa;
83594725
JM
1815 if (hapd->conf->gas_address3 == 1)
1816 std_addr3 = 1;
1817 else if (hapd->conf->gas_address3 == 2)
1818 std_addr3 = 0;
1819 else
1820 std_addr3 = is_broadcast_ether_addr(mgmt->bssid);
062833c6
JM
1821 len -= IEEE80211_HDRLEN + 1;
1822 data = buf + IEEE80211_HDRLEN + 1;
dca30c3f
JK
1823 switch (data[0]) {
1824 case WLAN_PA_GAS_INITIAL_REQ:
78a36327
JM
1825 gas_serv_rx_gas_initial_req(hapd, sa, data + 1, len - 1, prot,
1826 std_addr3);
dca30c3f
JK
1827 break;
1828 case WLAN_PA_GAS_COMEBACK_REQ:
78a36327
JM
1829 gas_serv_rx_gas_comeback_req(hapd, sa, data + 1, len - 1, prot,
1830 std_addr3);
dca30c3f
JK
1831 break;
1832 }
1833}
1834
1835
1836int gas_serv_init(struct hostapd_data *hapd)
1837{
2d9ffe1e
JM
1838 hapd->public_action_cb2 = gas_serv_rx_public_action;
1839 hapd->public_action_cb2_ctx = hapd;
dca30c3f
JK
1840 return 0;
1841}
1842
1843
1844void gas_serv_deinit(struct hostapd_data *hapd)
1845{
1846}