blob: b4306c66925eb06518686a65ec8a81e4834fd998 [file] [log] [blame]
Dmitry Shmidt04949592012-07-19 12:16:46 -07001/*
2 * Generic advertisement service (GAS) server
Dmitry Shmidt18463232014-01-24 12:29:41 -08003 * Copyright (c) 2011-2014, Qualcomm Atheros, Inc.
Dmitry Shmidt04949592012-07-19 12:16:46 -07004 *
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
Dmitry Shmidt18463232014-01-24 12:29:41 -080022static void convert_to_protected_dual(struct wpabuf *msg)
23{
24 u8 *categ = wpabuf_mhead_u8(msg);
25 *categ = WLAN_ACTION_PROTECTED_DUAL;
26}
27
28
Dmitry Shmidt04949592012-07-19 12:16:46 -070029static struct gas_dialog_info *
30gas_dialog_create(struct hostapd_data *hapd, const u8 *addr, u8 dialog_token)
31{
32 struct sta_info *sta;
33 struct gas_dialog_info *dia = NULL;
34 int i, j;
35
36 sta = ap_get_sta(hapd, addr);
37 if (!sta) {
38 /*
39 * We need a STA entry to be able to maintain state for
40 * the GAS query.
41 */
42 wpa_printf(MSG_DEBUG, "ANQP: Add a temporary STA entry for "
43 "GAS query");
44 sta = ap_sta_add(hapd, addr);
45 if (!sta) {
46 wpa_printf(MSG_DEBUG, "Failed to add STA " MACSTR
47 " for GAS query", MAC2STR(addr));
48 return NULL;
49 }
50 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 ap_sta_session_timeout(hapd, sta, 5);
Dmitry Shmidt54605472013-11-08 11:10:19 -080056 } else {
57 ap_sta_replenish_timeout(hapd, sta, 5);
Dmitry Shmidt04949592012-07-19 12:16:46 -070058 }
59
60 if (sta->gas_dialog == NULL) {
Dmitry Shmidtfb45fd52015-01-05 13:08:17 -080061 sta->gas_dialog = os_calloc(GAS_DIALOG_MAX,
Dmitry Shmidt04949592012-07-19 12:16:46 -070062 sizeof(struct gas_dialog_info));
63 if (sta->gas_dialog == NULL)
64 return NULL;
65 }
66
67 for (i = sta->gas_dialog_next, j = 0; j < GAS_DIALOG_MAX; i++, j++) {
68 if (i == GAS_DIALOG_MAX)
69 i = 0;
70 if (sta->gas_dialog[i].valid)
71 continue;
72 dia = &sta->gas_dialog[i];
73 dia->valid = 1;
Dmitry Shmidt04949592012-07-19 12:16:46 -070074 dia->dialog_token = dialog_token;
75 sta->gas_dialog_next = (++i == GAS_DIALOG_MAX) ? 0 : i;
76 return dia;
77 }
78
79 wpa_msg(hapd->msg_ctx, MSG_ERROR, "ANQP: Could not create dialog for "
80 MACSTR " dialog_token %u. Consider increasing "
81 "GAS_DIALOG_MAX.", MAC2STR(addr), dialog_token);
82
83 return NULL;
84}
85
86
87struct gas_dialog_info *
88gas_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 sta = ap_get_sta(hapd, addr);
95 if (!sta) {
96 wpa_printf(MSG_DEBUG, "ANQP: could not find STA " MACSTR,
97 MAC2STR(addr));
98 return NULL;
99 }
100 for (i = 0; sta->gas_dialog && i < GAS_DIALOG_MAX; i++) {
101 if (sta->gas_dialog[i].dialog_token != dialog_token ||
102 !sta->gas_dialog[i].valid)
103 continue;
Dmitry Shmidt7d56b752015-12-22 10:59:44 -0800104 ap_sta_replenish_timeout(hapd, sta, 5);
Dmitry Shmidt04949592012-07-19 12:16:46 -0700105 return &sta->gas_dialog[i];
106 }
107 wpa_printf(MSG_DEBUG, "ANQP: Could not find dialog for "
108 MACSTR " dialog_token %u", MAC2STR(addr), dialog_token);
109 return NULL;
110}
111
112
113void gas_serv_dialog_clear(struct gas_dialog_info *dia)
114{
115 wpabuf_free(dia->sd_resp);
116 os_memset(dia, 0, sizeof(*dia));
117}
118
119
120static void gas_serv_free_dialogs(struct hostapd_data *hapd,
121 const u8 *sta_addr)
122{
123 struct sta_info *sta;
124 int i;
125
126 sta = ap_get_sta(hapd, sta_addr);
127 if (sta == NULL || sta->gas_dialog == NULL)
128 return;
129
130 for (i = 0; i < GAS_DIALOG_MAX; i++) {
131 if (sta->gas_dialog[i].valid)
132 return;
133 }
134
135 os_free(sta->gas_dialog);
136 sta->gas_dialog = NULL;
137}
138
139
Dmitry Shmidtaa532512012-09-24 10:35:31 -0700140#ifdef CONFIG_HS20
Dmitry Shmidt61d9df32012-08-29 16:22:06 -0700141static void anqp_add_hs_capab_list(struct hostapd_data *hapd,
142 struct wpabuf *buf)
143{
144 u8 *len;
145
146 len = gas_anqp_add_element(buf, ANQP_VENDOR_SPECIFIC);
147 wpabuf_put_be24(buf, OUI_WFA);
148 wpabuf_put_u8(buf, HS20_ANQP_OUI_TYPE);
149 wpabuf_put_u8(buf, HS20_STYPE_CAPABILITY_LIST);
150 wpabuf_put_u8(buf, 0); /* Reserved */
151 wpabuf_put_u8(buf, HS20_STYPE_CAPABILITY_LIST);
152 if (hapd->conf->hs20_oper_friendly_name)
153 wpabuf_put_u8(buf, HS20_STYPE_OPERATOR_FRIENDLY_NAME);
154 if (hapd->conf->hs20_wan_metrics)
155 wpabuf_put_u8(buf, HS20_STYPE_WAN_METRICS);
156 if (hapd->conf->hs20_connection_capability)
157 wpabuf_put_u8(buf, HS20_STYPE_CONNECTION_CAPABILITY);
158 if (hapd->conf->nai_realm_data)
159 wpabuf_put_u8(buf, HS20_STYPE_NAI_HOME_REALM_QUERY);
160 if (hapd->conf->hs20_operating_class)
161 wpabuf_put_u8(buf, HS20_STYPE_OPERATING_CLASS);
Dmitry Shmidtf21452a2014-02-26 10:55:25 -0800162 if (hapd->conf->hs20_osu_providers_count)
163 wpabuf_put_u8(buf, HS20_STYPE_OSU_PROVIDERS_LIST);
164 if (hapd->conf->hs20_icons_count)
165 wpabuf_put_u8(buf, HS20_STYPE_ICON_REQUEST);
Dmitry Shmidt61d9df32012-08-29 16:22:06 -0700166 gas_anqp_set_element_len(buf, len);
167}
Dmitry Shmidtaa532512012-09-24 10:35:31 -0700168#endif /* CONFIG_HS20 */
Dmitry Shmidt61d9df32012-08-29 16:22:06 -0700169
170
Dmitry Shmidtd80a4012015-11-05 16:35:40 -0800171static struct anqp_element * get_anqp_elem(struct hostapd_data *hapd,
172 u16 infoid)
173{
174 struct anqp_element *elem;
175
176 dl_list_for_each(elem, &hapd->conf->anqp_elem, struct anqp_element,
177 list) {
178 if (elem->infoid == infoid)
179 return elem;
180 }
181
182 return NULL;
183}
184
185
186static void anqp_add_elem(struct hostapd_data *hapd, struct wpabuf *buf,
187 u16 infoid)
188{
189 struct anqp_element *elem;
190
191 elem = get_anqp_elem(hapd, infoid);
192 if (!elem)
193 return;
194 if (wpabuf_tailroom(buf) < 2 + 2 + wpabuf_len(elem->payload)) {
195 wpa_printf(MSG_DEBUG, "ANQP: No room for InfoID %u payload",
196 infoid);
197 return;
198 }
199
200 wpabuf_put_le16(buf, infoid);
201 wpabuf_put_le16(buf, wpabuf_len(elem->payload));
202 wpabuf_put_buf(buf, elem->payload);
203}
204
205
206static int anqp_add_override(struct hostapd_data *hapd, struct wpabuf *buf,
207 u16 infoid)
208{
209 if (get_anqp_elem(hapd, infoid)) {
210 anqp_add_elem(hapd, buf, infoid);
211 return 1;
212 }
213
214 return 0;
215}
216
217
Dmitry Shmidt04949592012-07-19 12:16:46 -0700218static void anqp_add_capab_list(struct hostapd_data *hapd,
219 struct wpabuf *buf)
220{
221 u8 *len;
Dmitry Shmidtd80a4012015-11-05 16:35:40 -0800222 u16 id;
223
224 if (anqp_add_override(hapd, buf, ANQP_CAPABILITY_LIST))
225 return;
Dmitry Shmidt04949592012-07-19 12:16:46 -0700226
227 len = gas_anqp_add_element(buf, ANQP_CAPABILITY_LIST);
228 wpabuf_put_le16(buf, ANQP_CAPABILITY_LIST);
Dmitry Shmidtd80a4012015-11-05 16:35:40 -0800229 if (hapd->conf->venue_name || get_anqp_elem(hapd, ANQP_VENUE_NAME))
Dmitry Shmidt04949592012-07-19 12:16:46 -0700230 wpabuf_put_le16(buf, ANQP_VENUE_NAME);
Dmitry Shmidtd80a4012015-11-05 16:35:40 -0800231 if (get_anqp_elem(hapd, ANQP_EMERGENCY_CALL_NUMBER))
232 wpabuf_put_le16(buf, ANQP_EMERGENCY_CALL_NUMBER);
233 if (hapd->conf->network_auth_type ||
234 get_anqp_elem(hapd, ANQP_NETWORK_AUTH_TYPE))
Dmitry Shmidt61d9df32012-08-29 16:22:06 -0700235 wpabuf_put_le16(buf, ANQP_NETWORK_AUTH_TYPE);
Dmitry Shmidtd80a4012015-11-05 16:35:40 -0800236 if (hapd->conf->roaming_consortium ||
237 get_anqp_elem(hapd, ANQP_ROAMING_CONSORTIUM))
Dmitry Shmidt04949592012-07-19 12:16:46 -0700238 wpabuf_put_le16(buf, ANQP_ROAMING_CONSORTIUM);
Dmitry Shmidtd80a4012015-11-05 16:35:40 -0800239 if (hapd->conf->ipaddr_type_configured ||
240 get_anqp_elem(hapd, ANQP_IP_ADDR_TYPE_AVAILABILITY))
Dmitry Shmidt61d9df32012-08-29 16:22:06 -0700241 wpabuf_put_le16(buf, ANQP_IP_ADDR_TYPE_AVAILABILITY);
Dmitry Shmidtd80a4012015-11-05 16:35:40 -0800242 if (hapd->conf->nai_realm_data ||
243 get_anqp_elem(hapd, ANQP_NAI_REALM))
Dmitry Shmidt61d9df32012-08-29 16:22:06 -0700244 wpabuf_put_le16(buf, ANQP_NAI_REALM);
Dmitry Shmidtd80a4012015-11-05 16:35:40 -0800245 if (hapd->conf->anqp_3gpp_cell_net ||
246 get_anqp_elem(hapd, ANQP_3GPP_CELLULAR_NETWORK))
Dmitry Shmidt61d9df32012-08-29 16:22:06 -0700247 wpabuf_put_le16(buf, ANQP_3GPP_CELLULAR_NETWORK);
Dmitry Shmidtd80a4012015-11-05 16:35:40 -0800248 if (get_anqp_elem(hapd, ANQP_AP_GEOSPATIAL_LOCATION))
249 wpabuf_put_le16(buf, ANQP_AP_GEOSPATIAL_LOCATION);
250 if (get_anqp_elem(hapd, ANQP_AP_CIVIC_LOCATION))
251 wpabuf_put_le16(buf, ANQP_AP_CIVIC_LOCATION);
252 if (get_anqp_elem(hapd, ANQP_AP_LOCATION_PUBLIC_URI))
253 wpabuf_put_le16(buf, ANQP_AP_LOCATION_PUBLIC_URI);
254 if (hapd->conf->domain_name || get_anqp_elem(hapd, ANQP_DOMAIN_NAME))
Dmitry Shmidt61d9df32012-08-29 16:22:06 -0700255 wpabuf_put_le16(buf, ANQP_DOMAIN_NAME);
Dmitry Shmidtd80a4012015-11-05 16:35:40 -0800256 if (get_anqp_elem(hapd, ANQP_EMERGENCY_ALERT_URI))
257 wpabuf_put_le16(buf, ANQP_EMERGENCY_ALERT_URI);
Dmitry Shmidtabb90a32016-12-05 15:34:39 -0800258 if (get_anqp_elem(hapd, ANQP_TDLS_CAPABILITY))
259 wpabuf_put_le16(buf, ANQP_TDLS_CAPABILITY);
Dmitry Shmidtd80a4012015-11-05 16:35:40 -0800260 if (get_anqp_elem(hapd, ANQP_EMERGENCY_NAI))
261 wpabuf_put_le16(buf, ANQP_EMERGENCY_NAI);
262 if (get_anqp_elem(hapd, ANQP_NEIGHBOR_REPORT))
263 wpabuf_put_le16(buf, ANQP_NEIGHBOR_REPORT);
264 for (id = 273; id < 277; id++) {
265 if (get_anqp_elem(hapd, id))
266 wpabuf_put_le16(buf, id);
267 }
268 if (get_anqp_elem(hapd, ANQP_VENUE_URL))
269 wpabuf_put_le16(buf, ANQP_VENUE_URL);
270 if (get_anqp_elem(hapd, ANQP_ADVICE_OF_CHARGE))
271 wpabuf_put_le16(buf, ANQP_ADVICE_OF_CHARGE);
272 if (get_anqp_elem(hapd, ANQP_LOCAL_CONTENT))
273 wpabuf_put_le16(buf, ANQP_LOCAL_CONTENT);
Dmitry Shmidtabb90a32016-12-05 15:34:39 -0800274 for (id = 280; id < 300; id++) {
275 if (get_anqp_elem(hapd, id))
276 wpabuf_put_le16(buf, id);
277 }
Dmitry Shmidtaa532512012-09-24 10:35:31 -0700278#ifdef CONFIG_HS20
Dmitry Shmidt61d9df32012-08-29 16:22:06 -0700279 anqp_add_hs_capab_list(hapd, buf);
Dmitry Shmidtaa532512012-09-24 10:35:31 -0700280#endif /* CONFIG_HS20 */
Dmitry Shmidt04949592012-07-19 12:16:46 -0700281 gas_anqp_set_element_len(buf, len);
282}
283
284
285static void anqp_add_venue_name(struct hostapd_data *hapd, struct wpabuf *buf)
286{
Dmitry Shmidtd80a4012015-11-05 16:35:40 -0800287 if (anqp_add_override(hapd, buf, ANQP_VENUE_NAME))
288 return;
289
Dmitry Shmidt04949592012-07-19 12:16:46 -0700290 if (hapd->conf->venue_name) {
291 u8 *len;
292 unsigned int i;
293 len = gas_anqp_add_element(buf, ANQP_VENUE_NAME);
294 wpabuf_put_u8(buf, hapd->conf->venue_group);
295 wpabuf_put_u8(buf, hapd->conf->venue_type);
296 for (i = 0; i < hapd->conf->venue_name_count; i++) {
Dmitry Shmidt61d9df32012-08-29 16:22:06 -0700297 struct hostapd_lang_string *vn;
Dmitry Shmidt04949592012-07-19 12:16:46 -0700298 vn = &hapd->conf->venue_name[i];
299 wpabuf_put_u8(buf, 3 + vn->name_len);
300 wpabuf_put_data(buf, vn->lang, 3);
301 wpabuf_put_data(buf, vn->name, vn->name_len);
302 }
303 gas_anqp_set_element_len(buf, len);
304 }
305}
306
307
Dmitry Shmidt61d9df32012-08-29 16:22:06 -0700308static void anqp_add_network_auth_type(struct hostapd_data *hapd,
309 struct wpabuf *buf)
310{
Dmitry Shmidtd80a4012015-11-05 16:35:40 -0800311 if (anqp_add_override(hapd, buf, ANQP_NETWORK_AUTH_TYPE))
312 return;
313
Dmitry Shmidt61d9df32012-08-29 16:22:06 -0700314 if (hapd->conf->network_auth_type) {
315 wpabuf_put_le16(buf, ANQP_NETWORK_AUTH_TYPE);
316 wpabuf_put_le16(buf, hapd->conf->network_auth_type_len);
317 wpabuf_put_data(buf, hapd->conf->network_auth_type,
318 hapd->conf->network_auth_type_len);
319 }
320}
321
322
Dmitry Shmidt04949592012-07-19 12:16:46 -0700323static void anqp_add_roaming_consortium(struct hostapd_data *hapd,
324 struct wpabuf *buf)
325{
326 unsigned int i;
327 u8 *len;
328
Dmitry Shmidtd80a4012015-11-05 16:35:40 -0800329 if (anqp_add_override(hapd, buf, ANQP_ROAMING_CONSORTIUM))
330 return;
331
Dmitry Shmidt04949592012-07-19 12:16:46 -0700332 len = gas_anqp_add_element(buf, ANQP_ROAMING_CONSORTIUM);
333 for (i = 0; i < hapd->conf->roaming_consortium_count; i++) {
334 struct hostapd_roaming_consortium *rc;
335 rc = &hapd->conf->roaming_consortium[i];
336 wpabuf_put_u8(buf, rc->len);
337 wpabuf_put_data(buf, rc->oi, rc->len);
338 }
339 gas_anqp_set_element_len(buf, len);
340}
341
342
Dmitry Shmidt61d9df32012-08-29 16:22:06 -0700343static void anqp_add_ip_addr_type_availability(struct hostapd_data *hapd,
344 struct wpabuf *buf)
345{
Dmitry Shmidtd80a4012015-11-05 16:35:40 -0800346 if (anqp_add_override(hapd, buf, ANQP_IP_ADDR_TYPE_AVAILABILITY))
347 return;
348
Dmitry Shmidt61d9df32012-08-29 16:22:06 -0700349 if (hapd->conf->ipaddr_type_configured) {
350 wpabuf_put_le16(buf, ANQP_IP_ADDR_TYPE_AVAILABILITY);
351 wpabuf_put_le16(buf, 1);
352 wpabuf_put_u8(buf, hapd->conf->ipaddr_type_availability);
353 }
354}
355
356
357static void anqp_add_nai_realm_eap(struct wpabuf *buf,
358 struct hostapd_nai_realm_data *realm)
359{
360 unsigned int i, j;
361
362 wpabuf_put_u8(buf, realm->eap_method_count);
363
364 for (i = 0; i < realm->eap_method_count; i++) {
365 struct hostapd_nai_realm_eap *eap = &realm->eap_method[i];
366 wpabuf_put_u8(buf, 2 + (3 * eap->num_auths));
367 wpabuf_put_u8(buf, eap->eap_method);
368 wpabuf_put_u8(buf, eap->num_auths);
369 for (j = 0; j < eap->num_auths; j++) {
370 wpabuf_put_u8(buf, eap->auth_id[j]);
371 wpabuf_put_u8(buf, 1);
372 wpabuf_put_u8(buf, eap->auth_val[j]);
373 }
374 }
375}
376
377
378static void anqp_add_nai_realm_data(struct wpabuf *buf,
379 struct hostapd_nai_realm_data *realm,
380 unsigned int realm_idx)
381{
382 u8 *realm_data_len;
383
384 wpa_printf(MSG_DEBUG, "realm=%s, len=%d", realm->realm[realm_idx],
385 (int) os_strlen(realm->realm[realm_idx]));
386 realm_data_len = wpabuf_put(buf, 2);
387 wpabuf_put_u8(buf, realm->encoding);
388 wpabuf_put_u8(buf, os_strlen(realm->realm[realm_idx]));
389 wpabuf_put_str(buf, realm->realm[realm_idx]);
390 anqp_add_nai_realm_eap(buf, realm);
391 gas_anqp_set_element_len(buf, realm_data_len);
392}
393
394
395static int hs20_add_nai_home_realm_matches(struct hostapd_data *hapd,
396 struct wpabuf *buf,
397 const u8 *home_realm,
398 size_t home_realm_len)
399{
400 unsigned int i, j, k;
401 u8 num_realms, num_matching = 0, encoding, realm_len, *realm_list_len;
402 struct hostapd_nai_realm_data *realm;
403 const u8 *pos, *realm_name, *end;
404 struct {
405 unsigned int realm_data_idx;
406 unsigned int realm_idx;
407 } matches[10];
408
409 pos = home_realm;
410 end = pos + home_realm_len;
Dmitry Shmidtd80a4012015-11-05 16:35:40 -0800411 if (end - pos < 1) {
Dmitry Shmidt61d9df32012-08-29 16:22:06 -0700412 wpa_hexdump(MSG_DEBUG, "Too short NAI Home Realm Query",
413 home_realm, home_realm_len);
414 return -1;
415 }
416 num_realms = *pos++;
417
418 for (i = 0; i < num_realms && num_matching < 10; i++) {
Dmitry Shmidtd80a4012015-11-05 16:35:40 -0800419 if (end - pos < 2) {
Dmitry Shmidt61d9df32012-08-29 16:22:06 -0700420 wpa_hexdump(MSG_DEBUG,
421 "Truncated NAI Home Realm Query",
422 home_realm, home_realm_len);
423 return -1;
424 }
425 encoding = *pos++;
426 realm_len = *pos++;
Dmitry Shmidtd80a4012015-11-05 16:35:40 -0800427 if (realm_len > end - pos) {
Dmitry Shmidt61d9df32012-08-29 16:22:06 -0700428 wpa_hexdump(MSG_DEBUG,
429 "Truncated NAI Home Realm Query",
430 home_realm, home_realm_len);
431 return -1;
432 }
433 realm_name = pos;
434 for (j = 0; j < hapd->conf->nai_realm_count &&
435 num_matching < 10; j++) {
436 const u8 *rpos, *rend;
437 realm = &hapd->conf->nai_realm_data[j];
438 if (encoding != realm->encoding)
439 continue;
440
441 rpos = realm_name;
442 while (rpos < realm_name + realm_len &&
443 num_matching < 10) {
444 for (rend = rpos;
445 rend < realm_name + realm_len; rend++) {
446 if (*rend == ';')
447 break;
448 }
449 for (k = 0; k < MAX_NAI_REALMS &&
450 realm->realm[k] &&
451 num_matching < 10; k++) {
452 if ((int) os_strlen(realm->realm[k]) !=
453 rend - rpos ||
454 os_strncmp((char *) rpos,
455 realm->realm[k],
456 rend - rpos) != 0)
457 continue;
458 matches[num_matching].realm_data_idx =
459 j;
460 matches[num_matching].realm_idx = k;
461 num_matching++;
462 }
463 rpos = rend + 1;
464 }
465 }
466 pos += realm_len;
467 }
468
469 realm_list_len = gas_anqp_add_element(buf, ANQP_NAI_REALM);
470 wpabuf_put_le16(buf, num_matching);
471
472 /*
473 * There are two ways to format. 1. each realm in a NAI Realm Data unit
474 * 2. all realms that share the same EAP methods in a NAI Realm Data
475 * unit. The first format is likely to be bigger in size than the
476 * second, but may be easier to parse and process by the receiver.
477 */
478 for (i = 0; i < num_matching; i++) {
479 wpa_printf(MSG_DEBUG, "realm_idx %d, realm_data_idx %d",
480 matches[i].realm_data_idx, matches[i].realm_idx);
481 realm = &hapd->conf->nai_realm_data[matches[i].realm_data_idx];
482 anqp_add_nai_realm_data(buf, realm, matches[i].realm_idx);
483 }
484 gas_anqp_set_element_len(buf, realm_list_len);
485 return 0;
486}
487
488
489static void anqp_add_nai_realm(struct hostapd_data *hapd, struct wpabuf *buf,
490 const u8 *home_realm, size_t home_realm_len,
491 int nai_realm, int nai_home_realm)
492{
Dmitry Shmidtd80a4012015-11-05 16:35:40 -0800493 if (nai_realm && !nai_home_realm &&
494 anqp_add_override(hapd, buf, ANQP_NAI_REALM))
495 return;
496
Dmitry Shmidt61d9df32012-08-29 16:22:06 -0700497 if (nai_realm && hapd->conf->nai_realm_data) {
498 u8 *len;
499 unsigned int i, j;
500 len = gas_anqp_add_element(buf, ANQP_NAI_REALM);
501 wpabuf_put_le16(buf, hapd->conf->nai_realm_count);
502 for (i = 0; i < hapd->conf->nai_realm_count; i++) {
503 u8 *realm_data_len, *realm_len;
504 struct hostapd_nai_realm_data *realm;
505
506 realm = &hapd->conf->nai_realm_data[i];
507 realm_data_len = wpabuf_put(buf, 2);
508 wpabuf_put_u8(buf, realm->encoding);
509 realm_len = wpabuf_put(buf, 1);
510 for (j = 0; realm->realm[j]; j++) {
511 if (j > 0)
512 wpabuf_put_u8(buf, ';');
513 wpabuf_put_str(buf, realm->realm[j]);
514 }
515 *realm_len = (u8 *) wpabuf_put(buf, 0) - realm_len - 1;
516 anqp_add_nai_realm_eap(buf, realm);
517 gas_anqp_set_element_len(buf, realm_data_len);
518 }
519 gas_anqp_set_element_len(buf, len);
Dmitry Shmidt09f57ba2014-06-10 16:07:13 -0700520 } else if (nai_home_realm && hapd->conf->nai_realm_data && home_realm) {
Dmitry Shmidt61d9df32012-08-29 16:22:06 -0700521 hs20_add_nai_home_realm_matches(hapd, buf, home_realm,
522 home_realm_len);
523 }
524}
525
526
527static void anqp_add_3gpp_cellular_network(struct hostapd_data *hapd,
528 struct wpabuf *buf)
529{
Dmitry Shmidtd80a4012015-11-05 16:35:40 -0800530 if (anqp_add_override(hapd, buf, ANQP_3GPP_CELLULAR_NETWORK))
531 return;
532
Dmitry Shmidt61d9df32012-08-29 16:22:06 -0700533 if (hapd->conf->anqp_3gpp_cell_net) {
534 wpabuf_put_le16(buf, ANQP_3GPP_CELLULAR_NETWORK);
535 wpabuf_put_le16(buf,
536 hapd->conf->anqp_3gpp_cell_net_len);
537 wpabuf_put_data(buf, hapd->conf->anqp_3gpp_cell_net,
538 hapd->conf->anqp_3gpp_cell_net_len);
539 }
540}
541
542
543static void anqp_add_domain_name(struct hostapd_data *hapd, struct wpabuf *buf)
544{
Dmitry Shmidtd80a4012015-11-05 16:35:40 -0800545 if (anqp_add_override(hapd, buf, ANQP_DOMAIN_NAME))
546 return;
547
Dmitry Shmidt61d9df32012-08-29 16:22:06 -0700548 if (hapd->conf->domain_name) {
549 wpabuf_put_le16(buf, ANQP_DOMAIN_NAME);
550 wpabuf_put_le16(buf, hapd->conf->domain_name_len);
551 wpabuf_put_data(buf, hapd->conf->domain_name,
552 hapd->conf->domain_name_len);
553 }
554}
555
556
Dmitry Shmidtaa532512012-09-24 10:35:31 -0700557#ifdef CONFIG_HS20
558
Dmitry Shmidt61d9df32012-08-29 16:22:06 -0700559static void anqp_add_operator_friendly_name(struct hostapd_data *hapd,
560 struct wpabuf *buf)
561{
562 if (hapd->conf->hs20_oper_friendly_name) {
563 u8 *len;
564 unsigned int i;
565 len = gas_anqp_add_element(buf, ANQP_VENDOR_SPECIFIC);
566 wpabuf_put_be24(buf, OUI_WFA);
567 wpabuf_put_u8(buf, HS20_ANQP_OUI_TYPE);
568 wpabuf_put_u8(buf, HS20_STYPE_OPERATOR_FRIENDLY_NAME);
569 wpabuf_put_u8(buf, 0); /* Reserved */
570 for (i = 0; i < hapd->conf->hs20_oper_friendly_name_count; i++)
571 {
572 struct hostapd_lang_string *vn;
573 vn = &hapd->conf->hs20_oper_friendly_name[i];
574 wpabuf_put_u8(buf, 3 + vn->name_len);
575 wpabuf_put_data(buf, vn->lang, 3);
576 wpabuf_put_data(buf, vn->name, vn->name_len);
577 }
578 gas_anqp_set_element_len(buf, len);
579 }
580}
581
582
583static void anqp_add_wan_metrics(struct hostapd_data *hapd,
584 struct wpabuf *buf)
585{
586 if (hapd->conf->hs20_wan_metrics) {
587 u8 *len = gas_anqp_add_element(buf, ANQP_VENDOR_SPECIFIC);
588 wpabuf_put_be24(buf, OUI_WFA);
589 wpabuf_put_u8(buf, HS20_ANQP_OUI_TYPE);
590 wpabuf_put_u8(buf, HS20_STYPE_WAN_METRICS);
591 wpabuf_put_u8(buf, 0); /* Reserved */
592 wpabuf_put_data(buf, hapd->conf->hs20_wan_metrics, 13);
593 gas_anqp_set_element_len(buf, len);
594 }
595}
596
597
598static void anqp_add_connection_capability(struct hostapd_data *hapd,
599 struct wpabuf *buf)
600{
601 if (hapd->conf->hs20_connection_capability) {
602 u8 *len = gas_anqp_add_element(buf, ANQP_VENDOR_SPECIFIC);
603 wpabuf_put_be24(buf, OUI_WFA);
604 wpabuf_put_u8(buf, HS20_ANQP_OUI_TYPE);
605 wpabuf_put_u8(buf, HS20_STYPE_CONNECTION_CAPABILITY);
606 wpabuf_put_u8(buf, 0); /* Reserved */
607 wpabuf_put_data(buf, hapd->conf->hs20_connection_capability,
608 hapd->conf->hs20_connection_capability_len);
609 gas_anqp_set_element_len(buf, len);
610 }
611}
612
613
614static void anqp_add_operating_class(struct hostapd_data *hapd,
615 struct wpabuf *buf)
616{
617 if (hapd->conf->hs20_operating_class) {
618 u8 *len = gas_anqp_add_element(buf, ANQP_VENDOR_SPECIFIC);
619 wpabuf_put_be24(buf, OUI_WFA);
620 wpabuf_put_u8(buf, HS20_ANQP_OUI_TYPE);
621 wpabuf_put_u8(buf, HS20_STYPE_OPERATING_CLASS);
622 wpabuf_put_u8(buf, 0); /* Reserved */
623 wpabuf_put_data(buf, hapd->conf->hs20_operating_class,
624 hapd->conf->hs20_operating_class_len);
625 gas_anqp_set_element_len(buf, len);
626 }
627}
628
Dmitry Shmidtf21452a2014-02-26 10:55:25 -0800629
630static void anqp_add_osu_provider(struct wpabuf *buf,
631 struct hostapd_bss_config *bss,
632 struct hs20_osu_provider *p)
633{
634 u8 *len, *len2, *count;
635 unsigned int i;
636
637 len = wpabuf_put(buf, 2); /* OSU Provider Length to be filled */
638
639 /* OSU Friendly Name Duples */
640 len2 = wpabuf_put(buf, 2);
641 for (i = 0; i < p->friendly_name_count; i++) {
642 struct hostapd_lang_string *s = &p->friendly_name[i];
643 wpabuf_put_u8(buf, 3 + s->name_len);
644 wpabuf_put_data(buf, s->lang, 3);
645 wpabuf_put_data(buf, s->name, s->name_len);
646 }
647 WPA_PUT_LE16(len2, (u8 *) wpabuf_put(buf, 0) - len2 - 2);
648
649 /* OSU Server URI */
650 if (p->server_uri) {
651 wpabuf_put_u8(buf, os_strlen(p->server_uri));
652 wpabuf_put_str(buf, p->server_uri);
653 } else
654 wpabuf_put_u8(buf, 0);
655
656 /* OSU Method List */
657 count = wpabuf_put(buf, 1);
658 for (i = 0; p->method_list[i] >= 0; i++)
659 wpabuf_put_u8(buf, p->method_list[i]);
660 *count = i;
661
662 /* Icons Available */
663 len2 = wpabuf_put(buf, 2);
664 for (i = 0; i < p->icons_count; i++) {
665 size_t j;
666 struct hs20_icon *icon = NULL;
667
668 for (j = 0; j < bss->hs20_icons_count && !icon; j++) {
669 if (os_strcmp(p->icons[i], bss->hs20_icons[j].name) ==
670 0)
671 icon = &bss->hs20_icons[j];
672 }
673 if (!icon)
674 continue; /* icon info not found */
675
676 wpabuf_put_le16(buf, icon->width);
677 wpabuf_put_le16(buf, icon->height);
678 wpabuf_put_data(buf, icon->language, 3);
679 wpabuf_put_u8(buf, os_strlen(icon->type));
680 wpabuf_put_str(buf, icon->type);
681 wpabuf_put_u8(buf, os_strlen(icon->name));
682 wpabuf_put_str(buf, icon->name);
683 }
684 WPA_PUT_LE16(len2, (u8 *) wpabuf_put(buf, 0) - len2 - 2);
685
686 /* OSU_NAI */
687 if (p->osu_nai) {
688 wpabuf_put_u8(buf, os_strlen(p->osu_nai));
689 wpabuf_put_str(buf, p->osu_nai);
690 } else
691 wpabuf_put_u8(buf, 0);
692
693 /* OSU Service Description Duples */
694 len2 = wpabuf_put(buf, 2);
695 for (i = 0; i < p->service_desc_count; i++) {
696 struct hostapd_lang_string *s = &p->service_desc[i];
697 wpabuf_put_u8(buf, 3 + s->name_len);
698 wpabuf_put_data(buf, s->lang, 3);
699 wpabuf_put_data(buf, s->name, s->name_len);
700 }
701 WPA_PUT_LE16(len2, (u8 *) wpabuf_put(buf, 0) - len2 - 2);
702
703 WPA_PUT_LE16(len, (u8 *) wpabuf_put(buf, 0) - len - 2);
704}
705
706
707static void anqp_add_osu_providers_list(struct hostapd_data *hapd,
708 struct wpabuf *buf)
709{
710 if (hapd->conf->hs20_osu_providers_count) {
711 size_t i;
712 u8 *len = gas_anqp_add_element(buf, ANQP_VENDOR_SPECIFIC);
713 wpabuf_put_be24(buf, OUI_WFA);
714 wpabuf_put_u8(buf, HS20_ANQP_OUI_TYPE);
715 wpabuf_put_u8(buf, HS20_STYPE_OSU_PROVIDERS_LIST);
716 wpabuf_put_u8(buf, 0); /* Reserved */
717
718 /* OSU SSID */
719 wpabuf_put_u8(buf, hapd->conf->osu_ssid_len);
720 wpabuf_put_data(buf, hapd->conf->osu_ssid,
721 hapd->conf->osu_ssid_len);
722
723 /* Number of OSU Providers */
724 wpabuf_put_u8(buf, hapd->conf->hs20_osu_providers_count);
725
726 for (i = 0; i < hapd->conf->hs20_osu_providers_count; i++) {
727 anqp_add_osu_provider(
728 buf, hapd->conf,
729 &hapd->conf->hs20_osu_providers[i]);
730 }
731
732 gas_anqp_set_element_len(buf, len);
733 }
734}
735
736
737static void anqp_add_icon_binary_file(struct hostapd_data *hapd,
738 struct wpabuf *buf,
739 const u8 *name, size_t name_len)
740{
741 struct hs20_icon *icon;
742 size_t i;
743 u8 *len;
744
745 wpa_hexdump_ascii(MSG_DEBUG, "HS 2.0: Requested Icon Filename",
746 name, name_len);
747 for (i = 0; i < hapd->conf->hs20_icons_count; i++) {
748 icon = &hapd->conf->hs20_icons[i];
749 if (name_len == os_strlen(icon->name) &&
750 os_memcmp(name, icon->name, name_len) == 0)
751 break;
752 }
753
754 if (i < hapd->conf->hs20_icons_count)
755 icon = &hapd->conf->hs20_icons[i];
756 else
757 icon = NULL;
758
759 len = gas_anqp_add_element(buf, ANQP_VENDOR_SPECIFIC);
760 wpabuf_put_be24(buf, OUI_WFA);
761 wpabuf_put_u8(buf, HS20_ANQP_OUI_TYPE);
762 wpabuf_put_u8(buf, HS20_STYPE_ICON_BINARY_FILE);
763 wpabuf_put_u8(buf, 0); /* Reserved */
764
765 if (icon) {
766 char *data;
767 size_t data_len;
768
769 data = os_readfile(icon->file, &data_len);
770 if (data == NULL || data_len > 65535) {
771 wpabuf_put_u8(buf, 2); /* Download Status:
772 * Unspecified file error */
773 wpabuf_put_u8(buf, 0);
774 wpabuf_put_le16(buf, 0);
775 } else {
776 wpabuf_put_u8(buf, 0); /* Download Status: Success */
777 wpabuf_put_u8(buf, os_strlen(icon->type));
778 wpabuf_put_str(buf, icon->type);
779 wpabuf_put_le16(buf, data_len);
780 wpabuf_put_data(buf, data, data_len);
781 }
782 os_free(data);
783 } else {
784 wpabuf_put_u8(buf, 1); /* Download Status: File not found */
785 wpabuf_put_u8(buf, 0);
786 wpabuf_put_le16(buf, 0);
787 }
788
789 gas_anqp_set_element_len(buf, len);
790}
791
Dmitry Shmidtaa532512012-09-24 10:35:31 -0700792#endif /* CONFIG_HS20 */
793
Dmitry Shmidt61d9df32012-08-29 16:22:06 -0700794
Dmitry Shmidtb97e4282016-02-08 10:16:07 -0800795static size_t anqp_get_required_len(struct hostapd_data *hapd,
796 const u16 *infoid,
797 unsigned int num_infoid)
798{
799 size_t len = 0;
800 unsigned int i;
801
802 for (i = 0; i < num_infoid; i++) {
803 struct anqp_element *elem = get_anqp_elem(hapd, infoid[i]);
804
805 if (elem)
806 len += 2 + 2 + wpabuf_len(elem->payload);
807 }
808
809 return len;
810}
811
812
Dmitry Shmidt04949592012-07-19 12:16:46 -0700813static struct wpabuf *
814gas_serv_build_gas_resp_payload(struct hostapd_data *hapd,
815 unsigned int request,
Dmitry Shmidtf21452a2014-02-26 10:55:25 -0800816 const u8 *home_realm, size_t home_realm_len,
Dmitry Shmidtd80a4012015-11-05 16:35:40 -0800817 const u8 *icon_name, size_t icon_name_len,
818 const u16 *extra_req,
819 unsigned int num_extra_req)
Dmitry Shmidt04949592012-07-19 12:16:46 -0700820{
821 struct wpabuf *buf;
Dmitry Shmidtf21452a2014-02-26 10:55:25 -0800822 size_t len;
Dmitry Shmidtd80a4012015-11-05 16:35:40 -0800823 unsigned int i;
Dmitry Shmidt04949592012-07-19 12:16:46 -0700824
Dmitry Shmidtf21452a2014-02-26 10:55:25 -0800825 len = 1400;
826 if (request & (ANQP_REQ_NAI_REALM | ANQP_REQ_NAI_HOME_REALM))
827 len += 1000;
828 if (request & ANQP_REQ_ICON_REQUEST)
829 len += 65536;
Dmitry Shmidtb97e4282016-02-08 10:16:07 -0800830 len += anqp_get_required_len(hapd, extra_req, num_extra_req);
Dmitry Shmidtf21452a2014-02-26 10:55:25 -0800831
832 buf = wpabuf_alloc(len);
Dmitry Shmidt04949592012-07-19 12:16:46 -0700833 if (buf == NULL)
834 return NULL;
835
836 if (request & ANQP_REQ_CAPABILITY_LIST)
837 anqp_add_capab_list(hapd, buf);
838 if (request & ANQP_REQ_VENUE_NAME)
839 anqp_add_venue_name(hapd, buf);
Dmitry Shmidtd80a4012015-11-05 16:35:40 -0800840 if (request & ANQP_REQ_EMERGENCY_CALL_NUMBER)
841 anqp_add_elem(hapd, buf, ANQP_EMERGENCY_CALL_NUMBER);
Dmitry Shmidt61d9df32012-08-29 16:22:06 -0700842 if (request & ANQP_REQ_NETWORK_AUTH_TYPE)
843 anqp_add_network_auth_type(hapd, buf);
Dmitry Shmidt04949592012-07-19 12:16:46 -0700844 if (request & ANQP_REQ_ROAMING_CONSORTIUM)
845 anqp_add_roaming_consortium(hapd, buf);
Dmitry Shmidt61d9df32012-08-29 16:22:06 -0700846 if (request & ANQP_REQ_IP_ADDR_TYPE_AVAILABILITY)
847 anqp_add_ip_addr_type_availability(hapd, buf);
848 if (request & (ANQP_REQ_NAI_REALM | ANQP_REQ_NAI_HOME_REALM))
849 anqp_add_nai_realm(hapd, buf, home_realm, home_realm_len,
850 request & ANQP_REQ_NAI_REALM,
851 request & ANQP_REQ_NAI_HOME_REALM);
852 if (request & ANQP_REQ_3GPP_CELLULAR_NETWORK)
853 anqp_add_3gpp_cellular_network(hapd, buf);
Dmitry Shmidtd80a4012015-11-05 16:35:40 -0800854 if (request & ANQP_REQ_AP_GEOSPATIAL_LOCATION)
855 anqp_add_elem(hapd, buf, ANQP_AP_GEOSPATIAL_LOCATION);
856 if (request & ANQP_REQ_AP_CIVIC_LOCATION)
857 anqp_add_elem(hapd, buf, ANQP_AP_CIVIC_LOCATION);
858 if (request & ANQP_REQ_AP_LOCATION_PUBLIC_URI)
859 anqp_add_elem(hapd, buf, ANQP_AP_LOCATION_PUBLIC_URI);
Dmitry Shmidt61d9df32012-08-29 16:22:06 -0700860 if (request & ANQP_REQ_DOMAIN_NAME)
861 anqp_add_domain_name(hapd, buf);
Dmitry Shmidtd80a4012015-11-05 16:35:40 -0800862 if (request & ANQP_REQ_EMERGENCY_ALERT_URI)
863 anqp_add_elem(hapd, buf, ANQP_EMERGENCY_ALERT_URI);
864 if (request & ANQP_REQ_TDLS_CAPABILITY)
865 anqp_add_elem(hapd, buf, ANQP_TDLS_CAPABILITY);
866 if (request & ANQP_REQ_EMERGENCY_NAI)
867 anqp_add_elem(hapd, buf, ANQP_EMERGENCY_NAI);
868
869 for (i = 0; i < num_extra_req; i++)
870 anqp_add_elem(hapd, buf, extra_req[i]);
Dmitry Shmidt61d9df32012-08-29 16:22:06 -0700871
Dmitry Shmidtaa532512012-09-24 10:35:31 -0700872#ifdef CONFIG_HS20
Dmitry Shmidt61d9df32012-08-29 16:22:06 -0700873 if (request & ANQP_REQ_HS_CAPABILITY_LIST)
874 anqp_add_hs_capab_list(hapd, buf);
875 if (request & ANQP_REQ_OPERATOR_FRIENDLY_NAME)
876 anqp_add_operator_friendly_name(hapd, buf);
877 if (request & ANQP_REQ_WAN_METRICS)
878 anqp_add_wan_metrics(hapd, buf);
879 if (request & ANQP_REQ_CONNECTION_CAPABILITY)
880 anqp_add_connection_capability(hapd, buf);
881 if (request & ANQP_REQ_OPERATING_CLASS)
882 anqp_add_operating_class(hapd, buf);
Dmitry Shmidtf21452a2014-02-26 10:55:25 -0800883 if (request & ANQP_REQ_OSU_PROVIDERS_LIST)
884 anqp_add_osu_providers_list(hapd, buf);
885 if (request & ANQP_REQ_ICON_REQUEST)
886 anqp_add_icon_binary_file(hapd, buf, icon_name, icon_name_len);
Dmitry Shmidtaa532512012-09-24 10:35:31 -0700887#endif /* CONFIG_HS20 */
Dmitry Shmidt04949592012-07-19 12:16:46 -0700888
889 return buf;
890}
891
892
Dmitry Shmidtd80a4012015-11-05 16:35:40 -0800893#define ANQP_MAX_EXTRA_REQ 20
894
Dmitry Shmidt04949592012-07-19 12:16:46 -0700895struct anqp_query_info {
896 unsigned int request;
Dmitry Shmidt61d9df32012-08-29 16:22:06 -0700897 const u8 *home_realm_query;
898 size_t home_realm_query_len;
Dmitry Shmidtf21452a2014-02-26 10:55:25 -0800899 const u8 *icon_name;
900 size_t icon_name_len;
Dmitry Shmidtfb45fd52015-01-05 13:08:17 -0800901 int p2p_sd;
Dmitry Shmidtd80a4012015-11-05 16:35:40 -0800902 u16 extra_req[ANQP_MAX_EXTRA_REQ];
903 unsigned int num_extra_req;
Dmitry Shmidt04949592012-07-19 12:16:46 -0700904};
905
906
907static void set_anqp_req(unsigned int bit, const char *name, int local,
Dmitry Shmidt04949592012-07-19 12:16:46 -0700908 struct anqp_query_info *qi)
909{
910 qi->request |= bit;
911 if (local) {
912 wpa_printf(MSG_DEBUG, "ANQP: %s (local)", name);
Dmitry Shmidt04949592012-07-19 12:16:46 -0700913 } else {
914 wpa_printf(MSG_DEBUG, "ANQP: %s not available", name);
915 }
916}
917
918
919static void rx_anqp_query_list_id(struct hostapd_data *hapd, u16 info_id,
920 struct anqp_query_info *qi)
921{
922 switch (info_id) {
923 case ANQP_CAPABILITY_LIST:
Dmitry Shmidtb36ed7c2014-03-17 10:57:26 -0700924 set_anqp_req(ANQP_REQ_CAPABILITY_LIST, "Capability List", 1,
925 qi);
Dmitry Shmidt04949592012-07-19 12:16:46 -0700926 break;
927 case ANQP_VENUE_NAME:
928 set_anqp_req(ANQP_REQ_VENUE_NAME, "Venue Name",
Dmitry Shmidtb36ed7c2014-03-17 10:57:26 -0700929 hapd->conf->venue_name != NULL, qi);
Dmitry Shmidt04949592012-07-19 12:16:46 -0700930 break;
Dmitry Shmidtd80a4012015-11-05 16:35:40 -0800931 case ANQP_EMERGENCY_CALL_NUMBER:
932 set_anqp_req(ANQP_REQ_EMERGENCY_CALL_NUMBER,
933 "Emergency Call Number",
934 get_anqp_elem(hapd, info_id) != NULL, qi);
935 break;
Dmitry Shmidt61d9df32012-08-29 16:22:06 -0700936 case ANQP_NETWORK_AUTH_TYPE:
937 set_anqp_req(ANQP_REQ_NETWORK_AUTH_TYPE, "Network Auth Type",
Dmitry Shmidtb36ed7c2014-03-17 10:57:26 -0700938 hapd->conf->network_auth_type != NULL, qi);
Dmitry Shmidt61d9df32012-08-29 16:22:06 -0700939 break;
Dmitry Shmidt04949592012-07-19 12:16:46 -0700940 case ANQP_ROAMING_CONSORTIUM:
941 set_anqp_req(ANQP_REQ_ROAMING_CONSORTIUM, "Roaming Consortium",
Dmitry Shmidtb36ed7c2014-03-17 10:57:26 -0700942 hapd->conf->roaming_consortium != NULL, qi);
Dmitry Shmidt04949592012-07-19 12:16:46 -0700943 break;
Dmitry Shmidt61d9df32012-08-29 16:22:06 -0700944 case ANQP_IP_ADDR_TYPE_AVAILABILITY:
945 set_anqp_req(ANQP_REQ_IP_ADDR_TYPE_AVAILABILITY,
946 "IP Addr Type Availability",
Dmitry Shmidtb36ed7c2014-03-17 10:57:26 -0700947 hapd->conf->ipaddr_type_configured, qi);
Dmitry Shmidt61d9df32012-08-29 16:22:06 -0700948 break;
949 case ANQP_NAI_REALM:
950 set_anqp_req(ANQP_REQ_NAI_REALM, "NAI Realm",
Dmitry Shmidtb36ed7c2014-03-17 10:57:26 -0700951 hapd->conf->nai_realm_data != NULL, qi);
Dmitry Shmidt61d9df32012-08-29 16:22:06 -0700952 break;
953 case ANQP_3GPP_CELLULAR_NETWORK:
954 set_anqp_req(ANQP_REQ_3GPP_CELLULAR_NETWORK,
955 "3GPP Cellular Network",
Dmitry Shmidtb36ed7c2014-03-17 10:57:26 -0700956 hapd->conf->anqp_3gpp_cell_net != NULL, qi);
Dmitry Shmidt61d9df32012-08-29 16:22:06 -0700957 break;
Dmitry Shmidtd80a4012015-11-05 16:35:40 -0800958 case ANQP_AP_GEOSPATIAL_LOCATION:
959 set_anqp_req(ANQP_REQ_AP_GEOSPATIAL_LOCATION,
960 "AP Geospatial Location",
961 get_anqp_elem(hapd, info_id) != NULL, qi);
962 break;
963 case ANQP_AP_CIVIC_LOCATION:
964 set_anqp_req(ANQP_REQ_AP_CIVIC_LOCATION,
965 "AP Civic Location",
966 get_anqp_elem(hapd, info_id) != NULL, qi);
967 break;
968 case ANQP_AP_LOCATION_PUBLIC_URI:
969 set_anqp_req(ANQP_REQ_AP_LOCATION_PUBLIC_URI,
970 "AP Location Public URI",
971 get_anqp_elem(hapd, info_id) != NULL, qi);
972 break;
Dmitry Shmidt61d9df32012-08-29 16:22:06 -0700973 case ANQP_DOMAIN_NAME:
974 set_anqp_req(ANQP_REQ_DOMAIN_NAME, "Domain Name",
Dmitry Shmidtb36ed7c2014-03-17 10:57:26 -0700975 hapd->conf->domain_name != NULL, qi);
Dmitry Shmidt61d9df32012-08-29 16:22:06 -0700976 break;
Dmitry Shmidtd80a4012015-11-05 16:35:40 -0800977 case ANQP_EMERGENCY_ALERT_URI:
978 set_anqp_req(ANQP_REQ_EMERGENCY_ALERT_URI,
979 "Emergency Alert URI",
980 get_anqp_elem(hapd, info_id) != NULL, qi);
981 break;
982 case ANQP_TDLS_CAPABILITY:
983 set_anqp_req(ANQP_REQ_TDLS_CAPABILITY,
984 "TDLS Capability",
985 get_anqp_elem(hapd, info_id) != NULL, qi);
986 break;
987 case ANQP_EMERGENCY_NAI:
988 set_anqp_req(ANQP_REQ_EMERGENCY_NAI,
989 "Emergency NAI",
990 get_anqp_elem(hapd, info_id) != NULL, qi);
991 break;
Dmitry Shmidt04949592012-07-19 12:16:46 -0700992 default:
Dmitry Shmidtd80a4012015-11-05 16:35:40 -0800993 if (!get_anqp_elem(hapd, info_id)) {
994 wpa_printf(MSG_DEBUG, "ANQP: Unsupported Info Id %u",
995 info_id);
996 break;
997 }
998 if (qi->num_extra_req == ANQP_MAX_EXTRA_REQ) {
999 wpa_printf(MSG_DEBUG,
1000 "ANQP: No more room for extra requests - ignore Info Id %u",
1001 info_id);
1002 break;
1003 }
1004 wpa_printf(MSG_DEBUG, "ANQP: Info Id %u (local)", info_id);
1005 qi->extra_req[qi->num_extra_req] = info_id;
1006 qi->num_extra_req++;
Dmitry Shmidt04949592012-07-19 12:16:46 -07001007 break;
1008 }
1009}
1010
1011
1012static void rx_anqp_query_list(struct hostapd_data *hapd,
1013 const u8 *pos, const u8 *end,
1014 struct anqp_query_info *qi)
1015{
1016 wpa_printf(MSG_DEBUG, "ANQP: %u Info IDs requested in Query list",
1017 (unsigned int) (end - pos) / 2);
1018
Dmitry Shmidtd80a4012015-11-05 16:35:40 -08001019 while (end - pos >= 2) {
Dmitry Shmidt04949592012-07-19 12:16:46 -07001020 rx_anqp_query_list_id(hapd, WPA_GET_LE16(pos), qi);
1021 pos += 2;
1022 }
1023}
1024
1025
Dmitry Shmidtaa532512012-09-24 10:35:31 -07001026#ifdef CONFIG_HS20
1027
Dmitry Shmidt61d9df32012-08-29 16:22:06 -07001028static void rx_anqp_hs_query_list(struct hostapd_data *hapd, u8 subtype,
1029 struct anqp_query_info *qi)
1030{
1031 switch (subtype) {
1032 case HS20_STYPE_CAPABILITY_LIST:
1033 set_anqp_req(ANQP_REQ_HS_CAPABILITY_LIST, "HS Capability List",
Dmitry Shmidtb36ed7c2014-03-17 10:57:26 -07001034 1, qi);
Dmitry Shmidt61d9df32012-08-29 16:22:06 -07001035 break;
1036 case HS20_STYPE_OPERATOR_FRIENDLY_NAME:
1037 set_anqp_req(ANQP_REQ_OPERATOR_FRIENDLY_NAME,
1038 "Operator Friendly Name",
Dmitry Shmidtb36ed7c2014-03-17 10:57:26 -07001039 hapd->conf->hs20_oper_friendly_name != NULL, qi);
Dmitry Shmidt61d9df32012-08-29 16:22:06 -07001040 break;
1041 case HS20_STYPE_WAN_METRICS:
1042 set_anqp_req(ANQP_REQ_WAN_METRICS, "WAN Metrics",
Dmitry Shmidtb36ed7c2014-03-17 10:57:26 -07001043 hapd->conf->hs20_wan_metrics != NULL, qi);
Dmitry Shmidt61d9df32012-08-29 16:22:06 -07001044 break;
1045 case HS20_STYPE_CONNECTION_CAPABILITY:
1046 set_anqp_req(ANQP_REQ_CONNECTION_CAPABILITY,
1047 "Connection Capability",
1048 hapd->conf->hs20_connection_capability != NULL,
Dmitry Shmidtb36ed7c2014-03-17 10:57:26 -07001049 qi);
Dmitry Shmidt61d9df32012-08-29 16:22:06 -07001050 break;
1051 case HS20_STYPE_OPERATING_CLASS:
1052 set_anqp_req(ANQP_REQ_OPERATING_CLASS, "Operating Class",
Dmitry Shmidtb36ed7c2014-03-17 10:57:26 -07001053 hapd->conf->hs20_operating_class != NULL, qi);
Dmitry Shmidt61d9df32012-08-29 16:22:06 -07001054 break;
Dmitry Shmidtf21452a2014-02-26 10:55:25 -08001055 case HS20_STYPE_OSU_PROVIDERS_LIST:
1056 set_anqp_req(ANQP_REQ_OSU_PROVIDERS_LIST, "OSU Providers list",
Dmitry Shmidtb36ed7c2014-03-17 10:57:26 -07001057 hapd->conf->hs20_osu_providers_count, qi);
Dmitry Shmidtf21452a2014-02-26 10:55:25 -08001058 break;
Dmitry Shmidt61d9df32012-08-29 16:22:06 -07001059 default:
1060 wpa_printf(MSG_DEBUG, "ANQP: Unsupported HS 2.0 subtype %u",
1061 subtype);
1062 break;
1063 }
1064}
1065
1066
1067static void rx_anqp_hs_nai_home_realm(struct hostapd_data *hapd,
1068 const u8 *pos, const u8 *end,
1069 struct anqp_query_info *qi)
1070{
1071 qi->request |= ANQP_REQ_NAI_HOME_REALM;
1072 qi->home_realm_query = pos;
1073 qi->home_realm_query_len = end - pos;
1074 if (hapd->conf->nai_realm_data != NULL) {
1075 wpa_printf(MSG_DEBUG, "ANQP: HS 2.0 NAI Home Realm Query "
1076 "(local)");
1077 } else {
1078 wpa_printf(MSG_DEBUG, "ANQP: HS 2.0 NAI Home Realm Query not "
1079 "available");
1080 }
1081}
1082
1083
Dmitry Shmidtf21452a2014-02-26 10:55:25 -08001084static void rx_anqp_hs_icon_request(struct hostapd_data *hapd,
1085 const u8 *pos, const u8 *end,
1086 struct anqp_query_info *qi)
1087{
1088 qi->request |= ANQP_REQ_ICON_REQUEST;
1089 qi->icon_name = pos;
1090 qi->icon_name_len = end - pos;
1091 if (hapd->conf->hs20_icons_count) {
1092 wpa_printf(MSG_DEBUG, "ANQP: HS 2.0 Icon Request Query "
1093 "(local)");
1094 } else {
1095 wpa_printf(MSG_DEBUG, "ANQP: HS 2.0 Icon Request Query not "
1096 "available");
1097 }
1098}
1099
1100
Dmitry Shmidt61d9df32012-08-29 16:22:06 -07001101static void rx_anqp_vendor_specific(struct hostapd_data *hapd,
1102 const u8 *pos, const u8 *end,
1103 struct anqp_query_info *qi)
1104{
1105 u32 oui;
1106 u8 subtype;
1107
Dmitry Shmidtd80a4012015-11-05 16:35:40 -08001108 if (end - pos < 4) {
Dmitry Shmidt61d9df32012-08-29 16:22:06 -07001109 wpa_printf(MSG_DEBUG, "ANQP: Too short vendor specific ANQP "
1110 "Query element");
1111 return;
1112 }
1113
1114 oui = WPA_GET_BE24(pos);
1115 pos += 3;
1116 if (oui != OUI_WFA) {
1117 wpa_printf(MSG_DEBUG, "ANQP: Unsupported vendor OUI %06x",
1118 oui);
1119 return;
1120 }
1121
Dmitry Shmidtfb45fd52015-01-05 13:08:17 -08001122#ifdef CONFIG_P2P
1123 if (*pos == P2P_OUI_TYPE) {
1124 /*
1125 * This is for P2P SD and will be taken care of by the P2P
1126 * implementation. This query needs to be ignored in the generic
1127 * GAS server to avoid duplicated response.
1128 */
1129 wpa_printf(MSG_DEBUG,
1130 "ANQP: Ignore WFA vendor type %u (P2P SD) in generic GAS server",
1131 *pos);
1132 qi->p2p_sd = 1;
1133 return;
1134 }
1135#endif /* CONFIG_P2P */
1136
Dmitry Shmidt61d9df32012-08-29 16:22:06 -07001137 if (*pos != HS20_ANQP_OUI_TYPE) {
1138 wpa_printf(MSG_DEBUG, "ANQP: Unsupported WFA vendor type %u",
1139 *pos);
1140 return;
1141 }
1142 pos++;
1143
Dmitry Shmidtd80a4012015-11-05 16:35:40 -08001144 if (end - pos <= 1)
Dmitry Shmidt61d9df32012-08-29 16:22:06 -07001145 return;
1146
1147 subtype = *pos++;
1148 pos++; /* Reserved */
1149 switch (subtype) {
1150 case HS20_STYPE_QUERY_LIST:
1151 wpa_printf(MSG_DEBUG, "ANQP: HS 2.0 Query List");
1152 while (pos < end) {
1153 rx_anqp_hs_query_list(hapd, *pos, qi);
1154 pos++;
1155 }
1156 break;
1157 case HS20_STYPE_NAI_HOME_REALM_QUERY:
1158 rx_anqp_hs_nai_home_realm(hapd, pos, end, qi);
1159 break;
Dmitry Shmidtf21452a2014-02-26 10:55:25 -08001160 case HS20_STYPE_ICON_REQUEST:
1161 rx_anqp_hs_icon_request(hapd, pos, end, qi);
1162 break;
Dmitry Shmidt61d9df32012-08-29 16:22:06 -07001163 default:
1164 wpa_printf(MSG_DEBUG, "ANQP: Unsupported HS 2.0 query subtype "
1165 "%u", subtype);
1166 break;
1167 }
1168}
1169
Dmitry Shmidtaa532512012-09-24 10:35:31 -07001170#endif /* CONFIG_HS20 */
1171
Dmitry Shmidt61d9df32012-08-29 16:22:06 -07001172
Dmitry Shmidt04949592012-07-19 12:16:46 -07001173static void gas_serv_req_local_processing(struct hostapd_data *hapd,
1174 const u8 *sa, u8 dialog_token,
Dmitry Shmidtd5ab1b52016-06-21 12:38:41 -07001175 struct anqp_query_info *qi, int prot,
1176 int std_addr3)
Dmitry Shmidt04949592012-07-19 12:16:46 -07001177{
1178 struct wpabuf *buf, *tx_buf;
1179
Dmitry Shmidt09f57ba2014-06-10 16:07:13 -07001180 buf = gas_serv_build_gas_resp_payload(hapd, qi->request,
Dmitry Shmidt61d9df32012-08-29 16:22:06 -07001181 qi->home_realm_query,
Dmitry Shmidtf21452a2014-02-26 10:55:25 -08001182 qi->home_realm_query_len,
Dmitry Shmidtd80a4012015-11-05 16:35:40 -08001183 qi->icon_name, qi->icon_name_len,
1184 qi->extra_req, qi->num_extra_req);
Dmitry Shmidt04949592012-07-19 12:16:46 -07001185 wpa_hexdump_buf(MSG_MSGDUMP, "ANQP: Locally generated ANQP responses",
1186 buf);
1187 if (!buf)
1188 return;
Dmitry Shmidtfb45fd52015-01-05 13:08:17 -08001189#ifdef CONFIG_P2P
1190 if (wpabuf_len(buf) == 0 && qi->p2p_sd) {
1191 wpa_printf(MSG_DEBUG,
1192 "ANQP: Do not send response to P2P SD from generic GAS service (P2P SD implementation will process this)");
1193 wpabuf_free(buf);
1194 return;
1195 }
1196#endif /* CONFIG_P2P */
Dmitry Shmidt04949592012-07-19 12:16:46 -07001197
1198 if (wpabuf_len(buf) > hapd->gas_frag_limit ||
1199 hapd->conf->gas_comeback_delay) {
1200 struct gas_dialog_info *di;
1201 u16 comeback_delay = 1;
1202
1203 if (hapd->conf->gas_comeback_delay) {
1204 /* Testing - allow overriding of the delay value */
1205 comeback_delay = hapd->conf->gas_comeback_delay;
1206 }
1207
1208 wpa_printf(MSG_DEBUG, "ANQP: Too long response to fit in "
1209 "initial response - use GAS comeback");
1210 di = gas_dialog_create(hapd, sa, dialog_token);
1211 if (!di) {
1212 wpa_printf(MSG_INFO, "ANQP: Could not create dialog "
1213 "for " MACSTR " (dialog token %u)",
1214 MAC2STR(sa), dialog_token);
1215 wpabuf_free(buf);
Dmitry Shmidt76cd2cc2014-05-27 12:56:04 -07001216 tx_buf = gas_anqp_build_initial_resp_buf(
1217 dialog_token, WLAN_STATUS_UNSPECIFIED_FAILURE,
1218 0, NULL);
1219 } else {
1220 di->prot = prot;
1221 di->sd_resp = buf;
1222 di->sd_resp_pos = 0;
1223 tx_buf = gas_anqp_build_initial_resp_buf(
1224 dialog_token, WLAN_STATUS_SUCCESS,
1225 comeback_delay, NULL);
Dmitry Shmidt04949592012-07-19 12:16:46 -07001226 }
Dmitry Shmidt04949592012-07-19 12:16:46 -07001227 } else {
1228 wpa_printf(MSG_DEBUG, "ANQP: Initial response (no comeback)");
1229 tx_buf = gas_anqp_build_initial_resp_buf(
1230 dialog_token, WLAN_STATUS_SUCCESS, 0, buf);
1231 wpabuf_free(buf);
1232 }
1233 if (!tx_buf)
1234 return;
Dmitry Shmidt18463232014-01-24 12:29:41 -08001235 if (prot)
1236 convert_to_protected_dual(tx_buf);
Dmitry Shmidtd5ab1b52016-06-21 12:38:41 -07001237 if (std_addr3)
1238 hostapd_drv_send_action(hapd, hapd->iface->freq, 0, sa,
1239 wpabuf_head(tx_buf),
1240 wpabuf_len(tx_buf));
1241 else
1242 hostapd_drv_send_action_addr3_ap(hapd, hapd->iface->freq, 0, sa,
1243 wpabuf_head(tx_buf),
1244 wpabuf_len(tx_buf));
Dmitry Shmidt04949592012-07-19 12:16:46 -07001245 wpabuf_free(tx_buf);
1246}
1247
1248
1249static void gas_serv_rx_gas_initial_req(struct hostapd_data *hapd,
1250 const u8 *sa,
Dmitry Shmidtd5ab1b52016-06-21 12:38:41 -07001251 const u8 *data, size_t len, int prot,
1252 int std_addr3)
Dmitry Shmidt04949592012-07-19 12:16:46 -07001253{
1254 const u8 *pos = data;
1255 const u8 *end = data + len;
1256 const u8 *next;
1257 u8 dialog_token;
1258 u16 slen;
1259 struct anqp_query_info qi;
1260 const u8 *adv_proto;
1261
1262 if (len < 1 + 2)
1263 return;
1264
1265 os_memset(&qi, 0, sizeof(qi));
1266
1267 dialog_token = *pos++;
1268 wpa_msg(hapd->msg_ctx, MSG_DEBUG,
1269 "GAS: GAS Initial Request from " MACSTR " (dialog token %u) ",
1270 MAC2STR(sa), dialog_token);
1271
1272 if (*pos != WLAN_EID_ADV_PROTO) {
1273 wpa_msg(hapd->msg_ctx, MSG_DEBUG,
1274 "GAS: Unexpected IE in GAS Initial Request: %u", *pos);
1275 return;
1276 }
1277 adv_proto = pos++;
1278
1279 slen = *pos++;
Dmitry Shmidtd80a4012015-11-05 16:35:40 -08001280 if (slen > end - pos || slen < 2) {
Dmitry Shmidt04949592012-07-19 12:16:46 -07001281 wpa_msg(hapd->msg_ctx, MSG_DEBUG,
1282 "GAS: Invalid IE in GAS Initial Request");
1283 return;
1284 }
Dmitry Shmidtd80a4012015-11-05 16:35:40 -08001285 next = pos + slen;
Dmitry Shmidt04949592012-07-19 12:16:46 -07001286 pos++; /* skip QueryRespLenLimit and PAME-BI */
1287
1288 if (*pos != ACCESS_NETWORK_QUERY_PROTOCOL) {
1289 struct wpabuf *buf;
1290 wpa_msg(hapd->msg_ctx, MSG_DEBUG,
1291 "GAS: Unsupported GAS advertisement protocol id %u",
1292 *pos);
1293 if (sa[0] & 0x01)
1294 return; /* Invalid source address - drop silently */
1295 buf = gas_build_initial_resp(
1296 dialog_token, WLAN_STATUS_GAS_ADV_PROTO_NOT_SUPPORTED,
1297 0, 2 + slen + 2);
1298 if (buf == NULL)
1299 return;
1300 wpabuf_put_data(buf, adv_proto, 2 + slen);
1301 wpabuf_put_le16(buf, 0); /* Query Response Length */
Dmitry Shmidt18463232014-01-24 12:29:41 -08001302 if (prot)
1303 convert_to_protected_dual(buf);
Dmitry Shmidtd5ab1b52016-06-21 12:38:41 -07001304 if (std_addr3)
1305 hostapd_drv_send_action(hapd, hapd->iface->freq, 0, sa,
1306 wpabuf_head(buf),
1307 wpabuf_len(buf));
1308 else
1309 hostapd_drv_send_action_addr3_ap(hapd,
1310 hapd->iface->freq, 0,
1311 sa, wpabuf_head(buf),
1312 wpabuf_len(buf));
Dmitry Shmidt04949592012-07-19 12:16:46 -07001313 wpabuf_free(buf);
1314 return;
1315 }
1316
1317 pos = next;
1318 /* Query Request */
Dmitry Shmidtd80a4012015-11-05 16:35:40 -08001319 if (end - pos < 2)
Dmitry Shmidt04949592012-07-19 12:16:46 -07001320 return;
1321 slen = WPA_GET_LE16(pos);
1322 pos += 2;
Dmitry Shmidtd80a4012015-11-05 16:35:40 -08001323 if (slen > end - pos)
Dmitry Shmidt04949592012-07-19 12:16:46 -07001324 return;
1325 end = pos + slen;
1326
1327 /* ANQP Query Request */
1328 while (pos < end) {
1329 u16 info_id, elen;
1330
Dmitry Shmidtd80a4012015-11-05 16:35:40 -08001331 if (end - pos < 4)
Dmitry Shmidt04949592012-07-19 12:16:46 -07001332 return;
1333
1334 info_id = WPA_GET_LE16(pos);
1335 pos += 2;
1336 elen = WPA_GET_LE16(pos);
1337 pos += 2;
1338
Dmitry Shmidtd80a4012015-11-05 16:35:40 -08001339 if (elen > end - pos) {
Dmitry Shmidt04949592012-07-19 12:16:46 -07001340 wpa_printf(MSG_DEBUG, "ANQP: Invalid Query Request");
1341 return;
1342 }
1343
1344 switch (info_id) {
1345 case ANQP_QUERY_LIST:
1346 rx_anqp_query_list(hapd, pos, pos + elen, &qi);
1347 break;
Dmitry Shmidtaa532512012-09-24 10:35:31 -07001348#ifdef CONFIG_HS20
Dmitry Shmidt61d9df32012-08-29 16:22:06 -07001349 case ANQP_VENDOR_SPECIFIC:
1350 rx_anqp_vendor_specific(hapd, pos, pos + elen, &qi);
1351 break;
Dmitry Shmidtaa532512012-09-24 10:35:31 -07001352#endif /* CONFIG_HS20 */
Dmitry Shmidt04949592012-07-19 12:16:46 -07001353 default:
1354 wpa_printf(MSG_DEBUG, "ANQP: Unsupported Query "
1355 "Request element %u", info_id);
1356 break;
1357 }
1358
1359 pos += elen;
1360 }
1361
Dmitry Shmidtd5ab1b52016-06-21 12:38:41 -07001362 gas_serv_req_local_processing(hapd, sa, dialog_token, &qi, prot,
1363 std_addr3);
Dmitry Shmidt04949592012-07-19 12:16:46 -07001364}
1365
1366
Dmitry Shmidt04949592012-07-19 12:16:46 -07001367static void gas_serv_rx_gas_comeback_req(struct hostapd_data *hapd,
1368 const u8 *sa,
Dmitry Shmidtd5ab1b52016-06-21 12:38:41 -07001369 const u8 *data, size_t len, int prot,
1370 int std_addr3)
Dmitry Shmidt04949592012-07-19 12:16:46 -07001371{
1372 struct gas_dialog_info *dialog;
1373 struct wpabuf *buf, *tx_buf;
1374 u8 dialog_token;
1375 size_t frag_len;
1376 int more = 0;
1377
1378 wpa_hexdump(MSG_DEBUG, "GAS: RX GAS Comeback Request", data, len);
1379 if (len < 1)
1380 return;
1381 dialog_token = *data;
1382 wpa_msg(hapd->msg_ctx, MSG_DEBUG, "GAS: Dialog Token: %u",
1383 dialog_token);
1384
1385 dialog = gas_serv_dialog_find(hapd, sa, dialog_token);
1386 if (!dialog) {
1387 wpa_msg(hapd->msg_ctx, MSG_DEBUG, "GAS: No pending SD "
1388 "response fragment for " MACSTR " dialog token %u",
1389 MAC2STR(sa), dialog_token);
1390
1391 if (sa[0] & 0x01)
1392 return; /* Invalid source address - drop silently */
1393 tx_buf = gas_anqp_build_comeback_resp_buf(
1394 dialog_token, WLAN_STATUS_NO_OUTSTANDING_GAS_REQ, 0, 0,
1395 0, NULL);
1396 if (tx_buf == NULL)
1397 return;
1398 goto send_resp;
1399 }
1400
Dmitry Shmidt04949592012-07-19 12:16:46 -07001401 frag_len = wpabuf_len(dialog->sd_resp) - dialog->sd_resp_pos;
1402 if (frag_len > hapd->gas_frag_limit) {
1403 frag_len = hapd->gas_frag_limit;
1404 more = 1;
1405 }
1406 wpa_msg(hapd->msg_ctx, MSG_DEBUG, "GAS: resp frag_len %u",
1407 (unsigned int) frag_len);
1408 buf = wpabuf_alloc_copy(wpabuf_head_u8(dialog->sd_resp) +
1409 dialog->sd_resp_pos, frag_len);
1410 if (buf == NULL) {
1411 wpa_msg(hapd->msg_ctx, MSG_DEBUG, "GAS: Failed to allocate "
1412 "buffer");
Dmitry Shmidtb36ed7c2014-03-17 10:57:26 -07001413 gas_serv_dialog_clear(dialog);
1414 return;
Dmitry Shmidt04949592012-07-19 12:16:46 -07001415 }
1416 tx_buf = gas_anqp_build_comeback_resp_buf(dialog_token,
1417 WLAN_STATUS_SUCCESS,
1418 dialog->sd_frag_id,
1419 more, 0, buf);
1420 wpabuf_free(buf);
Dmitry Shmidtb36ed7c2014-03-17 10:57:26 -07001421 if (tx_buf == NULL) {
1422 gas_serv_dialog_clear(dialog);
1423 return;
1424 }
Dmitry Shmidt04949592012-07-19 12:16:46 -07001425 wpa_msg(hapd->msg_ctx, MSG_DEBUG, "GAS: Tx GAS Comeback Response "
1426 "(frag_id %d more=%d frag_len=%d)",
1427 dialog->sd_frag_id, more, (int) frag_len);
1428 dialog->sd_frag_id++;
1429 dialog->sd_resp_pos += frag_len;
1430
1431 if (more) {
1432 wpa_msg(hapd->msg_ctx, MSG_DEBUG, "GAS: %d more bytes remain "
1433 "to be sent",
1434 (int) (wpabuf_len(dialog->sd_resp) -
1435 dialog->sd_resp_pos));
1436 } else {
1437 wpa_msg(hapd->msg_ctx, MSG_DEBUG, "GAS: All fragments of "
1438 "SD response sent");
1439 gas_serv_dialog_clear(dialog);
1440 gas_serv_free_dialogs(hapd, sa);
1441 }
1442
1443send_resp:
Dmitry Shmidt18463232014-01-24 12:29:41 -08001444 if (prot)
1445 convert_to_protected_dual(tx_buf);
Dmitry Shmidtd5ab1b52016-06-21 12:38:41 -07001446 if (std_addr3)
1447 hostapd_drv_send_action(hapd, hapd->iface->freq, 0, sa,
1448 wpabuf_head(tx_buf),
1449 wpabuf_len(tx_buf));
1450 else
1451 hostapd_drv_send_action_addr3_ap(hapd, hapd->iface->freq, 0, sa,
1452 wpabuf_head(tx_buf),
1453 wpabuf_len(tx_buf));
Dmitry Shmidt04949592012-07-19 12:16:46 -07001454 wpabuf_free(tx_buf);
Dmitry Shmidt04949592012-07-19 12:16:46 -07001455}
1456
1457
1458static void gas_serv_rx_public_action(void *ctx, const u8 *buf, size_t len,
1459 int freq)
1460{
1461 struct hostapd_data *hapd = ctx;
1462 const struct ieee80211_mgmt *mgmt;
Dmitry Shmidt04949592012-07-19 12:16:46 -07001463 const u8 *sa, *data;
Dmitry Shmidtd5ab1b52016-06-21 12:38:41 -07001464 int prot, std_addr3;
Dmitry Shmidt04949592012-07-19 12:16:46 -07001465
1466 mgmt = (const struct ieee80211_mgmt *) buf;
Dmitry Shmidt623d63a2014-06-13 11:05:14 -07001467 if (len < IEEE80211_HDRLEN + 2)
Dmitry Shmidt04949592012-07-19 12:16:46 -07001468 return;
Dmitry Shmidt18463232014-01-24 12:29:41 -08001469 if (mgmt->u.action.category != WLAN_ACTION_PUBLIC &&
1470 mgmt->u.action.category != WLAN_ACTION_PROTECTED_DUAL)
Dmitry Shmidt04949592012-07-19 12:16:46 -07001471 return;
Dmitry Shmidt18463232014-01-24 12:29:41 -08001472 /*
1473 * Note: Public Action and Protected Dual of Public Action frames share
1474 * the same payload structure, so it is fine to use definitions of
1475 * Public Action frames to process both.
1476 */
1477 prot = mgmt->u.action.category == WLAN_ACTION_PROTECTED_DUAL;
Dmitry Shmidt04949592012-07-19 12:16:46 -07001478 sa = mgmt->sa;
Dmitry Shmidtd5ab1b52016-06-21 12:38:41 -07001479 if (hapd->conf->gas_address3 == 1)
1480 std_addr3 = 1;
1481 else if (hapd->conf->gas_address3 == 2)
1482 std_addr3 = 0;
1483 else
1484 std_addr3 = is_broadcast_ether_addr(mgmt->bssid);
Dmitry Shmidt623d63a2014-06-13 11:05:14 -07001485 len -= IEEE80211_HDRLEN + 1;
1486 data = buf + IEEE80211_HDRLEN + 1;
Dmitry Shmidt04949592012-07-19 12:16:46 -07001487 switch (data[0]) {
1488 case WLAN_PA_GAS_INITIAL_REQ:
Dmitry Shmidtd5ab1b52016-06-21 12:38:41 -07001489 gas_serv_rx_gas_initial_req(hapd, sa, data + 1, len - 1, prot,
1490 std_addr3);
Dmitry Shmidt04949592012-07-19 12:16:46 -07001491 break;
1492 case WLAN_PA_GAS_COMEBACK_REQ:
Dmitry Shmidtd5ab1b52016-06-21 12:38:41 -07001493 gas_serv_rx_gas_comeback_req(hapd, sa, data + 1, len - 1, prot,
1494 std_addr3);
Dmitry Shmidt04949592012-07-19 12:16:46 -07001495 break;
1496 }
1497}
1498
1499
1500int gas_serv_init(struct hostapd_data *hapd)
1501{
Dmitry Shmidt4b9d52f2013-02-05 17:44:43 -08001502 hapd->public_action_cb2 = gas_serv_rx_public_action;
1503 hapd->public_action_cb2_ctx = hapd;
Dmitry Shmidt04949592012-07-19 12:16:46 -07001504 hapd->gas_frag_limit = 1400;
1505 if (hapd->conf->gas_frag_limit > 0)
1506 hapd->gas_frag_limit = hapd->conf->gas_frag_limit;
1507 return 0;
1508}
1509
1510
1511void gas_serv_deinit(struct hostapd_data *hapd)
1512{
1513}