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