blob: 4bcdf6f92cd700be796ad8a091663c862748baae [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;
104 return &sta->gas_dialog[i];
105 }
106 wpa_printf(MSG_DEBUG, "ANQP: Could not find dialog for "
107 MACSTR " dialog_token %u", MAC2STR(addr), dialog_token);
108 return NULL;
109}
110
111
112void gas_serv_dialog_clear(struct gas_dialog_info *dia)
113{
114 wpabuf_free(dia->sd_resp);
115 os_memset(dia, 0, sizeof(*dia));
116}
117
118
119static 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 sta = ap_get_sta(hapd, sta_addr);
126 if (sta == NULL || sta->gas_dialog == NULL)
127 return;
128
129 for (i = 0; i < GAS_DIALOG_MAX; i++) {
130 if (sta->gas_dialog[i].valid)
131 return;
132 }
133
134 os_free(sta->gas_dialog);
135 sta->gas_dialog = NULL;
136}
137
138
Dmitry Shmidtaa532512012-09-24 10:35:31 -0700139#ifdef CONFIG_HS20
Dmitry Shmidt61d9df32012-08-29 16:22:06 -0700140static void anqp_add_hs_capab_list(struct hostapd_data *hapd,
141 struct wpabuf *buf)
142{
143 u8 *len;
144
145 len = gas_anqp_add_element(buf, ANQP_VENDOR_SPECIFIC);
146 wpabuf_put_be24(buf, OUI_WFA);
147 wpabuf_put_u8(buf, HS20_ANQP_OUI_TYPE);
148 wpabuf_put_u8(buf, HS20_STYPE_CAPABILITY_LIST);
149 wpabuf_put_u8(buf, 0); /* Reserved */
150 wpabuf_put_u8(buf, HS20_STYPE_CAPABILITY_LIST);
151 if (hapd->conf->hs20_oper_friendly_name)
152 wpabuf_put_u8(buf, HS20_STYPE_OPERATOR_FRIENDLY_NAME);
153 if (hapd->conf->hs20_wan_metrics)
154 wpabuf_put_u8(buf, HS20_STYPE_WAN_METRICS);
155 if (hapd->conf->hs20_connection_capability)
156 wpabuf_put_u8(buf, HS20_STYPE_CONNECTION_CAPABILITY);
157 if (hapd->conf->nai_realm_data)
158 wpabuf_put_u8(buf, HS20_STYPE_NAI_HOME_REALM_QUERY);
159 if (hapd->conf->hs20_operating_class)
160 wpabuf_put_u8(buf, HS20_STYPE_OPERATING_CLASS);
Dmitry Shmidtf21452a2014-02-26 10:55:25 -0800161 if (hapd->conf->hs20_osu_providers_count)
162 wpabuf_put_u8(buf, HS20_STYPE_OSU_PROVIDERS_LIST);
163 if (hapd->conf->hs20_icons_count)
164 wpabuf_put_u8(buf, HS20_STYPE_ICON_REQUEST);
Dmitry Shmidt61d9df32012-08-29 16:22:06 -0700165 gas_anqp_set_element_len(buf, len);
166}
Dmitry Shmidtaa532512012-09-24 10:35:31 -0700167#endif /* CONFIG_HS20 */
Dmitry Shmidt61d9df32012-08-29 16:22:06 -0700168
169
Dmitry Shmidtd80a4012015-11-05 16:35:40 -0800170static struct anqp_element * get_anqp_elem(struct hostapd_data *hapd,
171 u16 infoid)
172{
173 struct anqp_element *elem;
174
175 dl_list_for_each(elem, &hapd->conf->anqp_elem, struct anqp_element,
176 list) {
177 if (elem->infoid == infoid)
178 return elem;
179 }
180
181 return NULL;
182}
183
184
185static void anqp_add_elem(struct hostapd_data *hapd, struct wpabuf *buf,
186 u16 infoid)
187{
188 struct anqp_element *elem;
189
190 elem = get_anqp_elem(hapd, infoid);
191 if (!elem)
192 return;
193 if (wpabuf_tailroom(buf) < 2 + 2 + wpabuf_len(elem->payload)) {
194 wpa_printf(MSG_DEBUG, "ANQP: No room for InfoID %u payload",
195 infoid);
196 return;
197 }
198
199 wpabuf_put_le16(buf, infoid);
200 wpabuf_put_le16(buf, wpabuf_len(elem->payload));
201 wpabuf_put_buf(buf, elem->payload);
202}
203
204
205static int anqp_add_override(struct hostapd_data *hapd, struct wpabuf *buf,
206 u16 infoid)
207{
208 if (get_anqp_elem(hapd, infoid)) {
209 anqp_add_elem(hapd, buf, infoid);
210 return 1;
211 }
212
213 return 0;
214}
215
216
Dmitry Shmidt04949592012-07-19 12:16:46 -0700217static void anqp_add_capab_list(struct hostapd_data *hapd,
218 struct wpabuf *buf)
219{
220 u8 *len;
Dmitry Shmidtd80a4012015-11-05 16:35:40 -0800221 u16 id;
222
223 if (anqp_add_override(hapd, buf, ANQP_CAPABILITY_LIST))
224 return;
Dmitry Shmidt04949592012-07-19 12:16:46 -0700225
226 len = gas_anqp_add_element(buf, ANQP_CAPABILITY_LIST);
227 wpabuf_put_le16(buf, ANQP_CAPABILITY_LIST);
Dmitry Shmidtd80a4012015-11-05 16:35:40 -0800228 if (hapd->conf->venue_name || get_anqp_elem(hapd, ANQP_VENUE_NAME))
Dmitry Shmidt04949592012-07-19 12:16:46 -0700229 wpabuf_put_le16(buf, ANQP_VENUE_NAME);
Dmitry Shmidtd80a4012015-11-05 16:35:40 -0800230 if (get_anqp_elem(hapd, ANQP_EMERGENCY_CALL_NUMBER))
231 wpabuf_put_le16(buf, ANQP_EMERGENCY_CALL_NUMBER);
232 if (hapd->conf->network_auth_type ||
233 get_anqp_elem(hapd, ANQP_NETWORK_AUTH_TYPE))
Dmitry Shmidt61d9df32012-08-29 16:22:06 -0700234 wpabuf_put_le16(buf, ANQP_NETWORK_AUTH_TYPE);
Dmitry Shmidtd80a4012015-11-05 16:35:40 -0800235 if (hapd->conf->roaming_consortium ||
236 get_anqp_elem(hapd, ANQP_ROAMING_CONSORTIUM))
Dmitry Shmidt04949592012-07-19 12:16:46 -0700237 wpabuf_put_le16(buf, ANQP_ROAMING_CONSORTIUM);
Dmitry Shmidtd80a4012015-11-05 16:35:40 -0800238 if (hapd->conf->ipaddr_type_configured ||
239 get_anqp_elem(hapd, ANQP_IP_ADDR_TYPE_AVAILABILITY))
Dmitry Shmidt61d9df32012-08-29 16:22:06 -0700240 wpabuf_put_le16(buf, ANQP_IP_ADDR_TYPE_AVAILABILITY);
Dmitry Shmidtd80a4012015-11-05 16:35:40 -0800241 if (hapd->conf->nai_realm_data ||
242 get_anqp_elem(hapd, ANQP_NAI_REALM))
Dmitry Shmidt61d9df32012-08-29 16:22:06 -0700243 wpabuf_put_le16(buf, ANQP_NAI_REALM);
Dmitry Shmidtd80a4012015-11-05 16:35:40 -0800244 if (hapd->conf->anqp_3gpp_cell_net ||
245 get_anqp_elem(hapd, ANQP_3GPP_CELLULAR_NETWORK))
Dmitry Shmidt61d9df32012-08-29 16:22:06 -0700246 wpabuf_put_le16(buf, ANQP_3GPP_CELLULAR_NETWORK);
Dmitry Shmidtd80a4012015-11-05 16:35:40 -0800247 if (get_anqp_elem(hapd, ANQP_AP_GEOSPATIAL_LOCATION))
248 wpabuf_put_le16(buf, ANQP_AP_GEOSPATIAL_LOCATION);
249 if (get_anqp_elem(hapd, ANQP_AP_CIVIC_LOCATION))
250 wpabuf_put_le16(buf, ANQP_AP_CIVIC_LOCATION);
251 if (get_anqp_elem(hapd, ANQP_AP_LOCATION_PUBLIC_URI))
252 wpabuf_put_le16(buf, ANQP_AP_LOCATION_PUBLIC_URI);
253 if (hapd->conf->domain_name || get_anqp_elem(hapd, ANQP_DOMAIN_NAME))
Dmitry Shmidt61d9df32012-08-29 16:22:06 -0700254 wpabuf_put_le16(buf, ANQP_DOMAIN_NAME);
Dmitry Shmidtd80a4012015-11-05 16:35:40 -0800255 if (get_anqp_elem(hapd, ANQP_EMERGENCY_ALERT_URI))
256 wpabuf_put_le16(buf, ANQP_EMERGENCY_ALERT_URI);
257 if (get_anqp_elem(hapd, ANQP_EMERGENCY_NAI))
258 wpabuf_put_le16(buf, ANQP_EMERGENCY_NAI);
259 if (get_anqp_elem(hapd, ANQP_NEIGHBOR_REPORT))
260 wpabuf_put_le16(buf, ANQP_NEIGHBOR_REPORT);
261 for (id = 273; id < 277; id++) {
262 if (get_anqp_elem(hapd, id))
263 wpabuf_put_le16(buf, id);
264 }
265 if (get_anqp_elem(hapd, ANQP_VENUE_URL))
266 wpabuf_put_le16(buf, ANQP_VENUE_URL);
267 if (get_anqp_elem(hapd, ANQP_ADVICE_OF_CHARGE))
268 wpabuf_put_le16(buf, ANQP_ADVICE_OF_CHARGE);
269 if (get_anqp_elem(hapd, ANQP_LOCAL_CONTENT))
270 wpabuf_put_le16(buf, ANQP_LOCAL_CONTENT);
Dmitry Shmidtaa532512012-09-24 10:35:31 -0700271#ifdef CONFIG_HS20
Dmitry Shmidt61d9df32012-08-29 16:22:06 -0700272 anqp_add_hs_capab_list(hapd, buf);
Dmitry Shmidtaa532512012-09-24 10:35:31 -0700273#endif /* CONFIG_HS20 */
Dmitry Shmidt04949592012-07-19 12:16:46 -0700274 gas_anqp_set_element_len(buf, len);
275}
276
277
278static void anqp_add_venue_name(struct hostapd_data *hapd, struct wpabuf *buf)
279{
Dmitry Shmidtd80a4012015-11-05 16:35:40 -0800280 if (anqp_add_override(hapd, buf, ANQP_VENUE_NAME))
281 return;
282
Dmitry Shmidt04949592012-07-19 12:16:46 -0700283 if (hapd->conf->venue_name) {
284 u8 *len;
285 unsigned int i;
286 len = gas_anqp_add_element(buf, ANQP_VENUE_NAME);
287 wpabuf_put_u8(buf, hapd->conf->venue_group);
288 wpabuf_put_u8(buf, hapd->conf->venue_type);
289 for (i = 0; i < hapd->conf->venue_name_count; i++) {
Dmitry Shmidt61d9df32012-08-29 16:22:06 -0700290 struct hostapd_lang_string *vn;
Dmitry Shmidt04949592012-07-19 12:16:46 -0700291 vn = &hapd->conf->venue_name[i];
292 wpabuf_put_u8(buf, 3 + vn->name_len);
293 wpabuf_put_data(buf, vn->lang, 3);
294 wpabuf_put_data(buf, vn->name, vn->name_len);
295 }
296 gas_anqp_set_element_len(buf, len);
297 }
298}
299
300
Dmitry Shmidt61d9df32012-08-29 16:22:06 -0700301static void anqp_add_network_auth_type(struct hostapd_data *hapd,
302 struct wpabuf *buf)
303{
Dmitry Shmidtd80a4012015-11-05 16:35:40 -0800304 if (anqp_add_override(hapd, buf, ANQP_NETWORK_AUTH_TYPE))
305 return;
306
Dmitry Shmidt61d9df32012-08-29 16:22:06 -0700307 if (hapd->conf->network_auth_type) {
308 wpabuf_put_le16(buf, ANQP_NETWORK_AUTH_TYPE);
309 wpabuf_put_le16(buf, hapd->conf->network_auth_type_len);
310 wpabuf_put_data(buf, hapd->conf->network_auth_type,
311 hapd->conf->network_auth_type_len);
312 }
313}
314
315
Dmitry Shmidt04949592012-07-19 12:16:46 -0700316static void anqp_add_roaming_consortium(struct hostapd_data *hapd,
317 struct wpabuf *buf)
318{
319 unsigned int i;
320 u8 *len;
321
Dmitry Shmidtd80a4012015-11-05 16:35:40 -0800322 if (anqp_add_override(hapd, buf, ANQP_ROAMING_CONSORTIUM))
323 return;
324
Dmitry Shmidt04949592012-07-19 12:16:46 -0700325 len = gas_anqp_add_element(buf, ANQP_ROAMING_CONSORTIUM);
326 for (i = 0; i < hapd->conf->roaming_consortium_count; i++) {
327 struct hostapd_roaming_consortium *rc;
328 rc = &hapd->conf->roaming_consortium[i];
329 wpabuf_put_u8(buf, rc->len);
330 wpabuf_put_data(buf, rc->oi, rc->len);
331 }
332 gas_anqp_set_element_len(buf, len);
333}
334
335
Dmitry Shmidt61d9df32012-08-29 16:22:06 -0700336static void anqp_add_ip_addr_type_availability(struct hostapd_data *hapd,
337 struct wpabuf *buf)
338{
Dmitry Shmidtd80a4012015-11-05 16:35:40 -0800339 if (anqp_add_override(hapd, buf, ANQP_IP_ADDR_TYPE_AVAILABILITY))
340 return;
341
Dmitry Shmidt61d9df32012-08-29 16:22:06 -0700342 if (hapd->conf->ipaddr_type_configured) {
343 wpabuf_put_le16(buf, ANQP_IP_ADDR_TYPE_AVAILABILITY);
344 wpabuf_put_le16(buf, 1);
345 wpabuf_put_u8(buf, hapd->conf->ipaddr_type_availability);
346 }
347}
348
349
350static void anqp_add_nai_realm_eap(struct wpabuf *buf,
351 struct hostapd_nai_realm_data *realm)
352{
353 unsigned int i, j;
354
355 wpabuf_put_u8(buf, realm->eap_method_count);
356
357 for (i = 0; i < realm->eap_method_count; i++) {
358 struct hostapd_nai_realm_eap *eap = &realm->eap_method[i];
359 wpabuf_put_u8(buf, 2 + (3 * eap->num_auths));
360 wpabuf_put_u8(buf, eap->eap_method);
361 wpabuf_put_u8(buf, eap->num_auths);
362 for (j = 0; j < eap->num_auths; j++) {
363 wpabuf_put_u8(buf, eap->auth_id[j]);
364 wpabuf_put_u8(buf, 1);
365 wpabuf_put_u8(buf, eap->auth_val[j]);
366 }
367 }
368}
369
370
371static void anqp_add_nai_realm_data(struct wpabuf *buf,
372 struct hostapd_nai_realm_data *realm,
373 unsigned int realm_idx)
374{
375 u8 *realm_data_len;
376
377 wpa_printf(MSG_DEBUG, "realm=%s, len=%d", realm->realm[realm_idx],
378 (int) os_strlen(realm->realm[realm_idx]));
379 realm_data_len = wpabuf_put(buf, 2);
380 wpabuf_put_u8(buf, realm->encoding);
381 wpabuf_put_u8(buf, os_strlen(realm->realm[realm_idx]));
382 wpabuf_put_str(buf, realm->realm[realm_idx]);
383 anqp_add_nai_realm_eap(buf, realm);
384 gas_anqp_set_element_len(buf, realm_data_len);
385}
386
387
388static int hs20_add_nai_home_realm_matches(struct hostapd_data *hapd,
389 struct wpabuf *buf,
390 const u8 *home_realm,
391 size_t home_realm_len)
392{
393 unsigned int i, j, k;
394 u8 num_realms, num_matching = 0, encoding, realm_len, *realm_list_len;
395 struct hostapd_nai_realm_data *realm;
396 const u8 *pos, *realm_name, *end;
397 struct {
398 unsigned int realm_data_idx;
399 unsigned int realm_idx;
400 } matches[10];
401
402 pos = home_realm;
403 end = pos + home_realm_len;
Dmitry Shmidtd80a4012015-11-05 16:35:40 -0800404 if (end - pos < 1) {
Dmitry Shmidt61d9df32012-08-29 16:22:06 -0700405 wpa_hexdump(MSG_DEBUG, "Too short NAI Home Realm Query",
406 home_realm, home_realm_len);
407 return -1;
408 }
409 num_realms = *pos++;
410
411 for (i = 0; i < num_realms && num_matching < 10; i++) {
Dmitry Shmidtd80a4012015-11-05 16:35:40 -0800412 if (end - pos < 2) {
Dmitry Shmidt61d9df32012-08-29 16:22:06 -0700413 wpa_hexdump(MSG_DEBUG,
414 "Truncated NAI Home Realm Query",
415 home_realm, home_realm_len);
416 return -1;
417 }
418 encoding = *pos++;
419 realm_len = *pos++;
Dmitry Shmidtd80a4012015-11-05 16:35:40 -0800420 if (realm_len > end - pos) {
Dmitry Shmidt61d9df32012-08-29 16:22:06 -0700421 wpa_hexdump(MSG_DEBUG,
422 "Truncated NAI Home Realm Query",
423 home_realm, home_realm_len);
424 return -1;
425 }
426 realm_name = pos;
427 for (j = 0; j < hapd->conf->nai_realm_count &&
428 num_matching < 10; j++) {
429 const u8 *rpos, *rend;
430 realm = &hapd->conf->nai_realm_data[j];
431 if (encoding != realm->encoding)
432 continue;
433
434 rpos = realm_name;
435 while (rpos < realm_name + realm_len &&
436 num_matching < 10) {
437 for (rend = rpos;
438 rend < realm_name + realm_len; rend++) {
439 if (*rend == ';')
440 break;
441 }
442 for (k = 0; k < MAX_NAI_REALMS &&
443 realm->realm[k] &&
444 num_matching < 10; k++) {
445 if ((int) os_strlen(realm->realm[k]) !=
446 rend - rpos ||
447 os_strncmp((char *) rpos,
448 realm->realm[k],
449 rend - rpos) != 0)
450 continue;
451 matches[num_matching].realm_data_idx =
452 j;
453 matches[num_matching].realm_idx = k;
454 num_matching++;
455 }
456 rpos = rend + 1;
457 }
458 }
459 pos += realm_len;
460 }
461
462 realm_list_len = gas_anqp_add_element(buf, ANQP_NAI_REALM);
463 wpabuf_put_le16(buf, num_matching);
464
465 /*
466 * There are two ways to format. 1. each realm in a NAI Realm Data unit
467 * 2. all realms that share the same EAP methods in a NAI Realm Data
468 * unit. The first format is likely to be bigger in size than the
469 * second, but may be easier to parse and process by the receiver.
470 */
471 for (i = 0; i < num_matching; i++) {
472 wpa_printf(MSG_DEBUG, "realm_idx %d, realm_data_idx %d",
473 matches[i].realm_data_idx, matches[i].realm_idx);
474 realm = &hapd->conf->nai_realm_data[matches[i].realm_data_idx];
475 anqp_add_nai_realm_data(buf, realm, matches[i].realm_idx);
476 }
477 gas_anqp_set_element_len(buf, realm_list_len);
478 return 0;
479}
480
481
482static void anqp_add_nai_realm(struct hostapd_data *hapd, struct wpabuf *buf,
483 const u8 *home_realm, size_t home_realm_len,
484 int nai_realm, int nai_home_realm)
485{
Dmitry Shmidtd80a4012015-11-05 16:35:40 -0800486 if (nai_realm && !nai_home_realm &&
487 anqp_add_override(hapd, buf, ANQP_NAI_REALM))
488 return;
489
Dmitry Shmidt61d9df32012-08-29 16:22:06 -0700490 if (nai_realm && hapd->conf->nai_realm_data) {
491 u8 *len;
492 unsigned int i, j;
493 len = gas_anqp_add_element(buf, ANQP_NAI_REALM);
494 wpabuf_put_le16(buf, hapd->conf->nai_realm_count);
495 for (i = 0; i < hapd->conf->nai_realm_count; i++) {
496 u8 *realm_data_len, *realm_len;
497 struct hostapd_nai_realm_data *realm;
498
499 realm = &hapd->conf->nai_realm_data[i];
500 realm_data_len = wpabuf_put(buf, 2);
501 wpabuf_put_u8(buf, realm->encoding);
502 realm_len = wpabuf_put(buf, 1);
503 for (j = 0; realm->realm[j]; j++) {
504 if (j > 0)
505 wpabuf_put_u8(buf, ';');
506 wpabuf_put_str(buf, realm->realm[j]);
507 }
508 *realm_len = (u8 *) wpabuf_put(buf, 0) - realm_len - 1;
509 anqp_add_nai_realm_eap(buf, realm);
510 gas_anqp_set_element_len(buf, realm_data_len);
511 }
512 gas_anqp_set_element_len(buf, len);
Dmitry Shmidt09f57ba2014-06-10 16:07:13 -0700513 } else if (nai_home_realm && hapd->conf->nai_realm_data && home_realm) {
Dmitry Shmidt61d9df32012-08-29 16:22:06 -0700514 hs20_add_nai_home_realm_matches(hapd, buf, home_realm,
515 home_realm_len);
516 }
517}
518
519
520static void anqp_add_3gpp_cellular_network(struct hostapd_data *hapd,
521 struct wpabuf *buf)
522{
Dmitry Shmidtd80a4012015-11-05 16:35:40 -0800523 if (anqp_add_override(hapd, buf, ANQP_3GPP_CELLULAR_NETWORK))
524 return;
525
Dmitry Shmidt61d9df32012-08-29 16:22:06 -0700526 if (hapd->conf->anqp_3gpp_cell_net) {
527 wpabuf_put_le16(buf, ANQP_3GPP_CELLULAR_NETWORK);
528 wpabuf_put_le16(buf,
529 hapd->conf->anqp_3gpp_cell_net_len);
530 wpabuf_put_data(buf, hapd->conf->anqp_3gpp_cell_net,
531 hapd->conf->anqp_3gpp_cell_net_len);
532 }
533}
534
535
536static void anqp_add_domain_name(struct hostapd_data *hapd, struct wpabuf *buf)
537{
Dmitry Shmidtd80a4012015-11-05 16:35:40 -0800538 if (anqp_add_override(hapd, buf, ANQP_DOMAIN_NAME))
539 return;
540
Dmitry Shmidt61d9df32012-08-29 16:22:06 -0700541 if (hapd->conf->domain_name) {
542 wpabuf_put_le16(buf, ANQP_DOMAIN_NAME);
543 wpabuf_put_le16(buf, hapd->conf->domain_name_len);
544 wpabuf_put_data(buf, hapd->conf->domain_name,
545 hapd->conf->domain_name_len);
546 }
547}
548
549
Dmitry Shmidtaa532512012-09-24 10:35:31 -0700550#ifdef CONFIG_HS20
551
Dmitry Shmidt61d9df32012-08-29 16:22:06 -0700552static void anqp_add_operator_friendly_name(struct hostapd_data *hapd,
553 struct wpabuf *buf)
554{
555 if (hapd->conf->hs20_oper_friendly_name) {
556 u8 *len;
557 unsigned int i;
558 len = gas_anqp_add_element(buf, ANQP_VENDOR_SPECIFIC);
559 wpabuf_put_be24(buf, OUI_WFA);
560 wpabuf_put_u8(buf, HS20_ANQP_OUI_TYPE);
561 wpabuf_put_u8(buf, HS20_STYPE_OPERATOR_FRIENDLY_NAME);
562 wpabuf_put_u8(buf, 0); /* Reserved */
563 for (i = 0; i < hapd->conf->hs20_oper_friendly_name_count; i++)
564 {
565 struct hostapd_lang_string *vn;
566 vn = &hapd->conf->hs20_oper_friendly_name[i];
567 wpabuf_put_u8(buf, 3 + vn->name_len);
568 wpabuf_put_data(buf, vn->lang, 3);
569 wpabuf_put_data(buf, vn->name, vn->name_len);
570 }
571 gas_anqp_set_element_len(buf, len);
572 }
573}
574
575
576static void anqp_add_wan_metrics(struct hostapd_data *hapd,
577 struct wpabuf *buf)
578{
579 if (hapd->conf->hs20_wan_metrics) {
580 u8 *len = gas_anqp_add_element(buf, ANQP_VENDOR_SPECIFIC);
581 wpabuf_put_be24(buf, OUI_WFA);
582 wpabuf_put_u8(buf, HS20_ANQP_OUI_TYPE);
583 wpabuf_put_u8(buf, HS20_STYPE_WAN_METRICS);
584 wpabuf_put_u8(buf, 0); /* Reserved */
585 wpabuf_put_data(buf, hapd->conf->hs20_wan_metrics, 13);
586 gas_anqp_set_element_len(buf, len);
587 }
588}
589
590
591static void anqp_add_connection_capability(struct hostapd_data *hapd,
592 struct wpabuf *buf)
593{
594 if (hapd->conf->hs20_connection_capability) {
595 u8 *len = gas_anqp_add_element(buf, ANQP_VENDOR_SPECIFIC);
596 wpabuf_put_be24(buf, OUI_WFA);
597 wpabuf_put_u8(buf, HS20_ANQP_OUI_TYPE);
598 wpabuf_put_u8(buf, HS20_STYPE_CONNECTION_CAPABILITY);
599 wpabuf_put_u8(buf, 0); /* Reserved */
600 wpabuf_put_data(buf, hapd->conf->hs20_connection_capability,
601 hapd->conf->hs20_connection_capability_len);
602 gas_anqp_set_element_len(buf, len);
603 }
604}
605
606
607static void anqp_add_operating_class(struct hostapd_data *hapd,
608 struct wpabuf *buf)
609{
610 if (hapd->conf->hs20_operating_class) {
611 u8 *len = gas_anqp_add_element(buf, ANQP_VENDOR_SPECIFIC);
612 wpabuf_put_be24(buf, OUI_WFA);
613 wpabuf_put_u8(buf, HS20_ANQP_OUI_TYPE);
614 wpabuf_put_u8(buf, HS20_STYPE_OPERATING_CLASS);
615 wpabuf_put_u8(buf, 0); /* Reserved */
616 wpabuf_put_data(buf, hapd->conf->hs20_operating_class,
617 hapd->conf->hs20_operating_class_len);
618 gas_anqp_set_element_len(buf, len);
619 }
620}
621
Dmitry Shmidtf21452a2014-02-26 10:55:25 -0800622
623static void anqp_add_osu_provider(struct wpabuf *buf,
624 struct hostapd_bss_config *bss,
625 struct hs20_osu_provider *p)
626{
627 u8 *len, *len2, *count;
628 unsigned int i;
629
630 len = wpabuf_put(buf, 2); /* OSU Provider Length to be filled */
631
632 /* OSU Friendly Name Duples */
633 len2 = wpabuf_put(buf, 2);
634 for (i = 0; i < p->friendly_name_count; i++) {
635 struct hostapd_lang_string *s = &p->friendly_name[i];
636 wpabuf_put_u8(buf, 3 + s->name_len);
637 wpabuf_put_data(buf, s->lang, 3);
638 wpabuf_put_data(buf, s->name, s->name_len);
639 }
640 WPA_PUT_LE16(len2, (u8 *) wpabuf_put(buf, 0) - len2 - 2);
641
642 /* OSU Server URI */
643 if (p->server_uri) {
644 wpabuf_put_u8(buf, os_strlen(p->server_uri));
645 wpabuf_put_str(buf, p->server_uri);
646 } else
647 wpabuf_put_u8(buf, 0);
648
649 /* OSU Method List */
650 count = wpabuf_put(buf, 1);
651 for (i = 0; p->method_list[i] >= 0; i++)
652 wpabuf_put_u8(buf, p->method_list[i]);
653 *count = i;
654
655 /* Icons Available */
656 len2 = wpabuf_put(buf, 2);
657 for (i = 0; i < p->icons_count; i++) {
658 size_t j;
659 struct hs20_icon *icon = NULL;
660
661 for (j = 0; j < bss->hs20_icons_count && !icon; j++) {
662 if (os_strcmp(p->icons[i], bss->hs20_icons[j].name) ==
663 0)
664 icon = &bss->hs20_icons[j];
665 }
666 if (!icon)
667 continue; /* icon info not found */
668
669 wpabuf_put_le16(buf, icon->width);
670 wpabuf_put_le16(buf, icon->height);
671 wpabuf_put_data(buf, icon->language, 3);
672 wpabuf_put_u8(buf, os_strlen(icon->type));
673 wpabuf_put_str(buf, icon->type);
674 wpabuf_put_u8(buf, os_strlen(icon->name));
675 wpabuf_put_str(buf, icon->name);
676 }
677 WPA_PUT_LE16(len2, (u8 *) wpabuf_put(buf, 0) - len2 - 2);
678
679 /* OSU_NAI */
680 if (p->osu_nai) {
681 wpabuf_put_u8(buf, os_strlen(p->osu_nai));
682 wpabuf_put_str(buf, p->osu_nai);
683 } else
684 wpabuf_put_u8(buf, 0);
685
686 /* OSU Service Description Duples */
687 len2 = wpabuf_put(buf, 2);
688 for (i = 0; i < p->service_desc_count; i++) {
689 struct hostapd_lang_string *s = &p->service_desc[i];
690 wpabuf_put_u8(buf, 3 + s->name_len);
691 wpabuf_put_data(buf, s->lang, 3);
692 wpabuf_put_data(buf, s->name, s->name_len);
693 }
694 WPA_PUT_LE16(len2, (u8 *) wpabuf_put(buf, 0) - len2 - 2);
695
696 WPA_PUT_LE16(len, (u8 *) wpabuf_put(buf, 0) - len - 2);
697}
698
699
700static void anqp_add_osu_providers_list(struct hostapd_data *hapd,
701 struct wpabuf *buf)
702{
703 if (hapd->conf->hs20_osu_providers_count) {
704 size_t i;
705 u8 *len = gas_anqp_add_element(buf, ANQP_VENDOR_SPECIFIC);
706 wpabuf_put_be24(buf, OUI_WFA);
707 wpabuf_put_u8(buf, HS20_ANQP_OUI_TYPE);
708 wpabuf_put_u8(buf, HS20_STYPE_OSU_PROVIDERS_LIST);
709 wpabuf_put_u8(buf, 0); /* Reserved */
710
711 /* OSU SSID */
712 wpabuf_put_u8(buf, hapd->conf->osu_ssid_len);
713 wpabuf_put_data(buf, hapd->conf->osu_ssid,
714 hapd->conf->osu_ssid_len);
715
716 /* Number of OSU Providers */
717 wpabuf_put_u8(buf, hapd->conf->hs20_osu_providers_count);
718
719 for (i = 0; i < hapd->conf->hs20_osu_providers_count; i++) {
720 anqp_add_osu_provider(
721 buf, hapd->conf,
722 &hapd->conf->hs20_osu_providers[i]);
723 }
724
725 gas_anqp_set_element_len(buf, len);
726 }
727}
728
729
730static void anqp_add_icon_binary_file(struct hostapd_data *hapd,
731 struct wpabuf *buf,
732 const u8 *name, size_t name_len)
733{
734 struct hs20_icon *icon;
735 size_t i;
736 u8 *len;
737
738 wpa_hexdump_ascii(MSG_DEBUG, "HS 2.0: Requested Icon Filename",
739 name, name_len);
740 for (i = 0; i < hapd->conf->hs20_icons_count; i++) {
741 icon = &hapd->conf->hs20_icons[i];
742 if (name_len == os_strlen(icon->name) &&
743 os_memcmp(name, icon->name, name_len) == 0)
744 break;
745 }
746
747 if (i < hapd->conf->hs20_icons_count)
748 icon = &hapd->conf->hs20_icons[i];
749 else
750 icon = NULL;
751
752 len = gas_anqp_add_element(buf, ANQP_VENDOR_SPECIFIC);
753 wpabuf_put_be24(buf, OUI_WFA);
754 wpabuf_put_u8(buf, HS20_ANQP_OUI_TYPE);
755 wpabuf_put_u8(buf, HS20_STYPE_ICON_BINARY_FILE);
756 wpabuf_put_u8(buf, 0); /* Reserved */
757
758 if (icon) {
759 char *data;
760 size_t data_len;
761
762 data = os_readfile(icon->file, &data_len);
763 if (data == NULL || data_len > 65535) {
764 wpabuf_put_u8(buf, 2); /* Download Status:
765 * Unspecified file error */
766 wpabuf_put_u8(buf, 0);
767 wpabuf_put_le16(buf, 0);
768 } else {
769 wpabuf_put_u8(buf, 0); /* Download Status: Success */
770 wpabuf_put_u8(buf, os_strlen(icon->type));
771 wpabuf_put_str(buf, icon->type);
772 wpabuf_put_le16(buf, data_len);
773 wpabuf_put_data(buf, data, data_len);
774 }
775 os_free(data);
776 } else {
777 wpabuf_put_u8(buf, 1); /* Download Status: File not found */
778 wpabuf_put_u8(buf, 0);
779 wpabuf_put_le16(buf, 0);
780 }
781
782 gas_anqp_set_element_len(buf, len);
783}
784
Dmitry Shmidtaa532512012-09-24 10:35:31 -0700785#endif /* CONFIG_HS20 */
786
Dmitry Shmidt61d9df32012-08-29 16:22:06 -0700787
Dmitry Shmidt04949592012-07-19 12:16:46 -0700788static struct wpabuf *
789gas_serv_build_gas_resp_payload(struct hostapd_data *hapd,
790 unsigned int request,
Dmitry Shmidtf21452a2014-02-26 10:55:25 -0800791 const u8 *home_realm, size_t home_realm_len,
Dmitry Shmidtd80a4012015-11-05 16:35:40 -0800792 const u8 *icon_name, size_t icon_name_len,
793 const u16 *extra_req,
794 unsigned int num_extra_req)
Dmitry Shmidt04949592012-07-19 12:16:46 -0700795{
796 struct wpabuf *buf;
Dmitry Shmidtf21452a2014-02-26 10:55:25 -0800797 size_t len;
Dmitry Shmidtd80a4012015-11-05 16:35:40 -0800798 unsigned int i;
Dmitry Shmidt04949592012-07-19 12:16:46 -0700799
Dmitry Shmidtf21452a2014-02-26 10:55:25 -0800800 len = 1400;
801 if (request & (ANQP_REQ_NAI_REALM | ANQP_REQ_NAI_HOME_REALM))
802 len += 1000;
803 if (request & ANQP_REQ_ICON_REQUEST)
804 len += 65536;
Dmitry Shmidtd80a4012015-11-05 16:35:40 -0800805 len += num_extra_req * 1000;
Dmitry Shmidtf21452a2014-02-26 10:55:25 -0800806
807 buf = wpabuf_alloc(len);
Dmitry Shmidt04949592012-07-19 12:16:46 -0700808 if (buf == NULL)
809 return NULL;
810
811 if (request & ANQP_REQ_CAPABILITY_LIST)
812 anqp_add_capab_list(hapd, buf);
813 if (request & ANQP_REQ_VENUE_NAME)
814 anqp_add_venue_name(hapd, buf);
Dmitry Shmidtd80a4012015-11-05 16:35:40 -0800815 if (request & ANQP_REQ_EMERGENCY_CALL_NUMBER)
816 anqp_add_elem(hapd, buf, ANQP_EMERGENCY_CALL_NUMBER);
Dmitry Shmidt61d9df32012-08-29 16:22:06 -0700817 if (request & ANQP_REQ_NETWORK_AUTH_TYPE)
818 anqp_add_network_auth_type(hapd, buf);
Dmitry Shmidt04949592012-07-19 12:16:46 -0700819 if (request & ANQP_REQ_ROAMING_CONSORTIUM)
820 anqp_add_roaming_consortium(hapd, buf);
Dmitry Shmidt61d9df32012-08-29 16:22:06 -0700821 if (request & ANQP_REQ_IP_ADDR_TYPE_AVAILABILITY)
822 anqp_add_ip_addr_type_availability(hapd, buf);
823 if (request & (ANQP_REQ_NAI_REALM | ANQP_REQ_NAI_HOME_REALM))
824 anqp_add_nai_realm(hapd, buf, home_realm, home_realm_len,
825 request & ANQP_REQ_NAI_REALM,
826 request & ANQP_REQ_NAI_HOME_REALM);
827 if (request & ANQP_REQ_3GPP_CELLULAR_NETWORK)
828 anqp_add_3gpp_cellular_network(hapd, buf);
Dmitry Shmidtd80a4012015-11-05 16:35:40 -0800829 if (request & ANQP_REQ_AP_GEOSPATIAL_LOCATION)
830 anqp_add_elem(hapd, buf, ANQP_AP_GEOSPATIAL_LOCATION);
831 if (request & ANQP_REQ_AP_CIVIC_LOCATION)
832 anqp_add_elem(hapd, buf, ANQP_AP_CIVIC_LOCATION);
833 if (request & ANQP_REQ_AP_LOCATION_PUBLIC_URI)
834 anqp_add_elem(hapd, buf, ANQP_AP_LOCATION_PUBLIC_URI);
Dmitry Shmidt61d9df32012-08-29 16:22:06 -0700835 if (request & ANQP_REQ_DOMAIN_NAME)
836 anqp_add_domain_name(hapd, buf);
Dmitry Shmidtd80a4012015-11-05 16:35:40 -0800837 if (request & ANQP_REQ_EMERGENCY_ALERT_URI)
838 anqp_add_elem(hapd, buf, ANQP_EMERGENCY_ALERT_URI);
839 if (request & ANQP_REQ_TDLS_CAPABILITY)
840 anqp_add_elem(hapd, buf, ANQP_TDLS_CAPABILITY);
841 if (request & ANQP_REQ_EMERGENCY_NAI)
842 anqp_add_elem(hapd, buf, ANQP_EMERGENCY_NAI);
843
844 for (i = 0; i < num_extra_req; i++)
845 anqp_add_elem(hapd, buf, extra_req[i]);
Dmitry Shmidt61d9df32012-08-29 16:22:06 -0700846
Dmitry Shmidtaa532512012-09-24 10:35:31 -0700847#ifdef CONFIG_HS20
Dmitry Shmidt61d9df32012-08-29 16:22:06 -0700848 if (request & ANQP_REQ_HS_CAPABILITY_LIST)
849 anqp_add_hs_capab_list(hapd, buf);
850 if (request & ANQP_REQ_OPERATOR_FRIENDLY_NAME)
851 anqp_add_operator_friendly_name(hapd, buf);
852 if (request & ANQP_REQ_WAN_METRICS)
853 anqp_add_wan_metrics(hapd, buf);
854 if (request & ANQP_REQ_CONNECTION_CAPABILITY)
855 anqp_add_connection_capability(hapd, buf);
856 if (request & ANQP_REQ_OPERATING_CLASS)
857 anqp_add_operating_class(hapd, buf);
Dmitry Shmidtf21452a2014-02-26 10:55:25 -0800858 if (request & ANQP_REQ_OSU_PROVIDERS_LIST)
859 anqp_add_osu_providers_list(hapd, buf);
860 if (request & ANQP_REQ_ICON_REQUEST)
861 anqp_add_icon_binary_file(hapd, buf, icon_name, icon_name_len);
Dmitry Shmidtaa532512012-09-24 10:35:31 -0700862#endif /* CONFIG_HS20 */
Dmitry Shmidt04949592012-07-19 12:16:46 -0700863
864 return buf;
865}
866
867
Dmitry Shmidtd80a4012015-11-05 16:35:40 -0800868#define ANQP_MAX_EXTRA_REQ 20
869
Dmitry Shmidt04949592012-07-19 12:16:46 -0700870struct anqp_query_info {
871 unsigned int request;
Dmitry Shmidt61d9df32012-08-29 16:22:06 -0700872 const u8 *home_realm_query;
873 size_t home_realm_query_len;
Dmitry Shmidtf21452a2014-02-26 10:55:25 -0800874 const u8 *icon_name;
875 size_t icon_name_len;
Dmitry Shmidtfb45fd52015-01-05 13:08:17 -0800876 int p2p_sd;
Dmitry Shmidtd80a4012015-11-05 16:35:40 -0800877 u16 extra_req[ANQP_MAX_EXTRA_REQ];
878 unsigned int num_extra_req;
Dmitry Shmidt04949592012-07-19 12:16:46 -0700879};
880
881
882static void set_anqp_req(unsigned int bit, const char *name, int local,
Dmitry Shmidt04949592012-07-19 12:16:46 -0700883 struct anqp_query_info *qi)
884{
885 qi->request |= bit;
886 if (local) {
887 wpa_printf(MSG_DEBUG, "ANQP: %s (local)", name);
Dmitry Shmidt04949592012-07-19 12:16:46 -0700888 } else {
889 wpa_printf(MSG_DEBUG, "ANQP: %s not available", name);
890 }
891}
892
893
894static void rx_anqp_query_list_id(struct hostapd_data *hapd, u16 info_id,
895 struct anqp_query_info *qi)
896{
897 switch (info_id) {
898 case ANQP_CAPABILITY_LIST:
Dmitry Shmidtb36ed7c2014-03-17 10:57:26 -0700899 set_anqp_req(ANQP_REQ_CAPABILITY_LIST, "Capability List", 1,
900 qi);
Dmitry Shmidt04949592012-07-19 12:16:46 -0700901 break;
902 case ANQP_VENUE_NAME:
903 set_anqp_req(ANQP_REQ_VENUE_NAME, "Venue Name",
Dmitry Shmidtb36ed7c2014-03-17 10:57:26 -0700904 hapd->conf->venue_name != NULL, qi);
Dmitry Shmidt04949592012-07-19 12:16:46 -0700905 break;
Dmitry Shmidtd80a4012015-11-05 16:35:40 -0800906 case ANQP_EMERGENCY_CALL_NUMBER:
907 set_anqp_req(ANQP_REQ_EMERGENCY_CALL_NUMBER,
908 "Emergency Call Number",
909 get_anqp_elem(hapd, info_id) != NULL, qi);
910 break;
Dmitry Shmidt61d9df32012-08-29 16:22:06 -0700911 case ANQP_NETWORK_AUTH_TYPE:
912 set_anqp_req(ANQP_REQ_NETWORK_AUTH_TYPE, "Network Auth Type",
Dmitry Shmidtb36ed7c2014-03-17 10:57:26 -0700913 hapd->conf->network_auth_type != NULL, qi);
Dmitry Shmidt61d9df32012-08-29 16:22:06 -0700914 break;
Dmitry Shmidt04949592012-07-19 12:16:46 -0700915 case ANQP_ROAMING_CONSORTIUM:
916 set_anqp_req(ANQP_REQ_ROAMING_CONSORTIUM, "Roaming Consortium",
Dmitry Shmidtb36ed7c2014-03-17 10:57:26 -0700917 hapd->conf->roaming_consortium != NULL, qi);
Dmitry Shmidt04949592012-07-19 12:16:46 -0700918 break;
Dmitry Shmidt61d9df32012-08-29 16:22:06 -0700919 case ANQP_IP_ADDR_TYPE_AVAILABILITY:
920 set_anqp_req(ANQP_REQ_IP_ADDR_TYPE_AVAILABILITY,
921 "IP Addr Type Availability",
Dmitry Shmidtb36ed7c2014-03-17 10:57:26 -0700922 hapd->conf->ipaddr_type_configured, qi);
Dmitry Shmidt61d9df32012-08-29 16:22:06 -0700923 break;
924 case ANQP_NAI_REALM:
925 set_anqp_req(ANQP_REQ_NAI_REALM, "NAI Realm",
Dmitry Shmidtb36ed7c2014-03-17 10:57:26 -0700926 hapd->conf->nai_realm_data != NULL, qi);
Dmitry Shmidt61d9df32012-08-29 16:22:06 -0700927 break;
928 case ANQP_3GPP_CELLULAR_NETWORK:
929 set_anqp_req(ANQP_REQ_3GPP_CELLULAR_NETWORK,
930 "3GPP Cellular Network",
Dmitry Shmidtb36ed7c2014-03-17 10:57:26 -0700931 hapd->conf->anqp_3gpp_cell_net != NULL, qi);
Dmitry Shmidt61d9df32012-08-29 16:22:06 -0700932 break;
Dmitry Shmidtd80a4012015-11-05 16:35:40 -0800933 case ANQP_AP_GEOSPATIAL_LOCATION:
934 set_anqp_req(ANQP_REQ_AP_GEOSPATIAL_LOCATION,
935 "AP Geospatial Location",
936 get_anqp_elem(hapd, info_id) != NULL, qi);
937 break;
938 case ANQP_AP_CIVIC_LOCATION:
939 set_anqp_req(ANQP_REQ_AP_CIVIC_LOCATION,
940 "AP Civic Location",
941 get_anqp_elem(hapd, info_id) != NULL, qi);
942 break;
943 case ANQP_AP_LOCATION_PUBLIC_URI:
944 set_anqp_req(ANQP_REQ_AP_LOCATION_PUBLIC_URI,
945 "AP Location Public URI",
946 get_anqp_elem(hapd, info_id) != NULL, qi);
947 break;
Dmitry Shmidt61d9df32012-08-29 16:22:06 -0700948 case ANQP_DOMAIN_NAME:
949 set_anqp_req(ANQP_REQ_DOMAIN_NAME, "Domain Name",
Dmitry Shmidtb36ed7c2014-03-17 10:57:26 -0700950 hapd->conf->domain_name != NULL, qi);
Dmitry Shmidt61d9df32012-08-29 16:22:06 -0700951 break;
Dmitry Shmidtd80a4012015-11-05 16:35:40 -0800952 case ANQP_EMERGENCY_ALERT_URI:
953 set_anqp_req(ANQP_REQ_EMERGENCY_ALERT_URI,
954 "Emergency Alert URI",
955 get_anqp_elem(hapd, info_id) != NULL, qi);
956 break;
957 case ANQP_TDLS_CAPABILITY:
958 set_anqp_req(ANQP_REQ_TDLS_CAPABILITY,
959 "TDLS Capability",
960 get_anqp_elem(hapd, info_id) != NULL, qi);
961 break;
962 case ANQP_EMERGENCY_NAI:
963 set_anqp_req(ANQP_REQ_EMERGENCY_NAI,
964 "Emergency NAI",
965 get_anqp_elem(hapd, info_id) != NULL, qi);
966 break;
Dmitry Shmidt04949592012-07-19 12:16:46 -0700967 default:
Dmitry Shmidtd80a4012015-11-05 16:35:40 -0800968 if (!get_anqp_elem(hapd, info_id)) {
969 wpa_printf(MSG_DEBUG, "ANQP: Unsupported Info Id %u",
970 info_id);
971 break;
972 }
973 if (qi->num_extra_req == ANQP_MAX_EXTRA_REQ) {
974 wpa_printf(MSG_DEBUG,
975 "ANQP: No more room for extra requests - ignore Info Id %u",
976 info_id);
977 break;
978 }
979 wpa_printf(MSG_DEBUG, "ANQP: Info Id %u (local)", info_id);
980 qi->extra_req[qi->num_extra_req] = info_id;
981 qi->num_extra_req++;
Dmitry Shmidt04949592012-07-19 12:16:46 -0700982 break;
983 }
984}
985
986
987static void rx_anqp_query_list(struct hostapd_data *hapd,
988 const u8 *pos, const u8 *end,
989 struct anqp_query_info *qi)
990{
991 wpa_printf(MSG_DEBUG, "ANQP: %u Info IDs requested in Query list",
992 (unsigned int) (end - pos) / 2);
993
Dmitry Shmidtd80a4012015-11-05 16:35:40 -0800994 while (end - pos >= 2) {
Dmitry Shmidt04949592012-07-19 12:16:46 -0700995 rx_anqp_query_list_id(hapd, WPA_GET_LE16(pos), qi);
996 pos += 2;
997 }
998}
999
1000
Dmitry Shmidtaa532512012-09-24 10:35:31 -07001001#ifdef CONFIG_HS20
1002
Dmitry Shmidt61d9df32012-08-29 16:22:06 -07001003static void rx_anqp_hs_query_list(struct hostapd_data *hapd, u8 subtype,
1004 struct anqp_query_info *qi)
1005{
1006 switch (subtype) {
1007 case HS20_STYPE_CAPABILITY_LIST:
1008 set_anqp_req(ANQP_REQ_HS_CAPABILITY_LIST, "HS Capability List",
Dmitry Shmidtb36ed7c2014-03-17 10:57:26 -07001009 1, qi);
Dmitry Shmidt61d9df32012-08-29 16:22:06 -07001010 break;
1011 case HS20_STYPE_OPERATOR_FRIENDLY_NAME:
1012 set_anqp_req(ANQP_REQ_OPERATOR_FRIENDLY_NAME,
1013 "Operator Friendly Name",
Dmitry Shmidtb36ed7c2014-03-17 10:57:26 -07001014 hapd->conf->hs20_oper_friendly_name != NULL, qi);
Dmitry Shmidt61d9df32012-08-29 16:22:06 -07001015 break;
1016 case HS20_STYPE_WAN_METRICS:
1017 set_anqp_req(ANQP_REQ_WAN_METRICS, "WAN Metrics",
Dmitry Shmidtb36ed7c2014-03-17 10:57:26 -07001018 hapd->conf->hs20_wan_metrics != NULL, qi);
Dmitry Shmidt61d9df32012-08-29 16:22:06 -07001019 break;
1020 case HS20_STYPE_CONNECTION_CAPABILITY:
1021 set_anqp_req(ANQP_REQ_CONNECTION_CAPABILITY,
1022 "Connection Capability",
1023 hapd->conf->hs20_connection_capability != NULL,
Dmitry Shmidtb36ed7c2014-03-17 10:57:26 -07001024 qi);
Dmitry Shmidt61d9df32012-08-29 16:22:06 -07001025 break;
1026 case HS20_STYPE_OPERATING_CLASS:
1027 set_anqp_req(ANQP_REQ_OPERATING_CLASS, "Operating Class",
Dmitry Shmidtb36ed7c2014-03-17 10:57:26 -07001028 hapd->conf->hs20_operating_class != NULL, qi);
Dmitry Shmidt61d9df32012-08-29 16:22:06 -07001029 break;
Dmitry Shmidtf21452a2014-02-26 10:55:25 -08001030 case HS20_STYPE_OSU_PROVIDERS_LIST:
1031 set_anqp_req(ANQP_REQ_OSU_PROVIDERS_LIST, "OSU Providers list",
Dmitry Shmidtb36ed7c2014-03-17 10:57:26 -07001032 hapd->conf->hs20_osu_providers_count, qi);
Dmitry Shmidtf21452a2014-02-26 10:55:25 -08001033 break;
Dmitry Shmidt61d9df32012-08-29 16:22:06 -07001034 default:
1035 wpa_printf(MSG_DEBUG, "ANQP: Unsupported HS 2.0 subtype %u",
1036 subtype);
1037 break;
1038 }
1039}
1040
1041
1042static void rx_anqp_hs_nai_home_realm(struct hostapd_data *hapd,
1043 const u8 *pos, const u8 *end,
1044 struct anqp_query_info *qi)
1045{
1046 qi->request |= ANQP_REQ_NAI_HOME_REALM;
1047 qi->home_realm_query = pos;
1048 qi->home_realm_query_len = end - pos;
1049 if (hapd->conf->nai_realm_data != NULL) {
1050 wpa_printf(MSG_DEBUG, "ANQP: HS 2.0 NAI Home Realm Query "
1051 "(local)");
1052 } else {
1053 wpa_printf(MSG_DEBUG, "ANQP: HS 2.0 NAI Home Realm Query not "
1054 "available");
1055 }
1056}
1057
1058
Dmitry Shmidtf21452a2014-02-26 10:55:25 -08001059static void rx_anqp_hs_icon_request(struct hostapd_data *hapd,
1060 const u8 *pos, const u8 *end,
1061 struct anqp_query_info *qi)
1062{
1063 qi->request |= ANQP_REQ_ICON_REQUEST;
1064 qi->icon_name = pos;
1065 qi->icon_name_len = end - pos;
1066 if (hapd->conf->hs20_icons_count) {
1067 wpa_printf(MSG_DEBUG, "ANQP: HS 2.0 Icon Request Query "
1068 "(local)");
1069 } else {
1070 wpa_printf(MSG_DEBUG, "ANQP: HS 2.0 Icon Request Query not "
1071 "available");
1072 }
1073}
1074
1075
Dmitry Shmidt61d9df32012-08-29 16:22:06 -07001076static void rx_anqp_vendor_specific(struct hostapd_data *hapd,
1077 const u8 *pos, const u8 *end,
1078 struct anqp_query_info *qi)
1079{
1080 u32 oui;
1081 u8 subtype;
1082
Dmitry Shmidtd80a4012015-11-05 16:35:40 -08001083 if (end - pos < 4) {
Dmitry Shmidt61d9df32012-08-29 16:22:06 -07001084 wpa_printf(MSG_DEBUG, "ANQP: Too short vendor specific ANQP "
1085 "Query element");
1086 return;
1087 }
1088
1089 oui = WPA_GET_BE24(pos);
1090 pos += 3;
1091 if (oui != OUI_WFA) {
1092 wpa_printf(MSG_DEBUG, "ANQP: Unsupported vendor OUI %06x",
1093 oui);
1094 return;
1095 }
1096
Dmitry Shmidtfb45fd52015-01-05 13:08:17 -08001097#ifdef CONFIG_P2P
1098 if (*pos == P2P_OUI_TYPE) {
1099 /*
1100 * This is for P2P SD and will be taken care of by the P2P
1101 * implementation. This query needs to be ignored in the generic
1102 * GAS server to avoid duplicated response.
1103 */
1104 wpa_printf(MSG_DEBUG,
1105 "ANQP: Ignore WFA vendor type %u (P2P SD) in generic GAS server",
1106 *pos);
1107 qi->p2p_sd = 1;
1108 return;
1109 }
1110#endif /* CONFIG_P2P */
1111
Dmitry Shmidt61d9df32012-08-29 16:22:06 -07001112 if (*pos != HS20_ANQP_OUI_TYPE) {
1113 wpa_printf(MSG_DEBUG, "ANQP: Unsupported WFA vendor type %u",
1114 *pos);
1115 return;
1116 }
1117 pos++;
1118
Dmitry Shmidtd80a4012015-11-05 16:35:40 -08001119 if (end - pos <= 1)
Dmitry Shmidt61d9df32012-08-29 16:22:06 -07001120 return;
1121
1122 subtype = *pos++;
1123 pos++; /* Reserved */
1124 switch (subtype) {
1125 case HS20_STYPE_QUERY_LIST:
1126 wpa_printf(MSG_DEBUG, "ANQP: HS 2.0 Query List");
1127 while (pos < end) {
1128 rx_anqp_hs_query_list(hapd, *pos, qi);
1129 pos++;
1130 }
1131 break;
1132 case HS20_STYPE_NAI_HOME_REALM_QUERY:
1133 rx_anqp_hs_nai_home_realm(hapd, pos, end, qi);
1134 break;
Dmitry Shmidtf21452a2014-02-26 10:55:25 -08001135 case HS20_STYPE_ICON_REQUEST:
1136 rx_anqp_hs_icon_request(hapd, pos, end, qi);
1137 break;
Dmitry Shmidt61d9df32012-08-29 16:22:06 -07001138 default:
1139 wpa_printf(MSG_DEBUG, "ANQP: Unsupported HS 2.0 query subtype "
1140 "%u", subtype);
1141 break;
1142 }
1143}
1144
Dmitry Shmidtaa532512012-09-24 10:35:31 -07001145#endif /* CONFIG_HS20 */
1146
Dmitry Shmidt61d9df32012-08-29 16:22:06 -07001147
Dmitry Shmidt04949592012-07-19 12:16:46 -07001148static void gas_serv_req_local_processing(struct hostapd_data *hapd,
1149 const u8 *sa, u8 dialog_token,
Dmitry Shmidt18463232014-01-24 12:29:41 -08001150 struct anqp_query_info *qi, int prot)
Dmitry Shmidt04949592012-07-19 12:16:46 -07001151{
1152 struct wpabuf *buf, *tx_buf;
1153
Dmitry Shmidt09f57ba2014-06-10 16:07:13 -07001154 buf = gas_serv_build_gas_resp_payload(hapd, qi->request,
Dmitry Shmidt61d9df32012-08-29 16:22:06 -07001155 qi->home_realm_query,
Dmitry Shmidtf21452a2014-02-26 10:55:25 -08001156 qi->home_realm_query_len,
Dmitry Shmidtd80a4012015-11-05 16:35:40 -08001157 qi->icon_name, qi->icon_name_len,
1158 qi->extra_req, qi->num_extra_req);
Dmitry Shmidt04949592012-07-19 12:16:46 -07001159 wpa_hexdump_buf(MSG_MSGDUMP, "ANQP: Locally generated ANQP responses",
1160 buf);
1161 if (!buf)
1162 return;
Dmitry Shmidtfb45fd52015-01-05 13:08:17 -08001163#ifdef CONFIG_P2P
1164 if (wpabuf_len(buf) == 0 && qi->p2p_sd) {
1165 wpa_printf(MSG_DEBUG,
1166 "ANQP: Do not send response to P2P SD from generic GAS service (P2P SD implementation will process this)");
1167 wpabuf_free(buf);
1168 return;
1169 }
1170#endif /* CONFIG_P2P */
Dmitry Shmidt04949592012-07-19 12:16:46 -07001171
1172 if (wpabuf_len(buf) > hapd->gas_frag_limit ||
1173 hapd->conf->gas_comeback_delay) {
1174 struct gas_dialog_info *di;
1175 u16 comeback_delay = 1;
1176
1177 if (hapd->conf->gas_comeback_delay) {
1178 /* Testing - allow overriding of the delay value */
1179 comeback_delay = hapd->conf->gas_comeback_delay;
1180 }
1181
1182 wpa_printf(MSG_DEBUG, "ANQP: Too long response to fit in "
1183 "initial response - use GAS comeback");
1184 di = gas_dialog_create(hapd, sa, dialog_token);
1185 if (!di) {
1186 wpa_printf(MSG_INFO, "ANQP: Could not create dialog "
1187 "for " MACSTR " (dialog token %u)",
1188 MAC2STR(sa), dialog_token);
1189 wpabuf_free(buf);
Dmitry Shmidt76cd2cc2014-05-27 12:56:04 -07001190 tx_buf = gas_anqp_build_initial_resp_buf(
1191 dialog_token, WLAN_STATUS_UNSPECIFIED_FAILURE,
1192 0, NULL);
1193 } else {
1194 di->prot = prot;
1195 di->sd_resp = buf;
1196 di->sd_resp_pos = 0;
1197 tx_buf = gas_anqp_build_initial_resp_buf(
1198 dialog_token, WLAN_STATUS_SUCCESS,
1199 comeback_delay, NULL);
Dmitry Shmidt04949592012-07-19 12:16:46 -07001200 }
Dmitry Shmidt04949592012-07-19 12:16:46 -07001201 } else {
1202 wpa_printf(MSG_DEBUG, "ANQP: Initial response (no comeback)");
1203 tx_buf = gas_anqp_build_initial_resp_buf(
1204 dialog_token, WLAN_STATUS_SUCCESS, 0, buf);
1205 wpabuf_free(buf);
1206 }
1207 if (!tx_buf)
1208 return;
Dmitry Shmidt18463232014-01-24 12:29:41 -08001209 if (prot)
1210 convert_to_protected_dual(tx_buf);
Dmitry Shmidt04949592012-07-19 12:16:46 -07001211 hostapd_drv_send_action(hapd, hapd->iface->freq, 0, sa,
1212 wpabuf_head(tx_buf), wpabuf_len(tx_buf));
1213 wpabuf_free(tx_buf);
1214}
1215
1216
1217static void gas_serv_rx_gas_initial_req(struct hostapd_data *hapd,
1218 const u8 *sa,
Dmitry Shmidt18463232014-01-24 12:29:41 -08001219 const u8 *data, size_t len, int prot)
Dmitry Shmidt04949592012-07-19 12:16:46 -07001220{
1221 const u8 *pos = data;
1222 const u8 *end = data + len;
1223 const u8 *next;
1224 u8 dialog_token;
1225 u16 slen;
1226 struct anqp_query_info qi;
1227 const u8 *adv_proto;
1228
1229 if (len < 1 + 2)
1230 return;
1231
1232 os_memset(&qi, 0, sizeof(qi));
1233
1234 dialog_token = *pos++;
1235 wpa_msg(hapd->msg_ctx, MSG_DEBUG,
1236 "GAS: GAS Initial Request from " MACSTR " (dialog token %u) ",
1237 MAC2STR(sa), dialog_token);
1238
1239 if (*pos != WLAN_EID_ADV_PROTO) {
1240 wpa_msg(hapd->msg_ctx, MSG_DEBUG,
1241 "GAS: Unexpected IE in GAS Initial Request: %u", *pos);
1242 return;
1243 }
1244 adv_proto = pos++;
1245
1246 slen = *pos++;
Dmitry Shmidtd80a4012015-11-05 16:35:40 -08001247 if (slen > end - pos || slen < 2) {
Dmitry Shmidt04949592012-07-19 12:16:46 -07001248 wpa_msg(hapd->msg_ctx, MSG_DEBUG,
1249 "GAS: Invalid IE in GAS Initial Request");
1250 return;
1251 }
Dmitry Shmidtd80a4012015-11-05 16:35:40 -08001252 next = pos + slen;
Dmitry Shmidt04949592012-07-19 12:16:46 -07001253 pos++; /* skip QueryRespLenLimit and PAME-BI */
1254
1255 if (*pos != ACCESS_NETWORK_QUERY_PROTOCOL) {
1256 struct wpabuf *buf;
1257 wpa_msg(hapd->msg_ctx, MSG_DEBUG,
1258 "GAS: Unsupported GAS advertisement protocol id %u",
1259 *pos);
1260 if (sa[0] & 0x01)
1261 return; /* Invalid source address - drop silently */
1262 buf = gas_build_initial_resp(
1263 dialog_token, WLAN_STATUS_GAS_ADV_PROTO_NOT_SUPPORTED,
1264 0, 2 + slen + 2);
1265 if (buf == NULL)
1266 return;
1267 wpabuf_put_data(buf, adv_proto, 2 + slen);
1268 wpabuf_put_le16(buf, 0); /* Query Response Length */
Dmitry Shmidt18463232014-01-24 12:29:41 -08001269 if (prot)
1270 convert_to_protected_dual(buf);
Dmitry Shmidt04949592012-07-19 12:16:46 -07001271 hostapd_drv_send_action(hapd, hapd->iface->freq, 0, sa,
1272 wpabuf_head(buf), wpabuf_len(buf));
1273 wpabuf_free(buf);
1274 return;
1275 }
1276
1277 pos = next;
1278 /* Query Request */
Dmitry Shmidtd80a4012015-11-05 16:35:40 -08001279 if (end - pos < 2)
Dmitry Shmidt04949592012-07-19 12:16:46 -07001280 return;
1281 slen = WPA_GET_LE16(pos);
1282 pos += 2;
Dmitry Shmidtd80a4012015-11-05 16:35:40 -08001283 if (slen > end - pos)
Dmitry Shmidt04949592012-07-19 12:16:46 -07001284 return;
1285 end = pos + slen;
1286
1287 /* ANQP Query Request */
1288 while (pos < end) {
1289 u16 info_id, elen;
1290
Dmitry Shmidtd80a4012015-11-05 16:35:40 -08001291 if (end - pos < 4)
Dmitry Shmidt04949592012-07-19 12:16:46 -07001292 return;
1293
1294 info_id = WPA_GET_LE16(pos);
1295 pos += 2;
1296 elen = WPA_GET_LE16(pos);
1297 pos += 2;
1298
Dmitry Shmidtd80a4012015-11-05 16:35:40 -08001299 if (elen > end - pos) {
Dmitry Shmidt04949592012-07-19 12:16:46 -07001300 wpa_printf(MSG_DEBUG, "ANQP: Invalid Query Request");
1301 return;
1302 }
1303
1304 switch (info_id) {
1305 case ANQP_QUERY_LIST:
1306 rx_anqp_query_list(hapd, pos, pos + elen, &qi);
1307 break;
Dmitry Shmidtaa532512012-09-24 10:35:31 -07001308#ifdef CONFIG_HS20
Dmitry Shmidt61d9df32012-08-29 16:22:06 -07001309 case ANQP_VENDOR_SPECIFIC:
1310 rx_anqp_vendor_specific(hapd, pos, pos + elen, &qi);
1311 break;
Dmitry Shmidtaa532512012-09-24 10:35:31 -07001312#endif /* CONFIG_HS20 */
Dmitry Shmidt04949592012-07-19 12:16:46 -07001313 default:
1314 wpa_printf(MSG_DEBUG, "ANQP: Unsupported Query "
1315 "Request element %u", info_id);
1316 break;
1317 }
1318
1319 pos += elen;
1320 }
1321
Dmitry Shmidt18463232014-01-24 12:29:41 -08001322 gas_serv_req_local_processing(hapd, sa, dialog_token, &qi, prot);
Dmitry Shmidt04949592012-07-19 12:16:46 -07001323}
1324
1325
Dmitry Shmidt04949592012-07-19 12:16:46 -07001326static void gas_serv_rx_gas_comeback_req(struct hostapd_data *hapd,
1327 const u8 *sa,
Dmitry Shmidt18463232014-01-24 12:29:41 -08001328 const u8 *data, size_t len, int prot)
Dmitry Shmidt04949592012-07-19 12:16:46 -07001329{
1330 struct gas_dialog_info *dialog;
1331 struct wpabuf *buf, *tx_buf;
1332 u8 dialog_token;
1333 size_t frag_len;
1334 int more = 0;
1335
1336 wpa_hexdump(MSG_DEBUG, "GAS: RX GAS Comeback Request", data, len);
1337 if (len < 1)
1338 return;
1339 dialog_token = *data;
1340 wpa_msg(hapd->msg_ctx, MSG_DEBUG, "GAS: Dialog Token: %u",
1341 dialog_token);
1342
1343 dialog = gas_serv_dialog_find(hapd, sa, dialog_token);
1344 if (!dialog) {
1345 wpa_msg(hapd->msg_ctx, MSG_DEBUG, "GAS: No pending SD "
1346 "response fragment for " MACSTR " dialog token %u",
1347 MAC2STR(sa), dialog_token);
1348
1349 if (sa[0] & 0x01)
1350 return; /* Invalid source address - drop silently */
1351 tx_buf = gas_anqp_build_comeback_resp_buf(
1352 dialog_token, WLAN_STATUS_NO_OUTSTANDING_GAS_REQ, 0, 0,
1353 0, NULL);
1354 if (tx_buf == NULL)
1355 return;
1356 goto send_resp;
1357 }
1358
Dmitry Shmidt04949592012-07-19 12:16:46 -07001359 frag_len = wpabuf_len(dialog->sd_resp) - dialog->sd_resp_pos;
1360 if (frag_len > hapd->gas_frag_limit) {
1361 frag_len = hapd->gas_frag_limit;
1362 more = 1;
1363 }
1364 wpa_msg(hapd->msg_ctx, MSG_DEBUG, "GAS: resp frag_len %u",
1365 (unsigned int) frag_len);
1366 buf = wpabuf_alloc_copy(wpabuf_head_u8(dialog->sd_resp) +
1367 dialog->sd_resp_pos, frag_len);
1368 if (buf == NULL) {
1369 wpa_msg(hapd->msg_ctx, MSG_DEBUG, "GAS: Failed to allocate "
1370 "buffer");
Dmitry Shmidtb36ed7c2014-03-17 10:57:26 -07001371 gas_serv_dialog_clear(dialog);
1372 return;
Dmitry Shmidt04949592012-07-19 12:16:46 -07001373 }
1374 tx_buf = gas_anqp_build_comeback_resp_buf(dialog_token,
1375 WLAN_STATUS_SUCCESS,
1376 dialog->sd_frag_id,
1377 more, 0, buf);
1378 wpabuf_free(buf);
Dmitry Shmidtb36ed7c2014-03-17 10:57:26 -07001379 if (tx_buf == NULL) {
1380 gas_serv_dialog_clear(dialog);
1381 return;
1382 }
Dmitry Shmidt04949592012-07-19 12:16:46 -07001383 wpa_msg(hapd->msg_ctx, MSG_DEBUG, "GAS: Tx GAS Comeback Response "
1384 "(frag_id %d more=%d frag_len=%d)",
1385 dialog->sd_frag_id, more, (int) frag_len);
1386 dialog->sd_frag_id++;
1387 dialog->sd_resp_pos += frag_len;
1388
1389 if (more) {
1390 wpa_msg(hapd->msg_ctx, MSG_DEBUG, "GAS: %d more bytes remain "
1391 "to be sent",
1392 (int) (wpabuf_len(dialog->sd_resp) -
1393 dialog->sd_resp_pos));
1394 } else {
1395 wpa_msg(hapd->msg_ctx, MSG_DEBUG, "GAS: All fragments of "
1396 "SD response sent");
1397 gas_serv_dialog_clear(dialog);
1398 gas_serv_free_dialogs(hapd, sa);
1399 }
1400
1401send_resp:
Dmitry Shmidt18463232014-01-24 12:29:41 -08001402 if (prot)
1403 convert_to_protected_dual(tx_buf);
Dmitry Shmidt04949592012-07-19 12:16:46 -07001404 hostapd_drv_send_action(hapd, hapd->iface->freq, 0, sa,
1405 wpabuf_head(tx_buf), wpabuf_len(tx_buf));
1406 wpabuf_free(tx_buf);
Dmitry Shmidt04949592012-07-19 12:16:46 -07001407}
1408
1409
1410static void gas_serv_rx_public_action(void *ctx, const u8 *buf, size_t len,
1411 int freq)
1412{
1413 struct hostapd_data *hapd = ctx;
1414 const struct ieee80211_mgmt *mgmt;
Dmitry Shmidt04949592012-07-19 12:16:46 -07001415 const u8 *sa, *data;
Dmitry Shmidt18463232014-01-24 12:29:41 -08001416 int prot;
Dmitry Shmidt04949592012-07-19 12:16:46 -07001417
1418 mgmt = (const struct ieee80211_mgmt *) buf;
Dmitry Shmidt623d63a2014-06-13 11:05:14 -07001419 if (len < IEEE80211_HDRLEN + 2)
Dmitry Shmidt04949592012-07-19 12:16:46 -07001420 return;
Dmitry Shmidt18463232014-01-24 12:29:41 -08001421 if (mgmt->u.action.category != WLAN_ACTION_PUBLIC &&
1422 mgmt->u.action.category != WLAN_ACTION_PROTECTED_DUAL)
Dmitry Shmidt04949592012-07-19 12:16:46 -07001423 return;
Dmitry Shmidt18463232014-01-24 12:29:41 -08001424 /*
1425 * Note: Public Action and Protected Dual of Public Action frames share
1426 * the same payload structure, so it is fine to use definitions of
1427 * Public Action frames to process both.
1428 */
1429 prot = mgmt->u.action.category == WLAN_ACTION_PROTECTED_DUAL;
Dmitry Shmidt04949592012-07-19 12:16:46 -07001430 sa = mgmt->sa;
Dmitry Shmidt623d63a2014-06-13 11:05:14 -07001431 len -= IEEE80211_HDRLEN + 1;
1432 data = buf + IEEE80211_HDRLEN + 1;
Dmitry Shmidt04949592012-07-19 12:16:46 -07001433 switch (data[0]) {
1434 case WLAN_PA_GAS_INITIAL_REQ:
Dmitry Shmidt18463232014-01-24 12:29:41 -08001435 gas_serv_rx_gas_initial_req(hapd, sa, data + 1, len - 1, prot);
Dmitry Shmidt04949592012-07-19 12:16:46 -07001436 break;
1437 case WLAN_PA_GAS_COMEBACK_REQ:
Dmitry Shmidt18463232014-01-24 12:29:41 -08001438 gas_serv_rx_gas_comeback_req(hapd, sa, data + 1, len - 1, prot);
Dmitry Shmidt04949592012-07-19 12:16:46 -07001439 break;
1440 }
1441}
1442
1443
1444int gas_serv_init(struct hostapd_data *hapd)
1445{
Dmitry Shmidt4b9d52f2013-02-05 17:44:43 -08001446 hapd->public_action_cb2 = gas_serv_rx_public_action;
1447 hapd->public_action_cb2_ctx = hapd;
Dmitry Shmidt04949592012-07-19 12:16:46 -07001448 hapd->gas_frag_limit = 1400;
1449 if (hapd->conf->gas_frag_limit > 0)
1450 hapd->gas_frag_limit = hapd->conf->gas_frag_limit;
1451 return 0;
1452}
1453
1454
1455void gas_serv_deinit(struct hostapd_data *hapd)
1456{
1457}