xref: /freebsd/contrib/wpa/src/ap/gas_serv.c (revision 40f65a4df509c6b29b9e72407c7afefe28838844)
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 }
1173