blob: b5fb7dfbc21a96e4e5618e09567d28846a11c4ca [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) {
61 sta->gas_dialog = os_zalloc(GAS_DIALOG_MAX *
62 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;
74 dia->index = i;
75 dia->dialog_token = dialog_token;
76 sta->gas_dialog_next = (++i == GAS_DIALOG_MAX) ? 0 : i;
77 return dia;
78 }
79
80 wpa_msg(hapd->msg_ctx, MSG_ERROR, "ANQP: Could not create dialog for "
81 MACSTR " dialog_token %u. Consider increasing "
82 "GAS_DIALOG_MAX.", MAC2STR(addr), dialog_token);
83
84 return NULL;
85}
86
87
88struct gas_dialog_info *
89gas_serv_dialog_find(struct hostapd_data *hapd, const u8 *addr,
90 u8 dialog_token)
91{
92 struct sta_info *sta;
93 int i;
94
95 sta = ap_get_sta(hapd, addr);
96 if (!sta) {
97 wpa_printf(MSG_DEBUG, "ANQP: could not find STA " MACSTR,
98 MAC2STR(addr));
99 return NULL;
100 }
101 for (i = 0; sta->gas_dialog && i < GAS_DIALOG_MAX; i++) {
102 if (sta->gas_dialog[i].dialog_token != dialog_token ||
103 !sta->gas_dialog[i].valid)
104 continue;
105 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);
162 gas_anqp_set_element_len(buf, len);
163}
Dmitry Shmidtaa532512012-09-24 10:35:31 -0700164#endif /* CONFIG_HS20 */
Dmitry Shmidt61d9df32012-08-29 16:22:06 -0700165
166
Dmitry Shmidt04949592012-07-19 12:16:46 -0700167static void anqp_add_capab_list(struct hostapd_data *hapd,
168 struct wpabuf *buf)
169{
170 u8 *len;
171
172 len = gas_anqp_add_element(buf, ANQP_CAPABILITY_LIST);
173 wpabuf_put_le16(buf, ANQP_CAPABILITY_LIST);
174 if (hapd->conf->venue_name)
175 wpabuf_put_le16(buf, ANQP_VENUE_NAME);
Dmitry Shmidt61d9df32012-08-29 16:22:06 -0700176 if (hapd->conf->network_auth_type)
177 wpabuf_put_le16(buf, ANQP_NETWORK_AUTH_TYPE);
Dmitry Shmidt04949592012-07-19 12:16:46 -0700178 if (hapd->conf->roaming_consortium)
179 wpabuf_put_le16(buf, ANQP_ROAMING_CONSORTIUM);
Dmitry Shmidt61d9df32012-08-29 16:22:06 -0700180 if (hapd->conf->ipaddr_type_configured)
181 wpabuf_put_le16(buf, ANQP_IP_ADDR_TYPE_AVAILABILITY);
182 if (hapd->conf->nai_realm_data)
183 wpabuf_put_le16(buf, ANQP_NAI_REALM);
184 if (hapd->conf->anqp_3gpp_cell_net)
185 wpabuf_put_le16(buf, ANQP_3GPP_CELLULAR_NETWORK);
186 if (hapd->conf->domain_name)
187 wpabuf_put_le16(buf, ANQP_DOMAIN_NAME);
Dmitry Shmidtaa532512012-09-24 10:35:31 -0700188#ifdef CONFIG_HS20
Dmitry Shmidt61d9df32012-08-29 16:22:06 -0700189 anqp_add_hs_capab_list(hapd, buf);
Dmitry Shmidtaa532512012-09-24 10:35:31 -0700190#endif /* CONFIG_HS20 */
Dmitry Shmidt04949592012-07-19 12:16:46 -0700191 gas_anqp_set_element_len(buf, len);
192}
193
194
195static void anqp_add_venue_name(struct hostapd_data *hapd, struct wpabuf *buf)
196{
197 if (hapd->conf->venue_name) {
198 u8 *len;
199 unsigned int i;
200 len = gas_anqp_add_element(buf, ANQP_VENUE_NAME);
201 wpabuf_put_u8(buf, hapd->conf->venue_group);
202 wpabuf_put_u8(buf, hapd->conf->venue_type);
203 for (i = 0; i < hapd->conf->venue_name_count; i++) {
Dmitry Shmidt61d9df32012-08-29 16:22:06 -0700204 struct hostapd_lang_string *vn;
Dmitry Shmidt04949592012-07-19 12:16:46 -0700205 vn = &hapd->conf->venue_name[i];
206 wpabuf_put_u8(buf, 3 + vn->name_len);
207 wpabuf_put_data(buf, vn->lang, 3);
208 wpabuf_put_data(buf, vn->name, vn->name_len);
209 }
210 gas_anqp_set_element_len(buf, len);
211 }
212}
213
214
Dmitry Shmidt61d9df32012-08-29 16:22:06 -0700215static void anqp_add_network_auth_type(struct hostapd_data *hapd,
216 struct wpabuf *buf)
217{
218 if (hapd->conf->network_auth_type) {
219 wpabuf_put_le16(buf, ANQP_NETWORK_AUTH_TYPE);
220 wpabuf_put_le16(buf, hapd->conf->network_auth_type_len);
221 wpabuf_put_data(buf, hapd->conf->network_auth_type,
222 hapd->conf->network_auth_type_len);
223 }
224}
225
226
Dmitry Shmidt04949592012-07-19 12:16:46 -0700227static void anqp_add_roaming_consortium(struct hostapd_data *hapd,
228 struct wpabuf *buf)
229{
230 unsigned int i;
231 u8 *len;
232
233 len = gas_anqp_add_element(buf, ANQP_ROAMING_CONSORTIUM);
234 for (i = 0; i < hapd->conf->roaming_consortium_count; i++) {
235 struct hostapd_roaming_consortium *rc;
236 rc = &hapd->conf->roaming_consortium[i];
237 wpabuf_put_u8(buf, rc->len);
238 wpabuf_put_data(buf, rc->oi, rc->len);
239 }
240 gas_anqp_set_element_len(buf, len);
241}
242
243
Dmitry Shmidt61d9df32012-08-29 16:22:06 -0700244static void anqp_add_ip_addr_type_availability(struct hostapd_data *hapd,
245 struct wpabuf *buf)
246{
247 if (hapd->conf->ipaddr_type_configured) {
248 wpabuf_put_le16(buf, ANQP_IP_ADDR_TYPE_AVAILABILITY);
249 wpabuf_put_le16(buf, 1);
250 wpabuf_put_u8(buf, hapd->conf->ipaddr_type_availability);
251 }
252}
253
254
255static void anqp_add_nai_realm_eap(struct wpabuf *buf,
256 struct hostapd_nai_realm_data *realm)
257{
258 unsigned int i, j;
259
260 wpabuf_put_u8(buf, realm->eap_method_count);
261
262 for (i = 0; i < realm->eap_method_count; i++) {
263 struct hostapd_nai_realm_eap *eap = &realm->eap_method[i];
264 wpabuf_put_u8(buf, 2 + (3 * eap->num_auths));
265 wpabuf_put_u8(buf, eap->eap_method);
266 wpabuf_put_u8(buf, eap->num_auths);
267 for (j = 0; j < eap->num_auths; j++) {
268 wpabuf_put_u8(buf, eap->auth_id[j]);
269 wpabuf_put_u8(buf, 1);
270 wpabuf_put_u8(buf, eap->auth_val[j]);
271 }
272 }
273}
274
275
276static void anqp_add_nai_realm_data(struct wpabuf *buf,
277 struct hostapd_nai_realm_data *realm,
278 unsigned int realm_idx)
279{
280 u8 *realm_data_len;
281
282 wpa_printf(MSG_DEBUG, "realm=%s, len=%d", realm->realm[realm_idx],
283 (int) os_strlen(realm->realm[realm_idx]));
284 realm_data_len = wpabuf_put(buf, 2);
285 wpabuf_put_u8(buf, realm->encoding);
286 wpabuf_put_u8(buf, os_strlen(realm->realm[realm_idx]));
287 wpabuf_put_str(buf, realm->realm[realm_idx]);
288 anqp_add_nai_realm_eap(buf, realm);
289 gas_anqp_set_element_len(buf, realm_data_len);
290}
291
292
293static int hs20_add_nai_home_realm_matches(struct hostapd_data *hapd,
294 struct wpabuf *buf,
295 const u8 *home_realm,
296 size_t home_realm_len)
297{
298 unsigned int i, j, k;
299 u8 num_realms, num_matching = 0, encoding, realm_len, *realm_list_len;
300 struct hostapd_nai_realm_data *realm;
301 const u8 *pos, *realm_name, *end;
302 struct {
303 unsigned int realm_data_idx;
304 unsigned int realm_idx;
305 } matches[10];
306
307 pos = home_realm;
308 end = pos + home_realm_len;
309 if (pos + 1 > end) {
310 wpa_hexdump(MSG_DEBUG, "Too short NAI Home Realm Query",
311 home_realm, home_realm_len);
312 return -1;
313 }
314 num_realms = *pos++;
315
316 for (i = 0; i < num_realms && num_matching < 10; i++) {
317 if (pos + 2 > end) {
318 wpa_hexdump(MSG_DEBUG,
319 "Truncated NAI Home Realm Query",
320 home_realm, home_realm_len);
321 return -1;
322 }
323 encoding = *pos++;
324 realm_len = *pos++;
325 if (pos + realm_len > end) {
326 wpa_hexdump(MSG_DEBUG,
327 "Truncated NAI Home Realm Query",
328 home_realm, home_realm_len);
329 return -1;
330 }
331 realm_name = pos;
332 for (j = 0; j < hapd->conf->nai_realm_count &&
333 num_matching < 10; j++) {
334 const u8 *rpos, *rend;
335 realm = &hapd->conf->nai_realm_data[j];
336 if (encoding != realm->encoding)
337 continue;
338
339 rpos = realm_name;
340 while (rpos < realm_name + realm_len &&
341 num_matching < 10) {
342 for (rend = rpos;
343 rend < realm_name + realm_len; rend++) {
344 if (*rend == ';')
345 break;
346 }
347 for (k = 0; k < MAX_NAI_REALMS &&
348 realm->realm[k] &&
349 num_matching < 10; k++) {
350 if ((int) os_strlen(realm->realm[k]) !=
351 rend - rpos ||
352 os_strncmp((char *) rpos,
353 realm->realm[k],
354 rend - rpos) != 0)
355 continue;
356 matches[num_matching].realm_data_idx =
357 j;
358 matches[num_matching].realm_idx = k;
359 num_matching++;
360 }
361 rpos = rend + 1;
362 }
363 }
364 pos += realm_len;
365 }
366
367 realm_list_len = gas_anqp_add_element(buf, ANQP_NAI_REALM);
368 wpabuf_put_le16(buf, num_matching);
369
370 /*
371 * There are two ways to format. 1. each realm in a NAI Realm Data unit
372 * 2. all realms that share the same EAP methods in a NAI Realm Data
373 * unit. The first format is likely to be bigger in size than the
374 * second, but may be easier to parse and process by the receiver.
375 */
376 for (i = 0; i < num_matching; i++) {
377 wpa_printf(MSG_DEBUG, "realm_idx %d, realm_data_idx %d",
378 matches[i].realm_data_idx, matches[i].realm_idx);
379 realm = &hapd->conf->nai_realm_data[matches[i].realm_data_idx];
380 anqp_add_nai_realm_data(buf, realm, matches[i].realm_idx);
381 }
382 gas_anqp_set_element_len(buf, realm_list_len);
383 return 0;
384}
385
386
387static void anqp_add_nai_realm(struct hostapd_data *hapd, struct wpabuf *buf,
388 const u8 *home_realm, size_t home_realm_len,
389 int nai_realm, int nai_home_realm)
390{
391 if (nai_realm && hapd->conf->nai_realm_data) {
392 u8 *len;
393 unsigned int i, j;
394 len = gas_anqp_add_element(buf, ANQP_NAI_REALM);
395 wpabuf_put_le16(buf, hapd->conf->nai_realm_count);
396 for (i = 0; i < hapd->conf->nai_realm_count; i++) {
397 u8 *realm_data_len, *realm_len;
398 struct hostapd_nai_realm_data *realm;
399
400 realm = &hapd->conf->nai_realm_data[i];
401 realm_data_len = wpabuf_put(buf, 2);
402 wpabuf_put_u8(buf, realm->encoding);
403 realm_len = wpabuf_put(buf, 1);
404 for (j = 0; realm->realm[j]; j++) {
405 if (j > 0)
406 wpabuf_put_u8(buf, ';');
407 wpabuf_put_str(buf, realm->realm[j]);
408 }
409 *realm_len = (u8 *) wpabuf_put(buf, 0) - realm_len - 1;
410 anqp_add_nai_realm_eap(buf, realm);
411 gas_anqp_set_element_len(buf, realm_data_len);
412 }
413 gas_anqp_set_element_len(buf, len);
414 } else if (nai_home_realm && hapd->conf->nai_realm_data) {
415 hs20_add_nai_home_realm_matches(hapd, buf, home_realm,
416 home_realm_len);
417 }
418}
419
420
421static void anqp_add_3gpp_cellular_network(struct hostapd_data *hapd,
422 struct wpabuf *buf)
423{
424 if (hapd->conf->anqp_3gpp_cell_net) {
425 wpabuf_put_le16(buf, ANQP_3GPP_CELLULAR_NETWORK);
426 wpabuf_put_le16(buf,
427 hapd->conf->anqp_3gpp_cell_net_len);
428 wpabuf_put_data(buf, hapd->conf->anqp_3gpp_cell_net,
429 hapd->conf->anqp_3gpp_cell_net_len);
430 }
431}
432
433
434static void anqp_add_domain_name(struct hostapd_data *hapd, struct wpabuf *buf)
435{
436 if (hapd->conf->domain_name) {
437 wpabuf_put_le16(buf, ANQP_DOMAIN_NAME);
438 wpabuf_put_le16(buf, hapd->conf->domain_name_len);
439 wpabuf_put_data(buf, hapd->conf->domain_name,
440 hapd->conf->domain_name_len);
441 }
442}
443
444
Dmitry Shmidtaa532512012-09-24 10:35:31 -0700445#ifdef CONFIG_HS20
446
Dmitry Shmidt61d9df32012-08-29 16:22:06 -0700447static void anqp_add_operator_friendly_name(struct hostapd_data *hapd,
448 struct wpabuf *buf)
449{
450 if (hapd->conf->hs20_oper_friendly_name) {
451 u8 *len;
452 unsigned int i;
453 len = gas_anqp_add_element(buf, ANQP_VENDOR_SPECIFIC);
454 wpabuf_put_be24(buf, OUI_WFA);
455 wpabuf_put_u8(buf, HS20_ANQP_OUI_TYPE);
456 wpabuf_put_u8(buf, HS20_STYPE_OPERATOR_FRIENDLY_NAME);
457 wpabuf_put_u8(buf, 0); /* Reserved */
458 for (i = 0; i < hapd->conf->hs20_oper_friendly_name_count; i++)
459 {
460 struct hostapd_lang_string *vn;
461 vn = &hapd->conf->hs20_oper_friendly_name[i];
462 wpabuf_put_u8(buf, 3 + vn->name_len);
463 wpabuf_put_data(buf, vn->lang, 3);
464 wpabuf_put_data(buf, vn->name, vn->name_len);
465 }
466 gas_anqp_set_element_len(buf, len);
467 }
468}
469
470
471static void anqp_add_wan_metrics(struct hostapd_data *hapd,
472 struct wpabuf *buf)
473{
474 if (hapd->conf->hs20_wan_metrics) {
475 u8 *len = gas_anqp_add_element(buf, ANQP_VENDOR_SPECIFIC);
476 wpabuf_put_be24(buf, OUI_WFA);
477 wpabuf_put_u8(buf, HS20_ANQP_OUI_TYPE);
478 wpabuf_put_u8(buf, HS20_STYPE_WAN_METRICS);
479 wpabuf_put_u8(buf, 0); /* Reserved */
480 wpabuf_put_data(buf, hapd->conf->hs20_wan_metrics, 13);
481 gas_anqp_set_element_len(buf, len);
482 }
483}
484
485
486static void anqp_add_connection_capability(struct hostapd_data *hapd,
487 struct wpabuf *buf)
488{
489 if (hapd->conf->hs20_connection_capability) {
490 u8 *len = gas_anqp_add_element(buf, ANQP_VENDOR_SPECIFIC);
491 wpabuf_put_be24(buf, OUI_WFA);
492 wpabuf_put_u8(buf, HS20_ANQP_OUI_TYPE);
493 wpabuf_put_u8(buf, HS20_STYPE_CONNECTION_CAPABILITY);
494 wpabuf_put_u8(buf, 0); /* Reserved */
495 wpabuf_put_data(buf, hapd->conf->hs20_connection_capability,
496 hapd->conf->hs20_connection_capability_len);
497 gas_anqp_set_element_len(buf, len);
498 }
499}
500
501
502static void anqp_add_operating_class(struct hostapd_data *hapd,
503 struct wpabuf *buf)
504{
505 if (hapd->conf->hs20_operating_class) {
506 u8 *len = gas_anqp_add_element(buf, ANQP_VENDOR_SPECIFIC);
507 wpabuf_put_be24(buf, OUI_WFA);
508 wpabuf_put_u8(buf, HS20_ANQP_OUI_TYPE);
509 wpabuf_put_u8(buf, HS20_STYPE_OPERATING_CLASS);
510 wpabuf_put_u8(buf, 0); /* Reserved */
511 wpabuf_put_data(buf, hapd->conf->hs20_operating_class,
512 hapd->conf->hs20_operating_class_len);
513 gas_anqp_set_element_len(buf, len);
514 }
515}
516
Dmitry Shmidtaa532512012-09-24 10:35:31 -0700517#endif /* CONFIG_HS20 */
518
Dmitry Shmidt61d9df32012-08-29 16:22:06 -0700519
Dmitry Shmidt04949592012-07-19 12:16:46 -0700520static struct wpabuf *
521gas_serv_build_gas_resp_payload(struct hostapd_data *hapd,
522 unsigned int request,
Dmitry Shmidt61d9df32012-08-29 16:22:06 -0700523 struct gas_dialog_info *di,
524 const u8 *home_realm, size_t home_realm_len)
Dmitry Shmidt04949592012-07-19 12:16:46 -0700525{
526 struct wpabuf *buf;
527
528 buf = wpabuf_alloc(1400);
529 if (buf == NULL)
530 return NULL;
531
532 if (request & ANQP_REQ_CAPABILITY_LIST)
533 anqp_add_capab_list(hapd, buf);
534 if (request & ANQP_REQ_VENUE_NAME)
535 anqp_add_venue_name(hapd, buf);
Dmitry Shmidt61d9df32012-08-29 16:22:06 -0700536 if (request & ANQP_REQ_NETWORK_AUTH_TYPE)
537 anqp_add_network_auth_type(hapd, buf);
Dmitry Shmidt04949592012-07-19 12:16:46 -0700538 if (request & ANQP_REQ_ROAMING_CONSORTIUM)
539 anqp_add_roaming_consortium(hapd, buf);
Dmitry Shmidt61d9df32012-08-29 16:22:06 -0700540 if (request & ANQP_REQ_IP_ADDR_TYPE_AVAILABILITY)
541 anqp_add_ip_addr_type_availability(hapd, buf);
542 if (request & (ANQP_REQ_NAI_REALM | ANQP_REQ_NAI_HOME_REALM))
543 anqp_add_nai_realm(hapd, buf, home_realm, home_realm_len,
544 request & ANQP_REQ_NAI_REALM,
545 request & ANQP_REQ_NAI_HOME_REALM);
546 if (request & ANQP_REQ_3GPP_CELLULAR_NETWORK)
547 anqp_add_3gpp_cellular_network(hapd, buf);
548 if (request & ANQP_REQ_DOMAIN_NAME)
549 anqp_add_domain_name(hapd, buf);
550
Dmitry Shmidtaa532512012-09-24 10:35:31 -0700551#ifdef CONFIG_HS20
Dmitry Shmidt61d9df32012-08-29 16:22:06 -0700552 if (request & ANQP_REQ_HS_CAPABILITY_LIST)
553 anqp_add_hs_capab_list(hapd, buf);
554 if (request & ANQP_REQ_OPERATOR_FRIENDLY_NAME)
555 anqp_add_operator_friendly_name(hapd, buf);
556 if (request & ANQP_REQ_WAN_METRICS)
557 anqp_add_wan_metrics(hapd, buf);
558 if (request & ANQP_REQ_CONNECTION_CAPABILITY)
559 anqp_add_connection_capability(hapd, buf);
560 if (request & ANQP_REQ_OPERATING_CLASS)
561 anqp_add_operating_class(hapd, buf);
Dmitry Shmidtaa532512012-09-24 10:35:31 -0700562#endif /* CONFIG_HS20 */
Dmitry Shmidt04949592012-07-19 12:16:46 -0700563
564 return buf;
565}
566
567
568static void gas_serv_clear_cached_ies(void *eloop_data, void *user_ctx)
569{
570 struct gas_dialog_info *dia = eloop_data;
571
572 wpa_printf(MSG_DEBUG, "GAS: Timeout triggered, clearing dialog for "
573 "dialog token %d", dia->dialog_token);
574
575 gas_serv_dialog_clear(dia);
576}
577
578
579struct anqp_query_info {
580 unsigned int request;
581 unsigned int remote_request;
Dmitry Shmidt61d9df32012-08-29 16:22:06 -0700582 const u8 *home_realm_query;
583 size_t home_realm_query_len;
Dmitry Shmidt04949592012-07-19 12:16:46 -0700584 u16 remote_delay;
585};
586
587
588static void set_anqp_req(unsigned int bit, const char *name, int local,
589 unsigned int remote, u16 remote_delay,
590 struct anqp_query_info *qi)
591{
592 qi->request |= bit;
593 if (local) {
594 wpa_printf(MSG_DEBUG, "ANQP: %s (local)", name);
595 } else if (bit & remote) {
596 wpa_printf(MSG_DEBUG, "ANQP: %s (remote)", name);
597 qi->remote_request |= bit;
598 if (remote_delay > qi->remote_delay)
599 qi->remote_delay = remote_delay;
600 } else {
601 wpa_printf(MSG_DEBUG, "ANQP: %s not available", name);
602 }
603}
604
605
606static void rx_anqp_query_list_id(struct hostapd_data *hapd, u16 info_id,
607 struct anqp_query_info *qi)
608{
609 switch (info_id) {
610 case ANQP_CAPABILITY_LIST:
611 set_anqp_req(ANQP_REQ_CAPABILITY_LIST, "Capability List", 1, 0,
612 0, qi);
613 break;
614 case ANQP_VENUE_NAME:
615 set_anqp_req(ANQP_REQ_VENUE_NAME, "Venue Name",
616 hapd->conf->venue_name != NULL, 0, 0, qi);
617 break;
Dmitry Shmidt61d9df32012-08-29 16:22:06 -0700618 case ANQP_NETWORK_AUTH_TYPE:
619 set_anqp_req(ANQP_REQ_NETWORK_AUTH_TYPE, "Network Auth Type",
620 hapd->conf->network_auth_type != NULL,
621 0, 0, qi);
622 break;
Dmitry Shmidt04949592012-07-19 12:16:46 -0700623 case ANQP_ROAMING_CONSORTIUM:
624 set_anqp_req(ANQP_REQ_ROAMING_CONSORTIUM, "Roaming Consortium",
625 hapd->conf->roaming_consortium != NULL, 0, 0, qi);
626 break;
Dmitry Shmidt61d9df32012-08-29 16:22:06 -0700627 case ANQP_IP_ADDR_TYPE_AVAILABILITY:
628 set_anqp_req(ANQP_REQ_IP_ADDR_TYPE_AVAILABILITY,
629 "IP Addr Type Availability",
630 hapd->conf->ipaddr_type_configured,
631 0, 0, qi);
632 break;
633 case ANQP_NAI_REALM:
634 set_anqp_req(ANQP_REQ_NAI_REALM, "NAI Realm",
635 hapd->conf->nai_realm_data != NULL,
636 0, 0, qi);
637 break;
638 case ANQP_3GPP_CELLULAR_NETWORK:
639 set_anqp_req(ANQP_REQ_3GPP_CELLULAR_NETWORK,
640 "3GPP Cellular Network",
641 hapd->conf->anqp_3gpp_cell_net != NULL,
642 0, 0, qi);
643 break;
644 case ANQP_DOMAIN_NAME:
645 set_anqp_req(ANQP_REQ_DOMAIN_NAME, "Domain Name",
646 hapd->conf->domain_name != NULL,
647 0, 0, qi);
648 break;
Dmitry Shmidt04949592012-07-19 12:16:46 -0700649 default:
650 wpa_printf(MSG_DEBUG, "ANQP: Unsupported Info Id %u",
651 info_id);
652 break;
653 }
654}
655
656
657static void rx_anqp_query_list(struct hostapd_data *hapd,
658 const u8 *pos, const u8 *end,
659 struct anqp_query_info *qi)
660{
661 wpa_printf(MSG_DEBUG, "ANQP: %u Info IDs requested in Query list",
662 (unsigned int) (end - pos) / 2);
663
664 while (pos + 2 <= end) {
665 rx_anqp_query_list_id(hapd, WPA_GET_LE16(pos), qi);
666 pos += 2;
667 }
668}
669
670
Dmitry Shmidtaa532512012-09-24 10:35:31 -0700671#ifdef CONFIG_HS20
672
Dmitry Shmidt61d9df32012-08-29 16:22:06 -0700673static void rx_anqp_hs_query_list(struct hostapd_data *hapd, u8 subtype,
674 struct anqp_query_info *qi)
675{
676 switch (subtype) {
677 case HS20_STYPE_CAPABILITY_LIST:
678 set_anqp_req(ANQP_REQ_HS_CAPABILITY_LIST, "HS Capability List",
679 1, 0, 0, qi);
680 break;
681 case HS20_STYPE_OPERATOR_FRIENDLY_NAME:
682 set_anqp_req(ANQP_REQ_OPERATOR_FRIENDLY_NAME,
683 "Operator Friendly Name",
684 hapd->conf->hs20_oper_friendly_name != NULL,
685 0, 0, qi);
686 break;
687 case HS20_STYPE_WAN_METRICS:
688 set_anqp_req(ANQP_REQ_WAN_METRICS, "WAN Metrics",
689 hapd->conf->hs20_wan_metrics != NULL,
690 0, 0, qi);
691 break;
692 case HS20_STYPE_CONNECTION_CAPABILITY:
693 set_anqp_req(ANQP_REQ_CONNECTION_CAPABILITY,
694 "Connection Capability",
695 hapd->conf->hs20_connection_capability != NULL,
696 0, 0, qi);
697 break;
698 case HS20_STYPE_OPERATING_CLASS:
699 set_anqp_req(ANQP_REQ_OPERATING_CLASS, "Operating Class",
700 hapd->conf->hs20_operating_class != NULL,
701 0, 0, qi);
702 break;
703 default:
704 wpa_printf(MSG_DEBUG, "ANQP: Unsupported HS 2.0 subtype %u",
705 subtype);
706 break;
707 }
708}
709
710
711static void rx_anqp_hs_nai_home_realm(struct hostapd_data *hapd,
712 const u8 *pos, const u8 *end,
713 struct anqp_query_info *qi)
714{
715 qi->request |= ANQP_REQ_NAI_HOME_REALM;
716 qi->home_realm_query = pos;
717 qi->home_realm_query_len = end - pos;
718 if (hapd->conf->nai_realm_data != NULL) {
719 wpa_printf(MSG_DEBUG, "ANQP: HS 2.0 NAI Home Realm Query "
720 "(local)");
721 } else {
722 wpa_printf(MSG_DEBUG, "ANQP: HS 2.0 NAI Home Realm Query not "
723 "available");
724 }
725}
726
727
728static void rx_anqp_vendor_specific(struct hostapd_data *hapd,
729 const u8 *pos, const u8 *end,
730 struct anqp_query_info *qi)
731{
732 u32 oui;
733 u8 subtype;
734
735 if (pos + 4 > end) {
736 wpa_printf(MSG_DEBUG, "ANQP: Too short vendor specific ANQP "
737 "Query element");
738 return;
739 }
740
741 oui = WPA_GET_BE24(pos);
742 pos += 3;
743 if (oui != OUI_WFA) {
744 wpa_printf(MSG_DEBUG, "ANQP: Unsupported vendor OUI %06x",
745 oui);
746 return;
747 }
748
749 if (*pos != HS20_ANQP_OUI_TYPE) {
750 wpa_printf(MSG_DEBUG, "ANQP: Unsupported WFA vendor type %u",
751 *pos);
752 return;
753 }
754 pos++;
755
756 if (pos + 1 >= end)
757 return;
758
759 subtype = *pos++;
760 pos++; /* Reserved */
761 switch (subtype) {
762 case HS20_STYPE_QUERY_LIST:
763 wpa_printf(MSG_DEBUG, "ANQP: HS 2.0 Query List");
764 while (pos < end) {
765 rx_anqp_hs_query_list(hapd, *pos, qi);
766 pos++;
767 }
768 break;
769 case HS20_STYPE_NAI_HOME_REALM_QUERY:
770 rx_anqp_hs_nai_home_realm(hapd, pos, end, qi);
771 break;
772 default:
773 wpa_printf(MSG_DEBUG, "ANQP: Unsupported HS 2.0 query subtype "
774 "%u", subtype);
775 break;
776 }
777}
778
Dmitry Shmidtaa532512012-09-24 10:35:31 -0700779#endif /* CONFIG_HS20 */
780
Dmitry Shmidt61d9df32012-08-29 16:22:06 -0700781
Dmitry Shmidt04949592012-07-19 12:16:46 -0700782static void gas_serv_req_local_processing(struct hostapd_data *hapd,
783 const u8 *sa, u8 dialog_token,
Dmitry Shmidt18463232014-01-24 12:29:41 -0800784 struct anqp_query_info *qi, int prot)
Dmitry Shmidt04949592012-07-19 12:16:46 -0700785{
786 struct wpabuf *buf, *tx_buf;
787
Dmitry Shmidt61d9df32012-08-29 16:22:06 -0700788 buf = gas_serv_build_gas_resp_payload(hapd, qi->request, NULL,
789 qi->home_realm_query,
790 qi->home_realm_query_len);
Dmitry Shmidt04949592012-07-19 12:16:46 -0700791 wpa_hexdump_buf(MSG_MSGDUMP, "ANQP: Locally generated ANQP responses",
792 buf);
793 if (!buf)
794 return;
795
796 if (wpabuf_len(buf) > hapd->gas_frag_limit ||
797 hapd->conf->gas_comeback_delay) {
798 struct gas_dialog_info *di;
799 u16 comeback_delay = 1;
800
801 if (hapd->conf->gas_comeback_delay) {
802 /* Testing - allow overriding of the delay value */
803 comeback_delay = hapd->conf->gas_comeback_delay;
804 }
805
806 wpa_printf(MSG_DEBUG, "ANQP: Too long response to fit in "
807 "initial response - use GAS comeback");
808 di = gas_dialog_create(hapd, sa, dialog_token);
809 if (!di) {
810 wpa_printf(MSG_INFO, "ANQP: Could not create dialog "
811 "for " MACSTR " (dialog token %u)",
812 MAC2STR(sa), dialog_token);
813 wpabuf_free(buf);
814 return;
815 }
Dmitry Shmidt18463232014-01-24 12:29:41 -0800816 di->prot = prot;
Dmitry Shmidt04949592012-07-19 12:16:46 -0700817 di->sd_resp = buf;
818 di->sd_resp_pos = 0;
819 tx_buf = gas_anqp_build_initial_resp_buf(
820 dialog_token, WLAN_STATUS_SUCCESS, comeback_delay,
821 NULL);
822 } else {
823 wpa_printf(MSG_DEBUG, "ANQP: Initial response (no comeback)");
824 tx_buf = gas_anqp_build_initial_resp_buf(
825 dialog_token, WLAN_STATUS_SUCCESS, 0, buf);
826 wpabuf_free(buf);
827 }
828 if (!tx_buf)
829 return;
Dmitry Shmidt18463232014-01-24 12:29:41 -0800830 if (prot)
831 convert_to_protected_dual(tx_buf);
Dmitry Shmidt04949592012-07-19 12:16:46 -0700832 hostapd_drv_send_action(hapd, hapd->iface->freq, 0, sa,
833 wpabuf_head(tx_buf), wpabuf_len(tx_buf));
834 wpabuf_free(tx_buf);
835}
836
837
838static void gas_serv_rx_gas_initial_req(struct hostapd_data *hapd,
839 const u8 *sa,
Dmitry Shmidt18463232014-01-24 12:29:41 -0800840 const u8 *data, size_t len, int prot)
Dmitry Shmidt04949592012-07-19 12:16:46 -0700841{
842 const u8 *pos = data;
843 const u8 *end = data + len;
844 const u8 *next;
845 u8 dialog_token;
846 u16 slen;
847 struct anqp_query_info qi;
848 const u8 *adv_proto;
849
850 if (len < 1 + 2)
851 return;
852
853 os_memset(&qi, 0, sizeof(qi));
854
855 dialog_token = *pos++;
856 wpa_msg(hapd->msg_ctx, MSG_DEBUG,
857 "GAS: GAS Initial Request from " MACSTR " (dialog token %u) ",
858 MAC2STR(sa), dialog_token);
859
860 if (*pos != WLAN_EID_ADV_PROTO) {
861 wpa_msg(hapd->msg_ctx, MSG_DEBUG,
862 "GAS: Unexpected IE in GAS Initial Request: %u", *pos);
863 return;
864 }
865 adv_proto = pos++;
866
867 slen = *pos++;
868 next = pos + slen;
869 if (next > end || slen < 2) {
870 wpa_msg(hapd->msg_ctx, MSG_DEBUG,
871 "GAS: Invalid IE in GAS Initial Request");
872 return;
873 }
874 pos++; /* skip QueryRespLenLimit and PAME-BI */
875
876 if (*pos != ACCESS_NETWORK_QUERY_PROTOCOL) {
877 struct wpabuf *buf;
878 wpa_msg(hapd->msg_ctx, MSG_DEBUG,
879 "GAS: Unsupported GAS advertisement protocol id %u",
880 *pos);
881 if (sa[0] & 0x01)
882 return; /* Invalid source address - drop silently */
883 buf = gas_build_initial_resp(
884 dialog_token, WLAN_STATUS_GAS_ADV_PROTO_NOT_SUPPORTED,
885 0, 2 + slen + 2);
886 if (buf == NULL)
887 return;
888 wpabuf_put_data(buf, adv_proto, 2 + slen);
889 wpabuf_put_le16(buf, 0); /* Query Response Length */
Dmitry Shmidt18463232014-01-24 12:29:41 -0800890 if (prot)
891 convert_to_protected_dual(buf);
Dmitry Shmidt04949592012-07-19 12:16:46 -0700892 hostapd_drv_send_action(hapd, hapd->iface->freq, 0, sa,
893 wpabuf_head(buf), wpabuf_len(buf));
894 wpabuf_free(buf);
895 return;
896 }
897
898 pos = next;
899 /* Query Request */
900 if (pos + 2 > end)
901 return;
902 slen = WPA_GET_LE16(pos);
903 pos += 2;
904 if (pos + slen > end)
905 return;
906 end = pos + slen;
907
908 /* ANQP Query Request */
909 while (pos < end) {
910 u16 info_id, elen;
911
912 if (pos + 4 > end)
913 return;
914
915 info_id = WPA_GET_LE16(pos);
916 pos += 2;
917 elen = WPA_GET_LE16(pos);
918 pos += 2;
919
920 if (pos + elen > end) {
921 wpa_printf(MSG_DEBUG, "ANQP: Invalid Query Request");
922 return;
923 }
924
925 switch (info_id) {
926 case ANQP_QUERY_LIST:
927 rx_anqp_query_list(hapd, pos, pos + elen, &qi);
928 break;
Dmitry Shmidtaa532512012-09-24 10:35:31 -0700929#ifdef CONFIG_HS20
Dmitry Shmidt61d9df32012-08-29 16:22:06 -0700930 case ANQP_VENDOR_SPECIFIC:
931 rx_anqp_vendor_specific(hapd, pos, pos + elen, &qi);
932 break;
Dmitry Shmidtaa532512012-09-24 10:35:31 -0700933#endif /* CONFIG_HS20 */
Dmitry Shmidt04949592012-07-19 12:16:46 -0700934 default:
935 wpa_printf(MSG_DEBUG, "ANQP: Unsupported Query "
936 "Request element %u", info_id);
937 break;
938 }
939
940 pos += elen;
941 }
942
Dmitry Shmidt18463232014-01-24 12:29:41 -0800943 gas_serv_req_local_processing(hapd, sa, dialog_token, &qi, prot);
Dmitry Shmidt04949592012-07-19 12:16:46 -0700944}
945
946
947void gas_serv_tx_gas_response(struct hostapd_data *hapd, const u8 *dst,
948 struct gas_dialog_info *dialog)
949{
950 struct wpabuf *buf, *tx_buf;
951 u8 dialog_token = dialog->dialog_token;
952 size_t frag_len;
953
954 if (dialog->sd_resp == NULL) {
955 buf = gas_serv_build_gas_resp_payload(hapd,
956 dialog->all_requested,
Dmitry Shmidt61d9df32012-08-29 16:22:06 -0700957 dialog, NULL, 0);
Dmitry Shmidt04949592012-07-19 12:16:46 -0700958 wpa_hexdump_buf(MSG_MSGDUMP, "ANQP: Generated ANQP responses",
959 buf);
960 if (!buf)
961 goto tx_gas_response_done;
962 dialog->sd_resp = buf;
963 dialog->sd_resp_pos = 0;
964 }
965 frag_len = wpabuf_len(dialog->sd_resp) - dialog->sd_resp_pos;
966 if (frag_len > hapd->gas_frag_limit || dialog->comeback_delay ||
967 hapd->conf->gas_comeback_delay) {
968 u16 comeback_delay_tus = dialog->comeback_delay +
969 GAS_SERV_COMEBACK_DELAY_FUDGE;
970 u32 comeback_delay_secs, comeback_delay_usecs;
971
972 if (hapd->conf->gas_comeback_delay) {
973 /* Testing - allow overriding of the delay value */
974 comeback_delay_tus = hapd->conf->gas_comeback_delay;
975 }
976
977 wpa_printf(MSG_DEBUG, "GAS: Response frag_len %u (frag limit "
978 "%u) and comeback delay %u, "
979 "requesting comebacks", (unsigned int) frag_len,
980 (unsigned int) hapd->gas_frag_limit,
981 dialog->comeback_delay);
982 tx_buf = gas_anqp_build_initial_resp_buf(dialog_token,
983 WLAN_STATUS_SUCCESS,
984 comeback_delay_tus,
985 NULL);
986 if (tx_buf) {
987 wpa_msg(hapd->msg_ctx, MSG_DEBUG,
988 "GAS: Tx GAS Initial Resp (comeback = 10TU)");
Dmitry Shmidt18463232014-01-24 12:29:41 -0800989 if (dialog->prot)
990 convert_to_protected_dual(tx_buf);
Dmitry Shmidt04949592012-07-19 12:16:46 -0700991 hostapd_drv_send_action(hapd, hapd->iface->freq, 0,
992 dst,
993 wpabuf_head(tx_buf),
994 wpabuf_len(tx_buf));
995 }
996 wpabuf_free(tx_buf);
997
998 /* start a timer of 1.5 * comeback-delay */
999 comeback_delay_tus = comeback_delay_tus +
1000 (comeback_delay_tus / 2);
1001 comeback_delay_secs = (comeback_delay_tus * 1024) / 1000000;
1002 comeback_delay_usecs = (comeback_delay_tus * 1024) -
1003 (comeback_delay_secs * 1000000);
1004 eloop_register_timeout(comeback_delay_secs,
1005 comeback_delay_usecs,
1006 gas_serv_clear_cached_ies, dialog,
1007 NULL);
1008 goto tx_gas_response_done;
1009 }
1010
1011 buf = wpabuf_alloc_copy(wpabuf_head_u8(dialog->sd_resp) +
1012 dialog->sd_resp_pos, frag_len);
1013 if (buf == NULL) {
1014 wpa_msg(hapd->msg_ctx, MSG_DEBUG, "GAS: Buffer allocation "
1015 "failed");
1016 goto tx_gas_response_done;
1017 }
1018 tx_buf = gas_anqp_build_initial_resp_buf(dialog_token,
1019 WLAN_STATUS_SUCCESS, 0, buf);
1020 wpabuf_free(buf);
1021 if (tx_buf == NULL)
1022 goto tx_gas_response_done;
1023 wpa_msg(hapd->msg_ctx, MSG_DEBUG, "GAS: Tx GAS Initial "
1024 "Response (frag_id %d frag_len %d)",
1025 dialog->sd_frag_id, (int) frag_len);
1026 dialog->sd_frag_id++;
1027
Dmitry Shmidt18463232014-01-24 12:29:41 -08001028 if (dialog->prot)
1029 convert_to_protected_dual(tx_buf);
Dmitry Shmidt04949592012-07-19 12:16:46 -07001030 hostapd_drv_send_action(hapd, hapd->iface->freq, 0, dst,
1031 wpabuf_head(tx_buf), wpabuf_len(tx_buf));
1032 wpabuf_free(tx_buf);
1033tx_gas_response_done:
1034 gas_serv_clear_cached_ies(dialog, NULL);
1035}
1036
1037
1038static void gas_serv_rx_gas_comeback_req(struct hostapd_data *hapd,
1039 const u8 *sa,
Dmitry Shmidt18463232014-01-24 12:29:41 -08001040 const u8 *data, size_t len, int prot)
Dmitry Shmidt04949592012-07-19 12:16:46 -07001041{
1042 struct gas_dialog_info *dialog;
1043 struct wpabuf *buf, *tx_buf;
1044 u8 dialog_token;
1045 size_t frag_len;
1046 int more = 0;
1047
1048 wpa_hexdump(MSG_DEBUG, "GAS: RX GAS Comeback Request", data, len);
1049 if (len < 1)
1050 return;
1051 dialog_token = *data;
1052 wpa_msg(hapd->msg_ctx, MSG_DEBUG, "GAS: Dialog Token: %u",
1053 dialog_token);
1054
1055 dialog = gas_serv_dialog_find(hapd, sa, dialog_token);
1056 if (!dialog) {
1057 wpa_msg(hapd->msg_ctx, MSG_DEBUG, "GAS: No pending SD "
1058 "response fragment for " MACSTR " dialog token %u",
1059 MAC2STR(sa), dialog_token);
1060
1061 if (sa[0] & 0x01)
1062 return; /* Invalid source address - drop silently */
1063 tx_buf = gas_anqp_build_comeback_resp_buf(
1064 dialog_token, WLAN_STATUS_NO_OUTSTANDING_GAS_REQ, 0, 0,
1065 0, NULL);
1066 if (tx_buf == NULL)
1067 return;
1068 goto send_resp;
1069 }
1070
1071 if (dialog->sd_resp == NULL) {
1072 wpa_printf(MSG_DEBUG, "GAS: Remote request 0x%x received 0x%x",
1073 dialog->requested, dialog->received);
1074 if ((dialog->requested & dialog->received) !=
1075 dialog->requested) {
1076 wpa_printf(MSG_DEBUG, "GAS: Did not receive response "
1077 "from remote processing");
1078 gas_serv_dialog_clear(dialog);
1079 tx_buf = gas_anqp_build_comeback_resp_buf(
1080 dialog_token,
1081 WLAN_STATUS_GAS_RESP_NOT_RECEIVED, 0, 0, 0,
1082 NULL);
1083 if (tx_buf == NULL)
1084 return;
1085 goto send_resp;
1086 }
1087
1088 buf = gas_serv_build_gas_resp_payload(hapd,
1089 dialog->all_requested,
Dmitry Shmidt61d9df32012-08-29 16:22:06 -07001090 dialog, NULL, 0);
Dmitry Shmidt04949592012-07-19 12:16:46 -07001091 wpa_hexdump_buf(MSG_MSGDUMP, "ANQP: Generated ANQP responses",
1092 buf);
1093 if (!buf)
1094 goto rx_gas_comeback_req_done;
1095 dialog->sd_resp = buf;
1096 dialog->sd_resp_pos = 0;
1097 }
1098 frag_len = wpabuf_len(dialog->sd_resp) - dialog->sd_resp_pos;
1099 if (frag_len > hapd->gas_frag_limit) {
1100 frag_len = hapd->gas_frag_limit;
1101 more = 1;
1102 }
1103 wpa_msg(hapd->msg_ctx, MSG_DEBUG, "GAS: resp frag_len %u",
1104 (unsigned int) frag_len);
1105 buf = wpabuf_alloc_copy(wpabuf_head_u8(dialog->sd_resp) +
1106 dialog->sd_resp_pos, frag_len);
1107 if (buf == NULL) {
1108 wpa_msg(hapd->msg_ctx, MSG_DEBUG, "GAS: Failed to allocate "
1109 "buffer");
1110 goto rx_gas_comeback_req_done;
1111 }
1112 tx_buf = gas_anqp_build_comeback_resp_buf(dialog_token,
1113 WLAN_STATUS_SUCCESS,
1114 dialog->sd_frag_id,
1115 more, 0, buf);
1116 wpabuf_free(buf);
1117 if (tx_buf == NULL)
1118 goto rx_gas_comeback_req_done;
1119 wpa_msg(hapd->msg_ctx, MSG_DEBUG, "GAS: Tx GAS Comeback Response "
1120 "(frag_id %d more=%d frag_len=%d)",
1121 dialog->sd_frag_id, more, (int) frag_len);
1122 dialog->sd_frag_id++;
1123 dialog->sd_resp_pos += frag_len;
1124
1125 if (more) {
1126 wpa_msg(hapd->msg_ctx, MSG_DEBUG, "GAS: %d more bytes remain "
1127 "to be sent",
1128 (int) (wpabuf_len(dialog->sd_resp) -
1129 dialog->sd_resp_pos));
1130 } else {
1131 wpa_msg(hapd->msg_ctx, MSG_DEBUG, "GAS: All fragments of "
1132 "SD response sent");
1133 gas_serv_dialog_clear(dialog);
1134 gas_serv_free_dialogs(hapd, sa);
1135 }
1136
1137send_resp:
Dmitry Shmidt18463232014-01-24 12:29:41 -08001138 if (prot)
1139 convert_to_protected_dual(tx_buf);
Dmitry Shmidt04949592012-07-19 12:16:46 -07001140 hostapd_drv_send_action(hapd, hapd->iface->freq, 0, sa,
1141 wpabuf_head(tx_buf), wpabuf_len(tx_buf));
1142 wpabuf_free(tx_buf);
1143 return;
1144
1145rx_gas_comeback_req_done:
1146 gas_serv_clear_cached_ies(dialog, NULL);
1147}
1148
1149
1150static void gas_serv_rx_public_action(void *ctx, const u8 *buf, size_t len,
1151 int freq)
1152{
1153 struct hostapd_data *hapd = ctx;
1154 const struct ieee80211_mgmt *mgmt;
1155 size_t hdr_len;
1156 const u8 *sa, *data;
Dmitry Shmidt18463232014-01-24 12:29:41 -08001157 int prot;
Dmitry Shmidt04949592012-07-19 12:16:46 -07001158
1159 mgmt = (const struct ieee80211_mgmt *) buf;
1160 hdr_len = (const u8 *) &mgmt->u.action.u.vs_public_action.action - buf;
1161 if (hdr_len > len)
1162 return;
Dmitry Shmidt18463232014-01-24 12:29:41 -08001163 if (mgmt->u.action.category != WLAN_ACTION_PUBLIC &&
1164 mgmt->u.action.category != WLAN_ACTION_PROTECTED_DUAL)
Dmitry Shmidt04949592012-07-19 12:16:46 -07001165 return;
Dmitry Shmidt18463232014-01-24 12:29:41 -08001166 /*
1167 * Note: Public Action and Protected Dual of Public Action frames share
1168 * the same payload structure, so it is fine to use definitions of
1169 * Public Action frames to process both.
1170 */
1171 prot = mgmt->u.action.category == WLAN_ACTION_PROTECTED_DUAL;
Dmitry Shmidt04949592012-07-19 12:16:46 -07001172 sa = mgmt->sa;
1173 len -= hdr_len;
1174 data = &mgmt->u.action.u.public_action.action;
1175 switch (data[0]) {
1176 case WLAN_PA_GAS_INITIAL_REQ:
Dmitry Shmidt18463232014-01-24 12:29:41 -08001177 gas_serv_rx_gas_initial_req(hapd, sa, data + 1, len - 1, prot);
Dmitry Shmidt04949592012-07-19 12:16:46 -07001178 break;
1179 case WLAN_PA_GAS_COMEBACK_REQ:
Dmitry Shmidt18463232014-01-24 12:29:41 -08001180 gas_serv_rx_gas_comeback_req(hapd, sa, data + 1, len - 1, prot);
Dmitry Shmidt04949592012-07-19 12:16:46 -07001181 break;
1182 }
1183}
1184
1185
1186int gas_serv_init(struct hostapd_data *hapd)
1187{
Dmitry Shmidt4b9d52f2013-02-05 17:44:43 -08001188 hapd->public_action_cb2 = gas_serv_rx_public_action;
1189 hapd->public_action_cb2_ctx = hapd;
Dmitry Shmidt04949592012-07-19 12:16:46 -07001190 hapd->gas_frag_limit = 1400;
1191 if (hapd->conf->gas_frag_limit > 0)
1192 hapd->gas_frag_limit = hapd->conf->gas_frag_limit;
1193 return 0;
1194}
1195
1196
1197void gas_serv_deinit(struct hostapd_data *hapd)
1198{
1199}