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