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 0 : static void convert_to_protected_dual(struct wpabuf *msg)
23 : {
24 0 : u8 *categ = wpabuf_mhead_u8(msg);
25 0 : *categ = WLAN_ACTION_PROTECTED_DUAL;
26 0 : }
27 :
28 :
29 : static struct gas_dialog_info *
30 0 : gas_dialog_create(struct hostapd_data *hapd, const u8 *addr, u8 dialog_token)
31 : {
32 : struct sta_info *sta;
33 0 : struct gas_dialog_info *dia = NULL;
34 : int i, j;
35 :
36 0 : sta = ap_get_sta(hapd, addr);
37 0 : if (!sta) {
38 : /*
39 : * We need a STA entry to be able to maintain state for
40 : * the GAS query.
41 : */
42 0 : wpa_printf(MSG_DEBUG, "ANQP: Add a temporary STA entry for "
43 : "GAS query");
44 0 : sta = ap_sta_add(hapd, addr);
45 0 : 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 0 : 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 0 : ap_sta_session_timeout(hapd, sta, 5);
56 : } else {
57 0 : ap_sta_replenish_timeout(hapd, sta, 5);
58 : }
59 :
60 0 : if (sta->gas_dialog == NULL) {
61 0 : sta->gas_dialog = os_zalloc(GAS_DIALOG_MAX *
62 : sizeof(struct gas_dialog_info));
63 0 : if (sta->gas_dialog == NULL)
64 0 : return NULL;
65 : }
66 :
67 0 : for (i = sta->gas_dialog_next, j = 0; j < GAS_DIALOG_MAX; i++, j++) {
68 0 : if (i == GAS_DIALOG_MAX)
69 0 : i = 0;
70 0 : if (sta->gas_dialog[i].valid)
71 0 : continue;
72 0 : dia = &sta->gas_dialog[i];
73 0 : dia->valid = 1;
74 0 : dia->dialog_token = dialog_token;
75 0 : sta->gas_dialog_next = (++i == GAS_DIALOG_MAX) ? 0 : i;
76 0 : return dia;
77 : }
78 :
79 0 : wpa_msg(hapd->msg_ctx, MSG_ERROR, "ANQP: Could not create dialog for "
80 : MACSTR " dialog_token %u. Consider increasing "
81 0 : "GAS_DIALOG_MAX.", MAC2STR(addr), dialog_token);
82 :
83 0 : return NULL;
84 : }
85 :
86 :
87 : struct gas_dialog_info *
88 0 : 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 0 : sta = ap_get_sta(hapd, addr);
95 0 : if (!sta) {
96 0 : wpa_printf(MSG_DEBUG, "ANQP: could not find STA " MACSTR,
97 0 : MAC2STR(addr));
98 0 : return NULL;
99 : }
100 0 : for (i = 0; sta->gas_dialog && i < GAS_DIALOG_MAX; i++) {
101 0 : if (sta->gas_dialog[i].dialog_token != dialog_token ||
102 0 : !sta->gas_dialog[i].valid)
103 0 : continue;
104 0 : 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 0 : void gas_serv_dialog_clear(struct gas_dialog_info *dia)
113 : {
114 0 : wpabuf_free(dia->sd_resp);
115 0 : os_memset(dia, 0, sizeof(*dia));
116 0 : }
117 :
118 :
119 0 : 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 0 : sta = ap_get_sta(hapd, sta_addr);
126 0 : if (sta == NULL || sta->gas_dialog == NULL)
127 0 : return;
128 :
129 0 : for (i = 0; i < GAS_DIALOG_MAX; i++) {
130 0 : if (sta->gas_dialog[i].valid)
131 0 : return;
132 : }
133 :
134 0 : os_free(sta->gas_dialog);
135 0 : sta->gas_dialog = NULL;
136 : }
137 :
138 :
139 : #ifdef CONFIG_HS20
140 0 : static void anqp_add_hs_capab_list(struct hostapd_data *hapd,
141 : struct wpabuf *buf)
142 : {
143 : u8 *len;
144 :
145 0 : len = gas_anqp_add_element(buf, ANQP_VENDOR_SPECIFIC);
146 0 : wpabuf_put_be24(buf, OUI_WFA);
147 0 : wpabuf_put_u8(buf, HS20_ANQP_OUI_TYPE);
148 0 : wpabuf_put_u8(buf, HS20_STYPE_CAPABILITY_LIST);
149 0 : wpabuf_put_u8(buf, 0); /* Reserved */
150 0 : wpabuf_put_u8(buf, HS20_STYPE_CAPABILITY_LIST);
151 0 : if (hapd->conf->hs20_oper_friendly_name)
152 0 : wpabuf_put_u8(buf, HS20_STYPE_OPERATOR_FRIENDLY_NAME);
153 0 : if (hapd->conf->hs20_wan_metrics)
154 0 : wpabuf_put_u8(buf, HS20_STYPE_WAN_METRICS);
155 0 : if (hapd->conf->hs20_connection_capability)
156 0 : wpabuf_put_u8(buf, HS20_STYPE_CONNECTION_CAPABILITY);
157 0 : if (hapd->conf->nai_realm_data)
158 0 : wpabuf_put_u8(buf, HS20_STYPE_NAI_HOME_REALM_QUERY);
159 0 : if (hapd->conf->hs20_operating_class)
160 0 : wpabuf_put_u8(buf, HS20_STYPE_OPERATING_CLASS);
161 0 : if (hapd->conf->hs20_osu_providers_count)
162 0 : wpabuf_put_u8(buf, HS20_STYPE_OSU_PROVIDERS_LIST);
163 0 : if (hapd->conf->hs20_icons_count)
164 0 : wpabuf_put_u8(buf, HS20_STYPE_ICON_REQUEST);
165 0 : gas_anqp_set_element_len(buf, len);
166 0 : }
167 : #endif /* CONFIG_HS20 */
168 :
169 :
170 0 : static void anqp_add_capab_list(struct hostapd_data *hapd,
171 : struct wpabuf *buf)
172 : {
173 : u8 *len;
174 :
175 0 : len = gas_anqp_add_element(buf, ANQP_CAPABILITY_LIST);
176 0 : wpabuf_put_le16(buf, ANQP_CAPABILITY_LIST);
177 0 : if (hapd->conf->venue_name)
178 0 : wpabuf_put_le16(buf, ANQP_VENUE_NAME);
179 0 : if (hapd->conf->network_auth_type)
180 0 : wpabuf_put_le16(buf, ANQP_NETWORK_AUTH_TYPE);
181 0 : if (hapd->conf->roaming_consortium)
182 0 : wpabuf_put_le16(buf, ANQP_ROAMING_CONSORTIUM);
183 0 : if (hapd->conf->ipaddr_type_configured)
184 0 : wpabuf_put_le16(buf, ANQP_IP_ADDR_TYPE_AVAILABILITY);
185 0 : if (hapd->conf->nai_realm_data)
186 0 : wpabuf_put_le16(buf, ANQP_NAI_REALM);
187 0 : if (hapd->conf->anqp_3gpp_cell_net)
188 0 : wpabuf_put_le16(buf, ANQP_3GPP_CELLULAR_NETWORK);
189 0 : if (hapd->conf->domain_name)
190 0 : wpabuf_put_le16(buf, ANQP_DOMAIN_NAME);
191 : #ifdef CONFIG_HS20
192 0 : anqp_add_hs_capab_list(hapd, buf);
193 : #endif /* CONFIG_HS20 */
194 0 : gas_anqp_set_element_len(buf, len);
195 0 : }
196 :
197 :
198 0 : static void anqp_add_venue_name(struct hostapd_data *hapd, struct wpabuf *buf)
199 : {
200 0 : if (hapd->conf->venue_name) {
201 : u8 *len;
202 : unsigned int i;
203 0 : len = gas_anqp_add_element(buf, ANQP_VENUE_NAME);
204 0 : wpabuf_put_u8(buf, hapd->conf->venue_group);
205 0 : wpabuf_put_u8(buf, hapd->conf->venue_type);
206 0 : for (i = 0; i < hapd->conf->venue_name_count; i++) {
207 : struct hostapd_lang_string *vn;
208 0 : vn = &hapd->conf->venue_name[i];
209 0 : wpabuf_put_u8(buf, 3 + vn->name_len);
210 0 : wpabuf_put_data(buf, vn->lang, 3);
211 0 : wpabuf_put_data(buf, vn->name, vn->name_len);
212 : }
213 0 : gas_anqp_set_element_len(buf, len);
214 : }
215 0 : }
216 :
217 :
218 0 : static void anqp_add_network_auth_type(struct hostapd_data *hapd,
219 : struct wpabuf *buf)
220 : {
221 0 : if (hapd->conf->network_auth_type) {
222 0 : wpabuf_put_le16(buf, ANQP_NETWORK_AUTH_TYPE);
223 0 : wpabuf_put_le16(buf, hapd->conf->network_auth_type_len);
224 0 : wpabuf_put_data(buf, hapd->conf->network_auth_type,
225 0 : hapd->conf->network_auth_type_len);
226 : }
227 0 : }
228 :
229 :
230 0 : static void anqp_add_roaming_consortium(struct hostapd_data *hapd,
231 : struct wpabuf *buf)
232 : {
233 : unsigned int i;
234 : u8 *len;
235 :
236 0 : len = gas_anqp_add_element(buf, ANQP_ROAMING_CONSORTIUM);
237 0 : for (i = 0; i < hapd->conf->roaming_consortium_count; i++) {
238 : struct hostapd_roaming_consortium *rc;
239 0 : rc = &hapd->conf->roaming_consortium[i];
240 0 : wpabuf_put_u8(buf, rc->len);
241 0 : wpabuf_put_data(buf, rc->oi, rc->len);
242 : }
243 0 : gas_anqp_set_element_len(buf, len);
244 0 : }
245 :
246 :
247 0 : static void anqp_add_ip_addr_type_availability(struct hostapd_data *hapd,
248 : struct wpabuf *buf)
249 : {
250 0 : if (hapd->conf->ipaddr_type_configured) {
251 0 : wpabuf_put_le16(buf, ANQP_IP_ADDR_TYPE_AVAILABILITY);
252 0 : wpabuf_put_le16(buf, 1);
253 0 : wpabuf_put_u8(buf, hapd->conf->ipaddr_type_availability);
254 : }
255 0 : }
256 :
257 :
258 0 : 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 0 : wpabuf_put_u8(buf, realm->eap_method_count);
264 :
265 0 : for (i = 0; i < realm->eap_method_count; i++) {
266 0 : struct hostapd_nai_realm_eap *eap = &realm->eap_method[i];
267 0 : wpabuf_put_u8(buf, 2 + (3 * eap->num_auths));
268 0 : wpabuf_put_u8(buf, eap->eap_method);
269 0 : wpabuf_put_u8(buf, eap->num_auths);
270 0 : for (j = 0; j < eap->num_auths; j++) {
271 0 : wpabuf_put_u8(buf, eap->auth_id[j]);
272 0 : wpabuf_put_u8(buf, 1);
273 0 : wpabuf_put_u8(buf, eap->auth_val[j]);
274 : }
275 : }
276 0 : }
277 :
278 :
279 0 : 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 0 : wpa_printf(MSG_DEBUG, "realm=%s, len=%d", realm->realm[realm_idx],
286 0 : (int) os_strlen(realm->realm[realm_idx]));
287 0 : realm_data_len = wpabuf_put(buf, 2);
288 0 : wpabuf_put_u8(buf, realm->encoding);
289 0 : wpabuf_put_u8(buf, os_strlen(realm->realm[realm_idx]));
290 0 : wpabuf_put_str(buf, realm->realm[realm_idx]);
291 0 : anqp_add_nai_realm_eap(buf, realm);
292 0 : gas_anqp_set_element_len(buf, realm_data_len);
293 0 : }
294 :
295 :
296 0 : 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 0 : 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 0 : pos = home_realm;
311 0 : end = pos + home_realm_len;
312 0 : 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 0 : num_realms = *pos++;
318 :
319 0 : for (i = 0; i < num_realms && num_matching < 10; i++) {
320 0 : 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 0 : encoding = *pos++;
327 0 : realm_len = *pos++;
328 0 : 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 0 : realm_name = pos;
335 0 : for (j = 0; j < hapd->conf->nai_realm_count &&
336 0 : num_matching < 10; j++) {
337 : const u8 *rpos, *rend;
338 0 : realm = &hapd->conf->nai_realm_data[j];
339 0 : if (encoding != realm->encoding)
340 0 : continue;
341 :
342 0 : rpos = realm_name;
343 0 : while (rpos < realm_name + realm_len &&
344 : num_matching < 10) {
345 0 : for (rend = rpos;
346 0 : rend < realm_name + realm_len; rend++) {
347 0 : if (*rend == ';')
348 0 : break;
349 : }
350 0 : for (k = 0; k < MAX_NAI_REALMS &&
351 0 : realm->realm[k] &&
352 0 : num_matching < 10; k++) {
353 0 : if ((int) os_strlen(realm->realm[k]) !=
354 0 : rend - rpos ||
355 0 : os_strncmp((char *) rpos,
356 : realm->realm[k],
357 : rend - rpos) != 0)
358 0 : continue;
359 0 : matches[num_matching].realm_data_idx =
360 : j;
361 0 : matches[num_matching].realm_idx = k;
362 0 : num_matching++;
363 : }
364 0 : rpos = rend + 1;
365 : }
366 : }
367 0 : pos += realm_len;
368 : }
369 :
370 0 : realm_list_len = gas_anqp_add_element(buf, ANQP_NAI_REALM);
371 0 : 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 0 : for (i = 0; i < num_matching; i++) {
380 0 : wpa_printf(MSG_DEBUG, "realm_idx %d, realm_data_idx %d",
381 : matches[i].realm_data_idx, matches[i].realm_idx);
382 0 : realm = &hapd->conf->nai_realm_data[matches[i].realm_data_idx];
383 0 : anqp_add_nai_realm_data(buf, realm, matches[i].realm_idx);
384 : }
385 0 : gas_anqp_set_element_len(buf, realm_list_len);
386 0 : return 0;
387 : }
388 :
389 :
390 0 : 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 0 : if (nai_realm && hapd->conf->nai_realm_data) {
395 : u8 *len;
396 : unsigned int i, j;
397 0 : len = gas_anqp_add_element(buf, ANQP_NAI_REALM);
398 0 : wpabuf_put_le16(buf, hapd->conf->nai_realm_count);
399 0 : 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 0 : realm = &hapd->conf->nai_realm_data[i];
404 0 : realm_data_len = wpabuf_put(buf, 2);
405 0 : wpabuf_put_u8(buf, realm->encoding);
406 0 : realm_len = wpabuf_put(buf, 1);
407 0 : for (j = 0; realm->realm[j]; j++) {
408 0 : if (j > 0)
409 0 : wpabuf_put_u8(buf, ';');
410 0 : wpabuf_put_str(buf, realm->realm[j]);
411 : }
412 0 : *realm_len = (u8 *) wpabuf_put(buf, 0) - realm_len - 1;
413 0 : anqp_add_nai_realm_eap(buf, realm);
414 0 : gas_anqp_set_element_len(buf, realm_data_len);
415 : }
416 0 : gas_anqp_set_element_len(buf, len);
417 0 : } else if (nai_home_realm && hapd->conf->nai_realm_data) {
418 0 : hs20_add_nai_home_realm_matches(hapd, buf, home_realm,
419 : home_realm_len);
420 : }
421 0 : }
422 :
423 :
424 0 : static void anqp_add_3gpp_cellular_network(struct hostapd_data *hapd,
425 : struct wpabuf *buf)
426 : {
427 0 : if (hapd->conf->anqp_3gpp_cell_net) {
428 0 : wpabuf_put_le16(buf, ANQP_3GPP_CELLULAR_NETWORK);
429 0 : wpabuf_put_le16(buf,
430 0 : hapd->conf->anqp_3gpp_cell_net_len);
431 0 : wpabuf_put_data(buf, hapd->conf->anqp_3gpp_cell_net,
432 0 : hapd->conf->anqp_3gpp_cell_net_len);
433 : }
434 0 : }
435 :
436 :
437 0 : static void anqp_add_domain_name(struct hostapd_data *hapd, struct wpabuf *buf)
438 : {
439 0 : if (hapd->conf->domain_name) {
440 0 : wpabuf_put_le16(buf, ANQP_DOMAIN_NAME);
441 0 : wpabuf_put_le16(buf, hapd->conf->domain_name_len);
442 0 : wpabuf_put_data(buf, hapd->conf->domain_name,
443 0 : hapd->conf->domain_name_len);
444 : }
445 0 : }
446 :
447 :
448 : #ifdef CONFIG_HS20
449 :
450 0 : static void anqp_add_operator_friendly_name(struct hostapd_data *hapd,
451 : struct wpabuf *buf)
452 : {
453 0 : if (hapd->conf->hs20_oper_friendly_name) {
454 : u8 *len;
455 : unsigned int i;
456 0 : len = gas_anqp_add_element(buf, ANQP_VENDOR_SPECIFIC);
457 0 : wpabuf_put_be24(buf, OUI_WFA);
458 0 : wpabuf_put_u8(buf, HS20_ANQP_OUI_TYPE);
459 0 : wpabuf_put_u8(buf, HS20_STYPE_OPERATOR_FRIENDLY_NAME);
460 0 : wpabuf_put_u8(buf, 0); /* Reserved */
461 0 : for (i = 0; i < hapd->conf->hs20_oper_friendly_name_count; i++)
462 : {
463 : struct hostapd_lang_string *vn;
464 0 : vn = &hapd->conf->hs20_oper_friendly_name[i];
465 0 : wpabuf_put_u8(buf, 3 + vn->name_len);
466 0 : wpabuf_put_data(buf, vn->lang, 3);
467 0 : wpabuf_put_data(buf, vn->name, vn->name_len);
468 : }
469 0 : gas_anqp_set_element_len(buf, len);
470 : }
471 0 : }
472 :
473 :
474 0 : static void anqp_add_wan_metrics(struct hostapd_data *hapd,
475 : struct wpabuf *buf)
476 : {
477 0 : if (hapd->conf->hs20_wan_metrics) {
478 0 : u8 *len = gas_anqp_add_element(buf, ANQP_VENDOR_SPECIFIC);
479 0 : wpabuf_put_be24(buf, OUI_WFA);
480 0 : wpabuf_put_u8(buf, HS20_ANQP_OUI_TYPE);
481 0 : wpabuf_put_u8(buf, HS20_STYPE_WAN_METRICS);
482 0 : wpabuf_put_u8(buf, 0); /* Reserved */
483 0 : wpabuf_put_data(buf, hapd->conf->hs20_wan_metrics, 13);
484 0 : gas_anqp_set_element_len(buf, len);
485 : }
486 0 : }
487 :
488 :
489 0 : static void anqp_add_connection_capability(struct hostapd_data *hapd,
490 : struct wpabuf *buf)
491 : {
492 0 : if (hapd->conf->hs20_connection_capability) {
493 0 : u8 *len = gas_anqp_add_element(buf, ANQP_VENDOR_SPECIFIC);
494 0 : wpabuf_put_be24(buf, OUI_WFA);
495 0 : wpabuf_put_u8(buf, HS20_ANQP_OUI_TYPE);
496 0 : wpabuf_put_u8(buf, HS20_STYPE_CONNECTION_CAPABILITY);
497 0 : wpabuf_put_u8(buf, 0); /* Reserved */
498 0 : wpabuf_put_data(buf, hapd->conf->hs20_connection_capability,
499 0 : hapd->conf->hs20_connection_capability_len);
500 0 : gas_anqp_set_element_len(buf, len);
501 : }
502 0 : }
503 :
504 :
505 0 : static void anqp_add_operating_class(struct hostapd_data *hapd,
506 : struct wpabuf *buf)
507 : {
508 0 : if (hapd->conf->hs20_operating_class) {
509 0 : u8 *len = gas_anqp_add_element(buf, ANQP_VENDOR_SPECIFIC);
510 0 : wpabuf_put_be24(buf, OUI_WFA);
511 0 : wpabuf_put_u8(buf, HS20_ANQP_OUI_TYPE);
512 0 : wpabuf_put_u8(buf, HS20_STYPE_OPERATING_CLASS);
513 0 : wpabuf_put_u8(buf, 0); /* Reserved */
514 0 : wpabuf_put_data(buf, hapd->conf->hs20_operating_class,
515 0 : hapd->conf->hs20_operating_class_len);
516 0 : gas_anqp_set_element_len(buf, len);
517 : }
518 0 : }
519 :
520 :
521 0 : 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 0 : len = wpabuf_put(buf, 2); /* OSU Provider Length to be filled */
529 :
530 : /* OSU Friendly Name Duples */
531 0 : len2 = wpabuf_put(buf, 2);
532 0 : for (i = 0; i < p->friendly_name_count; i++) {
533 0 : struct hostapd_lang_string *s = &p->friendly_name[i];
534 0 : wpabuf_put_u8(buf, 3 + s->name_len);
535 0 : wpabuf_put_data(buf, s->lang, 3);
536 0 : wpabuf_put_data(buf, s->name, s->name_len);
537 : }
538 0 : WPA_PUT_LE16(len2, (u8 *) wpabuf_put(buf, 0) - len2 - 2);
539 :
540 : /* OSU Server URI */
541 0 : if (p->server_uri) {
542 0 : wpabuf_put_u8(buf, os_strlen(p->server_uri));
543 0 : wpabuf_put_str(buf, p->server_uri);
544 : } else
545 0 : wpabuf_put_u8(buf, 0);
546 :
547 : /* OSU Method List */
548 0 : count = wpabuf_put(buf, 1);
549 0 : for (i = 0; p->method_list[i] >= 0; i++)
550 0 : wpabuf_put_u8(buf, p->method_list[i]);
551 0 : *count = i;
552 :
553 : /* Icons Available */
554 0 : len2 = wpabuf_put(buf, 2);
555 0 : for (i = 0; i < p->icons_count; i++) {
556 : size_t j;
557 0 : struct hs20_icon *icon = NULL;
558 :
559 0 : for (j = 0; j < bss->hs20_icons_count && !icon; j++) {
560 0 : if (os_strcmp(p->icons[i], bss->hs20_icons[j].name) ==
561 : 0)
562 0 : icon = &bss->hs20_icons[j];
563 : }
564 0 : if (!icon)
565 0 : continue; /* icon info not found */
566 :
567 0 : wpabuf_put_le16(buf, icon->width);
568 0 : wpabuf_put_le16(buf, icon->height);
569 0 : wpabuf_put_data(buf, icon->language, 3);
570 0 : wpabuf_put_u8(buf, os_strlen(icon->type));
571 0 : wpabuf_put_str(buf, icon->type);
572 0 : wpabuf_put_u8(buf, os_strlen(icon->name));
573 0 : wpabuf_put_str(buf, icon->name);
574 : }
575 0 : WPA_PUT_LE16(len2, (u8 *) wpabuf_put(buf, 0) - len2 - 2);
576 :
577 : /* OSU_NAI */
578 0 : if (p->osu_nai) {
579 0 : wpabuf_put_u8(buf, os_strlen(p->osu_nai));
580 0 : wpabuf_put_str(buf, p->osu_nai);
581 : } else
582 0 : wpabuf_put_u8(buf, 0);
583 :
584 : /* OSU Service Description Duples */
585 0 : len2 = wpabuf_put(buf, 2);
586 0 : for (i = 0; i < p->service_desc_count; i++) {
587 0 : struct hostapd_lang_string *s = &p->service_desc[i];
588 0 : wpabuf_put_u8(buf, 3 + s->name_len);
589 0 : wpabuf_put_data(buf, s->lang, 3);
590 0 : wpabuf_put_data(buf, s->name, s->name_len);
591 : }
592 0 : WPA_PUT_LE16(len2, (u8 *) wpabuf_put(buf, 0) - len2 - 2);
593 :
594 0 : WPA_PUT_LE16(len, (u8 *) wpabuf_put(buf, 0) - len - 2);
595 0 : }
596 :
597 :
598 0 : static void anqp_add_osu_providers_list(struct hostapd_data *hapd,
599 : struct wpabuf *buf)
600 : {
601 0 : if (hapd->conf->hs20_osu_providers_count) {
602 : size_t i;
603 0 : u8 *len = gas_anqp_add_element(buf, ANQP_VENDOR_SPECIFIC);
604 0 : wpabuf_put_be24(buf, OUI_WFA);
605 0 : wpabuf_put_u8(buf, HS20_ANQP_OUI_TYPE);
606 0 : wpabuf_put_u8(buf, HS20_STYPE_OSU_PROVIDERS_LIST);
607 0 : wpabuf_put_u8(buf, 0); /* Reserved */
608 :
609 : /* OSU SSID */
610 0 : wpabuf_put_u8(buf, hapd->conf->osu_ssid_len);
611 0 : wpabuf_put_data(buf, hapd->conf->osu_ssid,
612 0 : hapd->conf->osu_ssid_len);
613 :
614 : /* Number of OSU Providers */
615 0 : wpabuf_put_u8(buf, hapd->conf->hs20_osu_providers_count);
616 :
617 0 : for (i = 0; i < hapd->conf->hs20_osu_providers_count; i++) {
618 0 : anqp_add_osu_provider(
619 : buf, hapd->conf,
620 0 : &hapd->conf->hs20_osu_providers[i]);
621 : }
622 :
623 0 : gas_anqp_set_element_len(buf, len);
624 : }
625 0 : }
626 :
627 :
628 0 : 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 0 : wpa_hexdump_ascii(MSG_DEBUG, "HS 2.0: Requested Icon Filename",
637 : name, name_len);
638 0 : for (i = 0; i < hapd->conf->hs20_icons_count; i++) {
639 0 : icon = &hapd->conf->hs20_icons[i];
640 0 : if (name_len == os_strlen(icon->name) &&
641 0 : os_memcmp(name, icon->name, name_len) == 0)
642 0 : break;
643 : }
644 :
645 0 : if (i < hapd->conf->hs20_icons_count)
646 0 : icon = &hapd->conf->hs20_icons[i];
647 : else
648 0 : icon = NULL;
649 :
650 0 : len = gas_anqp_add_element(buf, ANQP_VENDOR_SPECIFIC);
651 0 : wpabuf_put_be24(buf, OUI_WFA);
652 0 : wpabuf_put_u8(buf, HS20_ANQP_OUI_TYPE);
653 0 : wpabuf_put_u8(buf, HS20_STYPE_ICON_BINARY_FILE);
654 0 : wpabuf_put_u8(buf, 0); /* Reserved */
655 :
656 0 : if (icon) {
657 : char *data;
658 : size_t data_len;
659 :
660 0 : data = os_readfile(icon->file, &data_len);
661 0 : 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 0 : wpabuf_put_u8(buf, 0); /* Download Status: Success */
668 0 : wpabuf_put_u8(buf, os_strlen(icon->type));
669 0 : wpabuf_put_str(buf, icon->type);
670 0 : wpabuf_put_le16(buf, data_len);
671 0 : wpabuf_put_data(buf, data, data_len);
672 : }
673 0 : 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 0 : gas_anqp_set_element_len(buf, len);
681 0 : }
682 :
683 : #endif /* CONFIG_HS20 */
684 :
685 :
686 : static struct wpabuf *
687 0 : 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 0 : len = 1400;
697 0 : if (request & (ANQP_REQ_NAI_REALM | ANQP_REQ_NAI_HOME_REALM))
698 0 : len += 1000;
699 0 : if (request & ANQP_REQ_ICON_REQUEST)
700 0 : len += 65536;
701 :
702 0 : buf = wpabuf_alloc(len);
703 0 : if (buf == NULL)
704 0 : return NULL;
705 :
706 0 : if (request & ANQP_REQ_CAPABILITY_LIST)
707 0 : anqp_add_capab_list(hapd, buf);
708 0 : if (request & ANQP_REQ_VENUE_NAME)
709 0 : anqp_add_venue_name(hapd, buf);
710 0 : if (request & ANQP_REQ_NETWORK_AUTH_TYPE)
711 0 : anqp_add_network_auth_type(hapd, buf);
712 0 : if (request & ANQP_REQ_ROAMING_CONSORTIUM)
713 0 : anqp_add_roaming_consortium(hapd, buf);
714 0 : if (request & ANQP_REQ_IP_ADDR_TYPE_AVAILABILITY)
715 0 : anqp_add_ip_addr_type_availability(hapd, buf);
716 0 : if (request & (ANQP_REQ_NAI_REALM | ANQP_REQ_NAI_HOME_REALM))
717 0 : 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 0 : if (request & ANQP_REQ_3GPP_CELLULAR_NETWORK)
721 0 : anqp_add_3gpp_cellular_network(hapd, buf);
722 0 : if (request & ANQP_REQ_DOMAIN_NAME)
723 0 : anqp_add_domain_name(hapd, buf);
724 :
725 : #ifdef CONFIG_HS20
726 0 : if (request & ANQP_REQ_HS_CAPABILITY_LIST)
727 0 : anqp_add_hs_capab_list(hapd, buf);
728 0 : if (request & ANQP_REQ_OPERATOR_FRIENDLY_NAME)
729 0 : anqp_add_operator_friendly_name(hapd, buf);
730 0 : if (request & ANQP_REQ_WAN_METRICS)
731 0 : anqp_add_wan_metrics(hapd, buf);
732 0 : if (request & ANQP_REQ_CONNECTION_CAPABILITY)
733 0 : anqp_add_connection_capability(hapd, buf);
734 0 : if (request & ANQP_REQ_OPERATING_CLASS)
735 0 : anqp_add_operating_class(hapd, buf);
736 0 : if (request & ANQP_REQ_OSU_PROVIDERS_LIST)
737 0 : anqp_add_osu_providers_list(hapd, buf);
738 0 : if (request & ANQP_REQ_ICON_REQUEST)
739 0 : anqp_add_icon_binary_file(hapd, buf, icon_name, icon_name_len);
740 : #endif /* CONFIG_HS20 */
741 :
742 0 : 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 0 : static void set_anqp_req(unsigned int bit, const char *name, int local,
756 : struct anqp_query_info *qi)
757 : {
758 0 : qi->request |= bit;
759 0 : if (local) {
760 0 : wpa_printf(MSG_DEBUG, "ANQP: %s (local)", name);
761 : } else {
762 0 : wpa_printf(MSG_DEBUG, "ANQP: %s not available", name);
763 : }
764 0 : }
765 :
766 :
767 0 : static void rx_anqp_query_list_id(struct hostapd_data *hapd, u16 info_id,
768 : struct anqp_query_info *qi)
769 : {
770 0 : switch (info_id) {
771 : case ANQP_CAPABILITY_LIST:
772 0 : set_anqp_req(ANQP_REQ_CAPABILITY_LIST, "Capability List", 1,
773 : qi);
774 0 : break;
775 : case ANQP_VENUE_NAME:
776 0 : set_anqp_req(ANQP_REQ_VENUE_NAME, "Venue Name",
777 0 : hapd->conf->venue_name != NULL, qi);
778 0 : break;
779 : case ANQP_NETWORK_AUTH_TYPE:
780 0 : set_anqp_req(ANQP_REQ_NETWORK_AUTH_TYPE, "Network Auth Type",
781 0 : hapd->conf->network_auth_type != NULL, qi);
782 0 : break;
783 : case ANQP_ROAMING_CONSORTIUM:
784 0 : set_anqp_req(ANQP_REQ_ROAMING_CONSORTIUM, "Roaming Consortium",
785 0 : hapd->conf->roaming_consortium != NULL, qi);
786 0 : break;
787 : case ANQP_IP_ADDR_TYPE_AVAILABILITY:
788 0 : set_anqp_req(ANQP_REQ_IP_ADDR_TYPE_AVAILABILITY,
789 : "IP Addr Type Availability",
790 0 : hapd->conf->ipaddr_type_configured, qi);
791 0 : break;
792 : case ANQP_NAI_REALM:
793 0 : set_anqp_req(ANQP_REQ_NAI_REALM, "NAI Realm",
794 0 : hapd->conf->nai_realm_data != NULL, qi);
795 0 : break;
796 : case ANQP_3GPP_CELLULAR_NETWORK:
797 0 : set_anqp_req(ANQP_REQ_3GPP_CELLULAR_NETWORK,
798 : "3GPP Cellular Network",
799 0 : hapd->conf->anqp_3gpp_cell_net != NULL, qi);
800 0 : break;
801 : case ANQP_DOMAIN_NAME:
802 0 : set_anqp_req(ANQP_REQ_DOMAIN_NAME, "Domain Name",
803 0 : hapd->conf->domain_name != NULL, qi);
804 0 : break;
805 : default:
806 0 : wpa_printf(MSG_DEBUG, "ANQP: Unsupported Info Id %u",
807 : info_id);
808 0 : break;
809 : }
810 0 : }
811 :
812 :
813 0 : 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 0 : wpa_printf(MSG_DEBUG, "ANQP: %u Info IDs requested in Query list",
818 0 : (unsigned int) (end - pos) / 2);
819 :
820 0 : while (pos + 2 <= end) {
821 0 : rx_anqp_query_list_id(hapd, WPA_GET_LE16(pos), qi);
822 0 : pos += 2;
823 : }
824 0 : }
825 :
826 :
827 : #ifdef CONFIG_HS20
828 :
829 0 : static void rx_anqp_hs_query_list(struct hostapd_data *hapd, u8 subtype,
830 : struct anqp_query_info *qi)
831 : {
832 0 : switch (subtype) {
833 : case HS20_STYPE_CAPABILITY_LIST:
834 0 : set_anqp_req(ANQP_REQ_HS_CAPABILITY_LIST, "HS Capability List",
835 : 1, qi);
836 0 : break;
837 : case HS20_STYPE_OPERATOR_FRIENDLY_NAME:
838 0 : set_anqp_req(ANQP_REQ_OPERATOR_FRIENDLY_NAME,
839 : "Operator Friendly Name",
840 0 : hapd->conf->hs20_oper_friendly_name != NULL, qi);
841 0 : break;
842 : case HS20_STYPE_WAN_METRICS:
843 0 : set_anqp_req(ANQP_REQ_WAN_METRICS, "WAN Metrics",
844 0 : hapd->conf->hs20_wan_metrics != NULL, qi);
845 0 : break;
846 : case HS20_STYPE_CONNECTION_CAPABILITY:
847 0 : set_anqp_req(ANQP_REQ_CONNECTION_CAPABILITY,
848 : "Connection Capability",
849 0 : hapd->conf->hs20_connection_capability != NULL,
850 : qi);
851 0 : break;
852 : case HS20_STYPE_OPERATING_CLASS:
853 0 : set_anqp_req(ANQP_REQ_OPERATING_CLASS, "Operating Class",
854 0 : hapd->conf->hs20_operating_class != NULL, qi);
855 0 : break;
856 : case HS20_STYPE_OSU_PROVIDERS_LIST:
857 0 : set_anqp_req(ANQP_REQ_OSU_PROVIDERS_LIST, "OSU Providers list",
858 0 : hapd->conf->hs20_osu_providers_count, qi);
859 0 : break;
860 : default:
861 0 : wpa_printf(MSG_DEBUG, "ANQP: Unsupported HS 2.0 subtype %u",
862 : subtype);
863 0 : break;
864 : }
865 0 : }
866 :
867 :
868 0 : 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 0 : qi->request |= ANQP_REQ_NAI_HOME_REALM;
873 0 : qi->home_realm_query = pos;
874 0 : qi->home_realm_query_len = end - pos;
875 0 : if (hapd->conf->nai_realm_data != NULL) {
876 0 : 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 0 : }
883 :
884 :
885 0 : 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 0 : qi->request |= ANQP_REQ_ICON_REQUEST;
890 0 : qi->icon_name = pos;
891 0 : qi->icon_name_len = end - pos;
892 0 : if (hapd->conf->hs20_icons_count) {
893 0 : 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 0 : }
900 :
901 :
902 0 : 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 0 : 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 0 : oui = WPA_GET_BE24(pos);
916 0 : pos += 3;
917 0 : if (oui != OUI_WFA) {
918 0 : wpa_printf(MSG_DEBUG, "ANQP: Unsupported vendor OUI %06x",
919 : oui);
920 0 : return;
921 : }
922 :
923 0 : 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 0 : pos++;
929 :
930 0 : if (pos + 1 >= end)
931 0 : return;
932 :
933 0 : subtype = *pos++;
934 0 : pos++; /* Reserved */
935 0 : switch (subtype) {
936 : case HS20_STYPE_QUERY_LIST:
937 0 : wpa_printf(MSG_DEBUG, "ANQP: HS 2.0 Query List");
938 0 : while (pos < end) {
939 0 : rx_anqp_hs_query_list(hapd, *pos, qi);
940 0 : pos++;
941 : }
942 0 : break;
943 : case HS20_STYPE_NAI_HOME_REALM_QUERY:
944 0 : rx_anqp_hs_nai_home_realm(hapd, pos, end, qi);
945 0 : break;
946 : case HS20_STYPE_ICON_REQUEST:
947 0 : rx_anqp_hs_icon_request(hapd, pos, end, qi);
948 0 : 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 0 : 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 0 : 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 0 : wpa_hexdump_buf(MSG_MSGDUMP, "ANQP: Locally generated ANQP responses",
970 : buf);
971 0 : if (!buf)
972 0 : return;
973 :
974 0 : if (wpabuf_len(buf) > hapd->gas_frag_limit ||
975 0 : hapd->conf->gas_comeback_delay) {
976 : struct gas_dialog_info *di;
977 0 : u16 comeback_delay = 1;
978 :
979 0 : if (hapd->conf->gas_comeback_delay) {
980 : /* Testing - allow overriding of the delay value */
981 0 : comeback_delay = hapd->conf->gas_comeback_delay;
982 : }
983 :
984 0 : wpa_printf(MSG_DEBUG, "ANQP: Too long response to fit in "
985 : "initial response - use GAS comeback");
986 0 : di = gas_dialog_create(hapd, sa, dialog_token);
987 0 : if (!di) {
988 0 : wpa_printf(MSG_INFO, "ANQP: Could not create dialog "
989 : "for " MACSTR " (dialog token %u)",
990 0 : MAC2STR(sa), dialog_token);
991 0 : wpabuf_free(buf);
992 0 : tx_buf = gas_anqp_build_initial_resp_buf(
993 : dialog_token, WLAN_STATUS_UNSPECIFIED_FAILURE,
994 : 0, NULL);
995 : } else {
996 0 : di->prot = prot;
997 0 : di->sd_resp = buf;
998 0 : di->sd_resp_pos = 0;
999 0 : tx_buf = gas_anqp_build_initial_resp_buf(
1000 : dialog_token, WLAN_STATUS_SUCCESS,
1001 : comeback_delay, NULL);
1002 : }
1003 : } else {
1004 0 : wpa_printf(MSG_DEBUG, "ANQP: Initial response (no comeback)");
1005 0 : tx_buf = gas_anqp_build_initial_resp_buf(
1006 : dialog_token, WLAN_STATUS_SUCCESS, 0, buf);
1007 0 : wpabuf_free(buf);
1008 : }
1009 0 : if (!tx_buf)
1010 0 : return;
1011 0 : if (prot)
1012 0 : convert_to_protected_dual(tx_buf);
1013 0 : hostapd_drv_send_action(hapd, hapd->iface->freq, 0, sa,
1014 0 : wpabuf_head(tx_buf), wpabuf_len(tx_buf));
1015 0 : wpabuf_free(tx_buf);
1016 : }
1017 :
1018 :
1019 0 : 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 0 : const u8 *pos = data;
1024 0 : 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 0 : if (len < 1 + 2)
1032 0 : return;
1033 :
1034 0 : os_memset(&qi, 0, sizeof(qi));
1035 :
1036 0 : dialog_token = *pos++;
1037 0 : wpa_msg(hapd->msg_ctx, MSG_DEBUG,
1038 : "GAS: GAS Initial Request from " MACSTR " (dialog token %u) ",
1039 0 : MAC2STR(sa), dialog_token);
1040 :
1041 0 : 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 0 : adv_proto = pos++;
1047 :
1048 0 : slen = *pos++;
1049 0 : next = pos + slen;
1050 0 : 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 0 : pos++; /* skip QueryRespLenLimit and PAME-BI */
1056 :
1057 0 : if (*pos != ACCESS_NETWORK_QUERY_PROTOCOL) {
1058 : struct wpabuf *buf;
1059 0 : wpa_msg(hapd->msg_ctx, MSG_DEBUG,
1060 : "GAS: Unsupported GAS advertisement protocol id %u",
1061 0 : *pos);
1062 0 : if (sa[0] & 0x01)
1063 0 : return; /* Invalid source address - drop silently */
1064 0 : buf = gas_build_initial_resp(
1065 : dialog_token, WLAN_STATUS_GAS_ADV_PROTO_NOT_SUPPORTED,
1066 0 : 0, 2 + slen + 2);
1067 0 : if (buf == NULL)
1068 0 : return;
1069 0 : wpabuf_put_data(buf, adv_proto, 2 + slen);
1070 0 : wpabuf_put_le16(buf, 0); /* Query Response Length */
1071 0 : if (prot)
1072 0 : convert_to_protected_dual(buf);
1073 0 : hostapd_drv_send_action(hapd, hapd->iface->freq, 0, sa,
1074 0 : wpabuf_head(buf), wpabuf_len(buf));
1075 0 : wpabuf_free(buf);
1076 0 : return;
1077 : }
1078 :
1079 0 : pos = next;
1080 : /* Query Request */
1081 0 : if (pos + 2 > end)
1082 0 : return;
1083 0 : slen = WPA_GET_LE16(pos);
1084 0 : pos += 2;
1085 0 : if (pos + slen > end)
1086 0 : return;
1087 0 : end = pos + slen;
1088 :
1089 : /* ANQP Query Request */
1090 0 : while (pos < end) {
1091 : u16 info_id, elen;
1092 :
1093 0 : if (pos + 4 > end)
1094 0 : return;
1095 :
1096 0 : info_id = WPA_GET_LE16(pos);
1097 0 : pos += 2;
1098 0 : elen = WPA_GET_LE16(pos);
1099 0 : pos += 2;
1100 :
1101 0 : if (pos + elen > end) {
1102 0 : wpa_printf(MSG_DEBUG, "ANQP: Invalid Query Request");
1103 0 : return;
1104 : }
1105 :
1106 0 : switch (info_id) {
1107 : case ANQP_QUERY_LIST:
1108 0 : rx_anqp_query_list(hapd, pos, pos + elen, &qi);
1109 0 : break;
1110 : #ifdef CONFIG_HS20
1111 : case ANQP_VENDOR_SPECIFIC:
1112 0 : rx_anqp_vendor_specific(hapd, pos, pos + elen, &qi);
1113 0 : 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 0 : pos += elen;
1122 : }
1123 :
1124 0 : gas_serv_req_local_processing(hapd, sa, dialog_token, &qi, prot);
1125 : }
1126 :
1127 :
1128 0 : 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 0 : int more = 0;
1137 :
1138 0 : wpa_hexdump(MSG_DEBUG, "GAS: RX GAS Comeback Request", data, len);
1139 0 : if (len < 1)
1140 0 : return;
1141 0 : dialog_token = *data;
1142 0 : wpa_msg(hapd->msg_ctx, MSG_DEBUG, "GAS: Dialog Token: %u",
1143 : dialog_token);
1144 :
1145 0 : dialog = gas_serv_dialog_find(hapd, sa, dialog_token);
1146 0 : if (!dialog) {
1147 0 : wpa_msg(hapd->msg_ctx, MSG_DEBUG, "GAS: No pending SD "
1148 : "response fragment for " MACSTR " dialog token %u",
1149 0 : MAC2STR(sa), dialog_token);
1150 :
1151 0 : if (sa[0] & 0x01)
1152 0 : return; /* Invalid source address - drop silently */
1153 0 : tx_buf = gas_anqp_build_comeback_resp_buf(
1154 : dialog_token, WLAN_STATUS_NO_OUTSTANDING_GAS_REQ, 0, 0,
1155 : 0, NULL);
1156 0 : if (tx_buf == NULL)
1157 0 : return;
1158 0 : goto send_resp;
1159 : }
1160 :
1161 0 : frag_len = wpabuf_len(dialog->sd_resp) - dialog->sd_resp_pos;
1162 0 : if (frag_len > hapd->gas_frag_limit) {
1163 0 : frag_len = hapd->gas_frag_limit;
1164 0 : more = 1;
1165 : }
1166 0 : wpa_msg(hapd->msg_ctx, MSG_DEBUG, "GAS: resp frag_len %u",
1167 : (unsigned int) frag_len);
1168 0 : buf = wpabuf_alloc_copy(wpabuf_head_u8(dialog->sd_resp) +
1169 : dialog->sd_resp_pos, frag_len);
1170 0 : 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 0 : tx_buf = gas_anqp_build_comeback_resp_buf(dialog_token,
1177 : WLAN_STATUS_SUCCESS,
1178 0 : dialog->sd_frag_id,
1179 : more, 0, buf);
1180 0 : wpabuf_free(buf);
1181 0 : if (tx_buf == NULL) {
1182 0 : gas_serv_dialog_clear(dialog);
1183 0 : return;
1184 : }
1185 0 : wpa_msg(hapd->msg_ctx, MSG_DEBUG, "GAS: Tx GAS Comeback Response "
1186 : "(frag_id %d more=%d frag_len=%d)",
1187 0 : dialog->sd_frag_id, more, (int) frag_len);
1188 0 : dialog->sd_frag_id++;
1189 0 : dialog->sd_resp_pos += frag_len;
1190 :
1191 0 : if (more) {
1192 0 : wpa_msg(hapd->msg_ctx, MSG_DEBUG, "GAS: %d more bytes remain "
1193 : "to be sent",
1194 0 : (int) (wpabuf_len(dialog->sd_resp) -
1195 0 : dialog->sd_resp_pos));
1196 : } else {
1197 0 : wpa_msg(hapd->msg_ctx, MSG_DEBUG, "GAS: All fragments of "
1198 : "SD response sent");
1199 0 : gas_serv_dialog_clear(dialog);
1200 0 : gas_serv_free_dialogs(hapd, sa);
1201 : }
1202 :
1203 : send_resp:
1204 0 : if (prot)
1205 0 : convert_to_protected_dual(tx_buf);
1206 0 : hostapd_drv_send_action(hapd, hapd->iface->freq, 0, sa,
1207 0 : wpabuf_head(tx_buf), wpabuf_len(tx_buf));
1208 0 : wpabuf_free(tx_buf);
1209 : }
1210 :
1211 :
1212 0 : static void gas_serv_rx_public_action(void *ctx, const u8 *buf, size_t len,
1213 : int freq)
1214 : {
1215 0 : 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 0 : mgmt = (const struct ieee80211_mgmt *) buf;
1222 0 : hdr_len = (const u8 *) &mgmt->u.action.u.vs_public_action.action - buf;
1223 0 : if (hdr_len > len)
1224 0 : return;
1225 0 : if (mgmt->u.action.category != WLAN_ACTION_PUBLIC &&
1226 0 : 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 0 : prot = mgmt->u.action.category == WLAN_ACTION_PROTECTED_DUAL;
1234 0 : sa = mgmt->sa;
1235 0 : len -= hdr_len;
1236 0 : data = &mgmt->u.action.u.public_action.action;
1237 0 : switch (data[0]) {
1238 : case WLAN_PA_GAS_INITIAL_REQ:
1239 0 : gas_serv_rx_gas_initial_req(hapd, sa, data + 1, len - 1, prot);
1240 0 : break;
1241 : case WLAN_PA_GAS_COMEBACK_REQ:
1242 0 : gas_serv_rx_gas_comeback_req(hapd, sa, data + 1, len - 1, prot);
1243 0 : break;
1244 : }
1245 : }
1246 :
1247 :
1248 2 : int gas_serv_init(struct hostapd_data *hapd)
1249 : {
1250 2 : hapd->public_action_cb2 = gas_serv_rx_public_action;
1251 2 : hapd->public_action_cb2_ctx = hapd;
1252 2 : hapd->gas_frag_limit = 1400;
1253 2 : if (hapd->conf->gas_frag_limit > 0)
1254 0 : hapd->gas_frag_limit = hapd->conf->gas_frag_limit;
1255 2 : return 0;
1256 : }
1257 :
1258 :
1259 2 : void gas_serv_deinit(struct hostapd_data *hapd)
1260 : {
1261 2 : }
|