blob: 8349c4dd2e31fd186efa2a48ab01eea749d691fc [file] [log] [blame]
Dmitry Shmidt04949592012-07-19 12:16:46 -07001/*
2 * Generic advertisement service (GAS) server
3 * Copyright (c) 2011-2012, Qualcomm Atheros, Inc.
4 *
5 * This software may be distributed under the terms of the BSD license.
6 * See README for more details.
7 */
8
9#include "includes.h"
10
11#include "common.h"
12#include "common/ieee802_11_defs.h"
13#include "common/gas.h"
14#include "utils/eloop.h"
15#include "hostapd.h"
16#include "ap_config.h"
17#include "ap_drv_ops.h"
18#include "sta_info.h"
19#include "gas_serv.h"
20
21
22static struct gas_dialog_info *
23gas_dialog_create(struct hostapd_data *hapd, const u8 *addr, u8 dialog_token)
24{
25 struct sta_info *sta;
26 struct gas_dialog_info *dia = NULL;
27 int i, j;
28
29 sta = ap_get_sta(hapd, addr);
30 if (!sta) {
31 /*
32 * We need a STA entry to be able to maintain state for
33 * the GAS query.
34 */
35 wpa_printf(MSG_DEBUG, "ANQP: Add a temporary STA entry for "
36 "GAS query");
37 sta = ap_sta_add(hapd, addr);
38 if (!sta) {
39 wpa_printf(MSG_DEBUG, "Failed to add STA " MACSTR
40 " for GAS query", MAC2STR(addr));
41 return NULL;
42 }
43 sta->flags |= WLAN_STA_GAS;
44 /*
45 * The default inactivity is 300 seconds. We don't need
46 * it to be that long.
47 */
48 ap_sta_session_timeout(hapd, sta, 5);
Dmitry Shmidt54605472013-11-08 11:10:19 -080049 } else {
50 ap_sta_replenish_timeout(hapd, sta, 5);
Dmitry Shmidt04949592012-07-19 12:16:46 -070051 }
52
53 if (sta->gas_dialog == NULL) {
54 sta->gas_dialog = os_zalloc(GAS_DIALOG_MAX *
55 sizeof(struct gas_dialog_info));
56 if (sta->gas_dialog == NULL)
57 return NULL;
58 }
59
60 for (i = sta->gas_dialog_next, j = 0; j < GAS_DIALOG_MAX; i++, j++) {
61 if (i == GAS_DIALOG_MAX)
62 i = 0;
63 if (sta->gas_dialog[i].valid)
64 continue;
65 dia = &sta->gas_dialog[i];
66 dia->valid = 1;
67 dia->index = i;
68 dia->dialog_token = dialog_token;
69 sta->gas_dialog_next = (++i == GAS_DIALOG_MAX) ? 0 : i;
70 return dia;
71 }
72
73 wpa_msg(hapd->msg_ctx, MSG_ERROR, "ANQP: Could not create dialog for "
74 MACSTR " dialog_token %u. Consider increasing "
75 "GAS_DIALOG_MAX.", MAC2STR(addr), dialog_token);
76
77 return NULL;
78}
79
80
81struct gas_dialog_info *
82gas_serv_dialog_find(struct hostapd_data *hapd, const u8 *addr,
83 u8 dialog_token)
84{
85 struct sta_info *sta;
86 int i;
87
88 sta = ap_get_sta(hapd, addr);
89 if (!sta) {
90 wpa_printf(MSG_DEBUG, "ANQP: could not find STA " MACSTR,
91 MAC2STR(addr));
92 return NULL;
93 }
94 for (i = 0; sta->gas_dialog && i < GAS_DIALOG_MAX; i++) {
95 if (sta->gas_dialog[i].dialog_token != dialog_token ||
96 !sta->gas_dialog[i].valid)
97 continue;
98 return &sta->gas_dialog[i];
99 }
100 wpa_printf(MSG_DEBUG, "ANQP: Could not find dialog for "
101 MACSTR " dialog_token %u", MAC2STR(addr), dialog_token);
102 return NULL;
103}
104
105
106void gas_serv_dialog_clear(struct gas_dialog_info *dia)
107{
108 wpabuf_free(dia->sd_resp);
109 os_memset(dia, 0, sizeof(*dia));
110}
111
112
113static void gas_serv_free_dialogs(struct hostapd_data *hapd,
114 const u8 *sta_addr)
115{
116 struct sta_info *sta;
117 int i;
118
119 sta = ap_get_sta(hapd, sta_addr);
120 if (sta == NULL || sta->gas_dialog == NULL)
121 return;
122
123 for (i = 0; i < GAS_DIALOG_MAX; i++) {
124 if (sta->gas_dialog[i].valid)
125 return;
126 }
127
128 os_free(sta->gas_dialog);
129 sta->gas_dialog = NULL;
130}
131
132
Dmitry Shmidtaa532512012-09-24 10:35:31 -0700133#ifdef CONFIG_HS20
Dmitry Shmidt61d9df32012-08-29 16:22:06 -0700134static void anqp_add_hs_capab_list(struct hostapd_data *hapd,
135 struct wpabuf *buf)
136{
137 u8 *len;
138
139 len = gas_anqp_add_element(buf, ANQP_VENDOR_SPECIFIC);
140 wpabuf_put_be24(buf, OUI_WFA);
141 wpabuf_put_u8(buf, HS20_ANQP_OUI_TYPE);
142 wpabuf_put_u8(buf, HS20_STYPE_CAPABILITY_LIST);
143 wpabuf_put_u8(buf, 0); /* Reserved */
144 wpabuf_put_u8(buf, HS20_STYPE_CAPABILITY_LIST);
145 if (hapd->conf->hs20_oper_friendly_name)
146 wpabuf_put_u8(buf, HS20_STYPE_OPERATOR_FRIENDLY_NAME);
147 if (hapd->conf->hs20_wan_metrics)
148 wpabuf_put_u8(buf, HS20_STYPE_WAN_METRICS);
149 if (hapd->conf->hs20_connection_capability)
150 wpabuf_put_u8(buf, HS20_STYPE_CONNECTION_CAPABILITY);
151 if (hapd->conf->nai_realm_data)
152 wpabuf_put_u8(buf, HS20_STYPE_NAI_HOME_REALM_QUERY);
153 if (hapd->conf->hs20_operating_class)
154 wpabuf_put_u8(buf, HS20_STYPE_OPERATING_CLASS);
155 gas_anqp_set_element_len(buf, len);
156}
Dmitry Shmidtaa532512012-09-24 10:35:31 -0700157#endif /* CONFIG_HS20 */
Dmitry Shmidt61d9df32012-08-29 16:22:06 -0700158
159
Dmitry Shmidt04949592012-07-19 12:16:46 -0700160static void anqp_add_capab_list(struct hostapd_data *hapd,
161 struct wpabuf *buf)
162{
163 u8 *len;
164
165 len = gas_anqp_add_element(buf, ANQP_CAPABILITY_LIST);
166 wpabuf_put_le16(buf, ANQP_CAPABILITY_LIST);
167 if (hapd->conf->venue_name)
168 wpabuf_put_le16(buf, ANQP_VENUE_NAME);
Dmitry Shmidt61d9df32012-08-29 16:22:06 -0700169 if (hapd->conf->network_auth_type)
170 wpabuf_put_le16(buf, ANQP_NETWORK_AUTH_TYPE);
Dmitry Shmidt04949592012-07-19 12:16:46 -0700171 if (hapd->conf->roaming_consortium)
172 wpabuf_put_le16(buf, ANQP_ROAMING_CONSORTIUM);
Dmitry Shmidt61d9df32012-08-29 16:22:06 -0700173 if (hapd->conf->ipaddr_type_configured)
174 wpabuf_put_le16(buf, ANQP_IP_ADDR_TYPE_AVAILABILITY);
175 if (hapd->conf->nai_realm_data)
176 wpabuf_put_le16(buf, ANQP_NAI_REALM);
177 if (hapd->conf->anqp_3gpp_cell_net)
178 wpabuf_put_le16(buf, ANQP_3GPP_CELLULAR_NETWORK);
179 if (hapd->conf->domain_name)
180 wpabuf_put_le16(buf, ANQP_DOMAIN_NAME);
Dmitry Shmidtaa532512012-09-24 10:35:31 -0700181#ifdef CONFIG_HS20
Dmitry Shmidt61d9df32012-08-29 16:22:06 -0700182 anqp_add_hs_capab_list(hapd, buf);
Dmitry Shmidtaa532512012-09-24 10:35:31 -0700183#endif /* CONFIG_HS20 */
Dmitry Shmidt04949592012-07-19 12:16:46 -0700184 gas_anqp_set_element_len(buf, len);
185}
186
187
188static void anqp_add_venue_name(struct hostapd_data *hapd, struct wpabuf *buf)
189{
190 if (hapd->conf->venue_name) {
191 u8 *len;
192 unsigned int i;
193 len = gas_anqp_add_element(buf, ANQP_VENUE_NAME);
194 wpabuf_put_u8(buf, hapd->conf->venue_group);
195 wpabuf_put_u8(buf, hapd->conf->venue_type);
196 for (i = 0; i < hapd->conf->venue_name_count; i++) {
Dmitry Shmidt61d9df32012-08-29 16:22:06 -0700197 struct hostapd_lang_string *vn;
Dmitry Shmidt04949592012-07-19 12:16:46 -0700198 vn = &hapd->conf->venue_name[i];
199 wpabuf_put_u8(buf, 3 + vn->name_len);
200 wpabuf_put_data(buf, vn->lang, 3);
201 wpabuf_put_data(buf, vn->name, vn->name_len);
202 }
203 gas_anqp_set_element_len(buf, len);
204 }
205}
206
207
Dmitry Shmidt61d9df32012-08-29 16:22:06 -0700208static void anqp_add_network_auth_type(struct hostapd_data *hapd,
209 struct wpabuf *buf)
210{
211 if (hapd->conf->network_auth_type) {
212 wpabuf_put_le16(buf, ANQP_NETWORK_AUTH_TYPE);
213 wpabuf_put_le16(buf, hapd->conf->network_auth_type_len);
214 wpabuf_put_data(buf, hapd->conf->network_auth_type,
215 hapd->conf->network_auth_type_len);
216 }
217}
218
219
Dmitry Shmidt04949592012-07-19 12:16:46 -0700220static void anqp_add_roaming_consortium(struct hostapd_data *hapd,
221 struct wpabuf *buf)
222{
223 unsigned int i;
224 u8 *len;
225
226 len = gas_anqp_add_element(buf, ANQP_ROAMING_CONSORTIUM);
227 for (i = 0; i < hapd->conf->roaming_consortium_count; i++) {
228 struct hostapd_roaming_consortium *rc;
229 rc = &hapd->conf->roaming_consortium[i];
230 wpabuf_put_u8(buf, rc->len);
231 wpabuf_put_data(buf, rc->oi, rc->len);
232 }
233 gas_anqp_set_element_len(buf, len);
234}
235
236
Dmitry Shmidt61d9df32012-08-29 16:22:06 -0700237static void anqp_add_ip_addr_type_availability(struct hostapd_data *hapd,
238 struct wpabuf *buf)
239{
240 if (hapd->conf->ipaddr_type_configured) {
241 wpabuf_put_le16(buf, ANQP_IP_ADDR_TYPE_AVAILABILITY);
242 wpabuf_put_le16(buf, 1);
243 wpabuf_put_u8(buf, hapd->conf->ipaddr_type_availability);
244 }
245}
246
247
248static void anqp_add_nai_realm_eap(struct wpabuf *buf,
249 struct hostapd_nai_realm_data *realm)
250{
251 unsigned int i, j;
252
253 wpabuf_put_u8(buf, realm->eap_method_count);
254
255 for (i = 0; i < realm->eap_method_count; i++) {
256 struct hostapd_nai_realm_eap *eap = &realm->eap_method[i];
257 wpabuf_put_u8(buf, 2 + (3 * eap->num_auths));
258 wpabuf_put_u8(buf, eap->eap_method);
259 wpabuf_put_u8(buf, eap->num_auths);
260 for (j = 0; j < eap->num_auths; j++) {
261 wpabuf_put_u8(buf, eap->auth_id[j]);
262 wpabuf_put_u8(buf, 1);
263 wpabuf_put_u8(buf, eap->auth_val[j]);
264 }
265 }
266}
267
268
269static void anqp_add_nai_realm_data(struct wpabuf *buf,
270 struct hostapd_nai_realm_data *realm,
271 unsigned int realm_idx)
272{
273 u8 *realm_data_len;
274
275 wpa_printf(MSG_DEBUG, "realm=%s, len=%d", realm->realm[realm_idx],
276 (int) os_strlen(realm->realm[realm_idx]));
277 realm_data_len = wpabuf_put(buf, 2);
278 wpabuf_put_u8(buf, realm->encoding);
279 wpabuf_put_u8(buf, os_strlen(realm->realm[realm_idx]));
280 wpabuf_put_str(buf, realm->realm[realm_idx]);
281 anqp_add_nai_realm_eap(buf, realm);
282 gas_anqp_set_element_len(buf, realm_data_len);
283}
284
285
286static int hs20_add_nai_home_realm_matches(struct hostapd_data *hapd,
287 struct wpabuf *buf,
288 const u8 *home_realm,
289 size_t home_realm_len)
290{
291 unsigned int i, j, k;
292 u8 num_realms, num_matching = 0, encoding, realm_len, *realm_list_len;
293 struct hostapd_nai_realm_data *realm;
294 const u8 *pos, *realm_name, *end;
295 struct {
296 unsigned int realm_data_idx;
297 unsigned int realm_idx;
298 } matches[10];
299
300 pos = home_realm;
301 end = pos + home_realm_len;
302 if (pos + 1 > end) {
303 wpa_hexdump(MSG_DEBUG, "Too short NAI Home Realm Query",
304 home_realm, home_realm_len);
305 return -1;
306 }
307 num_realms = *pos++;
308
309 for (i = 0; i < num_realms && num_matching < 10; i++) {
310 if (pos + 2 > end) {
311 wpa_hexdump(MSG_DEBUG,
312 "Truncated NAI Home Realm Query",
313 home_realm, home_realm_len);
314 return -1;
315 }
316 encoding = *pos++;
317 realm_len = *pos++;
318 if (pos + realm_len > end) {
319 wpa_hexdump(MSG_DEBUG,
320 "Truncated NAI Home Realm Query",
321 home_realm, home_realm_len);
322 return -1;
323 }
324 realm_name = pos;
325 for (j = 0; j < hapd->conf->nai_realm_count &&
326 num_matching < 10; j++) {
327 const u8 *rpos, *rend;
328 realm = &hapd->conf->nai_realm_data[j];
329 if (encoding != realm->encoding)
330 continue;
331
332 rpos = realm_name;
333 while (rpos < realm_name + realm_len &&
334 num_matching < 10) {
335 for (rend = rpos;
336 rend < realm_name + realm_len; rend++) {
337 if (*rend == ';')
338 break;
339 }
340 for (k = 0; k < MAX_NAI_REALMS &&
341 realm->realm[k] &&
342 num_matching < 10; k++) {
343 if ((int) os_strlen(realm->realm[k]) !=
344 rend - rpos ||
345 os_strncmp((char *) rpos,
346 realm->realm[k],
347 rend - rpos) != 0)
348 continue;
349 matches[num_matching].realm_data_idx =
350 j;
351 matches[num_matching].realm_idx = k;
352 num_matching++;
353 }
354 rpos = rend + 1;
355 }
356 }
357 pos += realm_len;
358 }
359
360 realm_list_len = gas_anqp_add_element(buf, ANQP_NAI_REALM);
361 wpabuf_put_le16(buf, num_matching);
362
363 /*
364 * There are two ways to format. 1. each realm in a NAI Realm Data unit
365 * 2. all realms that share the same EAP methods in a NAI Realm Data
366 * unit. The first format is likely to be bigger in size than the
367 * second, but may be easier to parse and process by the receiver.
368 */
369 for (i = 0; i < num_matching; i++) {
370 wpa_printf(MSG_DEBUG, "realm_idx %d, realm_data_idx %d",
371 matches[i].realm_data_idx, matches[i].realm_idx);
372 realm = &hapd->conf->nai_realm_data[matches[i].realm_data_idx];
373 anqp_add_nai_realm_data(buf, realm, matches[i].realm_idx);
374 }
375 gas_anqp_set_element_len(buf, realm_list_len);
376 return 0;
377}
378
379
380static void anqp_add_nai_realm(struct hostapd_data *hapd, struct wpabuf *buf,
381 const u8 *home_realm, size_t home_realm_len,
382 int nai_realm, int nai_home_realm)
383{
384 if (nai_realm && hapd->conf->nai_realm_data) {
385 u8 *len;
386 unsigned int i, j;
387 len = gas_anqp_add_element(buf, ANQP_NAI_REALM);
388 wpabuf_put_le16(buf, hapd->conf->nai_realm_count);
389 for (i = 0; i < hapd->conf->nai_realm_count; i++) {
390 u8 *realm_data_len, *realm_len;
391 struct hostapd_nai_realm_data *realm;
392
393 realm = &hapd->conf->nai_realm_data[i];
394 realm_data_len = wpabuf_put(buf, 2);
395 wpabuf_put_u8(buf, realm->encoding);
396 realm_len = wpabuf_put(buf, 1);
397 for (j = 0; realm->realm[j]; j++) {
398 if (j > 0)
399 wpabuf_put_u8(buf, ';');
400 wpabuf_put_str(buf, realm->realm[j]);
401 }
402 *realm_len = (u8 *) wpabuf_put(buf, 0) - realm_len - 1;
403 anqp_add_nai_realm_eap(buf, realm);
404 gas_anqp_set_element_len(buf, realm_data_len);
405 }
406 gas_anqp_set_element_len(buf, len);
407 } else if (nai_home_realm && hapd->conf->nai_realm_data) {
408 hs20_add_nai_home_realm_matches(hapd, buf, home_realm,
409 home_realm_len);
410 }
411}
412
413
414static void anqp_add_3gpp_cellular_network(struct hostapd_data *hapd,
415 struct wpabuf *buf)
416{
417 if (hapd->conf->anqp_3gpp_cell_net) {
418 wpabuf_put_le16(buf, ANQP_3GPP_CELLULAR_NETWORK);
419 wpabuf_put_le16(buf,
420 hapd->conf->anqp_3gpp_cell_net_len);
421 wpabuf_put_data(buf, hapd->conf->anqp_3gpp_cell_net,
422 hapd->conf->anqp_3gpp_cell_net_len);
423 }
424}
425
426
427static void anqp_add_domain_name(struct hostapd_data *hapd, struct wpabuf *buf)
428{
429 if (hapd->conf->domain_name) {
430 wpabuf_put_le16(buf, ANQP_DOMAIN_NAME);
431 wpabuf_put_le16(buf, hapd->conf->domain_name_len);
432 wpabuf_put_data(buf, hapd->conf->domain_name,
433 hapd->conf->domain_name_len);
434 }
435}
436
437
Dmitry Shmidtaa532512012-09-24 10:35:31 -0700438#ifdef CONFIG_HS20
439
Dmitry Shmidt61d9df32012-08-29 16:22:06 -0700440static void anqp_add_operator_friendly_name(struct hostapd_data *hapd,
441 struct wpabuf *buf)
442{
443 if (hapd->conf->hs20_oper_friendly_name) {
444 u8 *len;
445 unsigned int i;
446 len = gas_anqp_add_element(buf, ANQP_VENDOR_SPECIFIC);
447 wpabuf_put_be24(buf, OUI_WFA);
448 wpabuf_put_u8(buf, HS20_ANQP_OUI_TYPE);
449 wpabuf_put_u8(buf, HS20_STYPE_OPERATOR_FRIENDLY_NAME);
450 wpabuf_put_u8(buf, 0); /* Reserved */
451 for (i = 0; i < hapd->conf->hs20_oper_friendly_name_count; i++)
452 {
453 struct hostapd_lang_string *vn;
454 vn = &hapd->conf->hs20_oper_friendly_name[i];
455 wpabuf_put_u8(buf, 3 + vn->name_len);
456 wpabuf_put_data(buf, vn->lang, 3);
457 wpabuf_put_data(buf, vn->name, vn->name_len);
458 }
459 gas_anqp_set_element_len(buf, len);
460 }
461}
462
463
464static void anqp_add_wan_metrics(struct hostapd_data *hapd,
465 struct wpabuf *buf)
466{
467 if (hapd->conf->hs20_wan_metrics) {
468 u8 *len = gas_anqp_add_element(buf, ANQP_VENDOR_SPECIFIC);
469 wpabuf_put_be24(buf, OUI_WFA);
470 wpabuf_put_u8(buf, HS20_ANQP_OUI_TYPE);
471 wpabuf_put_u8(buf, HS20_STYPE_WAN_METRICS);
472 wpabuf_put_u8(buf, 0); /* Reserved */
473 wpabuf_put_data(buf, hapd->conf->hs20_wan_metrics, 13);
474 gas_anqp_set_element_len(buf, len);
475 }
476}
477
478
479static void anqp_add_connection_capability(struct hostapd_data *hapd,
480 struct wpabuf *buf)
481{
482 if (hapd->conf->hs20_connection_capability) {
483 u8 *len = gas_anqp_add_element(buf, ANQP_VENDOR_SPECIFIC);
484 wpabuf_put_be24(buf, OUI_WFA);
485 wpabuf_put_u8(buf, HS20_ANQP_OUI_TYPE);
486 wpabuf_put_u8(buf, HS20_STYPE_CONNECTION_CAPABILITY);
487 wpabuf_put_u8(buf, 0); /* Reserved */
488 wpabuf_put_data(buf, hapd->conf->hs20_connection_capability,
489 hapd->conf->hs20_connection_capability_len);
490 gas_anqp_set_element_len(buf, len);
491 }
492}
493
494
495static void anqp_add_operating_class(struct hostapd_data *hapd,
496 struct wpabuf *buf)
497{
498 if (hapd->conf->hs20_operating_class) {
499 u8 *len = gas_anqp_add_element(buf, ANQP_VENDOR_SPECIFIC);
500 wpabuf_put_be24(buf, OUI_WFA);
501 wpabuf_put_u8(buf, HS20_ANQP_OUI_TYPE);
502 wpabuf_put_u8(buf, HS20_STYPE_OPERATING_CLASS);
503 wpabuf_put_u8(buf, 0); /* Reserved */
504 wpabuf_put_data(buf, hapd->conf->hs20_operating_class,
505 hapd->conf->hs20_operating_class_len);
506 gas_anqp_set_element_len(buf, len);
507 }
508}
509
Dmitry Shmidtaa532512012-09-24 10:35:31 -0700510#endif /* CONFIG_HS20 */
511
Dmitry Shmidt61d9df32012-08-29 16:22:06 -0700512
Dmitry Shmidt04949592012-07-19 12:16:46 -0700513static struct wpabuf *
514gas_serv_build_gas_resp_payload(struct hostapd_data *hapd,
515 unsigned int request,
Dmitry Shmidt61d9df32012-08-29 16:22:06 -0700516 struct gas_dialog_info *di,
517 const u8 *home_realm, size_t home_realm_len)
Dmitry Shmidt04949592012-07-19 12:16:46 -0700518{
519 struct wpabuf *buf;
520
521 buf = wpabuf_alloc(1400);
522 if (buf == NULL)
523 return NULL;
524
525 if (request & ANQP_REQ_CAPABILITY_LIST)
526 anqp_add_capab_list(hapd, buf);
527 if (request & ANQP_REQ_VENUE_NAME)
528 anqp_add_venue_name(hapd, buf);
Dmitry Shmidt61d9df32012-08-29 16:22:06 -0700529 if (request & ANQP_REQ_NETWORK_AUTH_TYPE)
530 anqp_add_network_auth_type(hapd, buf);
Dmitry Shmidt04949592012-07-19 12:16:46 -0700531 if (request & ANQP_REQ_ROAMING_CONSORTIUM)
532 anqp_add_roaming_consortium(hapd, buf);
Dmitry Shmidt61d9df32012-08-29 16:22:06 -0700533 if (request & ANQP_REQ_IP_ADDR_TYPE_AVAILABILITY)
534 anqp_add_ip_addr_type_availability(hapd, buf);
535 if (request & (ANQP_REQ_NAI_REALM | ANQP_REQ_NAI_HOME_REALM))
536 anqp_add_nai_realm(hapd, buf, home_realm, home_realm_len,
537 request & ANQP_REQ_NAI_REALM,
538 request & ANQP_REQ_NAI_HOME_REALM);
539 if (request & ANQP_REQ_3GPP_CELLULAR_NETWORK)
540 anqp_add_3gpp_cellular_network(hapd, buf);
541 if (request & ANQP_REQ_DOMAIN_NAME)
542 anqp_add_domain_name(hapd, buf);
543
Dmitry Shmidtaa532512012-09-24 10:35:31 -0700544#ifdef CONFIG_HS20
Dmitry Shmidt61d9df32012-08-29 16:22:06 -0700545 if (request & ANQP_REQ_HS_CAPABILITY_LIST)
546 anqp_add_hs_capab_list(hapd, buf);
547 if (request & ANQP_REQ_OPERATOR_FRIENDLY_NAME)
548 anqp_add_operator_friendly_name(hapd, buf);
549 if (request & ANQP_REQ_WAN_METRICS)
550 anqp_add_wan_metrics(hapd, buf);
551 if (request & ANQP_REQ_CONNECTION_CAPABILITY)
552 anqp_add_connection_capability(hapd, buf);
553 if (request & ANQP_REQ_OPERATING_CLASS)
554 anqp_add_operating_class(hapd, buf);
Dmitry Shmidtaa532512012-09-24 10:35:31 -0700555#endif /* CONFIG_HS20 */
Dmitry Shmidt04949592012-07-19 12:16:46 -0700556
557 return buf;
558}
559
560
561static void gas_serv_clear_cached_ies(void *eloop_data, void *user_ctx)
562{
563 struct gas_dialog_info *dia = eloop_data;
564
565 wpa_printf(MSG_DEBUG, "GAS: Timeout triggered, clearing dialog for "
566 "dialog token %d", dia->dialog_token);
567
568 gas_serv_dialog_clear(dia);
569}
570
571
572struct anqp_query_info {
573 unsigned int request;
574 unsigned int remote_request;
Dmitry Shmidt61d9df32012-08-29 16:22:06 -0700575 const u8 *home_realm_query;
576 size_t home_realm_query_len;
Dmitry Shmidt04949592012-07-19 12:16:46 -0700577 u16 remote_delay;
578};
579
580
581static void set_anqp_req(unsigned int bit, const char *name, int local,
582 unsigned int remote, u16 remote_delay,
583 struct anqp_query_info *qi)
584{
585 qi->request |= bit;
586 if (local) {
587 wpa_printf(MSG_DEBUG, "ANQP: %s (local)", name);
588 } else if (bit & remote) {
589 wpa_printf(MSG_DEBUG, "ANQP: %s (remote)", name);
590 qi->remote_request |= bit;
591 if (remote_delay > qi->remote_delay)
592 qi->remote_delay = remote_delay;
593 } else {
594 wpa_printf(MSG_DEBUG, "ANQP: %s not available", name);
595 }
596}
597
598
599static void rx_anqp_query_list_id(struct hostapd_data *hapd, u16 info_id,
600 struct anqp_query_info *qi)
601{
602 switch (info_id) {
603 case ANQP_CAPABILITY_LIST:
604 set_anqp_req(ANQP_REQ_CAPABILITY_LIST, "Capability List", 1, 0,
605 0, qi);
606 break;
607 case ANQP_VENUE_NAME:
608 set_anqp_req(ANQP_REQ_VENUE_NAME, "Venue Name",
609 hapd->conf->venue_name != NULL, 0, 0, qi);
610 break;
Dmitry Shmidt61d9df32012-08-29 16:22:06 -0700611 case ANQP_NETWORK_AUTH_TYPE:
612 set_anqp_req(ANQP_REQ_NETWORK_AUTH_TYPE, "Network Auth Type",
613 hapd->conf->network_auth_type != NULL,
614 0, 0, qi);
615 break;
Dmitry Shmidt04949592012-07-19 12:16:46 -0700616 case ANQP_ROAMING_CONSORTIUM:
617 set_anqp_req(ANQP_REQ_ROAMING_CONSORTIUM, "Roaming Consortium",
618 hapd->conf->roaming_consortium != NULL, 0, 0, qi);
619 break;
Dmitry Shmidt61d9df32012-08-29 16:22:06 -0700620 case ANQP_IP_ADDR_TYPE_AVAILABILITY:
621 set_anqp_req(ANQP_REQ_IP_ADDR_TYPE_AVAILABILITY,
622 "IP Addr Type Availability",
623 hapd->conf->ipaddr_type_configured,
624 0, 0, qi);
625 break;
626 case ANQP_NAI_REALM:
627 set_anqp_req(ANQP_REQ_NAI_REALM, "NAI Realm",
628 hapd->conf->nai_realm_data != NULL,
629 0, 0, qi);
630 break;
631 case ANQP_3GPP_CELLULAR_NETWORK:
632 set_anqp_req(ANQP_REQ_3GPP_CELLULAR_NETWORK,
633 "3GPP Cellular Network",
634 hapd->conf->anqp_3gpp_cell_net != NULL,
635 0, 0, qi);
636 break;
637 case ANQP_DOMAIN_NAME:
638 set_anqp_req(ANQP_REQ_DOMAIN_NAME, "Domain Name",
639 hapd->conf->domain_name != NULL,
640 0, 0, qi);
641 break;
Dmitry Shmidt04949592012-07-19 12:16:46 -0700642 default:
643 wpa_printf(MSG_DEBUG, "ANQP: Unsupported Info Id %u",
644 info_id);
645 break;
646 }
647}
648
649
650static void rx_anqp_query_list(struct hostapd_data *hapd,
651 const u8 *pos, const u8 *end,
652 struct anqp_query_info *qi)
653{
654 wpa_printf(MSG_DEBUG, "ANQP: %u Info IDs requested in Query list",
655 (unsigned int) (end - pos) / 2);
656
657 while (pos + 2 <= end) {
658 rx_anqp_query_list_id(hapd, WPA_GET_LE16(pos), qi);
659 pos += 2;
660 }
661}
662
663
Dmitry Shmidtaa532512012-09-24 10:35:31 -0700664#ifdef CONFIG_HS20
665
Dmitry Shmidt61d9df32012-08-29 16:22:06 -0700666static void rx_anqp_hs_query_list(struct hostapd_data *hapd, u8 subtype,
667 struct anqp_query_info *qi)
668{
669 switch (subtype) {
670 case HS20_STYPE_CAPABILITY_LIST:
671 set_anqp_req(ANQP_REQ_HS_CAPABILITY_LIST, "HS Capability List",
672 1, 0, 0, qi);
673 break;
674 case HS20_STYPE_OPERATOR_FRIENDLY_NAME:
675 set_anqp_req(ANQP_REQ_OPERATOR_FRIENDLY_NAME,
676 "Operator Friendly Name",
677 hapd->conf->hs20_oper_friendly_name != NULL,
678 0, 0, qi);
679 break;
680 case HS20_STYPE_WAN_METRICS:
681 set_anqp_req(ANQP_REQ_WAN_METRICS, "WAN Metrics",
682 hapd->conf->hs20_wan_metrics != NULL,
683 0, 0, qi);
684 break;
685 case HS20_STYPE_CONNECTION_CAPABILITY:
686 set_anqp_req(ANQP_REQ_CONNECTION_CAPABILITY,
687 "Connection Capability",
688 hapd->conf->hs20_connection_capability != NULL,
689 0, 0, qi);
690 break;
691 case HS20_STYPE_OPERATING_CLASS:
692 set_anqp_req(ANQP_REQ_OPERATING_CLASS, "Operating Class",
693 hapd->conf->hs20_operating_class != NULL,
694 0, 0, qi);
695 break;
696 default:
697 wpa_printf(MSG_DEBUG, "ANQP: Unsupported HS 2.0 subtype %u",
698 subtype);
699 break;
700 }
701}
702
703
704static void rx_anqp_hs_nai_home_realm(struct hostapd_data *hapd,
705 const u8 *pos, const u8 *end,
706 struct anqp_query_info *qi)
707{
708 qi->request |= ANQP_REQ_NAI_HOME_REALM;
709 qi->home_realm_query = pos;
710 qi->home_realm_query_len = end - pos;
711 if (hapd->conf->nai_realm_data != NULL) {
712 wpa_printf(MSG_DEBUG, "ANQP: HS 2.0 NAI Home Realm Query "
713 "(local)");
714 } else {
715 wpa_printf(MSG_DEBUG, "ANQP: HS 2.0 NAI Home Realm Query not "
716 "available");
717 }
718}
719
720
721static void rx_anqp_vendor_specific(struct hostapd_data *hapd,
722 const u8 *pos, const u8 *end,
723 struct anqp_query_info *qi)
724{
725 u32 oui;
726 u8 subtype;
727
728 if (pos + 4 > end) {
729 wpa_printf(MSG_DEBUG, "ANQP: Too short vendor specific ANQP "
730 "Query element");
731 return;
732 }
733
734 oui = WPA_GET_BE24(pos);
735 pos += 3;
736 if (oui != OUI_WFA) {
737 wpa_printf(MSG_DEBUG, "ANQP: Unsupported vendor OUI %06x",
738 oui);
739 return;
740 }
741
742 if (*pos != HS20_ANQP_OUI_TYPE) {
743 wpa_printf(MSG_DEBUG, "ANQP: Unsupported WFA vendor type %u",
744 *pos);
745 return;
746 }
747 pos++;
748
749 if (pos + 1 >= end)
750 return;
751
752 subtype = *pos++;
753 pos++; /* Reserved */
754 switch (subtype) {
755 case HS20_STYPE_QUERY_LIST:
756 wpa_printf(MSG_DEBUG, "ANQP: HS 2.0 Query List");
757 while (pos < end) {
758 rx_anqp_hs_query_list(hapd, *pos, qi);
759 pos++;
760 }
761 break;
762 case HS20_STYPE_NAI_HOME_REALM_QUERY:
763 rx_anqp_hs_nai_home_realm(hapd, pos, end, qi);
764 break;
765 default:
766 wpa_printf(MSG_DEBUG, "ANQP: Unsupported HS 2.0 query subtype "
767 "%u", subtype);
768 break;
769 }
770}
771
Dmitry Shmidtaa532512012-09-24 10:35:31 -0700772#endif /* CONFIG_HS20 */
773
Dmitry Shmidt61d9df32012-08-29 16:22:06 -0700774
Dmitry Shmidt04949592012-07-19 12:16:46 -0700775static void gas_serv_req_local_processing(struct hostapd_data *hapd,
776 const u8 *sa, u8 dialog_token,
777 struct anqp_query_info *qi)
778{
779 struct wpabuf *buf, *tx_buf;
780
Dmitry Shmidt61d9df32012-08-29 16:22:06 -0700781 buf = gas_serv_build_gas_resp_payload(hapd, qi->request, NULL,
782 qi->home_realm_query,
783 qi->home_realm_query_len);
Dmitry Shmidt04949592012-07-19 12:16:46 -0700784 wpa_hexdump_buf(MSG_MSGDUMP, "ANQP: Locally generated ANQP responses",
785 buf);
786 if (!buf)
787 return;
788
789 if (wpabuf_len(buf) > hapd->gas_frag_limit ||
790 hapd->conf->gas_comeback_delay) {
791 struct gas_dialog_info *di;
792 u16 comeback_delay = 1;
793
794 if (hapd->conf->gas_comeback_delay) {
795 /* Testing - allow overriding of the delay value */
796 comeback_delay = hapd->conf->gas_comeback_delay;
797 }
798
799 wpa_printf(MSG_DEBUG, "ANQP: Too long response to fit in "
800 "initial response - use GAS comeback");
801 di = gas_dialog_create(hapd, sa, dialog_token);
802 if (!di) {
803 wpa_printf(MSG_INFO, "ANQP: Could not create dialog "
804 "for " MACSTR " (dialog token %u)",
805 MAC2STR(sa), dialog_token);
806 wpabuf_free(buf);
807 return;
808 }
809 di->sd_resp = buf;
810 di->sd_resp_pos = 0;
811 tx_buf = gas_anqp_build_initial_resp_buf(
812 dialog_token, WLAN_STATUS_SUCCESS, comeback_delay,
813 NULL);
814 } else {
815 wpa_printf(MSG_DEBUG, "ANQP: Initial response (no comeback)");
816 tx_buf = gas_anqp_build_initial_resp_buf(
817 dialog_token, WLAN_STATUS_SUCCESS, 0, buf);
818 wpabuf_free(buf);
819 }
820 if (!tx_buf)
821 return;
822
823 hostapd_drv_send_action(hapd, hapd->iface->freq, 0, sa,
824 wpabuf_head(tx_buf), wpabuf_len(tx_buf));
825 wpabuf_free(tx_buf);
826}
827
828
829static void gas_serv_rx_gas_initial_req(struct hostapd_data *hapd,
830 const u8 *sa,
831 const u8 *data, size_t len)
832{
833 const u8 *pos = data;
834 const u8 *end = data + len;
835 const u8 *next;
836 u8 dialog_token;
837 u16 slen;
838 struct anqp_query_info qi;
839 const u8 *adv_proto;
840
841 if (len < 1 + 2)
842 return;
843
844 os_memset(&qi, 0, sizeof(qi));
845
846 dialog_token = *pos++;
847 wpa_msg(hapd->msg_ctx, MSG_DEBUG,
848 "GAS: GAS Initial Request from " MACSTR " (dialog token %u) ",
849 MAC2STR(sa), dialog_token);
850
851 if (*pos != WLAN_EID_ADV_PROTO) {
852 wpa_msg(hapd->msg_ctx, MSG_DEBUG,
853 "GAS: Unexpected IE in GAS Initial Request: %u", *pos);
854 return;
855 }
856 adv_proto = pos++;
857
858 slen = *pos++;
859 next = pos + slen;
860 if (next > end || slen < 2) {
861 wpa_msg(hapd->msg_ctx, MSG_DEBUG,
862 "GAS: Invalid IE in GAS Initial Request");
863 return;
864 }
865 pos++; /* skip QueryRespLenLimit and PAME-BI */
866
867 if (*pos != ACCESS_NETWORK_QUERY_PROTOCOL) {
868 struct wpabuf *buf;
869 wpa_msg(hapd->msg_ctx, MSG_DEBUG,
870 "GAS: Unsupported GAS advertisement protocol id %u",
871 *pos);
872 if (sa[0] & 0x01)
873 return; /* Invalid source address - drop silently */
874 buf = gas_build_initial_resp(
875 dialog_token, WLAN_STATUS_GAS_ADV_PROTO_NOT_SUPPORTED,
876 0, 2 + slen + 2);
877 if (buf == NULL)
878 return;
879 wpabuf_put_data(buf, adv_proto, 2 + slen);
880 wpabuf_put_le16(buf, 0); /* Query Response Length */
881 hostapd_drv_send_action(hapd, hapd->iface->freq, 0, sa,
882 wpabuf_head(buf), wpabuf_len(buf));
883 wpabuf_free(buf);
884 return;
885 }
886
887 pos = next;
888 /* Query Request */
889 if (pos + 2 > end)
890 return;
891 slen = WPA_GET_LE16(pos);
892 pos += 2;
893 if (pos + slen > end)
894 return;
895 end = pos + slen;
896
897 /* ANQP Query Request */
898 while (pos < end) {
899 u16 info_id, elen;
900
901 if (pos + 4 > end)
902 return;
903
904 info_id = WPA_GET_LE16(pos);
905 pos += 2;
906 elen = WPA_GET_LE16(pos);
907 pos += 2;
908
909 if (pos + elen > end) {
910 wpa_printf(MSG_DEBUG, "ANQP: Invalid Query Request");
911 return;
912 }
913
914 switch (info_id) {
915 case ANQP_QUERY_LIST:
916 rx_anqp_query_list(hapd, pos, pos + elen, &qi);
917 break;
Dmitry Shmidtaa532512012-09-24 10:35:31 -0700918#ifdef CONFIG_HS20
Dmitry Shmidt61d9df32012-08-29 16:22:06 -0700919 case ANQP_VENDOR_SPECIFIC:
920 rx_anqp_vendor_specific(hapd, pos, pos + elen, &qi);
921 break;
Dmitry Shmidtaa532512012-09-24 10:35:31 -0700922#endif /* CONFIG_HS20 */
Dmitry Shmidt04949592012-07-19 12:16:46 -0700923 default:
924 wpa_printf(MSG_DEBUG, "ANQP: Unsupported Query "
925 "Request element %u", info_id);
926 break;
927 }
928
929 pos += elen;
930 }
931
932 gas_serv_req_local_processing(hapd, sa, dialog_token, &qi);
933}
934
935
936void gas_serv_tx_gas_response(struct hostapd_data *hapd, const u8 *dst,
937 struct gas_dialog_info *dialog)
938{
939 struct wpabuf *buf, *tx_buf;
940 u8 dialog_token = dialog->dialog_token;
941 size_t frag_len;
942
943 if (dialog->sd_resp == NULL) {
944 buf = gas_serv_build_gas_resp_payload(hapd,
945 dialog->all_requested,
Dmitry Shmidt61d9df32012-08-29 16:22:06 -0700946 dialog, NULL, 0);
Dmitry Shmidt04949592012-07-19 12:16:46 -0700947 wpa_hexdump_buf(MSG_MSGDUMP, "ANQP: Generated ANQP responses",
948 buf);
949 if (!buf)
950 goto tx_gas_response_done;
951 dialog->sd_resp = buf;
952 dialog->sd_resp_pos = 0;
953 }
954 frag_len = wpabuf_len(dialog->sd_resp) - dialog->sd_resp_pos;
955 if (frag_len > hapd->gas_frag_limit || dialog->comeback_delay ||
956 hapd->conf->gas_comeback_delay) {
957 u16 comeback_delay_tus = dialog->comeback_delay +
958 GAS_SERV_COMEBACK_DELAY_FUDGE;
959 u32 comeback_delay_secs, comeback_delay_usecs;
960
961 if (hapd->conf->gas_comeback_delay) {
962 /* Testing - allow overriding of the delay value */
963 comeback_delay_tus = hapd->conf->gas_comeback_delay;
964 }
965
966 wpa_printf(MSG_DEBUG, "GAS: Response frag_len %u (frag limit "
967 "%u) and comeback delay %u, "
968 "requesting comebacks", (unsigned int) frag_len,
969 (unsigned int) hapd->gas_frag_limit,
970 dialog->comeback_delay);
971 tx_buf = gas_anqp_build_initial_resp_buf(dialog_token,
972 WLAN_STATUS_SUCCESS,
973 comeback_delay_tus,
974 NULL);
975 if (tx_buf) {
976 wpa_msg(hapd->msg_ctx, MSG_DEBUG,
977 "GAS: Tx GAS Initial Resp (comeback = 10TU)");
978 hostapd_drv_send_action(hapd, hapd->iface->freq, 0,
979 dst,
980 wpabuf_head(tx_buf),
981 wpabuf_len(tx_buf));
982 }
983 wpabuf_free(tx_buf);
984
985 /* start a timer of 1.5 * comeback-delay */
986 comeback_delay_tus = comeback_delay_tus +
987 (comeback_delay_tus / 2);
988 comeback_delay_secs = (comeback_delay_tus * 1024) / 1000000;
989 comeback_delay_usecs = (comeback_delay_tus * 1024) -
990 (comeback_delay_secs * 1000000);
991 eloop_register_timeout(comeback_delay_secs,
992 comeback_delay_usecs,
993 gas_serv_clear_cached_ies, dialog,
994 NULL);
995 goto tx_gas_response_done;
996 }
997
998 buf = wpabuf_alloc_copy(wpabuf_head_u8(dialog->sd_resp) +
999 dialog->sd_resp_pos, frag_len);
1000 if (buf == NULL) {
1001 wpa_msg(hapd->msg_ctx, MSG_DEBUG, "GAS: Buffer allocation "
1002 "failed");
1003 goto tx_gas_response_done;
1004 }
1005 tx_buf = gas_anqp_build_initial_resp_buf(dialog_token,
1006 WLAN_STATUS_SUCCESS, 0, buf);
1007 wpabuf_free(buf);
1008 if (tx_buf == NULL)
1009 goto tx_gas_response_done;
1010 wpa_msg(hapd->msg_ctx, MSG_DEBUG, "GAS: Tx GAS Initial "
1011 "Response (frag_id %d frag_len %d)",
1012 dialog->sd_frag_id, (int) frag_len);
1013 dialog->sd_frag_id++;
1014
1015 hostapd_drv_send_action(hapd, hapd->iface->freq, 0, dst,
1016 wpabuf_head(tx_buf), wpabuf_len(tx_buf));
1017 wpabuf_free(tx_buf);
1018tx_gas_response_done:
1019 gas_serv_clear_cached_ies(dialog, NULL);
1020}
1021
1022
1023static void gas_serv_rx_gas_comeback_req(struct hostapd_data *hapd,
1024 const u8 *sa,
1025 const u8 *data, size_t len)
1026{
1027 struct gas_dialog_info *dialog;
1028 struct wpabuf *buf, *tx_buf;
1029 u8 dialog_token;
1030 size_t frag_len;
1031 int more = 0;
1032
1033 wpa_hexdump(MSG_DEBUG, "GAS: RX GAS Comeback Request", data, len);
1034 if (len < 1)
1035 return;
1036 dialog_token = *data;
1037 wpa_msg(hapd->msg_ctx, MSG_DEBUG, "GAS: Dialog Token: %u",
1038 dialog_token);
1039
1040 dialog = gas_serv_dialog_find(hapd, sa, dialog_token);
1041 if (!dialog) {
1042 wpa_msg(hapd->msg_ctx, MSG_DEBUG, "GAS: No pending SD "
1043 "response fragment for " MACSTR " dialog token %u",
1044 MAC2STR(sa), dialog_token);
1045
1046 if (sa[0] & 0x01)
1047 return; /* Invalid source address - drop silently */
1048 tx_buf = gas_anqp_build_comeback_resp_buf(
1049 dialog_token, WLAN_STATUS_NO_OUTSTANDING_GAS_REQ, 0, 0,
1050 0, NULL);
1051 if (tx_buf == NULL)
1052 return;
1053 goto send_resp;
1054 }
1055
1056 if (dialog->sd_resp == NULL) {
1057 wpa_printf(MSG_DEBUG, "GAS: Remote request 0x%x received 0x%x",
1058 dialog->requested, dialog->received);
1059 if ((dialog->requested & dialog->received) !=
1060 dialog->requested) {
1061 wpa_printf(MSG_DEBUG, "GAS: Did not receive response "
1062 "from remote processing");
1063 gas_serv_dialog_clear(dialog);
1064 tx_buf = gas_anqp_build_comeback_resp_buf(
1065 dialog_token,
1066 WLAN_STATUS_GAS_RESP_NOT_RECEIVED, 0, 0, 0,
1067 NULL);
1068 if (tx_buf == NULL)
1069 return;
1070 goto send_resp;
1071 }
1072
1073 buf = gas_serv_build_gas_resp_payload(hapd,
1074 dialog->all_requested,
Dmitry Shmidt61d9df32012-08-29 16:22:06 -07001075 dialog, NULL, 0);
Dmitry Shmidt04949592012-07-19 12:16:46 -07001076 wpa_hexdump_buf(MSG_MSGDUMP, "ANQP: Generated ANQP responses",
1077 buf);
1078 if (!buf)
1079 goto rx_gas_comeback_req_done;
1080 dialog->sd_resp = buf;
1081 dialog->sd_resp_pos = 0;
1082 }
1083 frag_len = wpabuf_len(dialog->sd_resp) - dialog->sd_resp_pos;
1084 if (frag_len > hapd->gas_frag_limit) {
1085 frag_len = hapd->gas_frag_limit;
1086 more = 1;
1087 }
1088 wpa_msg(hapd->msg_ctx, MSG_DEBUG, "GAS: resp frag_len %u",
1089 (unsigned int) frag_len);
1090 buf = wpabuf_alloc_copy(wpabuf_head_u8(dialog->sd_resp) +
1091 dialog->sd_resp_pos, frag_len);
1092 if (buf == NULL) {
1093 wpa_msg(hapd->msg_ctx, MSG_DEBUG, "GAS: Failed to allocate "
1094 "buffer");
1095 goto rx_gas_comeback_req_done;
1096 }
1097 tx_buf = gas_anqp_build_comeback_resp_buf(dialog_token,
1098 WLAN_STATUS_SUCCESS,
1099 dialog->sd_frag_id,
1100 more, 0, buf);
1101 wpabuf_free(buf);
1102 if (tx_buf == NULL)
1103 goto rx_gas_comeback_req_done;
1104 wpa_msg(hapd->msg_ctx, MSG_DEBUG, "GAS: Tx GAS Comeback Response "
1105 "(frag_id %d more=%d frag_len=%d)",
1106 dialog->sd_frag_id, more, (int) frag_len);
1107 dialog->sd_frag_id++;
1108 dialog->sd_resp_pos += frag_len;
1109
1110 if (more) {
1111 wpa_msg(hapd->msg_ctx, MSG_DEBUG, "GAS: %d more bytes remain "
1112 "to be sent",
1113 (int) (wpabuf_len(dialog->sd_resp) -
1114 dialog->sd_resp_pos));
1115 } else {
1116 wpa_msg(hapd->msg_ctx, MSG_DEBUG, "GAS: All fragments of "
1117 "SD response sent");
1118 gas_serv_dialog_clear(dialog);
1119 gas_serv_free_dialogs(hapd, sa);
1120 }
1121
1122send_resp:
1123 hostapd_drv_send_action(hapd, hapd->iface->freq, 0, sa,
1124 wpabuf_head(tx_buf), wpabuf_len(tx_buf));
1125 wpabuf_free(tx_buf);
1126 return;
1127
1128rx_gas_comeback_req_done:
1129 gas_serv_clear_cached_ies(dialog, NULL);
1130}
1131
1132
1133static void gas_serv_rx_public_action(void *ctx, const u8 *buf, size_t len,
1134 int freq)
1135{
1136 struct hostapd_data *hapd = ctx;
1137 const struct ieee80211_mgmt *mgmt;
1138 size_t hdr_len;
1139 const u8 *sa, *data;
1140
1141 mgmt = (const struct ieee80211_mgmt *) buf;
1142 hdr_len = (const u8 *) &mgmt->u.action.u.vs_public_action.action - buf;
1143 if (hdr_len > len)
1144 return;
1145 if (mgmt->u.action.category != WLAN_ACTION_PUBLIC)
1146 return;
1147 sa = mgmt->sa;
1148 len -= hdr_len;
1149 data = &mgmt->u.action.u.public_action.action;
1150 switch (data[0]) {
1151 case WLAN_PA_GAS_INITIAL_REQ:
1152 gas_serv_rx_gas_initial_req(hapd, sa, data + 1, len - 1);
1153 break;
1154 case WLAN_PA_GAS_COMEBACK_REQ:
1155 gas_serv_rx_gas_comeback_req(hapd, sa, data + 1, len - 1);
1156 break;
1157 }
1158}
1159
1160
1161int gas_serv_init(struct hostapd_data *hapd)
1162{
Dmitry Shmidt4b9d52f2013-02-05 17:44:43 -08001163 hapd->public_action_cb2 = gas_serv_rx_public_action;
1164 hapd->public_action_cb2_ctx = hapd;
Dmitry Shmidt04949592012-07-19 12:16:46 -07001165 hapd->gas_frag_limit = 1400;
1166 if (hapd->conf->gas_frag_limit > 0)
1167 hapd->gas_frag_limit = hapd->conf->gas_frag_limit;
1168 return 0;
1169}
1170
1171
1172void gas_serv_deinit(struct hostapd_data *hapd)
1173{
1174}