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