blob: 2177b02a35212c96786e569c5cc3d24dfee6eb3b [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);
49 }
50
51 if (sta->gas_dialog == NULL) {
52 sta->gas_dialog = os_zalloc(GAS_DIALOG_MAX *
53 sizeof(struct gas_dialog_info));
54 if (sta->gas_dialog == NULL)
55 return NULL;
56 }
57
58 for (i = sta->gas_dialog_next, j = 0; j < GAS_DIALOG_MAX; i++, j++) {
59 if (i == GAS_DIALOG_MAX)
60 i = 0;
61 if (sta->gas_dialog[i].valid)
62 continue;
63 dia = &sta->gas_dialog[i];
64 dia->valid = 1;
65 dia->index = i;
66 dia->dialog_token = dialog_token;
67 sta->gas_dialog_next = (++i == GAS_DIALOG_MAX) ? 0 : i;
68 return dia;
69 }
70
71 wpa_msg(hapd->msg_ctx, MSG_ERROR, "ANQP: Could not create dialog for "
72 MACSTR " dialog_token %u. Consider increasing "
73 "GAS_DIALOG_MAX.", MAC2STR(addr), dialog_token);
74
75 return NULL;
76}
77
78
79struct gas_dialog_info *
80gas_serv_dialog_find(struct hostapd_data *hapd, const u8 *addr,
81 u8 dialog_token)
82{
83 struct sta_info *sta;
84 int i;
85
86 sta = ap_get_sta(hapd, addr);
87 if (!sta) {
88 wpa_printf(MSG_DEBUG, "ANQP: could not find STA " MACSTR,
89 MAC2STR(addr));
90 return NULL;
91 }
92 for (i = 0; sta->gas_dialog && i < GAS_DIALOG_MAX; i++) {
93 if (sta->gas_dialog[i].dialog_token != dialog_token ||
94 !sta->gas_dialog[i].valid)
95 continue;
96 return &sta->gas_dialog[i];
97 }
98 wpa_printf(MSG_DEBUG, "ANQP: Could not find dialog for "
99 MACSTR " dialog_token %u", MAC2STR(addr), dialog_token);
100 return NULL;
101}
102
103
104void gas_serv_dialog_clear(struct gas_dialog_info *dia)
105{
106 wpabuf_free(dia->sd_resp);
107 os_memset(dia, 0, sizeof(*dia));
108}
109
110
111static void gas_serv_free_dialogs(struct hostapd_data *hapd,
112 const u8 *sta_addr)
113{
114 struct sta_info *sta;
115 int i;
116
117 sta = ap_get_sta(hapd, sta_addr);
118 if (sta == NULL || sta->gas_dialog == NULL)
119 return;
120
121 for (i = 0; i < GAS_DIALOG_MAX; i++) {
122 if (sta->gas_dialog[i].valid)
123 return;
124 }
125
126 os_free(sta->gas_dialog);
127 sta->gas_dialog = NULL;
128}
129
130
131static void anqp_add_capab_list(struct hostapd_data *hapd,
132 struct wpabuf *buf)
133{
134 u8 *len;
135
136 len = gas_anqp_add_element(buf, ANQP_CAPABILITY_LIST);
137 wpabuf_put_le16(buf, ANQP_CAPABILITY_LIST);
138 if (hapd->conf->venue_name)
139 wpabuf_put_le16(buf, ANQP_VENUE_NAME);
140 if (hapd->conf->roaming_consortium)
141 wpabuf_put_le16(buf, ANQP_ROAMING_CONSORTIUM);
142 gas_anqp_set_element_len(buf, len);
143}
144
145
146static void anqp_add_venue_name(struct hostapd_data *hapd, struct wpabuf *buf)
147{
148 if (hapd->conf->venue_name) {
149 u8 *len;
150 unsigned int i;
151 len = gas_anqp_add_element(buf, ANQP_VENUE_NAME);
152 wpabuf_put_u8(buf, hapd->conf->venue_group);
153 wpabuf_put_u8(buf, hapd->conf->venue_type);
154 for (i = 0; i < hapd->conf->venue_name_count; i++) {
155 struct hostapd_venue_name *vn;
156 vn = &hapd->conf->venue_name[i];
157 wpabuf_put_u8(buf, 3 + vn->name_len);
158 wpabuf_put_data(buf, vn->lang, 3);
159 wpabuf_put_data(buf, vn->name, vn->name_len);
160 }
161 gas_anqp_set_element_len(buf, len);
162 }
163}
164
165
166static void anqp_add_roaming_consortium(struct hostapd_data *hapd,
167 struct wpabuf *buf)
168{
169 unsigned int i;
170 u8 *len;
171
172 len = gas_anqp_add_element(buf, ANQP_ROAMING_CONSORTIUM);
173 for (i = 0; i < hapd->conf->roaming_consortium_count; i++) {
174 struct hostapd_roaming_consortium *rc;
175 rc = &hapd->conf->roaming_consortium[i];
176 wpabuf_put_u8(buf, rc->len);
177 wpabuf_put_data(buf, rc->oi, rc->len);
178 }
179 gas_anqp_set_element_len(buf, len);
180}
181
182
183static struct wpabuf *
184gas_serv_build_gas_resp_payload(struct hostapd_data *hapd,
185 unsigned int request,
186 struct gas_dialog_info *di)
187{
188 struct wpabuf *buf;
189
190 buf = wpabuf_alloc(1400);
191 if (buf == NULL)
192 return NULL;
193
194 if (request & ANQP_REQ_CAPABILITY_LIST)
195 anqp_add_capab_list(hapd, buf);
196 if (request & ANQP_REQ_VENUE_NAME)
197 anqp_add_venue_name(hapd, buf);
198 if (request & ANQP_REQ_ROAMING_CONSORTIUM)
199 anqp_add_roaming_consortium(hapd, buf);
200
201 return buf;
202}
203
204
205static void gas_serv_clear_cached_ies(void *eloop_data, void *user_ctx)
206{
207 struct gas_dialog_info *dia = eloop_data;
208
209 wpa_printf(MSG_DEBUG, "GAS: Timeout triggered, clearing dialog for "
210 "dialog token %d", dia->dialog_token);
211
212 gas_serv_dialog_clear(dia);
213}
214
215
216struct anqp_query_info {
217 unsigned int request;
218 unsigned int remote_request;
219 const void *param;
220 u32 param_arg;
221 u16 remote_delay;
222};
223
224
225static void set_anqp_req(unsigned int bit, const char *name, int local,
226 unsigned int remote, u16 remote_delay,
227 struct anqp_query_info *qi)
228{
229 qi->request |= bit;
230 if (local) {
231 wpa_printf(MSG_DEBUG, "ANQP: %s (local)", name);
232 } else if (bit & remote) {
233 wpa_printf(MSG_DEBUG, "ANQP: %s (remote)", name);
234 qi->remote_request |= bit;
235 if (remote_delay > qi->remote_delay)
236 qi->remote_delay = remote_delay;
237 } else {
238 wpa_printf(MSG_DEBUG, "ANQP: %s not available", name);
239 }
240}
241
242
243static void rx_anqp_query_list_id(struct hostapd_data *hapd, u16 info_id,
244 struct anqp_query_info *qi)
245{
246 switch (info_id) {
247 case ANQP_CAPABILITY_LIST:
248 set_anqp_req(ANQP_REQ_CAPABILITY_LIST, "Capability List", 1, 0,
249 0, qi);
250 break;
251 case ANQP_VENUE_NAME:
252 set_anqp_req(ANQP_REQ_VENUE_NAME, "Venue Name",
253 hapd->conf->venue_name != NULL, 0, 0, qi);
254 break;
255 case ANQP_ROAMING_CONSORTIUM:
256 set_anqp_req(ANQP_REQ_ROAMING_CONSORTIUM, "Roaming Consortium",
257 hapd->conf->roaming_consortium != NULL, 0, 0, qi);
258 break;
259 default:
260 wpa_printf(MSG_DEBUG, "ANQP: Unsupported Info Id %u",
261 info_id);
262 break;
263 }
264}
265
266
267static void rx_anqp_query_list(struct hostapd_data *hapd,
268 const u8 *pos, const u8 *end,
269 struct anqp_query_info *qi)
270{
271 wpa_printf(MSG_DEBUG, "ANQP: %u Info IDs requested in Query list",
272 (unsigned int) (end - pos) / 2);
273
274 while (pos + 2 <= end) {
275 rx_anqp_query_list_id(hapd, WPA_GET_LE16(pos), qi);
276 pos += 2;
277 }
278}
279
280
281static void gas_serv_req_local_processing(struct hostapd_data *hapd,
282 const u8 *sa, u8 dialog_token,
283 struct anqp_query_info *qi)
284{
285 struct wpabuf *buf, *tx_buf;
286
287 buf = gas_serv_build_gas_resp_payload(hapd, qi->request, NULL);
288 wpa_hexdump_buf(MSG_MSGDUMP, "ANQP: Locally generated ANQP responses",
289 buf);
290 if (!buf)
291 return;
292
293 if (wpabuf_len(buf) > hapd->gas_frag_limit ||
294 hapd->conf->gas_comeback_delay) {
295 struct gas_dialog_info *di;
296 u16 comeback_delay = 1;
297
298 if (hapd->conf->gas_comeback_delay) {
299 /* Testing - allow overriding of the delay value */
300 comeback_delay = hapd->conf->gas_comeback_delay;
301 }
302
303 wpa_printf(MSG_DEBUG, "ANQP: Too long response to fit in "
304 "initial response - use GAS comeback");
305 di = gas_dialog_create(hapd, sa, dialog_token);
306 if (!di) {
307 wpa_printf(MSG_INFO, "ANQP: Could not create dialog "
308 "for " MACSTR " (dialog token %u)",
309 MAC2STR(sa), dialog_token);
310 wpabuf_free(buf);
311 return;
312 }
313 di->sd_resp = buf;
314 di->sd_resp_pos = 0;
315 tx_buf = gas_anqp_build_initial_resp_buf(
316 dialog_token, WLAN_STATUS_SUCCESS, comeback_delay,
317 NULL);
318 } else {
319 wpa_printf(MSG_DEBUG, "ANQP: Initial response (no comeback)");
320 tx_buf = gas_anqp_build_initial_resp_buf(
321 dialog_token, WLAN_STATUS_SUCCESS, 0, buf);
322 wpabuf_free(buf);
323 }
324 if (!tx_buf)
325 return;
326
327 hostapd_drv_send_action(hapd, hapd->iface->freq, 0, sa,
328 wpabuf_head(tx_buf), wpabuf_len(tx_buf));
329 wpabuf_free(tx_buf);
330}
331
332
333static void gas_serv_rx_gas_initial_req(struct hostapd_data *hapd,
334 const u8 *sa,
335 const u8 *data, size_t len)
336{
337 const u8 *pos = data;
338 const u8 *end = data + len;
339 const u8 *next;
340 u8 dialog_token;
341 u16 slen;
342 struct anqp_query_info qi;
343 const u8 *adv_proto;
344
345 if (len < 1 + 2)
346 return;
347
348 os_memset(&qi, 0, sizeof(qi));
349
350 dialog_token = *pos++;
351 wpa_msg(hapd->msg_ctx, MSG_DEBUG,
352 "GAS: GAS Initial Request from " MACSTR " (dialog token %u) ",
353 MAC2STR(sa), dialog_token);
354
355 if (*pos != WLAN_EID_ADV_PROTO) {
356 wpa_msg(hapd->msg_ctx, MSG_DEBUG,
357 "GAS: Unexpected IE in GAS Initial Request: %u", *pos);
358 return;
359 }
360 adv_proto = pos++;
361
362 slen = *pos++;
363 next = pos + slen;
364 if (next > end || slen < 2) {
365 wpa_msg(hapd->msg_ctx, MSG_DEBUG,
366 "GAS: Invalid IE in GAS Initial Request");
367 return;
368 }
369 pos++; /* skip QueryRespLenLimit and PAME-BI */
370
371 if (*pos != ACCESS_NETWORK_QUERY_PROTOCOL) {
372 struct wpabuf *buf;
373 wpa_msg(hapd->msg_ctx, MSG_DEBUG,
374 "GAS: Unsupported GAS advertisement protocol id %u",
375 *pos);
376 if (sa[0] & 0x01)
377 return; /* Invalid source address - drop silently */
378 buf = gas_build_initial_resp(
379 dialog_token, WLAN_STATUS_GAS_ADV_PROTO_NOT_SUPPORTED,
380 0, 2 + slen + 2);
381 if (buf == NULL)
382 return;
383 wpabuf_put_data(buf, adv_proto, 2 + slen);
384 wpabuf_put_le16(buf, 0); /* Query Response Length */
385 hostapd_drv_send_action(hapd, hapd->iface->freq, 0, sa,
386 wpabuf_head(buf), wpabuf_len(buf));
387 wpabuf_free(buf);
388 return;
389 }
390
391 pos = next;
392 /* Query Request */
393 if (pos + 2 > end)
394 return;
395 slen = WPA_GET_LE16(pos);
396 pos += 2;
397 if (pos + slen > end)
398 return;
399 end = pos + slen;
400
401 /* ANQP Query Request */
402 while (pos < end) {
403 u16 info_id, elen;
404
405 if (pos + 4 > end)
406 return;
407
408 info_id = WPA_GET_LE16(pos);
409 pos += 2;
410 elen = WPA_GET_LE16(pos);
411 pos += 2;
412
413 if (pos + elen > end) {
414 wpa_printf(MSG_DEBUG, "ANQP: Invalid Query Request");
415 return;
416 }
417
418 switch (info_id) {
419 case ANQP_QUERY_LIST:
420 rx_anqp_query_list(hapd, pos, pos + elen, &qi);
421 break;
422 default:
423 wpa_printf(MSG_DEBUG, "ANQP: Unsupported Query "
424 "Request element %u", info_id);
425 break;
426 }
427
428 pos += elen;
429 }
430
431 gas_serv_req_local_processing(hapd, sa, dialog_token, &qi);
432}
433
434
435void gas_serv_tx_gas_response(struct hostapd_data *hapd, const u8 *dst,
436 struct gas_dialog_info *dialog)
437{
438 struct wpabuf *buf, *tx_buf;
439 u8 dialog_token = dialog->dialog_token;
440 size_t frag_len;
441
442 if (dialog->sd_resp == NULL) {
443 buf = gas_serv_build_gas_resp_payload(hapd,
444 dialog->all_requested,
445 dialog);
446 wpa_hexdump_buf(MSG_MSGDUMP, "ANQP: Generated ANQP responses",
447 buf);
448 if (!buf)
449 goto tx_gas_response_done;
450 dialog->sd_resp = buf;
451 dialog->sd_resp_pos = 0;
452 }
453 frag_len = wpabuf_len(dialog->sd_resp) - dialog->sd_resp_pos;
454 if (frag_len > hapd->gas_frag_limit || dialog->comeback_delay ||
455 hapd->conf->gas_comeback_delay) {
456 u16 comeback_delay_tus = dialog->comeback_delay +
457 GAS_SERV_COMEBACK_DELAY_FUDGE;
458 u32 comeback_delay_secs, comeback_delay_usecs;
459
460 if (hapd->conf->gas_comeback_delay) {
461 /* Testing - allow overriding of the delay value */
462 comeback_delay_tus = hapd->conf->gas_comeback_delay;
463 }
464
465 wpa_printf(MSG_DEBUG, "GAS: Response frag_len %u (frag limit "
466 "%u) and comeback delay %u, "
467 "requesting comebacks", (unsigned int) frag_len,
468 (unsigned int) hapd->gas_frag_limit,
469 dialog->comeback_delay);
470 tx_buf = gas_anqp_build_initial_resp_buf(dialog_token,
471 WLAN_STATUS_SUCCESS,
472 comeback_delay_tus,
473 NULL);
474 if (tx_buf) {
475 wpa_msg(hapd->msg_ctx, MSG_DEBUG,
476 "GAS: Tx GAS Initial Resp (comeback = 10TU)");
477 hostapd_drv_send_action(hapd, hapd->iface->freq, 0,
478 dst,
479 wpabuf_head(tx_buf),
480 wpabuf_len(tx_buf));
481 }
482 wpabuf_free(tx_buf);
483
484 /* start a timer of 1.5 * comeback-delay */
485 comeback_delay_tus = comeback_delay_tus +
486 (comeback_delay_tus / 2);
487 comeback_delay_secs = (comeback_delay_tus * 1024) / 1000000;
488 comeback_delay_usecs = (comeback_delay_tus * 1024) -
489 (comeback_delay_secs * 1000000);
490 eloop_register_timeout(comeback_delay_secs,
491 comeback_delay_usecs,
492 gas_serv_clear_cached_ies, dialog,
493 NULL);
494 goto tx_gas_response_done;
495 }
496
497 buf = wpabuf_alloc_copy(wpabuf_head_u8(dialog->sd_resp) +
498 dialog->sd_resp_pos, frag_len);
499 if (buf == NULL) {
500 wpa_msg(hapd->msg_ctx, MSG_DEBUG, "GAS: Buffer allocation "
501 "failed");
502 goto tx_gas_response_done;
503 }
504 tx_buf = gas_anqp_build_initial_resp_buf(dialog_token,
505 WLAN_STATUS_SUCCESS, 0, buf);
506 wpabuf_free(buf);
507 if (tx_buf == NULL)
508 goto tx_gas_response_done;
509 wpa_msg(hapd->msg_ctx, MSG_DEBUG, "GAS: Tx GAS Initial "
510 "Response (frag_id %d frag_len %d)",
511 dialog->sd_frag_id, (int) frag_len);
512 dialog->sd_frag_id++;
513
514 hostapd_drv_send_action(hapd, hapd->iface->freq, 0, dst,
515 wpabuf_head(tx_buf), wpabuf_len(tx_buf));
516 wpabuf_free(tx_buf);
517tx_gas_response_done:
518 gas_serv_clear_cached_ies(dialog, NULL);
519}
520
521
522static void gas_serv_rx_gas_comeback_req(struct hostapd_data *hapd,
523 const u8 *sa,
524 const u8 *data, size_t len)
525{
526 struct gas_dialog_info *dialog;
527 struct wpabuf *buf, *tx_buf;
528 u8 dialog_token;
529 size_t frag_len;
530 int more = 0;
531
532 wpa_hexdump(MSG_DEBUG, "GAS: RX GAS Comeback Request", data, len);
533 if (len < 1)
534 return;
535 dialog_token = *data;
536 wpa_msg(hapd->msg_ctx, MSG_DEBUG, "GAS: Dialog Token: %u",
537 dialog_token);
538
539 dialog = gas_serv_dialog_find(hapd, sa, dialog_token);
540 if (!dialog) {
541 wpa_msg(hapd->msg_ctx, MSG_DEBUG, "GAS: No pending SD "
542 "response fragment for " MACSTR " dialog token %u",
543 MAC2STR(sa), dialog_token);
544
545 if (sa[0] & 0x01)
546 return; /* Invalid source address - drop silently */
547 tx_buf = gas_anqp_build_comeback_resp_buf(
548 dialog_token, WLAN_STATUS_NO_OUTSTANDING_GAS_REQ, 0, 0,
549 0, NULL);
550 if (tx_buf == NULL)
551 return;
552 goto send_resp;
553 }
554
555 if (dialog->sd_resp == NULL) {
556 wpa_printf(MSG_DEBUG, "GAS: Remote request 0x%x received 0x%x",
557 dialog->requested, dialog->received);
558 if ((dialog->requested & dialog->received) !=
559 dialog->requested) {
560 wpa_printf(MSG_DEBUG, "GAS: Did not receive response "
561 "from remote processing");
562 gas_serv_dialog_clear(dialog);
563 tx_buf = gas_anqp_build_comeback_resp_buf(
564 dialog_token,
565 WLAN_STATUS_GAS_RESP_NOT_RECEIVED, 0, 0, 0,
566 NULL);
567 if (tx_buf == NULL)
568 return;
569 goto send_resp;
570 }
571
572 buf = gas_serv_build_gas_resp_payload(hapd,
573 dialog->all_requested,
574 dialog);
575 wpa_hexdump_buf(MSG_MSGDUMP, "ANQP: Generated ANQP responses",
576 buf);
577 if (!buf)
578 goto rx_gas_comeback_req_done;
579 dialog->sd_resp = buf;
580 dialog->sd_resp_pos = 0;
581 }
582 frag_len = wpabuf_len(dialog->sd_resp) - dialog->sd_resp_pos;
583 if (frag_len > hapd->gas_frag_limit) {
584 frag_len = hapd->gas_frag_limit;
585 more = 1;
586 }
587 wpa_msg(hapd->msg_ctx, MSG_DEBUG, "GAS: resp frag_len %u",
588 (unsigned int) frag_len);
589 buf = wpabuf_alloc_copy(wpabuf_head_u8(dialog->sd_resp) +
590 dialog->sd_resp_pos, frag_len);
591 if (buf == NULL) {
592 wpa_msg(hapd->msg_ctx, MSG_DEBUG, "GAS: Failed to allocate "
593 "buffer");
594 goto rx_gas_comeback_req_done;
595 }
596 tx_buf = gas_anqp_build_comeback_resp_buf(dialog_token,
597 WLAN_STATUS_SUCCESS,
598 dialog->sd_frag_id,
599 more, 0, buf);
600 wpabuf_free(buf);
601 if (tx_buf == NULL)
602 goto rx_gas_comeback_req_done;
603 wpa_msg(hapd->msg_ctx, MSG_DEBUG, "GAS: Tx GAS Comeback Response "
604 "(frag_id %d more=%d frag_len=%d)",
605 dialog->sd_frag_id, more, (int) frag_len);
606 dialog->sd_frag_id++;
607 dialog->sd_resp_pos += frag_len;
608
609 if (more) {
610 wpa_msg(hapd->msg_ctx, MSG_DEBUG, "GAS: %d more bytes remain "
611 "to be sent",
612 (int) (wpabuf_len(dialog->sd_resp) -
613 dialog->sd_resp_pos));
614 } else {
615 wpa_msg(hapd->msg_ctx, MSG_DEBUG, "GAS: All fragments of "
616 "SD response sent");
617 gas_serv_dialog_clear(dialog);
618 gas_serv_free_dialogs(hapd, sa);
619 }
620
621send_resp:
622 hostapd_drv_send_action(hapd, hapd->iface->freq, 0, sa,
623 wpabuf_head(tx_buf), wpabuf_len(tx_buf));
624 wpabuf_free(tx_buf);
625 return;
626
627rx_gas_comeback_req_done:
628 gas_serv_clear_cached_ies(dialog, NULL);
629}
630
631
632static void gas_serv_rx_public_action(void *ctx, const u8 *buf, size_t len,
633 int freq)
634{
635 struct hostapd_data *hapd = ctx;
636 const struct ieee80211_mgmt *mgmt;
637 size_t hdr_len;
638 const u8 *sa, *data;
639
640 mgmt = (const struct ieee80211_mgmt *) buf;
641 hdr_len = (const u8 *) &mgmt->u.action.u.vs_public_action.action - buf;
642 if (hdr_len > len)
643 return;
644 if (mgmt->u.action.category != WLAN_ACTION_PUBLIC)
645 return;
646 sa = mgmt->sa;
647 len -= hdr_len;
648 data = &mgmt->u.action.u.public_action.action;
649 switch (data[0]) {
650 case WLAN_PA_GAS_INITIAL_REQ:
651 gas_serv_rx_gas_initial_req(hapd, sa, data + 1, len - 1);
652 break;
653 case WLAN_PA_GAS_COMEBACK_REQ:
654 gas_serv_rx_gas_comeback_req(hapd, sa, data + 1, len - 1);
655 break;
656 }
657}
658
659
660int gas_serv_init(struct hostapd_data *hapd)
661{
662 hapd->public_action_cb = gas_serv_rx_public_action;
663 hapd->public_action_cb_ctx = hapd;
664 hapd->gas_frag_limit = 1400;
665 if (hapd->conf->gas_frag_limit > 0)
666 hapd->gas_frag_limit = hapd->conf->gas_frag_limit;
667 return 0;
668}
669
670
671void gas_serv_deinit(struct hostapd_data *hapd)
672{
673}