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 323 : static void anqp_add_hs_capab_list(struct hostapd_data *hapd,
141 : struct wpabuf *buf)
142 : {
143 : u8 *len;
144 :
145 323 : len = gas_anqp_add_element(buf, ANQP_VENDOR_SPECIFIC);
146 323 : wpabuf_put_be24(buf, OUI_WFA);
147 323 : wpabuf_put_u8(buf, HS20_ANQP_OUI_TYPE);
148 323 : wpabuf_put_u8(buf, HS20_STYPE_CAPABILITY_LIST);
149 323 : wpabuf_put_u8(buf, 0); /* Reserved */
150 323 : wpabuf_put_u8(buf, HS20_STYPE_CAPABILITY_LIST);
151 323 : if (hapd->conf->hs20_oper_friendly_name)
152 17 : wpabuf_put_u8(buf, HS20_STYPE_OPERATOR_FRIENDLY_NAME);
153 323 : if (hapd->conf->hs20_wan_metrics)
154 321 : wpabuf_put_u8(buf, HS20_STYPE_WAN_METRICS);
155 323 : if (hapd->conf->hs20_connection_capability)
156 323 : wpabuf_put_u8(buf, HS20_STYPE_CONNECTION_CAPABILITY);
157 323 : if (hapd->conf->nai_realm_data)
158 319 : wpabuf_put_u8(buf, HS20_STYPE_NAI_HOME_REALM_QUERY);
159 323 : if (hapd->conf->hs20_operating_class)
160 323 : wpabuf_put_u8(buf, HS20_STYPE_OPERATING_CLASS);
161 323 : if (hapd->conf->hs20_osu_providers_count)
162 4 : wpabuf_put_u8(buf, HS20_STYPE_OSU_PROVIDERS_LIST);
163 323 : if (hapd->conf->hs20_icons_count)
164 4 : wpabuf_put_u8(buf, HS20_STYPE_ICON_REQUEST);
165 323 : gas_anqp_set_element_len(buf, len);
166 323 : }
167 : #endif /* CONFIG_HS20 */
168 :
169 :
170 168 : static void anqp_add_capab_list(struct hostapd_data *hapd,
171 : struct wpabuf *buf)
172 : {
173 : u8 *len;
174 :
175 168 : len = gas_anqp_add_element(buf, ANQP_CAPABILITY_LIST);
176 168 : wpabuf_put_le16(buf, ANQP_CAPABILITY_LIST);
177 168 : if (hapd->conf->venue_name)
178 168 : wpabuf_put_le16(buf, ANQP_VENUE_NAME);
179 168 : if (hapd->conf->network_auth_type)
180 15 : wpabuf_put_le16(buf, ANQP_NETWORK_AUTH_TYPE);
181 168 : if (hapd->conf->roaming_consortium)
182 168 : wpabuf_put_le16(buf, ANQP_ROAMING_CONSORTIUM);
183 168 : if (hapd->conf->ipaddr_type_configured)
184 15 : wpabuf_put_le16(buf, ANQP_IP_ADDR_TYPE_AVAILABILITY);
185 168 : if (hapd->conf->nai_realm_data)
186 166 : wpabuf_put_le16(buf, ANQP_NAI_REALM);
187 168 : if (hapd->conf->anqp_3gpp_cell_net)
188 166 : wpabuf_put_le16(buf, ANQP_3GPP_CELLULAR_NETWORK);
189 168 : if (hapd->conf->domain_name)
190 161 : wpabuf_put_le16(buf, ANQP_DOMAIN_NAME);
191 : #ifdef CONFIG_HS20
192 168 : anqp_add_hs_capab_list(hapd, buf);
193 : #endif /* CONFIG_HS20 */
194 168 : gas_anqp_set_element_len(buf, len);
195 168 : }
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 269 : 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 269 : wpabuf_put_u8(buf, realm->eap_method_count);
264 :
265 544 : for (i = 0; i < realm->eap_method_count; i++) {
266 275 : struct hostapd_nai_realm_eap *eap = &realm->eap_method[i];
267 275 : wpabuf_put_u8(buf, 2 + (3 * eap->num_auths));
268 275 : wpabuf_put_u8(buf, eap->eap_method);
269 275 : wpabuf_put_u8(buf, eap->num_auths);
270 676 : for (j = 0; j < eap->num_auths; j++) {
271 401 : wpabuf_put_u8(buf, eap->auth_id[j]);
272 401 : wpabuf_put_u8(buf, 1);
273 401 : wpabuf_put_u8(buf, eap->auth_val[j]);
274 : }
275 : }
276 269 : }
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 151 : 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 299 : if (nai_realm && hapd->conf->nai_realm_data) {
395 : u8 *len;
396 : unsigned int i, j;
397 148 : len = gas_anqp_add_element(buf, ANQP_NAI_REALM);
398 148 : wpabuf_put_le16(buf, hapd->conf->nai_realm_count);
399 416 : 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 268 : realm = &hapd->conf->nai_realm_data[i];
404 268 : realm_data_len = wpabuf_put(buf, 2);
405 268 : wpabuf_put_u8(buf, realm->encoding);
406 268 : realm_len = wpabuf_put(buf, 1);
407 538 : for (j = 0; realm->realm[j]; j++) {
408 270 : if (j > 0)
409 2 : wpabuf_put_u8(buf, ';');
410 270 : wpabuf_put_str(buf, realm->realm[j]);
411 : }
412 268 : *realm_len = (u8 *) wpabuf_put(buf, 0) - realm_len - 1;
413 268 : anqp_add_nai_realm_eap(buf, realm);
414 268 : gas_anqp_set_element_len(buf, realm_data_len);
415 : }
416 148 : gas_anqp_set_element_len(buf, len);
417 3 : } else if (nai_home_realm && hapd->conf->nai_realm_data) {
418 1 : hs20_add_nai_home_realm_matches(hapd, buf, home_realm,
419 : home_realm_len);
420 : }
421 151 : }
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 125 : static void anqp_add_domain_name(struct hostapd_data *hapd, struct wpabuf *buf)
438 : {
439 125 : if (hapd->conf->domain_name) {
440 118 : wpabuf_put_le16(buf, ANQP_DOMAIN_NAME);
441 118 : wpabuf_put_le16(buf, hapd->conf->domain_name_len);
442 118 : wpabuf_put_data(buf, hapd->conf->domain_name,
443 118 : hapd->conf->domain_name_len);
444 : }
445 125 : }
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 181 : gas_serv_build_gas_resp_payload(struct hostapd_data *hapd,
688 : unsigned int request,
689 : struct gas_dialog_info *di,
690 : const u8 *home_realm, size_t home_realm_len,
691 : const u8 *icon_name, size_t icon_name_len)
692 : {
693 : struct wpabuf *buf;
694 : size_t len;
695 :
696 181 : len = 1400;
697 181 : if (request & (ANQP_REQ_NAI_REALM | ANQP_REQ_NAI_HOME_REALM))
698 151 : len += 1000;
699 181 : if (request & ANQP_REQ_ICON_REQUEST)
700 3 : len += 65536;
701 :
702 181 : buf = wpabuf_alloc(len);
703 181 : if (buf == NULL)
704 0 : return NULL;
705 :
706 181 : if (request & ANQP_REQ_CAPABILITY_LIST)
707 168 : anqp_add_capab_list(hapd, buf);
708 181 : if (request & ANQP_REQ_VENUE_NAME)
709 19 : anqp_add_venue_name(hapd, buf);
710 181 : if (request & ANQP_REQ_NETWORK_AUTH_TYPE)
711 17 : anqp_add_network_auth_type(hapd, buf);
712 181 : if (request & ANQP_REQ_ROAMING_CONSORTIUM)
713 28 : anqp_add_roaming_consortium(hapd, buf);
714 181 : if (request & ANQP_REQ_IP_ADDR_TYPE_AVAILABILITY)
715 17 : anqp_add_ip_addr_type_availability(hapd, buf);
716 181 : if (request & (ANQP_REQ_NAI_REALM | ANQP_REQ_NAI_HOME_REALM))
717 151 : anqp_add_nai_realm(hapd, buf, home_realm, home_realm_len,
718 : request & ANQP_REQ_NAI_REALM,
719 : request & ANQP_REQ_NAI_HOME_REALM);
720 181 : if (request & ANQP_REQ_3GPP_CELLULAR_NETWORK)
721 36 : anqp_add_3gpp_cellular_network(hapd, buf);
722 181 : if (request & ANQP_REQ_DOMAIN_NAME)
723 125 : anqp_add_domain_name(hapd, buf);
724 :
725 : #ifdef CONFIG_HS20
726 181 : if (request & ANQP_REQ_HS_CAPABILITY_LIST)
727 155 : anqp_add_hs_capab_list(hapd, buf);
728 181 : if (request & ANQP_REQ_OPERATOR_FRIENDLY_NAME)
729 10 : anqp_add_operator_friendly_name(hapd, buf);
730 181 : if (request & ANQP_REQ_WAN_METRICS)
731 29 : anqp_add_wan_metrics(hapd, buf);
732 181 : if (request & ANQP_REQ_CONNECTION_CAPABILITY)
733 19 : anqp_add_connection_capability(hapd, buf);
734 181 : if (request & ANQP_REQ_OPERATING_CLASS)
735 8 : anqp_add_operating_class(hapd, buf);
736 181 : if (request & ANQP_REQ_OSU_PROVIDERS_LIST)
737 8 : anqp_add_osu_providers_list(hapd, buf);
738 181 : if (request & ANQP_REQ_ICON_REQUEST)
739 3 : anqp_add_icon_binary_file(hapd, buf, icon_name, icon_name_len);
740 : #endif /* CONFIG_HS20 */
741 :
742 181 : return buf;
743 : }
744 :
745 :
746 : struct anqp_query_info {
747 : unsigned int request;
748 : const u8 *home_realm_query;
749 : size_t home_realm_query_len;
750 : const u8 *icon_name;
751 : size_t icon_name_len;
752 : };
753 :
754 :
755 789 : static void set_anqp_req(unsigned int bit, const char *name, int local,
756 : struct anqp_query_info *qi)
757 : {
758 789 : qi->request |= bit;
759 789 : if (local) {
760 753 : wpa_printf(MSG_DEBUG, "ANQP: %s (local)", name);
761 : } else {
762 36 : wpa_printf(MSG_DEBUG, "ANQP: %s not available", name);
763 : }
764 789 : }
765 :
766 :
767 560 : static void rx_anqp_query_list_id(struct hostapd_data *hapd, u16 info_id,
768 : struct anqp_query_info *qi)
769 : {
770 560 : switch (info_id) {
771 : case ANQP_CAPABILITY_LIST:
772 168 : set_anqp_req(ANQP_REQ_CAPABILITY_LIST, "Capability List", 1,
773 : qi);
774 168 : break;
775 : case ANQP_VENUE_NAME:
776 19 : set_anqp_req(ANQP_REQ_VENUE_NAME, "Venue Name",
777 19 : hapd->conf->venue_name != NULL, qi);
778 19 : break;
779 : case ANQP_NETWORK_AUTH_TYPE:
780 17 : set_anqp_req(ANQP_REQ_NETWORK_AUTH_TYPE, "Network Auth Type",
781 17 : hapd->conf->network_auth_type != NULL, qi);
782 17 : break;
783 : case ANQP_ROAMING_CONSORTIUM:
784 28 : set_anqp_req(ANQP_REQ_ROAMING_CONSORTIUM, "Roaming Consortium",
785 28 : hapd->conf->roaming_consortium != NULL, qi);
786 28 : break;
787 : case ANQP_IP_ADDR_TYPE_AVAILABILITY:
788 17 : set_anqp_req(ANQP_REQ_IP_ADDR_TYPE_AVAILABILITY,
789 : "IP Addr Type Availability",
790 17 : hapd->conf->ipaddr_type_configured, qi);
791 17 : break;
792 : case ANQP_NAI_REALM:
793 150 : set_anqp_req(ANQP_REQ_NAI_REALM, "NAI Realm",
794 150 : hapd->conf->nai_realm_data != NULL, qi);
795 150 : break;
796 : case ANQP_3GPP_CELLULAR_NETWORK:
797 36 : set_anqp_req(ANQP_REQ_3GPP_CELLULAR_NETWORK,
798 : "3GPP Cellular Network",
799 36 : hapd->conf->anqp_3gpp_cell_net != NULL, qi);
800 36 : break;
801 : case ANQP_DOMAIN_NAME:
802 125 : set_anqp_req(ANQP_REQ_DOMAIN_NAME, "Domain Name",
803 125 : hapd->conf->domain_name != NULL, qi);
804 125 : break;
805 : default:
806 0 : wpa_printf(MSG_DEBUG, "ANQP: Unsupported Info Id %u",
807 : info_id);
808 0 : break;
809 : }
810 560 : }
811 :
812 :
813 176 : 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 176 : wpa_printf(MSG_DEBUG, "ANQP: %u Info IDs requested in Query list",
818 176 : (unsigned int) (end - pos) / 2);
819 :
820 912 : while (pos + 2 <= end) {
821 560 : rx_anqp_query_list_id(hapd, WPA_GET_LE16(pos), qi);
822 560 : pos += 2;
823 : }
824 176 : }
825 :
826 :
827 : #ifdef CONFIG_HS20
828 :
829 229 : static void rx_anqp_hs_query_list(struct hostapd_data *hapd, u8 subtype,
830 : struct anqp_query_info *qi)
831 : {
832 229 : switch (subtype) {
833 : case HS20_STYPE_CAPABILITY_LIST:
834 155 : set_anqp_req(ANQP_REQ_HS_CAPABILITY_LIST, "HS Capability List",
835 : 1, qi);
836 155 : break;
837 : case HS20_STYPE_OPERATOR_FRIENDLY_NAME:
838 10 : set_anqp_req(ANQP_REQ_OPERATOR_FRIENDLY_NAME,
839 : "Operator Friendly Name",
840 10 : hapd->conf->hs20_oper_friendly_name != NULL, qi);
841 10 : break;
842 : case HS20_STYPE_WAN_METRICS:
843 29 : set_anqp_req(ANQP_REQ_WAN_METRICS, "WAN Metrics",
844 29 : hapd->conf->hs20_wan_metrics != NULL, qi);
845 29 : break;
846 : case HS20_STYPE_CONNECTION_CAPABILITY:
847 19 : set_anqp_req(ANQP_REQ_CONNECTION_CAPABILITY,
848 : "Connection Capability",
849 19 : hapd->conf->hs20_connection_capability != NULL,
850 : qi);
851 19 : break;
852 : case HS20_STYPE_OPERATING_CLASS:
853 8 : set_anqp_req(ANQP_REQ_OPERATING_CLASS, "Operating Class",
854 8 : hapd->conf->hs20_operating_class != NULL, qi);
855 8 : break;
856 : case HS20_STYPE_OSU_PROVIDERS_LIST:
857 8 : set_anqp_req(ANQP_REQ_OSU_PROVIDERS_LIST, "OSU Providers list",
858 8 : hapd->conf->hs20_osu_providers_count, qi);
859 8 : break;
860 : default:
861 0 : wpa_printf(MSG_DEBUG, "ANQP: Unsupported HS 2.0 subtype %u",
862 : subtype);
863 0 : break;
864 : }
865 229 : }
866 :
867 :
868 1 : 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 1 : qi->request |= ANQP_REQ_NAI_HOME_REALM;
873 1 : qi->home_realm_query = pos;
874 1 : qi->home_realm_query_len = end - pos;
875 1 : if (hapd->conf->nai_realm_data != NULL) {
876 1 : 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 1 : }
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 161 : 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 161 : 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 161 : oui = WPA_GET_BE24(pos);
916 161 : pos += 3;
917 161 : if (oui != OUI_WFA) {
918 0 : wpa_printf(MSG_DEBUG, "ANQP: Unsupported vendor OUI %06x",
919 : oui);
920 0 : return;
921 : }
922 :
923 161 : if (*pos != HS20_ANQP_OUI_TYPE) {
924 0 : wpa_printf(MSG_DEBUG, "ANQP: Unsupported WFA vendor type %u",
925 0 : *pos);
926 0 : return;
927 : }
928 161 : pos++;
929 :
930 161 : if (pos + 1 >= end)
931 0 : return;
932 :
933 161 : subtype = *pos++;
934 161 : pos++; /* Reserved */
935 161 : switch (subtype) {
936 : case HS20_STYPE_QUERY_LIST:
937 157 : wpa_printf(MSG_DEBUG, "ANQP: HS 2.0 Query List");
938 543 : while (pos < end) {
939 229 : rx_anqp_hs_query_list(hapd, *pos, qi);
940 229 : pos++;
941 : }
942 157 : break;
943 : case HS20_STYPE_NAI_HOME_REALM_QUERY:
944 1 : rx_anqp_hs_nai_home_realm(hapd, pos, end, qi);
945 1 : break;
946 : case HS20_STYPE_ICON_REQUEST:
947 3 : rx_anqp_hs_icon_request(hapd, pos, end, qi);
948 3 : break;
949 : default:
950 0 : wpa_printf(MSG_DEBUG, "ANQP: Unsupported HS 2.0 query subtype "
951 : "%u", subtype);
952 0 : break;
953 : }
954 : }
955 :
956 : #endif /* CONFIG_HS20 */
957 :
958 :
959 181 : static void gas_serv_req_local_processing(struct hostapd_data *hapd,
960 : const u8 *sa, u8 dialog_token,
961 : struct anqp_query_info *qi, int prot)
962 : {
963 : struct wpabuf *buf, *tx_buf;
964 :
965 181 : buf = gas_serv_build_gas_resp_payload(hapd, qi->request, NULL,
966 : qi->home_realm_query,
967 : qi->home_realm_query_len,
968 : qi->icon_name, qi->icon_name_len);
969 181 : wpa_hexdump_buf(MSG_MSGDUMP, "ANQP: Locally generated ANQP responses",
970 : buf);
971 181 : if (!buf)
972 0 : return;
973 :
974 347 : if (wpabuf_len(buf) > hapd->gas_frag_limit ||
975 182 : hapd->conf->gas_comeback_delay) {
976 : struct gas_dialog_info *di;
977 16 : u16 comeback_delay = 1;
978 :
979 16 : if (hapd->conf->gas_comeback_delay) {
980 : /* Testing - allow overriding of the delay value */
981 1 : comeback_delay = hapd->conf->gas_comeback_delay;
982 : }
983 :
984 16 : wpa_printf(MSG_DEBUG, "ANQP: Too long response to fit in "
985 : "initial response - use GAS comeback");
986 16 : di = gas_dialog_create(hapd, sa, dialog_token);
987 16 : if (!di) {
988 7 : wpa_printf(MSG_INFO, "ANQP: Could not create dialog "
989 : "for " MACSTR " (dialog token %u)",
990 6 : MAC2STR(sa), dialog_token);
991 1 : wpabuf_free(buf);
992 1 : tx_buf = gas_anqp_build_initial_resp_buf(
993 : dialog_token, WLAN_STATUS_UNSPECIFIED_FAILURE,
994 : 0, NULL);
995 : } else {
996 15 : di->prot = prot;
997 15 : di->sd_resp = buf;
998 15 : di->sd_resp_pos = 0;
999 15 : tx_buf = gas_anqp_build_initial_resp_buf(
1000 : dialog_token, WLAN_STATUS_SUCCESS,
1001 : comeback_delay, NULL);
1002 : }
1003 : } else {
1004 165 : wpa_printf(MSG_DEBUG, "ANQP: Initial response (no comeback)");
1005 165 : tx_buf = gas_anqp_build_initial_resp_buf(
1006 : dialog_token, WLAN_STATUS_SUCCESS, 0, buf);
1007 165 : wpabuf_free(buf);
1008 : }
1009 181 : if (!tx_buf)
1010 0 : return;
1011 181 : if (prot)
1012 1 : convert_to_protected_dual(tx_buf);
1013 362 : hostapd_drv_send_action(hapd, hapd->iface->freq, 0, sa,
1014 181 : wpabuf_head(tx_buf), wpabuf_len(tx_buf));
1015 181 : wpabuf_free(tx_buf);
1016 : }
1017 :
1018 :
1019 182 : static void gas_serv_rx_gas_initial_req(struct hostapd_data *hapd,
1020 : const u8 *sa,
1021 : const u8 *data, size_t len, int prot)
1022 : {
1023 182 : const u8 *pos = data;
1024 182 : const u8 *end = data + len;
1025 : const u8 *next;
1026 : u8 dialog_token;
1027 : u16 slen;
1028 : struct anqp_query_info qi;
1029 : const u8 *adv_proto;
1030 :
1031 182 : if (len < 1 + 2)
1032 1 : return;
1033 :
1034 182 : os_memset(&qi, 0, sizeof(qi));
1035 :
1036 182 : dialog_token = *pos++;
1037 1274 : wpa_msg(hapd->msg_ctx, MSG_DEBUG,
1038 : "GAS: GAS Initial Request from " MACSTR " (dialog token %u) ",
1039 1092 : MAC2STR(sa), dialog_token);
1040 :
1041 182 : if (*pos != WLAN_EID_ADV_PROTO) {
1042 0 : wpa_msg(hapd->msg_ctx, MSG_DEBUG,
1043 0 : "GAS: Unexpected IE in GAS Initial Request: %u", *pos);
1044 0 : return;
1045 : }
1046 182 : adv_proto = pos++;
1047 :
1048 182 : slen = *pos++;
1049 182 : next = pos + slen;
1050 182 : if (next > end || slen < 2) {
1051 0 : wpa_msg(hapd->msg_ctx, MSG_DEBUG,
1052 : "GAS: Invalid IE in GAS Initial Request");
1053 0 : return;
1054 : }
1055 182 : pos++; /* skip QueryRespLenLimit and PAME-BI */
1056 :
1057 182 : if (*pos != ACCESS_NETWORK_QUERY_PROTOCOL) {
1058 : struct wpabuf *buf;
1059 1 : wpa_msg(hapd->msg_ctx, MSG_DEBUG,
1060 : "GAS: Unsupported GAS advertisement protocol id %u",
1061 1 : *pos);
1062 1 : if (sa[0] & 0x01)
1063 0 : return; /* Invalid source address - drop silently */
1064 1 : buf = gas_build_initial_resp(
1065 : dialog_token, WLAN_STATUS_GAS_ADV_PROTO_NOT_SUPPORTED,
1066 1 : 0, 2 + slen + 2);
1067 1 : if (buf == NULL)
1068 0 : return;
1069 1 : wpabuf_put_data(buf, adv_proto, 2 + slen);
1070 1 : wpabuf_put_le16(buf, 0); /* Query Response Length */
1071 1 : if (prot)
1072 0 : convert_to_protected_dual(buf);
1073 2 : hostapd_drv_send_action(hapd, hapd->iface->freq, 0, sa,
1074 1 : wpabuf_head(buf), wpabuf_len(buf));
1075 1 : wpabuf_free(buf);
1076 1 : return;
1077 : }
1078 :
1079 181 : pos = next;
1080 : /* Query Request */
1081 181 : if (pos + 2 > end)
1082 0 : return;
1083 181 : slen = WPA_GET_LE16(pos);
1084 181 : pos += 2;
1085 181 : if (pos + slen > end)
1086 0 : return;
1087 181 : end = pos + slen;
1088 :
1089 : /* ANQP Query Request */
1090 699 : while (pos < end) {
1091 : u16 info_id, elen;
1092 :
1093 337 : if (pos + 4 > end)
1094 0 : return;
1095 :
1096 337 : info_id = WPA_GET_LE16(pos);
1097 337 : pos += 2;
1098 337 : elen = WPA_GET_LE16(pos);
1099 337 : pos += 2;
1100 :
1101 337 : if (pos + elen > end) {
1102 0 : wpa_printf(MSG_DEBUG, "ANQP: Invalid Query Request");
1103 0 : return;
1104 : }
1105 :
1106 337 : switch (info_id) {
1107 : case ANQP_QUERY_LIST:
1108 176 : rx_anqp_query_list(hapd, pos, pos + elen, &qi);
1109 176 : break;
1110 : #ifdef CONFIG_HS20
1111 : case ANQP_VENDOR_SPECIFIC:
1112 161 : rx_anqp_vendor_specific(hapd, pos, pos + elen, &qi);
1113 161 : break;
1114 : #endif /* CONFIG_HS20 */
1115 : default:
1116 0 : wpa_printf(MSG_DEBUG, "ANQP: Unsupported Query "
1117 : "Request element %u", info_id);
1118 0 : break;
1119 : }
1120 :
1121 337 : pos += elen;
1122 : }
1123 :
1124 181 : gas_serv_req_local_processing(hapd, sa, dialog_token, &qi, prot);
1125 : }
1126 :
1127 :
1128 37 : static void gas_serv_rx_gas_comeback_req(struct hostapd_data *hapd,
1129 : const u8 *sa,
1130 : const u8 *data, size_t len, int prot)
1131 : {
1132 : struct gas_dialog_info *dialog;
1133 : struct wpabuf *buf, *tx_buf;
1134 : u8 dialog_token;
1135 : size_t frag_len;
1136 37 : int more = 0;
1137 :
1138 37 : wpa_hexdump(MSG_DEBUG, "GAS: RX GAS Comeback Request", data, len);
1139 37 : if (len < 1)
1140 0 : return;
1141 37 : dialog_token = *data;
1142 37 : wpa_msg(hapd->msg_ctx, MSG_DEBUG, "GAS: Dialog Token: %u",
1143 : dialog_token);
1144 :
1145 37 : dialog = gas_serv_dialog_find(hapd, sa, dialog_token);
1146 37 : if (!dialog) {
1147 7 : wpa_msg(hapd->msg_ctx, MSG_DEBUG, "GAS: No pending SD "
1148 : "response fragment for " MACSTR " dialog token %u",
1149 6 : MAC2STR(sa), dialog_token);
1150 :
1151 1 : if (sa[0] & 0x01)
1152 0 : return; /* Invalid source address - drop silently */
1153 1 : tx_buf = gas_anqp_build_comeback_resp_buf(
1154 : dialog_token, WLAN_STATUS_NO_OUTSTANDING_GAS_REQ, 0, 0,
1155 : 0, NULL);
1156 1 : if (tx_buf == NULL)
1157 0 : return;
1158 1 : goto send_resp;
1159 : }
1160 :
1161 36 : frag_len = wpabuf_len(dialog->sd_resp) - dialog->sd_resp_pos;
1162 36 : if (frag_len > hapd->gas_frag_limit) {
1163 29 : frag_len = hapd->gas_frag_limit;
1164 29 : more = 1;
1165 : }
1166 36 : wpa_msg(hapd->msg_ctx, MSG_DEBUG, "GAS: resp frag_len %u",
1167 : (unsigned int) frag_len);
1168 36 : buf = wpabuf_alloc_copy(wpabuf_head_u8(dialog->sd_resp) +
1169 : dialog->sd_resp_pos, frag_len);
1170 36 : if (buf == NULL) {
1171 0 : wpa_msg(hapd->msg_ctx, MSG_DEBUG, "GAS: Failed to allocate "
1172 : "buffer");
1173 0 : gas_serv_dialog_clear(dialog);
1174 0 : return;
1175 : }
1176 72 : tx_buf = gas_anqp_build_comeback_resp_buf(dialog_token,
1177 : WLAN_STATUS_SUCCESS,
1178 36 : dialog->sd_frag_id,
1179 : more, 0, buf);
1180 36 : wpabuf_free(buf);
1181 36 : if (tx_buf == NULL) {
1182 0 : gas_serv_dialog_clear(dialog);
1183 0 : return;
1184 : }
1185 72 : wpa_msg(hapd->msg_ctx, MSG_DEBUG, "GAS: Tx GAS Comeback Response "
1186 : "(frag_id %d more=%d frag_len=%d)",
1187 36 : dialog->sd_frag_id, more, (int) frag_len);
1188 36 : dialog->sd_frag_id++;
1189 36 : dialog->sd_resp_pos += frag_len;
1190 :
1191 36 : if (more) {
1192 58 : wpa_msg(hapd->msg_ctx, MSG_DEBUG, "GAS: %d more bytes remain "
1193 : "to be sent",
1194 29 : (int) (wpabuf_len(dialog->sd_resp) -
1195 29 : dialog->sd_resp_pos));
1196 : } else {
1197 7 : wpa_msg(hapd->msg_ctx, MSG_DEBUG, "GAS: All fragments of "
1198 : "SD response sent");
1199 7 : gas_serv_dialog_clear(dialog);
1200 7 : gas_serv_free_dialogs(hapd, sa);
1201 : }
1202 :
1203 : send_resp:
1204 37 : if (prot)
1205 0 : convert_to_protected_dual(tx_buf);
1206 74 : hostapd_drv_send_action(hapd, hapd->iface->freq, 0, sa,
1207 37 : wpabuf_head(tx_buf), wpabuf_len(tx_buf));
1208 37 : wpabuf_free(tx_buf);
1209 : }
1210 :
1211 :
1212 263 : static void gas_serv_rx_public_action(void *ctx, const u8 *buf, size_t len,
1213 : int freq)
1214 : {
1215 263 : struct hostapd_data *hapd = ctx;
1216 : const struct ieee80211_mgmt *mgmt;
1217 : size_t hdr_len;
1218 : const u8 *sa, *data;
1219 : int prot;
1220 :
1221 263 : mgmt = (const struct ieee80211_mgmt *) buf;
1222 263 : hdr_len = (const u8 *) &mgmt->u.action.u.vs_public_action.action - buf;
1223 263 : if (hdr_len > len)
1224 0 : return;
1225 264 : if (mgmt->u.action.category != WLAN_ACTION_PUBLIC &&
1226 1 : mgmt->u.action.category != WLAN_ACTION_PROTECTED_DUAL)
1227 0 : return;
1228 : /*
1229 : * Note: Public Action and Protected Dual of Public Action frames share
1230 : * the same payload structure, so it is fine to use definitions of
1231 : * Public Action frames to process both.
1232 : */
1233 263 : prot = mgmt->u.action.category == WLAN_ACTION_PROTECTED_DUAL;
1234 263 : sa = mgmt->sa;
1235 263 : len -= hdr_len;
1236 263 : data = &mgmt->u.action.u.public_action.action;
1237 263 : switch (data[0]) {
1238 : case WLAN_PA_GAS_INITIAL_REQ:
1239 182 : gas_serv_rx_gas_initial_req(hapd, sa, data + 1, len - 1, prot);
1240 182 : break;
1241 : case WLAN_PA_GAS_COMEBACK_REQ:
1242 37 : gas_serv_rx_gas_comeback_req(hapd, sa, data + 1, len - 1, prot);
1243 37 : break;
1244 : }
1245 : }
1246 :
1247 :
1248 678 : int gas_serv_init(struct hostapd_data *hapd)
1249 : {
1250 678 : hapd->public_action_cb2 = gas_serv_rx_public_action;
1251 678 : hapd->public_action_cb2_ctx = hapd;
1252 678 : hapd->gas_frag_limit = 1400;
1253 678 : if (hapd->conf->gas_frag_limit > 0)
1254 0 : hapd->gas_frag_limit = hapd->conf->gas_frag_limit;
1255 678 : return 0;
1256 : }
1257 :
1258 :
1259 682 : void gas_serv_deinit(struct hostapd_data *hapd)
1260 : {
1261 682 : }
|