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 16 : gas_dialog_create(struct hostapd_data *hapd, const u8 *addr, u8 dialog_token)
31 : {
32 : struct sta_info *sta;
33 16 : struct gas_dialog_info *dia = NULL;
34 : int i, j;
35 :
36 16 : sta = ap_get_sta(hapd, addr);
37 16 : if (!sta) {
38 : /*
39 : * We need a STA entry to be able to maintain state for
40 : * the GAS query.
41 : */
42 7 : wpa_printf(MSG_DEBUG, "ANQP: Add a temporary STA entry for "
43 : "GAS query");
44 7 : sta = ap_sta_add(hapd, addr);
45 7 : 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 7 : 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 7 : ap_sta_session_timeout(hapd, sta, 5);
56 : } else {
57 9 : ap_sta_replenish_timeout(hapd, sta, 5);
58 : }
59 :
60 16 : if (sta->gas_dialog == NULL) {
61 8 : sta->gas_dialog = os_zalloc(GAS_DIALOG_MAX *
62 : sizeof(struct gas_dialog_info));
63 8 : if (sta->gas_dialog == NULL)
64 0 : return NULL;
65 : }
66 :
67 48 : for (i = sta->gas_dialog_next, j = 0; j < GAS_DIALOG_MAX; i++, j++) {
68 23 : if (i == GAS_DIALOG_MAX)
69 0 : i = 0;
70 23 : if (sta->gas_dialog[i].valid)
71 8 : continue;
72 15 : dia = &sta->gas_dialog[i];
73 15 : dia->valid = 1;
74 15 : dia->dialog_token = dialog_token;
75 15 : sta->gas_dialog_next = (++i == GAS_DIALOG_MAX) ? 0 : i;
76 15 : 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 37 : 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 37 : sta = ap_get_sta(hapd, addr);
95 37 : if (!sta) {
96 6 : wpa_printf(MSG_DEBUG, "ANQP: could not find STA " MACSTR,
97 6 : MAC2STR(addr));
98 1 : return NULL;
99 : }
100 84 : for (i = 0; sta->gas_dialog && i < GAS_DIALOG_MAX; i++) {
101 78 : if (sta->gas_dialog[i].dialog_token != dialog_token ||
102 36 : !sta->gas_dialog[i].valid)
103 6 : continue;
104 36 : 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 15 : void gas_serv_dialog_clear(struct gas_dialog_info *dia)
113 : {
114 15 : wpabuf_free(dia->sd_resp);
115 15 : os_memset(dia, 0, sizeof(*dia));
116 15 : }
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 325 : static void anqp_add_hs_capab_list(struct hostapd_data *hapd,
141 : struct wpabuf *buf)
142 : {
143 : u8 *len;
144 :
145 325 : len = gas_anqp_add_element(buf, ANQP_VENDOR_SPECIFIC);
146 325 : wpabuf_put_be24(buf, OUI_WFA);
147 325 : wpabuf_put_u8(buf, HS20_ANQP_OUI_TYPE);
148 325 : wpabuf_put_u8(buf, HS20_STYPE_CAPABILITY_LIST);
149 325 : wpabuf_put_u8(buf, 0); /* Reserved */
150 325 : wpabuf_put_u8(buf, HS20_STYPE_CAPABILITY_LIST);
151 325 : if (hapd->conf->hs20_oper_friendly_name)
152 17 : wpabuf_put_u8(buf, HS20_STYPE_OPERATOR_FRIENDLY_NAME);
153 325 : if (hapd->conf->hs20_wan_metrics)
154 323 : wpabuf_put_u8(buf, HS20_STYPE_WAN_METRICS);
155 325 : if (hapd->conf->hs20_connection_capability)
156 325 : wpabuf_put_u8(buf, HS20_STYPE_CONNECTION_CAPABILITY);
157 325 : if (hapd->conf->nai_realm_data)
158 321 : wpabuf_put_u8(buf, HS20_STYPE_NAI_HOME_REALM_QUERY);
159 325 : if (hapd->conf->hs20_operating_class)
160 325 : wpabuf_put_u8(buf, HS20_STYPE_OPERATING_CLASS);
161 325 : if (hapd->conf->hs20_osu_providers_count)
162 4 : wpabuf_put_u8(buf, HS20_STYPE_OSU_PROVIDERS_LIST);
163 325 : if (hapd->conf->hs20_icons_count)
164 4 : wpabuf_put_u8(buf, HS20_STYPE_ICON_REQUEST);
165 325 : gas_anqp_set_element_len(buf, len);
166 325 : }
167 : #endif /* CONFIG_HS20 */
168 :
169 :
170 169 : static void anqp_add_capab_list(struct hostapd_data *hapd,
171 : struct wpabuf *buf)
172 : {
173 : u8 *len;
174 :
175 169 : len = gas_anqp_add_element(buf, ANQP_CAPABILITY_LIST);
176 169 : wpabuf_put_le16(buf, ANQP_CAPABILITY_LIST);
177 169 : if (hapd->conf->venue_name)
178 169 : wpabuf_put_le16(buf, ANQP_VENUE_NAME);
179 169 : if (hapd->conf->network_auth_type)
180 15 : wpabuf_put_le16(buf, ANQP_NETWORK_AUTH_TYPE);
181 169 : if (hapd->conf->roaming_consortium)
182 169 : wpabuf_put_le16(buf, ANQP_ROAMING_CONSORTIUM);
183 169 : if (hapd->conf->ipaddr_type_configured)
184 15 : wpabuf_put_le16(buf, ANQP_IP_ADDR_TYPE_AVAILABILITY);
185 169 : if (hapd->conf->nai_realm_data)
186 167 : wpabuf_put_le16(buf, ANQP_NAI_REALM);
187 169 : if (hapd->conf->anqp_3gpp_cell_net)
188 167 : wpabuf_put_le16(buf, ANQP_3GPP_CELLULAR_NETWORK);
189 169 : if (hapd->conf->domain_name)
190 162 : wpabuf_put_le16(buf, ANQP_DOMAIN_NAME);
191 : #ifdef CONFIG_HS20
192 169 : anqp_add_hs_capab_list(hapd, buf);
193 : #endif /* CONFIG_HS20 */
194 169 : gas_anqp_set_element_len(buf, len);
195 169 : }
196 :
197 :
198 19 : static void anqp_add_venue_name(struct hostapd_data *hapd, struct wpabuf *buf)
199 : {
200 19 : if (hapd->conf->venue_name) {
201 : u8 *len;
202 : unsigned int i;
203 19 : len = gas_anqp_add_element(buf, ANQP_VENUE_NAME);
204 19 : wpabuf_put_u8(buf, hapd->conf->venue_group);
205 19 : wpabuf_put_u8(buf, hapd->conf->venue_type);
206 57 : for (i = 0; i < hapd->conf->venue_name_count; i++) {
207 : struct hostapd_lang_string *vn;
208 38 : vn = &hapd->conf->venue_name[i];
209 38 : wpabuf_put_u8(buf, 3 + vn->name_len);
210 38 : wpabuf_put_data(buf, vn->lang, 3);
211 38 : wpabuf_put_data(buf, vn->name, vn->name_len);
212 : }
213 19 : gas_anqp_set_element_len(buf, len);
214 : }
215 19 : }
216 :
217 :
218 17 : static void anqp_add_network_auth_type(struct hostapd_data *hapd,
219 : struct wpabuf *buf)
220 : {
221 17 : if (hapd->conf->network_auth_type) {
222 11 : wpabuf_put_le16(buf, ANQP_NETWORK_AUTH_TYPE);
223 11 : wpabuf_put_le16(buf, hapd->conf->network_auth_type_len);
224 11 : wpabuf_put_data(buf, hapd->conf->network_auth_type,
225 11 : hapd->conf->network_auth_type_len);
226 : }
227 17 : }
228 :
229 :
230 28 : static void anqp_add_roaming_consortium(struct hostapd_data *hapd,
231 : struct wpabuf *buf)
232 : {
233 : unsigned int i;
234 : u8 *len;
235 :
236 28 : len = gas_anqp_add_element(buf, ANQP_ROAMING_CONSORTIUM);
237 140 : for (i = 0; i < hapd->conf->roaming_consortium_count; i++) {
238 : struct hostapd_roaming_consortium *rc;
239 112 : rc = &hapd->conf->roaming_consortium[i];
240 112 : wpabuf_put_u8(buf, rc->len);
241 112 : wpabuf_put_data(buf, rc->oi, rc->len);
242 : }
243 28 : gas_anqp_set_element_len(buf, len);
244 28 : }
245 :
246 :
247 17 : static void anqp_add_ip_addr_type_availability(struct hostapd_data *hapd,
248 : struct wpabuf *buf)
249 : {
250 17 : if (hapd->conf->ipaddr_type_configured) {
251 11 : wpabuf_put_le16(buf, ANQP_IP_ADDR_TYPE_AVAILABILITY);
252 11 : wpabuf_put_le16(buf, 1);
253 11 : wpabuf_put_u8(buf, hapd->conf->ipaddr_type_availability);
254 : }
255 17 : }
256 :
257 :
258 271 : 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 271 : wpabuf_put_u8(buf, realm->eap_method_count);
264 :
265 548 : for (i = 0; i < realm->eap_method_count; i++) {
266 277 : struct hostapd_nai_realm_eap *eap = &realm->eap_method[i];
267 277 : wpabuf_put_u8(buf, 2 + (3 * eap->num_auths));
268 277 : wpabuf_put_u8(buf, eap->eap_method);
269 277 : wpabuf_put_u8(buf, eap->num_auths);
270 681 : for (j = 0; j < eap->num_auths; j++) {
271 404 : wpabuf_put_u8(buf, eap->auth_id[j]);
272 404 : wpabuf_put_u8(buf, 1);
273 404 : wpabuf_put_u8(buf, eap->auth_val[j]);
274 : }
275 : }
276 271 : }
277 :
278 :
279 1 : 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 1 : wpa_printf(MSG_DEBUG, "realm=%s, len=%d", realm->realm[realm_idx],
286 1 : (int) os_strlen(realm->realm[realm_idx]));
287 1 : realm_data_len = wpabuf_put(buf, 2);
288 1 : wpabuf_put_u8(buf, realm->encoding);
289 1 : wpabuf_put_u8(buf, os_strlen(realm->realm[realm_idx]));
290 1 : wpabuf_put_str(buf, realm->realm[realm_idx]);
291 1 : anqp_add_nai_realm_eap(buf, realm);
292 1 : gas_anqp_set_element_len(buf, realm_data_len);
293 1 : }
294 :
295 :
296 1 : 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 1 : 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 1 : pos = home_realm;
311 1 : end = pos + home_realm_len;
312 1 : if (pos + 1 > end) {
313 0 : wpa_hexdump(MSG_DEBUG, "Too short NAI Home Realm Query",
314 : home_realm, home_realm_len);
315 0 : return -1;
316 : }
317 1 : num_realms = *pos++;
318 :
319 2 : for (i = 0; i < num_realms && num_matching < 10; i++) {
320 1 : 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 1 : encoding = *pos++;
327 1 : realm_len = *pos++;
328 1 : 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 1 : realm_name = pos;
335 4 : for (j = 0; j < hapd->conf->nai_realm_count &&
336 2 : num_matching < 10; j++) {
337 : const u8 *rpos, *rend;
338 2 : realm = &hapd->conf->nai_realm_data[j];
339 2 : if (encoding != realm->encoding)
340 0 : continue;
341 :
342 2 : rpos = realm_name;
343 6 : while (rpos < realm_name + realm_len &&
344 : num_matching < 10) {
345 26 : for (rend = rpos;
346 22 : rend < realm_name + realm_len; rend++) {
347 22 : if (*rend == ';')
348 0 : break;
349 : }
350 10 : for (k = 0; k < MAX_NAI_REALMS &&
351 6 : realm->realm[k] &&
352 2 : num_matching < 10; k++) {
353 4 : if ((int) os_strlen(realm->realm[k]) !=
354 3 : rend - rpos ||
355 1 : os_strncmp((char *) rpos,
356 : realm->realm[k],
357 : rend - rpos) != 0)
358 1 : continue;
359 1 : matches[num_matching].realm_data_idx =
360 : j;
361 1 : matches[num_matching].realm_idx = k;
362 1 : num_matching++;
363 : }
364 2 : rpos = rend + 1;
365 : }
366 : }
367 1 : pos += realm_len;
368 : }
369 :
370 1 : realm_list_len = gas_anqp_add_element(buf, ANQP_NAI_REALM);
371 1 : 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 2 : for (i = 0; i < num_matching; i++) {
380 1 : wpa_printf(MSG_DEBUG, "realm_idx %d, realm_data_idx %d",
381 : matches[i].realm_data_idx, matches[i].realm_idx);
382 1 : realm = &hapd->conf->nai_realm_data[matches[i].realm_data_idx];
383 1 : anqp_add_nai_realm_data(buf, realm, matches[i].realm_idx);
384 : }
385 1 : gas_anqp_set_element_len(buf, realm_list_len);
386 1 : return 0;
387 : }
388 :
389 :
390 152 : 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 301 : if (nai_realm && hapd->conf->nai_realm_data) {
395 : u8 *len;
396 : unsigned int i, j;
397 149 : len = gas_anqp_add_element(buf, ANQP_NAI_REALM);
398 149 : wpabuf_put_le16(buf, hapd->conf->nai_realm_count);
399 419 : 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 270 : realm = &hapd->conf->nai_realm_data[i];
404 270 : realm_data_len = wpabuf_put(buf, 2);
405 270 : wpabuf_put_u8(buf, realm->encoding);
406 270 : realm_len = wpabuf_put(buf, 1);
407 542 : for (j = 0; realm->realm[j]; j++) {
408 272 : if (j > 0)
409 2 : wpabuf_put_u8(buf, ';');
410 272 : wpabuf_put_str(buf, realm->realm[j]);
411 : }
412 270 : *realm_len = (u8 *) wpabuf_put(buf, 0) - realm_len - 1;
413 270 : anqp_add_nai_realm_eap(buf, realm);
414 270 : gas_anqp_set_element_len(buf, realm_data_len);
415 : }
416 149 : gas_anqp_set_element_len(buf, len);
417 3 : } else if (nai_home_realm && hapd->conf->nai_realm_data && home_realm) {
418 1 : hs20_add_nai_home_realm_matches(hapd, buf, home_realm,
419 : home_realm_len);
420 : }
421 152 : }
422 :
423 :
424 36 : static void anqp_add_3gpp_cellular_network(struct hostapd_data *hapd,
425 : struct wpabuf *buf)
426 : {
427 36 : if (hapd->conf->anqp_3gpp_cell_net) {
428 34 : wpabuf_put_le16(buf, ANQP_3GPP_CELLULAR_NETWORK);
429 34 : wpabuf_put_le16(buf,
430 34 : hapd->conf->anqp_3gpp_cell_net_len);
431 34 : wpabuf_put_data(buf, hapd->conf->anqp_3gpp_cell_net,
432 34 : hapd->conf->anqp_3gpp_cell_net_len);
433 : }
434 36 : }
435 :
436 :
437 126 : static void anqp_add_domain_name(struct hostapd_data *hapd, struct wpabuf *buf)
438 : {
439 126 : if (hapd->conf->domain_name) {
440 119 : wpabuf_put_le16(buf, ANQP_DOMAIN_NAME);
441 119 : wpabuf_put_le16(buf, hapd->conf->domain_name_len);
442 119 : wpabuf_put_data(buf, hapd->conf->domain_name,
443 119 : hapd->conf->domain_name_len);
444 : }
445 126 : }
446 :
447 :
448 : #ifdef CONFIG_HS20
449 :
450 10 : static void anqp_add_operator_friendly_name(struct hostapd_data *hapd,
451 : struct wpabuf *buf)
452 : {
453 10 : if (hapd->conf->hs20_oper_friendly_name) {
454 : u8 *len;
455 : unsigned int i;
456 4 : len = gas_anqp_add_element(buf, ANQP_VENDOR_SPECIFIC);
457 4 : wpabuf_put_be24(buf, OUI_WFA);
458 4 : wpabuf_put_u8(buf, HS20_ANQP_OUI_TYPE);
459 4 : wpabuf_put_u8(buf, HS20_STYPE_OPERATOR_FRIENDLY_NAME);
460 4 : wpabuf_put_u8(buf, 0); /* Reserved */
461 12 : for (i = 0; i < hapd->conf->hs20_oper_friendly_name_count; i++)
462 : {
463 : struct hostapd_lang_string *vn;
464 8 : vn = &hapd->conf->hs20_oper_friendly_name[i];
465 8 : wpabuf_put_u8(buf, 3 + vn->name_len);
466 8 : wpabuf_put_data(buf, vn->lang, 3);
467 8 : wpabuf_put_data(buf, vn->name, vn->name_len);
468 : }
469 4 : gas_anqp_set_element_len(buf, len);
470 : }
471 10 : }
472 :
473 :
474 29 : static void anqp_add_wan_metrics(struct hostapd_data *hapd,
475 : struct wpabuf *buf)
476 : {
477 29 : if (hapd->conf->hs20_wan_metrics) {
478 28 : u8 *len = gas_anqp_add_element(buf, ANQP_VENDOR_SPECIFIC);
479 28 : wpabuf_put_be24(buf, OUI_WFA);
480 28 : wpabuf_put_u8(buf, HS20_ANQP_OUI_TYPE);
481 28 : wpabuf_put_u8(buf, HS20_STYPE_WAN_METRICS);
482 28 : wpabuf_put_u8(buf, 0); /* Reserved */
483 28 : wpabuf_put_data(buf, hapd->conf->hs20_wan_metrics, 13);
484 28 : gas_anqp_set_element_len(buf, len);
485 : }
486 29 : }
487 :
488 :
489 19 : static void anqp_add_connection_capability(struct hostapd_data *hapd,
490 : struct wpabuf *buf)
491 : {
492 19 : if (hapd->conf->hs20_connection_capability) {
493 19 : u8 *len = gas_anqp_add_element(buf, ANQP_VENDOR_SPECIFIC);
494 19 : wpabuf_put_be24(buf, OUI_WFA);
495 19 : wpabuf_put_u8(buf, HS20_ANQP_OUI_TYPE);
496 19 : wpabuf_put_u8(buf, HS20_STYPE_CONNECTION_CAPABILITY);
497 19 : wpabuf_put_u8(buf, 0); /* Reserved */
498 19 : wpabuf_put_data(buf, hapd->conf->hs20_connection_capability,
499 19 : hapd->conf->hs20_connection_capability_len);
500 19 : gas_anqp_set_element_len(buf, len);
501 : }
502 19 : }
503 :
504 :
505 8 : static void anqp_add_operating_class(struct hostapd_data *hapd,
506 : struct wpabuf *buf)
507 : {
508 8 : if (hapd->conf->hs20_operating_class) {
509 8 : u8 *len = gas_anqp_add_element(buf, ANQP_VENDOR_SPECIFIC);
510 8 : wpabuf_put_be24(buf, OUI_WFA);
511 8 : wpabuf_put_u8(buf, HS20_ANQP_OUI_TYPE);
512 8 : wpabuf_put_u8(buf, HS20_STYPE_OPERATING_CLASS);
513 8 : wpabuf_put_u8(buf, 0); /* Reserved */
514 8 : wpabuf_put_data(buf, hapd->conf->hs20_operating_class,
515 8 : hapd->conf->hs20_operating_class_len);
516 8 : gas_anqp_set_element_len(buf, len);
517 : }
518 8 : }
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 8 : static void anqp_add_osu_providers_list(struct hostapd_data *hapd,
599 : struct wpabuf *buf)
600 : {
601 8 : 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 8 : }
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 182 : 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 182 : len = 1400;
696 182 : if (request & (ANQP_REQ_NAI_REALM | ANQP_REQ_NAI_HOME_REALM))
697 152 : len += 1000;
698 182 : if (request & ANQP_REQ_ICON_REQUEST)
699 3 : len += 65536;
700 :
701 182 : buf = wpabuf_alloc(len);
702 182 : if (buf == NULL)
703 0 : return NULL;
704 :
705 182 : if (request & ANQP_REQ_CAPABILITY_LIST)
706 169 : anqp_add_capab_list(hapd, buf);
707 182 : if (request & ANQP_REQ_VENUE_NAME)
708 19 : anqp_add_venue_name(hapd, buf);
709 182 : if (request & ANQP_REQ_NETWORK_AUTH_TYPE)
710 17 : anqp_add_network_auth_type(hapd, buf);
711 182 : if (request & ANQP_REQ_ROAMING_CONSORTIUM)
712 28 : anqp_add_roaming_consortium(hapd, buf);
713 182 : if (request & ANQP_REQ_IP_ADDR_TYPE_AVAILABILITY)
714 17 : anqp_add_ip_addr_type_availability(hapd, buf);
715 182 : if (request & (ANQP_REQ_NAI_REALM | ANQP_REQ_NAI_HOME_REALM))
716 152 : 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 182 : if (request & ANQP_REQ_3GPP_CELLULAR_NETWORK)
720 36 : anqp_add_3gpp_cellular_network(hapd, buf);
721 182 : if (request & ANQP_REQ_DOMAIN_NAME)
722 126 : anqp_add_domain_name(hapd, buf);
723 :
724 : #ifdef CONFIG_HS20
725 182 : if (request & ANQP_REQ_HS_CAPABILITY_LIST)
726 156 : anqp_add_hs_capab_list(hapd, buf);
727 182 : if (request & ANQP_REQ_OPERATOR_FRIENDLY_NAME)
728 10 : anqp_add_operator_friendly_name(hapd, buf);
729 182 : if (request & ANQP_REQ_WAN_METRICS)
730 29 : anqp_add_wan_metrics(hapd, buf);
731 182 : if (request & ANQP_REQ_CONNECTION_CAPABILITY)
732 19 : anqp_add_connection_capability(hapd, buf);
733 182 : if (request & ANQP_REQ_OPERATING_CLASS)
734 8 : anqp_add_operating_class(hapd, buf);
735 182 : if (request & ANQP_REQ_OSU_PROVIDERS_LIST)
736 8 : anqp_add_osu_providers_list(hapd, buf);
737 182 : 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 182 : 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 : };
752 :
753 :
754 793 : static void set_anqp_req(unsigned int bit, const char *name, int local,
755 : struct anqp_query_info *qi)
756 : {
757 793 : qi->request |= bit;
758 793 : if (local) {
759 757 : wpa_printf(MSG_DEBUG, "ANQP: %s (local)", name);
760 : } else {
761 36 : wpa_printf(MSG_DEBUG, "ANQP: %s not available", name);
762 : }
763 793 : }
764 :
765 :
766 563 : static void rx_anqp_query_list_id(struct hostapd_data *hapd, u16 info_id,
767 : struct anqp_query_info *qi)
768 : {
769 563 : switch (info_id) {
770 : case ANQP_CAPABILITY_LIST:
771 169 : set_anqp_req(ANQP_REQ_CAPABILITY_LIST, "Capability List", 1,
772 : qi);
773 169 : break;
774 : case ANQP_VENUE_NAME:
775 19 : set_anqp_req(ANQP_REQ_VENUE_NAME, "Venue Name",
776 19 : hapd->conf->venue_name != NULL, qi);
777 19 : break;
778 : case ANQP_NETWORK_AUTH_TYPE:
779 17 : set_anqp_req(ANQP_REQ_NETWORK_AUTH_TYPE, "Network Auth Type",
780 17 : hapd->conf->network_auth_type != NULL, qi);
781 17 : break;
782 : case ANQP_ROAMING_CONSORTIUM:
783 28 : set_anqp_req(ANQP_REQ_ROAMING_CONSORTIUM, "Roaming Consortium",
784 28 : hapd->conf->roaming_consortium != NULL, qi);
785 28 : break;
786 : case ANQP_IP_ADDR_TYPE_AVAILABILITY:
787 17 : set_anqp_req(ANQP_REQ_IP_ADDR_TYPE_AVAILABILITY,
788 : "IP Addr Type Availability",
789 17 : hapd->conf->ipaddr_type_configured, qi);
790 17 : break;
791 : case ANQP_NAI_REALM:
792 151 : set_anqp_req(ANQP_REQ_NAI_REALM, "NAI Realm",
793 151 : hapd->conf->nai_realm_data != NULL, qi);
794 151 : break;
795 : case ANQP_3GPP_CELLULAR_NETWORK:
796 36 : set_anqp_req(ANQP_REQ_3GPP_CELLULAR_NETWORK,
797 : "3GPP Cellular Network",
798 36 : hapd->conf->anqp_3gpp_cell_net != NULL, qi);
799 36 : break;
800 : case ANQP_DOMAIN_NAME:
801 126 : set_anqp_req(ANQP_REQ_DOMAIN_NAME, "Domain Name",
802 126 : hapd->conf->domain_name != NULL, qi);
803 126 : break;
804 : default:
805 0 : wpa_printf(MSG_DEBUG, "ANQP: Unsupported Info Id %u",
806 : info_id);
807 0 : break;
808 : }
809 563 : }
810 :
811 :
812 177 : static void rx_anqp_query_list(struct hostapd_data *hapd,
813 : const u8 *pos, const u8 *end,
814 : struct anqp_query_info *qi)
815 : {
816 177 : wpa_printf(MSG_DEBUG, "ANQP: %u Info IDs requested in Query list",
817 177 : (unsigned int) (end - pos) / 2);
818 :
819 917 : while (pos + 2 <= end) {
820 563 : rx_anqp_query_list_id(hapd, WPA_GET_LE16(pos), qi);
821 563 : pos += 2;
822 : }
823 177 : }
824 :
825 :
826 : #ifdef CONFIG_HS20
827 :
828 230 : static void rx_anqp_hs_query_list(struct hostapd_data *hapd, u8 subtype,
829 : struct anqp_query_info *qi)
830 : {
831 230 : switch (subtype) {
832 : case HS20_STYPE_CAPABILITY_LIST:
833 156 : set_anqp_req(ANQP_REQ_HS_CAPABILITY_LIST, "HS Capability List",
834 : 1, qi);
835 156 : break;
836 : case HS20_STYPE_OPERATOR_FRIENDLY_NAME:
837 10 : set_anqp_req(ANQP_REQ_OPERATOR_FRIENDLY_NAME,
838 : "Operator Friendly Name",
839 10 : hapd->conf->hs20_oper_friendly_name != NULL, qi);
840 10 : break;
841 : case HS20_STYPE_WAN_METRICS:
842 29 : set_anqp_req(ANQP_REQ_WAN_METRICS, "WAN Metrics",
843 29 : hapd->conf->hs20_wan_metrics != NULL, qi);
844 29 : break;
845 : case HS20_STYPE_CONNECTION_CAPABILITY:
846 19 : set_anqp_req(ANQP_REQ_CONNECTION_CAPABILITY,
847 : "Connection Capability",
848 19 : hapd->conf->hs20_connection_capability != NULL,
849 : qi);
850 19 : break;
851 : case HS20_STYPE_OPERATING_CLASS:
852 8 : set_anqp_req(ANQP_REQ_OPERATING_CLASS, "Operating Class",
853 8 : hapd->conf->hs20_operating_class != NULL, qi);
854 8 : break;
855 : case HS20_STYPE_OSU_PROVIDERS_LIST:
856 8 : set_anqp_req(ANQP_REQ_OSU_PROVIDERS_LIST, "OSU Providers list",
857 8 : hapd->conf->hs20_osu_providers_count, qi);
858 8 : break;
859 : default:
860 0 : wpa_printf(MSG_DEBUG, "ANQP: Unsupported HS 2.0 subtype %u",
861 : subtype);
862 0 : break;
863 : }
864 230 : }
865 :
866 :
867 1 : static void rx_anqp_hs_nai_home_realm(struct hostapd_data *hapd,
868 : const u8 *pos, const u8 *end,
869 : struct anqp_query_info *qi)
870 : {
871 1 : qi->request |= ANQP_REQ_NAI_HOME_REALM;
872 1 : qi->home_realm_query = pos;
873 1 : qi->home_realm_query_len = end - pos;
874 1 : if (hapd->conf->nai_realm_data != NULL) {
875 1 : wpa_printf(MSG_DEBUG, "ANQP: HS 2.0 NAI Home Realm Query "
876 : "(local)");
877 : } else {
878 0 : wpa_printf(MSG_DEBUG, "ANQP: HS 2.0 NAI Home Realm Query not "
879 : "available");
880 : }
881 1 : }
882 :
883 :
884 3 : static void rx_anqp_hs_icon_request(struct hostapd_data *hapd,
885 : const u8 *pos, const u8 *end,
886 : struct anqp_query_info *qi)
887 : {
888 3 : qi->request |= ANQP_REQ_ICON_REQUEST;
889 3 : qi->icon_name = pos;
890 3 : qi->icon_name_len = end - pos;
891 3 : if (hapd->conf->hs20_icons_count) {
892 3 : wpa_printf(MSG_DEBUG, "ANQP: HS 2.0 Icon Request Query "
893 : "(local)");
894 : } else {
895 0 : wpa_printf(MSG_DEBUG, "ANQP: HS 2.0 Icon Request Query not "
896 : "available");
897 : }
898 3 : }
899 :
900 :
901 162 : static void rx_anqp_vendor_specific(struct hostapd_data *hapd,
902 : const u8 *pos, const u8 *end,
903 : struct anqp_query_info *qi)
904 : {
905 : u32 oui;
906 : u8 subtype;
907 :
908 162 : if (pos + 4 > end) {
909 0 : wpa_printf(MSG_DEBUG, "ANQP: Too short vendor specific ANQP "
910 : "Query element");
911 0 : return;
912 : }
913 :
914 162 : oui = WPA_GET_BE24(pos);
915 162 : pos += 3;
916 162 : if (oui != OUI_WFA) {
917 0 : wpa_printf(MSG_DEBUG, "ANQP: Unsupported vendor OUI %06x",
918 : oui);
919 0 : return;
920 : }
921 :
922 162 : if (*pos != HS20_ANQP_OUI_TYPE) {
923 0 : wpa_printf(MSG_DEBUG, "ANQP: Unsupported WFA vendor type %u",
924 0 : *pos);
925 0 : return;
926 : }
927 162 : pos++;
928 :
929 162 : if (pos + 1 >= end)
930 0 : return;
931 :
932 162 : subtype = *pos++;
933 162 : pos++; /* Reserved */
934 162 : switch (subtype) {
935 : case HS20_STYPE_QUERY_LIST:
936 158 : wpa_printf(MSG_DEBUG, "ANQP: HS 2.0 Query List");
937 546 : while (pos < end) {
938 230 : rx_anqp_hs_query_list(hapd, *pos, qi);
939 230 : pos++;
940 : }
941 158 : break;
942 : case HS20_STYPE_NAI_HOME_REALM_QUERY:
943 1 : rx_anqp_hs_nai_home_realm(hapd, pos, end, qi);
944 1 : break;
945 : case HS20_STYPE_ICON_REQUEST:
946 3 : rx_anqp_hs_icon_request(hapd, pos, end, qi);
947 3 : break;
948 : default:
949 0 : wpa_printf(MSG_DEBUG, "ANQP: Unsupported HS 2.0 query subtype "
950 : "%u", subtype);
951 0 : break;
952 : }
953 : }
954 :
955 : #endif /* CONFIG_HS20 */
956 :
957 :
958 182 : static void gas_serv_req_local_processing(struct hostapd_data *hapd,
959 : const u8 *sa, u8 dialog_token,
960 : struct anqp_query_info *qi, int prot)
961 : {
962 : struct wpabuf *buf, *tx_buf;
963 :
964 182 : buf = gas_serv_build_gas_resp_payload(hapd, qi->request,
965 : qi->home_realm_query,
966 : qi->home_realm_query_len,
967 : qi->icon_name, qi->icon_name_len);
968 182 : wpa_hexdump_buf(MSG_MSGDUMP, "ANQP: Locally generated ANQP responses",
969 : buf);
970 182 : if (!buf)
971 0 : return;
972 :
973 349 : if (wpabuf_len(buf) > hapd->gas_frag_limit ||
974 183 : hapd->conf->gas_comeback_delay) {
975 : struct gas_dialog_info *di;
976 16 : u16 comeback_delay = 1;
977 :
978 16 : if (hapd->conf->gas_comeback_delay) {
979 : /* Testing - allow overriding of the delay value */
980 1 : comeback_delay = hapd->conf->gas_comeback_delay;
981 : }
982 :
983 16 : wpa_printf(MSG_DEBUG, "ANQP: Too long response to fit in "
984 : "initial response - use GAS comeback");
985 16 : di = gas_dialog_create(hapd, sa, dialog_token);
986 16 : if (!di) {
987 7 : wpa_printf(MSG_INFO, "ANQP: Could not create dialog "
988 : "for " MACSTR " (dialog token %u)",
989 6 : MAC2STR(sa), dialog_token);
990 1 : wpabuf_free(buf);
991 1 : tx_buf = gas_anqp_build_initial_resp_buf(
992 : dialog_token, WLAN_STATUS_UNSPECIFIED_FAILURE,
993 : 0, NULL);
994 : } else {
995 15 : di->prot = prot;
996 15 : di->sd_resp = buf;
997 15 : di->sd_resp_pos = 0;
998 15 : tx_buf = gas_anqp_build_initial_resp_buf(
999 : dialog_token, WLAN_STATUS_SUCCESS,
1000 : comeback_delay, NULL);
1001 : }
1002 : } else {
1003 166 : wpa_printf(MSG_DEBUG, "ANQP: Initial response (no comeback)");
1004 166 : tx_buf = gas_anqp_build_initial_resp_buf(
1005 : dialog_token, WLAN_STATUS_SUCCESS, 0, buf);
1006 166 : wpabuf_free(buf);
1007 : }
1008 182 : if (!tx_buf)
1009 0 : return;
1010 182 : if (prot)
1011 1 : convert_to_protected_dual(tx_buf);
1012 364 : hostapd_drv_send_action(hapd, hapd->iface->freq, 0, sa,
1013 182 : wpabuf_head(tx_buf), wpabuf_len(tx_buf));
1014 182 : wpabuf_free(tx_buf);
1015 : }
1016 :
1017 :
1018 184 : static void gas_serv_rx_gas_initial_req(struct hostapd_data *hapd,
1019 : const u8 *sa,
1020 : const u8 *data, size_t len, int prot)
1021 : {
1022 184 : const u8 *pos = data;
1023 184 : const u8 *end = data + len;
1024 : const u8 *next;
1025 : u8 dialog_token;
1026 : u16 slen;
1027 : struct anqp_query_info qi;
1028 : const u8 *adv_proto;
1029 :
1030 184 : if (len < 1 + 2)
1031 3 : return;
1032 :
1033 183 : os_memset(&qi, 0, sizeof(qi));
1034 :
1035 183 : dialog_token = *pos++;
1036 1281 : wpa_msg(hapd->msg_ctx, MSG_DEBUG,
1037 : "GAS: GAS Initial Request from " MACSTR " (dialog token %u) ",
1038 1098 : MAC2STR(sa), dialog_token);
1039 :
1040 183 : if (*pos != WLAN_EID_ADV_PROTO) {
1041 0 : wpa_msg(hapd->msg_ctx, MSG_DEBUG,
1042 0 : "GAS: Unexpected IE in GAS Initial Request: %u", *pos);
1043 0 : return;
1044 : }
1045 183 : adv_proto = pos++;
1046 :
1047 183 : slen = *pos++;
1048 183 : next = pos + slen;
1049 183 : if (next > end || slen < 2) {
1050 0 : wpa_msg(hapd->msg_ctx, MSG_DEBUG,
1051 : "GAS: Invalid IE in GAS Initial Request");
1052 0 : return;
1053 : }
1054 183 : pos++; /* skip QueryRespLenLimit and PAME-BI */
1055 :
1056 183 : if (*pos != ACCESS_NETWORK_QUERY_PROTOCOL) {
1057 : struct wpabuf *buf;
1058 1 : wpa_msg(hapd->msg_ctx, MSG_DEBUG,
1059 : "GAS: Unsupported GAS advertisement protocol id %u",
1060 1 : *pos);
1061 1 : if (sa[0] & 0x01)
1062 0 : return; /* Invalid source address - drop silently */
1063 1 : buf = gas_build_initial_resp(
1064 : dialog_token, WLAN_STATUS_GAS_ADV_PROTO_NOT_SUPPORTED,
1065 1 : 0, 2 + slen + 2);
1066 1 : if (buf == NULL)
1067 0 : return;
1068 1 : wpabuf_put_data(buf, adv_proto, 2 + slen);
1069 1 : wpabuf_put_le16(buf, 0); /* Query Response Length */
1070 1 : if (prot)
1071 0 : convert_to_protected_dual(buf);
1072 2 : hostapd_drv_send_action(hapd, hapd->iface->freq, 0, sa,
1073 1 : wpabuf_head(buf), wpabuf_len(buf));
1074 1 : wpabuf_free(buf);
1075 1 : return;
1076 : }
1077 :
1078 182 : pos = next;
1079 : /* Query Request */
1080 182 : if (pos + 2 > end)
1081 0 : return;
1082 182 : slen = WPA_GET_LE16(pos);
1083 182 : pos += 2;
1084 182 : if (pos + slen > end)
1085 0 : return;
1086 182 : end = pos + slen;
1087 :
1088 : /* ANQP Query Request */
1089 703 : while (pos < end) {
1090 : u16 info_id, elen;
1091 :
1092 339 : if (pos + 4 > end)
1093 0 : return;
1094 :
1095 339 : info_id = WPA_GET_LE16(pos);
1096 339 : pos += 2;
1097 339 : elen = WPA_GET_LE16(pos);
1098 339 : pos += 2;
1099 :
1100 339 : if (pos + elen > end) {
1101 0 : wpa_printf(MSG_DEBUG, "ANQP: Invalid Query Request");
1102 0 : return;
1103 : }
1104 :
1105 339 : switch (info_id) {
1106 : case ANQP_QUERY_LIST:
1107 177 : rx_anqp_query_list(hapd, pos, pos + elen, &qi);
1108 177 : break;
1109 : #ifdef CONFIG_HS20
1110 : case ANQP_VENDOR_SPECIFIC:
1111 162 : rx_anqp_vendor_specific(hapd, pos, pos + elen, &qi);
1112 162 : break;
1113 : #endif /* CONFIG_HS20 */
1114 : default:
1115 0 : wpa_printf(MSG_DEBUG, "ANQP: Unsupported Query "
1116 : "Request element %u", info_id);
1117 0 : break;
1118 : }
1119 :
1120 339 : pos += elen;
1121 : }
1122 :
1123 182 : gas_serv_req_local_processing(hapd, sa, dialog_token, &qi, prot);
1124 : }
1125 :
1126 :
1127 37 : static void gas_serv_rx_gas_comeback_req(struct hostapd_data *hapd,
1128 : const u8 *sa,
1129 : const u8 *data, size_t len, int prot)
1130 : {
1131 : struct gas_dialog_info *dialog;
1132 : struct wpabuf *buf, *tx_buf;
1133 : u8 dialog_token;
1134 : size_t frag_len;
1135 37 : int more = 0;
1136 :
1137 37 : wpa_hexdump(MSG_DEBUG, "GAS: RX GAS Comeback Request", data, len);
1138 37 : if (len < 1)
1139 0 : return;
1140 37 : dialog_token = *data;
1141 37 : wpa_msg(hapd->msg_ctx, MSG_DEBUG, "GAS: Dialog Token: %u",
1142 : dialog_token);
1143 :
1144 37 : dialog = gas_serv_dialog_find(hapd, sa, dialog_token);
1145 37 : if (!dialog) {
1146 7 : wpa_msg(hapd->msg_ctx, MSG_DEBUG, "GAS: No pending SD "
1147 : "response fragment for " MACSTR " dialog token %u",
1148 6 : MAC2STR(sa), dialog_token);
1149 :
1150 1 : if (sa[0] & 0x01)
1151 0 : return; /* Invalid source address - drop silently */
1152 1 : tx_buf = gas_anqp_build_comeback_resp_buf(
1153 : dialog_token, WLAN_STATUS_NO_OUTSTANDING_GAS_REQ, 0, 0,
1154 : 0, NULL);
1155 1 : if (tx_buf == NULL)
1156 0 : return;
1157 1 : goto send_resp;
1158 : }
1159 :
1160 36 : frag_len = wpabuf_len(dialog->sd_resp) - dialog->sd_resp_pos;
1161 36 : if (frag_len > hapd->gas_frag_limit) {
1162 29 : frag_len = hapd->gas_frag_limit;
1163 29 : more = 1;
1164 : }
1165 36 : wpa_msg(hapd->msg_ctx, MSG_DEBUG, "GAS: resp frag_len %u",
1166 : (unsigned int) frag_len);
1167 36 : buf = wpabuf_alloc_copy(wpabuf_head_u8(dialog->sd_resp) +
1168 : dialog->sd_resp_pos, frag_len);
1169 36 : if (buf == NULL) {
1170 0 : wpa_msg(hapd->msg_ctx, MSG_DEBUG, "GAS: Failed to allocate "
1171 : "buffer");
1172 0 : gas_serv_dialog_clear(dialog);
1173 0 : return;
1174 : }
1175 72 : tx_buf = gas_anqp_build_comeback_resp_buf(dialog_token,
1176 : WLAN_STATUS_SUCCESS,
1177 36 : dialog->sd_frag_id,
1178 : more, 0, buf);
1179 36 : wpabuf_free(buf);
1180 36 : if (tx_buf == NULL) {
1181 0 : gas_serv_dialog_clear(dialog);
1182 0 : return;
1183 : }
1184 72 : wpa_msg(hapd->msg_ctx, MSG_DEBUG, "GAS: Tx GAS Comeback Response "
1185 : "(frag_id %d more=%d frag_len=%d)",
1186 36 : dialog->sd_frag_id, more, (int) frag_len);
1187 36 : dialog->sd_frag_id++;
1188 36 : dialog->sd_resp_pos += frag_len;
1189 :
1190 36 : if (more) {
1191 58 : wpa_msg(hapd->msg_ctx, MSG_DEBUG, "GAS: %d more bytes remain "
1192 : "to be sent",
1193 29 : (int) (wpabuf_len(dialog->sd_resp) -
1194 29 : dialog->sd_resp_pos));
1195 : } else {
1196 7 : wpa_msg(hapd->msg_ctx, MSG_DEBUG, "GAS: All fragments of "
1197 : "SD response sent");
1198 7 : gas_serv_dialog_clear(dialog);
1199 7 : gas_serv_free_dialogs(hapd, sa);
1200 : }
1201 :
1202 : send_resp:
1203 37 : if (prot)
1204 0 : convert_to_protected_dual(tx_buf);
1205 74 : hostapd_drv_send_action(hapd, hapd->iface->freq, 0, sa,
1206 37 : wpabuf_head(tx_buf), wpabuf_len(tx_buf));
1207 37 : wpabuf_free(tx_buf);
1208 : }
1209 :
1210 :
1211 225 : static void gas_serv_rx_public_action(void *ctx, const u8 *buf, size_t len,
1212 : int freq)
1213 : {
1214 225 : struct hostapd_data *hapd = ctx;
1215 : const struct ieee80211_mgmt *mgmt;
1216 : const u8 *sa, *data;
1217 : int prot;
1218 :
1219 225 : mgmt = (const struct ieee80211_mgmt *) buf;
1220 225 : if (len < IEEE80211_HDRLEN + 2)
1221 0 : return;
1222 226 : if (mgmt->u.action.category != WLAN_ACTION_PUBLIC &&
1223 1 : mgmt->u.action.category != WLAN_ACTION_PROTECTED_DUAL)
1224 0 : return;
1225 : /*
1226 : * Note: Public Action and Protected Dual of Public Action frames share
1227 : * the same payload structure, so it is fine to use definitions of
1228 : * Public Action frames to process both.
1229 : */
1230 225 : prot = mgmt->u.action.category == WLAN_ACTION_PROTECTED_DUAL;
1231 225 : sa = mgmt->sa;
1232 225 : len -= IEEE80211_HDRLEN + 1;
1233 225 : data = buf + IEEE80211_HDRLEN + 1;
1234 225 : switch (data[0]) {
1235 : case WLAN_PA_GAS_INITIAL_REQ:
1236 184 : gas_serv_rx_gas_initial_req(hapd, sa, data + 1, len - 1, prot);
1237 184 : break;
1238 : case WLAN_PA_GAS_COMEBACK_REQ:
1239 37 : gas_serv_rx_gas_comeback_req(hapd, sa, data + 1, len - 1, prot);
1240 37 : break;
1241 : }
1242 : }
1243 :
1244 :
1245 633 : int gas_serv_init(struct hostapd_data *hapd)
1246 : {
1247 633 : hapd->public_action_cb2 = gas_serv_rx_public_action;
1248 633 : hapd->public_action_cb2_ctx = hapd;
1249 633 : hapd->gas_frag_limit = 1400;
1250 633 : if (hapd->conf->gas_frag_limit > 0)
1251 0 : hapd->gas_frag_limit = hapd->conf->gas_frag_limit;
1252 633 : return 0;
1253 : }
1254 :
1255 :
1256 637 : void gas_serv_deinit(struct hostapd_data *hapd)
1257 : {
1258 637 : }
|