13ff40c12SJohn Marino /*
23ff40c12SJohn Marino  * Generic advertisement service (GAS) server
33ff40c12SJohn Marino  * Copyright (c) 2011-2014, Qualcomm Atheros, Inc.
43ff40c12SJohn Marino  *
53ff40c12SJohn Marino  * This software may be distributed under the terms of the BSD license.
63ff40c12SJohn Marino  * See README for more details.
73ff40c12SJohn Marino  */
83ff40c12SJohn Marino 
93ff40c12SJohn Marino #include "includes.h"
103ff40c12SJohn Marino 
113ff40c12SJohn Marino #include "common.h"
123ff40c12SJohn Marino #include "common/ieee802_11_defs.h"
133ff40c12SJohn Marino #include "common/gas.h"
14*a1157835SDaniel Fojt #include "common/wpa_ctrl.h"
153ff40c12SJohn Marino #include "utils/eloop.h"
163ff40c12SJohn Marino #include "hostapd.h"
173ff40c12SJohn Marino #include "ap_config.h"
183ff40c12SJohn Marino #include "ap_drv_ops.h"
19*a1157835SDaniel Fojt #include "dpp_hostapd.h"
203ff40c12SJohn Marino #include "sta_info.h"
213ff40c12SJohn Marino #include "gas_serv.h"
223ff40c12SJohn Marino 
233ff40c12SJohn Marino 
24*a1157835SDaniel Fojt #ifdef CONFIG_DPP
gas_serv_write_dpp_adv_proto(struct wpabuf * buf)25*a1157835SDaniel Fojt static void gas_serv_write_dpp_adv_proto(struct wpabuf *buf)
26*a1157835SDaniel Fojt {
27*a1157835SDaniel Fojt 	wpabuf_put_u8(buf, WLAN_EID_ADV_PROTO);
28*a1157835SDaniel Fojt 	wpabuf_put_u8(buf, 8); /* Length */
29*a1157835SDaniel Fojt 	wpabuf_put_u8(buf, 0x7f);
30*a1157835SDaniel Fojt 	wpabuf_put_u8(buf, WLAN_EID_VENDOR_SPECIFIC);
31*a1157835SDaniel Fojt 	wpabuf_put_u8(buf, 5);
32*a1157835SDaniel Fojt 	wpabuf_put_be24(buf, OUI_WFA);
33*a1157835SDaniel Fojt 	wpabuf_put_u8(buf, DPP_OUI_TYPE);
34*a1157835SDaniel Fojt 	wpabuf_put_u8(buf, 0x01);
35*a1157835SDaniel Fojt }
36*a1157835SDaniel Fojt #endif /* CONFIG_DPP */
37*a1157835SDaniel Fojt 
38*a1157835SDaniel Fojt 
convert_to_protected_dual(struct wpabuf * msg)393ff40c12SJohn Marino static void convert_to_protected_dual(struct wpabuf *msg)
403ff40c12SJohn Marino {
413ff40c12SJohn Marino 	u8 *categ = wpabuf_mhead_u8(msg);
423ff40c12SJohn Marino 	*categ = WLAN_ACTION_PROTECTED_DUAL;
433ff40c12SJohn Marino }
443ff40c12SJohn Marino 
453ff40c12SJohn Marino 
463ff40c12SJohn Marino static struct gas_dialog_info *
gas_dialog_create(struct hostapd_data * hapd,const u8 * addr,u8 dialog_token)473ff40c12SJohn Marino gas_dialog_create(struct hostapd_data *hapd, const u8 *addr, u8 dialog_token)
483ff40c12SJohn Marino {
493ff40c12SJohn Marino 	struct sta_info *sta;
503ff40c12SJohn Marino 	struct gas_dialog_info *dia = NULL;
513ff40c12SJohn Marino 	int i, j;
523ff40c12SJohn Marino 
533ff40c12SJohn Marino 	sta = ap_get_sta(hapd, addr);
543ff40c12SJohn Marino 	if (!sta) {
553ff40c12SJohn Marino 		/*
563ff40c12SJohn Marino 		 * We need a STA entry to be able to maintain state for
573ff40c12SJohn Marino 		 * the GAS query.
583ff40c12SJohn Marino 		 */
593ff40c12SJohn Marino 		wpa_printf(MSG_DEBUG, "ANQP: Add a temporary STA entry for "
603ff40c12SJohn Marino 			   "GAS query");
613ff40c12SJohn Marino 		sta = ap_sta_add(hapd, addr);
623ff40c12SJohn Marino 		if (!sta) {
633ff40c12SJohn Marino 			wpa_printf(MSG_DEBUG, "Failed to add STA " MACSTR
643ff40c12SJohn Marino 				   " for GAS query", MAC2STR(addr));
653ff40c12SJohn Marino 			return NULL;
663ff40c12SJohn Marino 		}
673ff40c12SJohn Marino 		sta->flags |= WLAN_STA_GAS;
683ff40c12SJohn Marino 		/*
693ff40c12SJohn Marino 		 * The default inactivity is 300 seconds. We don't need
70*a1157835SDaniel Fojt 		 * it to be that long. Use five second timeout and increase this
71*a1157835SDaniel Fojt 		 * with the comeback_delay for testing cases.
723ff40c12SJohn Marino 		 */
73*a1157835SDaniel Fojt 		ap_sta_session_timeout(hapd, sta,
74*a1157835SDaniel Fojt 				       hapd->conf->gas_comeback_delay / 1024 +
75*a1157835SDaniel Fojt 				       5);
763ff40c12SJohn Marino 	} else {
773ff40c12SJohn Marino 		ap_sta_replenish_timeout(hapd, sta, 5);
783ff40c12SJohn Marino 	}
793ff40c12SJohn Marino 
803ff40c12SJohn Marino 	if (sta->gas_dialog == NULL) {
81*a1157835SDaniel Fojt 		sta->gas_dialog = os_calloc(GAS_DIALOG_MAX,
823ff40c12SJohn Marino 					    sizeof(struct gas_dialog_info));
833ff40c12SJohn Marino 		if (sta->gas_dialog == NULL)
843ff40c12SJohn Marino 			return NULL;
853ff40c12SJohn Marino 	}
863ff40c12SJohn Marino 
873ff40c12SJohn Marino 	for (i = sta->gas_dialog_next, j = 0; j < GAS_DIALOG_MAX; i++, j++) {
883ff40c12SJohn Marino 		if (i == GAS_DIALOG_MAX)
893ff40c12SJohn Marino 			i = 0;
903ff40c12SJohn Marino 		if (sta->gas_dialog[i].valid)
913ff40c12SJohn Marino 			continue;
923ff40c12SJohn Marino 		dia = &sta->gas_dialog[i];
933ff40c12SJohn Marino 		dia->valid = 1;
943ff40c12SJohn Marino 		dia->dialog_token = dialog_token;
953ff40c12SJohn Marino 		sta->gas_dialog_next = (++i == GAS_DIALOG_MAX) ? 0 : i;
963ff40c12SJohn Marino 		return dia;
973ff40c12SJohn Marino 	}
983ff40c12SJohn Marino 
993ff40c12SJohn Marino 	wpa_msg(hapd->msg_ctx, MSG_ERROR, "ANQP: Could not create dialog for "
1003ff40c12SJohn Marino 		MACSTR " dialog_token %u. Consider increasing "
1013ff40c12SJohn Marino 		"GAS_DIALOG_MAX.", MAC2STR(addr), dialog_token);
1023ff40c12SJohn Marino 
1033ff40c12SJohn Marino 	return NULL;
1043ff40c12SJohn Marino }
1053ff40c12SJohn Marino 
1063ff40c12SJohn Marino 
1073ff40c12SJohn Marino struct gas_dialog_info *
gas_serv_dialog_find(struct hostapd_data * hapd,const u8 * addr,u8 dialog_token)1083ff40c12SJohn Marino gas_serv_dialog_find(struct hostapd_data *hapd, const u8 *addr,
1093ff40c12SJohn Marino 		     u8 dialog_token)
1103ff40c12SJohn Marino {
1113ff40c12SJohn Marino 	struct sta_info *sta;
1123ff40c12SJohn Marino 	int i;
1133ff40c12SJohn Marino 
1143ff40c12SJohn Marino 	sta = ap_get_sta(hapd, addr);
1153ff40c12SJohn Marino 	if (!sta) {
1163ff40c12SJohn Marino 		wpa_printf(MSG_DEBUG, "ANQP: could not find STA " MACSTR,
1173ff40c12SJohn Marino 			   MAC2STR(addr));
1183ff40c12SJohn Marino 		return NULL;
1193ff40c12SJohn Marino 	}
1203ff40c12SJohn Marino 	for (i = 0; sta->gas_dialog && i < GAS_DIALOG_MAX; i++) {
1213ff40c12SJohn Marino 		if (sta->gas_dialog[i].dialog_token != dialog_token ||
1223ff40c12SJohn Marino 		    !sta->gas_dialog[i].valid)
1233ff40c12SJohn Marino 			continue;
124*a1157835SDaniel Fojt 		ap_sta_replenish_timeout(hapd, sta, 5);
1253ff40c12SJohn Marino 		return &sta->gas_dialog[i];
1263ff40c12SJohn Marino 	}
1273ff40c12SJohn Marino 	wpa_printf(MSG_DEBUG, "ANQP: Could not find dialog for "
1283ff40c12SJohn Marino 		   MACSTR " dialog_token %u", MAC2STR(addr), dialog_token);
1293ff40c12SJohn Marino 	return NULL;
1303ff40c12SJohn Marino }
1313ff40c12SJohn Marino 
1323ff40c12SJohn Marino 
gas_serv_dialog_clear(struct gas_dialog_info * dia)1333ff40c12SJohn Marino void gas_serv_dialog_clear(struct gas_dialog_info *dia)
1343ff40c12SJohn Marino {
1353ff40c12SJohn Marino 	wpabuf_free(dia->sd_resp);
1363ff40c12SJohn Marino 	os_memset(dia, 0, sizeof(*dia));
1373ff40c12SJohn Marino }
1383ff40c12SJohn Marino 
1393ff40c12SJohn Marino 
gas_serv_free_dialogs(struct hostapd_data * hapd,const u8 * sta_addr)1403ff40c12SJohn Marino static void gas_serv_free_dialogs(struct hostapd_data *hapd,
1413ff40c12SJohn Marino 				  const u8 *sta_addr)
1423ff40c12SJohn Marino {
1433ff40c12SJohn Marino 	struct sta_info *sta;
1443ff40c12SJohn Marino 	int i;
1453ff40c12SJohn Marino 
1463ff40c12SJohn Marino 	sta = ap_get_sta(hapd, sta_addr);
1473ff40c12SJohn Marino 	if (sta == NULL || sta->gas_dialog == NULL)
1483ff40c12SJohn Marino 		return;
1493ff40c12SJohn Marino 
1503ff40c12SJohn Marino 	for (i = 0; i < GAS_DIALOG_MAX; i++) {
1513ff40c12SJohn Marino 		if (sta->gas_dialog[i].valid)
1523ff40c12SJohn Marino 			return;
1533ff40c12SJohn Marino 	}
1543ff40c12SJohn Marino 
1553ff40c12SJohn Marino 	os_free(sta->gas_dialog);
1563ff40c12SJohn Marino 	sta->gas_dialog = NULL;
1573ff40c12SJohn Marino }
1583ff40c12SJohn Marino 
1593ff40c12SJohn Marino 
1603ff40c12SJohn Marino #ifdef CONFIG_HS20
anqp_add_hs_capab_list(struct hostapd_data * hapd,struct wpabuf * buf)1613ff40c12SJohn Marino static void anqp_add_hs_capab_list(struct hostapd_data *hapd,
1623ff40c12SJohn Marino 				   struct wpabuf *buf)
1633ff40c12SJohn Marino {
1643ff40c12SJohn Marino 	u8 *len;
1653ff40c12SJohn Marino 
1663ff40c12SJohn Marino 	len = gas_anqp_add_element(buf, ANQP_VENDOR_SPECIFIC);
1673ff40c12SJohn Marino 	wpabuf_put_be24(buf, OUI_WFA);
1683ff40c12SJohn Marino 	wpabuf_put_u8(buf, HS20_ANQP_OUI_TYPE);
1693ff40c12SJohn Marino 	wpabuf_put_u8(buf, HS20_STYPE_CAPABILITY_LIST);
1703ff40c12SJohn Marino 	wpabuf_put_u8(buf, 0); /* Reserved */
1713ff40c12SJohn Marino 	wpabuf_put_u8(buf, HS20_STYPE_CAPABILITY_LIST);
1723ff40c12SJohn Marino 	if (hapd->conf->hs20_oper_friendly_name)
1733ff40c12SJohn Marino 		wpabuf_put_u8(buf, HS20_STYPE_OPERATOR_FRIENDLY_NAME);
1743ff40c12SJohn Marino 	if (hapd->conf->hs20_wan_metrics)
1753ff40c12SJohn Marino 		wpabuf_put_u8(buf, HS20_STYPE_WAN_METRICS);
1763ff40c12SJohn Marino 	if (hapd->conf->hs20_connection_capability)
1773ff40c12SJohn Marino 		wpabuf_put_u8(buf, HS20_STYPE_CONNECTION_CAPABILITY);
1783ff40c12SJohn Marino 	if (hapd->conf->nai_realm_data)
1793ff40c12SJohn Marino 		wpabuf_put_u8(buf, HS20_STYPE_NAI_HOME_REALM_QUERY);
1803ff40c12SJohn Marino 	if (hapd->conf->hs20_operating_class)
1813ff40c12SJohn Marino 		wpabuf_put_u8(buf, HS20_STYPE_OPERATING_CLASS);
182*a1157835SDaniel Fojt 	if (hapd->conf->hs20_osu_providers_count)
183*a1157835SDaniel Fojt 		wpabuf_put_u8(buf, HS20_STYPE_OSU_PROVIDERS_LIST);
184*a1157835SDaniel Fojt 	if (hapd->conf->hs20_osu_providers_nai_count)
185*a1157835SDaniel Fojt 		wpabuf_put_u8(buf, HS20_STYPE_OSU_PROVIDERS_NAI_LIST);
186*a1157835SDaniel Fojt 	if (hapd->conf->hs20_icons_count)
187*a1157835SDaniel Fojt 		wpabuf_put_u8(buf, HS20_STYPE_ICON_REQUEST);
188*a1157835SDaniel Fojt 	if (hapd->conf->hs20_operator_icon_count)
189*a1157835SDaniel Fojt 		wpabuf_put_u8(buf, HS20_STYPE_OPERATOR_ICON_METADATA);
1903ff40c12SJohn Marino 	gas_anqp_set_element_len(buf, len);
1913ff40c12SJohn Marino }
1923ff40c12SJohn Marino #endif /* CONFIG_HS20 */
1933ff40c12SJohn Marino 
1943ff40c12SJohn Marino 
get_anqp_elem(struct hostapd_data * hapd,u16 infoid)195*a1157835SDaniel Fojt static struct anqp_element * get_anqp_elem(struct hostapd_data *hapd,
196*a1157835SDaniel Fojt 					   u16 infoid)
197*a1157835SDaniel Fojt {
198*a1157835SDaniel Fojt 	struct anqp_element *elem;
199*a1157835SDaniel Fojt 
200*a1157835SDaniel Fojt 	dl_list_for_each(elem, &hapd->conf->anqp_elem, struct anqp_element,
201*a1157835SDaniel Fojt 			 list) {
202*a1157835SDaniel Fojt 		if (elem->infoid == infoid)
203*a1157835SDaniel Fojt 			return elem;
204*a1157835SDaniel Fojt 	}
205*a1157835SDaniel Fojt 
206*a1157835SDaniel Fojt 	return NULL;
207*a1157835SDaniel Fojt }
208*a1157835SDaniel Fojt 
209*a1157835SDaniel Fojt 
anqp_add_elem(struct hostapd_data * hapd,struct wpabuf * buf,u16 infoid)210*a1157835SDaniel Fojt static void anqp_add_elem(struct hostapd_data *hapd, struct wpabuf *buf,
211*a1157835SDaniel Fojt 			  u16 infoid)
212*a1157835SDaniel Fojt {
213*a1157835SDaniel Fojt 	struct anqp_element *elem;
214*a1157835SDaniel Fojt 
215*a1157835SDaniel Fojt 	elem = get_anqp_elem(hapd, infoid);
216*a1157835SDaniel Fojt 	if (!elem)
217*a1157835SDaniel Fojt 		return;
218*a1157835SDaniel Fojt 	if (wpabuf_tailroom(buf) < 2 + 2 + wpabuf_len(elem->payload)) {
219*a1157835SDaniel Fojt 		wpa_printf(MSG_DEBUG, "ANQP: No room for InfoID %u payload",
220*a1157835SDaniel Fojt 			   infoid);
221*a1157835SDaniel Fojt 		return;
222*a1157835SDaniel Fojt 	}
223*a1157835SDaniel Fojt 
224*a1157835SDaniel Fojt 	wpabuf_put_le16(buf, infoid);
225*a1157835SDaniel Fojt 	wpabuf_put_le16(buf, wpabuf_len(elem->payload));
226*a1157835SDaniel Fojt 	wpabuf_put_buf(buf, elem->payload);
227*a1157835SDaniel Fojt }
228*a1157835SDaniel Fojt 
229*a1157835SDaniel Fojt 
anqp_add_override(struct hostapd_data * hapd,struct wpabuf * buf,u16 infoid)230*a1157835SDaniel Fojt static int anqp_add_override(struct hostapd_data *hapd, struct wpabuf *buf,
231*a1157835SDaniel Fojt 			     u16 infoid)
232*a1157835SDaniel Fojt {
233*a1157835SDaniel Fojt 	if (get_anqp_elem(hapd, infoid)) {
234*a1157835SDaniel Fojt 		anqp_add_elem(hapd, buf, infoid);
235*a1157835SDaniel Fojt 		return 1;
236*a1157835SDaniel Fojt 	}
237*a1157835SDaniel Fojt 
238*a1157835SDaniel Fojt 	return 0;
239*a1157835SDaniel Fojt }
240*a1157835SDaniel Fojt 
241*a1157835SDaniel Fojt 
anqp_add_capab_list(struct hostapd_data * hapd,struct wpabuf * buf)2423ff40c12SJohn Marino static void anqp_add_capab_list(struct hostapd_data *hapd,
2433ff40c12SJohn Marino 				struct wpabuf *buf)
2443ff40c12SJohn Marino {
2453ff40c12SJohn Marino 	u8 *len;
246*a1157835SDaniel Fojt 	u16 id;
247*a1157835SDaniel Fojt 
248*a1157835SDaniel Fojt 	if (anqp_add_override(hapd, buf, ANQP_CAPABILITY_LIST))
249*a1157835SDaniel Fojt 		return;
2503ff40c12SJohn Marino 
2513ff40c12SJohn Marino 	len = gas_anqp_add_element(buf, ANQP_CAPABILITY_LIST);
2523ff40c12SJohn Marino 	wpabuf_put_le16(buf, ANQP_CAPABILITY_LIST);
253*a1157835SDaniel Fojt 	if (hapd->conf->venue_name || get_anqp_elem(hapd, ANQP_VENUE_NAME))
2543ff40c12SJohn Marino 		wpabuf_put_le16(buf, ANQP_VENUE_NAME);
255*a1157835SDaniel Fojt 	if (get_anqp_elem(hapd, ANQP_EMERGENCY_CALL_NUMBER))
256*a1157835SDaniel Fojt 		wpabuf_put_le16(buf, ANQP_EMERGENCY_CALL_NUMBER);
257*a1157835SDaniel Fojt 	if (hapd->conf->network_auth_type ||
258*a1157835SDaniel Fojt 	    get_anqp_elem(hapd, ANQP_NETWORK_AUTH_TYPE))
2593ff40c12SJohn Marino 		wpabuf_put_le16(buf, ANQP_NETWORK_AUTH_TYPE);
260*a1157835SDaniel Fojt 	if (hapd->conf->roaming_consortium ||
261*a1157835SDaniel Fojt 	    get_anqp_elem(hapd, ANQP_ROAMING_CONSORTIUM))
2623ff40c12SJohn Marino 		wpabuf_put_le16(buf, ANQP_ROAMING_CONSORTIUM);
263*a1157835SDaniel Fojt 	if (hapd->conf->ipaddr_type_configured ||
264*a1157835SDaniel Fojt 	    get_anqp_elem(hapd, ANQP_IP_ADDR_TYPE_AVAILABILITY))
2653ff40c12SJohn Marino 		wpabuf_put_le16(buf, ANQP_IP_ADDR_TYPE_AVAILABILITY);
266*a1157835SDaniel Fojt 	if (hapd->conf->nai_realm_data ||
267*a1157835SDaniel Fojt 	    get_anqp_elem(hapd, ANQP_NAI_REALM))
2683ff40c12SJohn Marino 		wpabuf_put_le16(buf, ANQP_NAI_REALM);
269*a1157835SDaniel Fojt 	if (hapd->conf->anqp_3gpp_cell_net ||
270*a1157835SDaniel Fojt 	    get_anqp_elem(hapd, ANQP_3GPP_CELLULAR_NETWORK))
2713ff40c12SJohn Marino 		wpabuf_put_le16(buf, ANQP_3GPP_CELLULAR_NETWORK);
272*a1157835SDaniel Fojt 	if (get_anqp_elem(hapd, ANQP_AP_GEOSPATIAL_LOCATION))
273*a1157835SDaniel Fojt 		wpabuf_put_le16(buf, ANQP_AP_GEOSPATIAL_LOCATION);
274*a1157835SDaniel Fojt 	if (get_anqp_elem(hapd, ANQP_AP_CIVIC_LOCATION))
275*a1157835SDaniel Fojt 		wpabuf_put_le16(buf, ANQP_AP_CIVIC_LOCATION);
276*a1157835SDaniel Fojt 	if (get_anqp_elem(hapd, ANQP_AP_LOCATION_PUBLIC_URI))
277*a1157835SDaniel Fojt 		wpabuf_put_le16(buf, ANQP_AP_LOCATION_PUBLIC_URI);
278*a1157835SDaniel Fojt 	if (hapd->conf->domain_name || get_anqp_elem(hapd, ANQP_DOMAIN_NAME))
2793ff40c12SJohn Marino 		wpabuf_put_le16(buf, ANQP_DOMAIN_NAME);
280*a1157835SDaniel Fojt 	if (get_anqp_elem(hapd, ANQP_EMERGENCY_ALERT_URI))
281*a1157835SDaniel Fojt 		wpabuf_put_le16(buf, ANQP_EMERGENCY_ALERT_URI);
282*a1157835SDaniel Fojt 	if (get_anqp_elem(hapd, ANQP_TDLS_CAPABILITY))
283*a1157835SDaniel Fojt 		wpabuf_put_le16(buf, ANQP_TDLS_CAPABILITY);
284*a1157835SDaniel Fojt 	if (get_anqp_elem(hapd, ANQP_EMERGENCY_NAI))
285*a1157835SDaniel Fojt 		wpabuf_put_le16(buf, ANQP_EMERGENCY_NAI);
286*a1157835SDaniel Fojt 	if (get_anqp_elem(hapd, ANQP_NEIGHBOR_REPORT))
287*a1157835SDaniel Fojt 		wpabuf_put_le16(buf, ANQP_NEIGHBOR_REPORT);
288*a1157835SDaniel Fojt #ifdef CONFIG_FILS
289*a1157835SDaniel Fojt 	if (!dl_list_empty(&hapd->conf->fils_realms) ||
290*a1157835SDaniel Fojt 	    get_anqp_elem(hapd, ANQP_FILS_REALM_INFO))
291*a1157835SDaniel Fojt 		wpabuf_put_le16(buf, ANQP_FILS_REALM_INFO);
292*a1157835SDaniel Fojt #endif /* CONFIG_FILS */
293*a1157835SDaniel Fojt 	if (get_anqp_elem(hapd, ANQP_CAG))
294*a1157835SDaniel Fojt 		wpabuf_put_le16(buf, ANQP_CAG);
295*a1157835SDaniel Fojt 	if (hapd->conf->venue_url || get_anqp_elem(hapd, ANQP_VENUE_URL))
296*a1157835SDaniel Fojt 		wpabuf_put_le16(buf, ANQP_VENUE_URL);
297*a1157835SDaniel Fojt 	if (get_anqp_elem(hapd, ANQP_ADVICE_OF_CHARGE))
298*a1157835SDaniel Fojt 		wpabuf_put_le16(buf, ANQP_ADVICE_OF_CHARGE);
299*a1157835SDaniel Fojt 	if (get_anqp_elem(hapd, ANQP_LOCAL_CONTENT))
300*a1157835SDaniel Fojt 		wpabuf_put_le16(buf, ANQP_LOCAL_CONTENT);
301*a1157835SDaniel Fojt 	for (id = 280; id < 300; id++) {
302*a1157835SDaniel Fojt 		if (get_anqp_elem(hapd, id))
303*a1157835SDaniel Fojt 			wpabuf_put_le16(buf, id);
304*a1157835SDaniel Fojt 	}
3053ff40c12SJohn Marino #ifdef CONFIG_HS20
3063ff40c12SJohn Marino 	anqp_add_hs_capab_list(hapd, buf);
3073ff40c12SJohn Marino #endif /* CONFIG_HS20 */
3083ff40c12SJohn Marino 	gas_anqp_set_element_len(buf, len);
3093ff40c12SJohn Marino }
3103ff40c12SJohn Marino 
3113ff40c12SJohn Marino 
anqp_add_venue_name(struct hostapd_data * hapd,struct wpabuf * buf)3123ff40c12SJohn Marino static void anqp_add_venue_name(struct hostapd_data *hapd, struct wpabuf *buf)
3133ff40c12SJohn Marino {
314*a1157835SDaniel Fojt 	if (anqp_add_override(hapd, buf, ANQP_VENUE_NAME))
315*a1157835SDaniel Fojt 		return;
316*a1157835SDaniel Fojt 
3173ff40c12SJohn Marino 	if (hapd->conf->venue_name) {
3183ff40c12SJohn Marino 		u8 *len;
3193ff40c12SJohn Marino 		unsigned int i;
3203ff40c12SJohn Marino 		len = gas_anqp_add_element(buf, ANQP_VENUE_NAME);
3213ff40c12SJohn Marino 		wpabuf_put_u8(buf, hapd->conf->venue_group);
3223ff40c12SJohn Marino 		wpabuf_put_u8(buf, hapd->conf->venue_type);
3233ff40c12SJohn Marino 		for (i = 0; i < hapd->conf->venue_name_count; i++) {
3243ff40c12SJohn Marino 			struct hostapd_lang_string *vn;
3253ff40c12SJohn Marino 			vn = &hapd->conf->venue_name[i];
3263ff40c12SJohn Marino 			wpabuf_put_u8(buf, 3 + vn->name_len);
3273ff40c12SJohn Marino 			wpabuf_put_data(buf, vn->lang, 3);
3283ff40c12SJohn Marino 			wpabuf_put_data(buf, vn->name, vn->name_len);
3293ff40c12SJohn Marino 		}
3303ff40c12SJohn Marino 		gas_anqp_set_element_len(buf, len);
3313ff40c12SJohn Marino 	}
3323ff40c12SJohn Marino }
3333ff40c12SJohn Marino 
3343ff40c12SJohn Marino 
anqp_add_venue_url(struct hostapd_data * hapd,struct wpabuf * buf)335*a1157835SDaniel Fojt static void anqp_add_venue_url(struct hostapd_data *hapd, struct wpabuf *buf)
336*a1157835SDaniel Fojt {
337*a1157835SDaniel Fojt 	if (anqp_add_override(hapd, buf, ANQP_VENUE_URL))
338*a1157835SDaniel Fojt 		return;
339*a1157835SDaniel Fojt 
340*a1157835SDaniel Fojt 	if (hapd->conf->venue_url) {
341*a1157835SDaniel Fojt 		u8 *len;
342*a1157835SDaniel Fojt 		unsigned int i;
343*a1157835SDaniel Fojt 
344*a1157835SDaniel Fojt 		len = gas_anqp_add_element(buf, ANQP_VENUE_URL);
345*a1157835SDaniel Fojt 		for (i = 0; i < hapd->conf->venue_url_count; i++) {
346*a1157835SDaniel Fojt 			struct hostapd_venue_url *url;
347*a1157835SDaniel Fojt 
348*a1157835SDaniel Fojt 			url = &hapd->conf->venue_url[i];
349*a1157835SDaniel Fojt 			wpabuf_put_u8(buf, 1 + url->url_len);
350*a1157835SDaniel Fojt 			wpabuf_put_u8(buf, url->venue_number);
351*a1157835SDaniel Fojt 			wpabuf_put_data(buf, url->url, url->url_len);
352*a1157835SDaniel Fojt 		}
353*a1157835SDaniel Fojt 		gas_anqp_set_element_len(buf, len);
354*a1157835SDaniel Fojt 	}
355*a1157835SDaniel Fojt }
356*a1157835SDaniel Fojt 
357*a1157835SDaniel Fojt 
anqp_add_network_auth_type(struct hostapd_data * hapd,struct wpabuf * buf)3583ff40c12SJohn Marino static void anqp_add_network_auth_type(struct hostapd_data *hapd,
3593ff40c12SJohn Marino 				       struct wpabuf *buf)
3603ff40c12SJohn Marino {
361*a1157835SDaniel Fojt 	if (anqp_add_override(hapd, buf, ANQP_NETWORK_AUTH_TYPE))
362*a1157835SDaniel Fojt 		return;
363*a1157835SDaniel Fojt 
3643ff40c12SJohn Marino 	if (hapd->conf->network_auth_type) {
3653ff40c12SJohn Marino 		wpabuf_put_le16(buf, ANQP_NETWORK_AUTH_TYPE);
3663ff40c12SJohn Marino 		wpabuf_put_le16(buf, hapd->conf->network_auth_type_len);
3673ff40c12SJohn Marino 		wpabuf_put_data(buf, hapd->conf->network_auth_type,
3683ff40c12SJohn Marino 				hapd->conf->network_auth_type_len);
3693ff40c12SJohn Marino 	}
3703ff40c12SJohn Marino }
3713ff40c12SJohn Marino 
3723ff40c12SJohn Marino 
anqp_add_roaming_consortium(struct hostapd_data * hapd,struct wpabuf * buf)3733ff40c12SJohn Marino static void anqp_add_roaming_consortium(struct hostapd_data *hapd,
3743ff40c12SJohn Marino 					struct wpabuf *buf)
3753ff40c12SJohn Marino {
3763ff40c12SJohn Marino 	unsigned int i;
3773ff40c12SJohn Marino 	u8 *len;
3783ff40c12SJohn Marino 
379*a1157835SDaniel Fojt 	if (anqp_add_override(hapd, buf, ANQP_ROAMING_CONSORTIUM))
380*a1157835SDaniel Fojt 		return;
381*a1157835SDaniel Fojt 
3823ff40c12SJohn Marino 	len = gas_anqp_add_element(buf, ANQP_ROAMING_CONSORTIUM);
3833ff40c12SJohn Marino 	for (i = 0; i < hapd->conf->roaming_consortium_count; i++) {
3843ff40c12SJohn Marino 		struct hostapd_roaming_consortium *rc;
3853ff40c12SJohn Marino 		rc = &hapd->conf->roaming_consortium[i];
3863ff40c12SJohn Marino 		wpabuf_put_u8(buf, rc->len);
3873ff40c12SJohn Marino 		wpabuf_put_data(buf, rc->oi, rc->len);
3883ff40c12SJohn Marino 	}
3893ff40c12SJohn Marino 	gas_anqp_set_element_len(buf, len);
3903ff40c12SJohn Marino }
3913ff40c12SJohn Marino 
3923ff40c12SJohn Marino 
anqp_add_ip_addr_type_availability(struct hostapd_data * hapd,struct wpabuf * buf)3933ff40c12SJohn Marino static void anqp_add_ip_addr_type_availability(struct hostapd_data *hapd,
3943ff40c12SJohn Marino 					       struct wpabuf *buf)
3953ff40c12SJohn Marino {
396*a1157835SDaniel Fojt 	if (anqp_add_override(hapd, buf, ANQP_IP_ADDR_TYPE_AVAILABILITY))
397*a1157835SDaniel Fojt 		return;
398*a1157835SDaniel Fojt 
3993ff40c12SJohn Marino 	if (hapd->conf->ipaddr_type_configured) {
4003ff40c12SJohn Marino 		wpabuf_put_le16(buf, ANQP_IP_ADDR_TYPE_AVAILABILITY);
4013ff40c12SJohn Marino 		wpabuf_put_le16(buf, 1);
4023ff40c12SJohn Marino 		wpabuf_put_u8(buf, hapd->conf->ipaddr_type_availability);
4033ff40c12SJohn Marino 	}
4043ff40c12SJohn Marino }
4053ff40c12SJohn Marino 
4063ff40c12SJohn Marino 
anqp_add_nai_realm_eap(struct wpabuf * buf,struct hostapd_nai_realm_data * realm)4073ff40c12SJohn Marino static void anqp_add_nai_realm_eap(struct wpabuf *buf,
4083ff40c12SJohn Marino 				   struct hostapd_nai_realm_data *realm)
4093ff40c12SJohn Marino {
4103ff40c12SJohn Marino 	unsigned int i, j;
4113ff40c12SJohn Marino 
4123ff40c12SJohn Marino 	wpabuf_put_u8(buf, realm->eap_method_count);
4133ff40c12SJohn Marino 
4143ff40c12SJohn Marino 	for (i = 0; i < realm->eap_method_count; i++) {
4153ff40c12SJohn Marino 		struct hostapd_nai_realm_eap *eap = &realm->eap_method[i];
4163ff40c12SJohn Marino 		wpabuf_put_u8(buf, 2 + (3 * eap->num_auths));
4173ff40c12SJohn Marino 		wpabuf_put_u8(buf, eap->eap_method);
4183ff40c12SJohn Marino 		wpabuf_put_u8(buf, eap->num_auths);
4193ff40c12SJohn Marino 		for (j = 0; j < eap->num_auths; j++) {
4203ff40c12SJohn Marino 			wpabuf_put_u8(buf, eap->auth_id[j]);
4213ff40c12SJohn Marino 			wpabuf_put_u8(buf, 1);
4223ff40c12SJohn Marino 			wpabuf_put_u8(buf, eap->auth_val[j]);
4233ff40c12SJohn Marino 		}
4243ff40c12SJohn Marino 	}
4253ff40c12SJohn Marino }
4263ff40c12SJohn Marino 
4273ff40c12SJohn Marino 
anqp_add_nai_realm_data(struct wpabuf * buf,struct hostapd_nai_realm_data * realm,unsigned int realm_idx)4283ff40c12SJohn Marino static void anqp_add_nai_realm_data(struct wpabuf *buf,
4293ff40c12SJohn Marino 				    struct hostapd_nai_realm_data *realm,
4303ff40c12SJohn Marino 				    unsigned int realm_idx)
4313ff40c12SJohn Marino {
4323ff40c12SJohn Marino 	u8 *realm_data_len;
4333ff40c12SJohn Marino 
4343ff40c12SJohn Marino 	wpa_printf(MSG_DEBUG, "realm=%s, len=%d", realm->realm[realm_idx],
4353ff40c12SJohn Marino 		   (int) os_strlen(realm->realm[realm_idx]));
4363ff40c12SJohn Marino 	realm_data_len = wpabuf_put(buf, 2);
4373ff40c12SJohn Marino 	wpabuf_put_u8(buf, realm->encoding);
4383ff40c12SJohn Marino 	wpabuf_put_u8(buf, os_strlen(realm->realm[realm_idx]));
4393ff40c12SJohn Marino 	wpabuf_put_str(buf, realm->realm[realm_idx]);
4403ff40c12SJohn Marino 	anqp_add_nai_realm_eap(buf, realm);
4413ff40c12SJohn Marino 	gas_anqp_set_element_len(buf, realm_data_len);
4423ff40c12SJohn Marino }
4433ff40c12SJohn Marino 
4443ff40c12SJohn Marino 
hs20_add_nai_home_realm_matches(struct hostapd_data * hapd,struct wpabuf * buf,const u8 * home_realm,size_t home_realm_len)4453ff40c12SJohn Marino static int hs20_add_nai_home_realm_matches(struct hostapd_data *hapd,
4463ff40c12SJohn Marino 					   struct wpabuf *buf,
4473ff40c12SJohn Marino 					   const u8 *home_realm,
4483ff40c12SJohn Marino 					   size_t home_realm_len)
4493ff40c12SJohn Marino {
4503ff40c12SJohn Marino 	unsigned int i, j, k;
4513ff40c12SJohn Marino 	u8 num_realms, num_matching = 0, encoding, realm_len, *realm_list_len;
4523ff40c12SJohn Marino 	struct hostapd_nai_realm_data *realm;
4533ff40c12SJohn Marino 	const u8 *pos, *realm_name, *end;
4543ff40c12SJohn Marino 	struct {
4553ff40c12SJohn Marino 		unsigned int realm_data_idx;
4563ff40c12SJohn Marino 		unsigned int realm_idx;
4573ff40c12SJohn Marino 	} matches[10];
4583ff40c12SJohn Marino 
4593ff40c12SJohn Marino 	pos = home_realm;
4603ff40c12SJohn Marino 	end = pos + home_realm_len;
461*a1157835SDaniel Fojt 	if (end - pos < 1) {
4623ff40c12SJohn Marino 		wpa_hexdump(MSG_DEBUG, "Too short NAI Home Realm Query",
4633ff40c12SJohn Marino 			    home_realm, home_realm_len);
4643ff40c12SJohn Marino 		return -1;
4653ff40c12SJohn Marino 	}
4663ff40c12SJohn Marino 	num_realms = *pos++;
4673ff40c12SJohn Marino 
4683ff40c12SJohn Marino 	for (i = 0; i < num_realms && num_matching < 10; i++) {
469*a1157835SDaniel Fojt 		if (end - pos < 2) {
4703ff40c12SJohn Marino 			wpa_hexdump(MSG_DEBUG,
4713ff40c12SJohn Marino 				    "Truncated NAI Home Realm Query",
4723ff40c12SJohn Marino 				    home_realm, home_realm_len);
4733ff40c12SJohn Marino 			return -1;
4743ff40c12SJohn Marino 		}
4753ff40c12SJohn Marino 		encoding = *pos++;
4763ff40c12SJohn Marino 		realm_len = *pos++;
477*a1157835SDaniel Fojt 		if (realm_len > end - pos) {
4783ff40c12SJohn Marino 			wpa_hexdump(MSG_DEBUG,
4793ff40c12SJohn Marino 				    "Truncated NAI Home Realm Query",
4803ff40c12SJohn Marino 				    home_realm, home_realm_len);
4813ff40c12SJohn Marino 			return -1;
4823ff40c12SJohn Marino 		}
4833ff40c12SJohn Marino 		realm_name = pos;
4843ff40c12SJohn Marino 		for (j = 0; j < hapd->conf->nai_realm_count &&
4853ff40c12SJohn Marino 			     num_matching < 10; j++) {
4863ff40c12SJohn Marino 			const u8 *rpos, *rend;
4873ff40c12SJohn Marino 			realm = &hapd->conf->nai_realm_data[j];
4883ff40c12SJohn Marino 			if (encoding != realm->encoding)
4893ff40c12SJohn Marino 				continue;
4903ff40c12SJohn Marino 
4913ff40c12SJohn Marino 			rpos = realm_name;
4923ff40c12SJohn Marino 			while (rpos < realm_name + realm_len &&
4933ff40c12SJohn Marino 			       num_matching < 10) {
4943ff40c12SJohn Marino 				for (rend = rpos;
4953ff40c12SJohn Marino 				     rend < realm_name + realm_len; rend++) {
4963ff40c12SJohn Marino 					if (*rend == ';')
4973ff40c12SJohn Marino 						break;
4983ff40c12SJohn Marino 				}
4993ff40c12SJohn Marino 				for (k = 0; k < MAX_NAI_REALMS &&
5003ff40c12SJohn Marino 					     realm->realm[k] &&
5013ff40c12SJohn Marino 					     num_matching < 10; k++) {
5023ff40c12SJohn Marino 					if ((int) os_strlen(realm->realm[k]) !=
5033ff40c12SJohn Marino 					    rend - rpos ||
5043ff40c12SJohn Marino 					    os_strncmp((char *) rpos,
5053ff40c12SJohn Marino 						       realm->realm[k],
5063ff40c12SJohn Marino 						       rend - rpos) != 0)
5073ff40c12SJohn Marino 						continue;
5083ff40c12SJohn Marino 					matches[num_matching].realm_data_idx =
5093ff40c12SJohn Marino 						j;
5103ff40c12SJohn Marino 					matches[num_matching].realm_idx = k;
5113ff40c12SJohn Marino 					num_matching++;
5123ff40c12SJohn Marino 				}
5133ff40c12SJohn Marino 				rpos = rend + 1;
5143ff40c12SJohn Marino 			}
5153ff40c12SJohn Marino 		}
5163ff40c12SJohn Marino 		pos += realm_len;
5173ff40c12SJohn Marino 	}
5183ff40c12SJohn Marino 
5193ff40c12SJohn Marino 	realm_list_len = gas_anqp_add_element(buf, ANQP_NAI_REALM);
5203ff40c12SJohn Marino 	wpabuf_put_le16(buf, num_matching);
5213ff40c12SJohn Marino 
5223ff40c12SJohn Marino 	/*
5233ff40c12SJohn Marino 	 * There are two ways to format. 1. each realm in a NAI Realm Data unit
5243ff40c12SJohn Marino 	 * 2. all realms that share the same EAP methods in a NAI Realm Data
5253ff40c12SJohn Marino 	 * unit. The first format is likely to be bigger in size than the
5263ff40c12SJohn Marino 	 * second, but may be easier to parse and process by the receiver.
5273ff40c12SJohn Marino 	 */
5283ff40c12SJohn Marino 	for (i = 0; i < num_matching; i++) {
5293ff40c12SJohn Marino 		wpa_printf(MSG_DEBUG, "realm_idx %d, realm_data_idx %d",
5303ff40c12SJohn Marino 			   matches[i].realm_data_idx, matches[i].realm_idx);
5313ff40c12SJohn Marino 		realm = &hapd->conf->nai_realm_data[matches[i].realm_data_idx];
5323ff40c12SJohn Marino 		anqp_add_nai_realm_data(buf, realm, matches[i].realm_idx);
5333ff40c12SJohn Marino 	}
5343ff40c12SJohn Marino 	gas_anqp_set_element_len(buf, realm_list_len);
5353ff40c12SJohn Marino 	return 0;
5363ff40c12SJohn Marino }
5373ff40c12SJohn Marino 
5383ff40c12SJohn Marino 
anqp_add_nai_realm(struct hostapd_data * hapd,struct wpabuf * buf,const u8 * home_realm,size_t home_realm_len,int nai_realm,int nai_home_realm)5393ff40c12SJohn Marino static void anqp_add_nai_realm(struct hostapd_data *hapd, struct wpabuf *buf,
5403ff40c12SJohn Marino 			       const u8 *home_realm, size_t home_realm_len,
5413ff40c12SJohn Marino 			       int nai_realm, int nai_home_realm)
5423ff40c12SJohn Marino {
543*a1157835SDaniel Fojt 	if (nai_realm && !nai_home_realm &&
544*a1157835SDaniel Fojt 	    anqp_add_override(hapd, buf, ANQP_NAI_REALM))
545*a1157835SDaniel Fojt 		return;
546*a1157835SDaniel Fojt 
5473ff40c12SJohn Marino 	if (nai_realm && hapd->conf->nai_realm_data) {
5483ff40c12SJohn Marino 		u8 *len;
5493ff40c12SJohn Marino 		unsigned int i, j;
5503ff40c12SJohn Marino 		len = gas_anqp_add_element(buf, ANQP_NAI_REALM);
5513ff40c12SJohn Marino 		wpabuf_put_le16(buf, hapd->conf->nai_realm_count);
5523ff40c12SJohn Marino 		for (i = 0; i < hapd->conf->nai_realm_count; i++) {
5533ff40c12SJohn Marino 			u8 *realm_data_len, *realm_len;
5543ff40c12SJohn Marino 			struct hostapd_nai_realm_data *realm;
5553ff40c12SJohn Marino 
5563ff40c12SJohn Marino 			realm = &hapd->conf->nai_realm_data[i];
5573ff40c12SJohn Marino 			realm_data_len = wpabuf_put(buf, 2);
5583ff40c12SJohn Marino 			wpabuf_put_u8(buf, realm->encoding);
5593ff40c12SJohn Marino 			realm_len = wpabuf_put(buf, 1);
5603ff40c12SJohn Marino 			for (j = 0; realm->realm[j]; j++) {
5613ff40c12SJohn Marino 				if (j > 0)
5623ff40c12SJohn Marino 					wpabuf_put_u8(buf, ';');
5633ff40c12SJohn Marino 				wpabuf_put_str(buf, realm->realm[j]);
5643ff40c12SJohn Marino 			}
5653ff40c12SJohn Marino 			*realm_len = (u8 *) wpabuf_put(buf, 0) - realm_len - 1;
5663ff40c12SJohn Marino 			anqp_add_nai_realm_eap(buf, realm);
5673ff40c12SJohn Marino 			gas_anqp_set_element_len(buf, realm_data_len);
5683ff40c12SJohn Marino 		}
5693ff40c12SJohn Marino 		gas_anqp_set_element_len(buf, len);
570*a1157835SDaniel Fojt 	} else if (nai_home_realm && hapd->conf->nai_realm_data && home_realm) {
5713ff40c12SJohn Marino 		hs20_add_nai_home_realm_matches(hapd, buf, home_realm,
5723ff40c12SJohn Marino 						home_realm_len);
5733ff40c12SJohn Marino 	}
5743ff40c12SJohn Marino }
5753ff40c12SJohn Marino 
5763ff40c12SJohn Marino 
anqp_add_3gpp_cellular_network(struct hostapd_data * hapd,struct wpabuf * buf)5773ff40c12SJohn Marino static void anqp_add_3gpp_cellular_network(struct hostapd_data *hapd,
5783ff40c12SJohn Marino 					   struct wpabuf *buf)
5793ff40c12SJohn Marino {
580*a1157835SDaniel Fojt 	if (anqp_add_override(hapd, buf, ANQP_3GPP_CELLULAR_NETWORK))
581*a1157835SDaniel Fojt 		return;
582*a1157835SDaniel Fojt 
5833ff40c12SJohn Marino 	if (hapd->conf->anqp_3gpp_cell_net) {
5843ff40c12SJohn Marino 		wpabuf_put_le16(buf, ANQP_3GPP_CELLULAR_NETWORK);
5853ff40c12SJohn Marino 		wpabuf_put_le16(buf,
5863ff40c12SJohn Marino 				hapd->conf->anqp_3gpp_cell_net_len);
5873ff40c12SJohn Marino 		wpabuf_put_data(buf, hapd->conf->anqp_3gpp_cell_net,
5883ff40c12SJohn Marino 				hapd->conf->anqp_3gpp_cell_net_len);
5893ff40c12SJohn Marino 	}
5903ff40c12SJohn Marino }
5913ff40c12SJohn Marino 
5923ff40c12SJohn Marino 
anqp_add_domain_name(struct hostapd_data * hapd,struct wpabuf * buf)5933ff40c12SJohn Marino static void anqp_add_domain_name(struct hostapd_data *hapd, struct wpabuf *buf)
5943ff40c12SJohn Marino {
595*a1157835SDaniel Fojt 	if (anqp_add_override(hapd, buf, ANQP_DOMAIN_NAME))
596*a1157835SDaniel Fojt 		return;
597*a1157835SDaniel Fojt 
5983ff40c12SJohn Marino 	if (hapd->conf->domain_name) {
5993ff40c12SJohn Marino 		wpabuf_put_le16(buf, ANQP_DOMAIN_NAME);
6003ff40c12SJohn Marino 		wpabuf_put_le16(buf, hapd->conf->domain_name_len);
6013ff40c12SJohn Marino 		wpabuf_put_data(buf, hapd->conf->domain_name,
6023ff40c12SJohn Marino 				hapd->conf->domain_name_len);
6033ff40c12SJohn Marino 	}
6043ff40c12SJohn Marino }
6053ff40c12SJohn Marino 
6063ff40c12SJohn Marino 
607*a1157835SDaniel Fojt #ifdef CONFIG_FILS
anqp_add_fils_realm_info(struct hostapd_data * hapd,struct wpabuf * buf)608*a1157835SDaniel Fojt static void anqp_add_fils_realm_info(struct hostapd_data *hapd,
609*a1157835SDaniel Fojt 				     struct wpabuf *buf)
610*a1157835SDaniel Fojt {
611*a1157835SDaniel Fojt 	size_t count;
612*a1157835SDaniel Fojt 
613*a1157835SDaniel Fojt 	if (anqp_add_override(hapd, buf, ANQP_FILS_REALM_INFO))
614*a1157835SDaniel Fojt 		return;
615*a1157835SDaniel Fojt 
616*a1157835SDaniel Fojt 	count = dl_list_len(&hapd->conf->fils_realms);
617*a1157835SDaniel Fojt 	if (count > 10000)
618*a1157835SDaniel Fojt 		count = 10000;
619*a1157835SDaniel Fojt 	if (count) {
620*a1157835SDaniel Fojt 		struct fils_realm *realm;
621*a1157835SDaniel Fojt 
622*a1157835SDaniel Fojt 		wpabuf_put_le16(buf, ANQP_FILS_REALM_INFO);
623*a1157835SDaniel Fojt 		wpabuf_put_le16(buf, 2 * count);
624*a1157835SDaniel Fojt 
625*a1157835SDaniel Fojt 		dl_list_for_each(realm, &hapd->conf->fils_realms,
626*a1157835SDaniel Fojt 				 struct fils_realm, list) {
627*a1157835SDaniel Fojt 			if (count == 0)
628*a1157835SDaniel Fojt 				break;
629*a1157835SDaniel Fojt 			wpabuf_put_data(buf, realm->hash, 2);
630*a1157835SDaniel Fojt 			count--;
631*a1157835SDaniel Fojt 		}
632*a1157835SDaniel Fojt 	}
633*a1157835SDaniel Fojt }
634*a1157835SDaniel Fojt #endif /* CONFIG_FILS */
635*a1157835SDaniel Fojt 
636*a1157835SDaniel Fojt 
6373ff40c12SJohn Marino #ifdef CONFIG_HS20
6383ff40c12SJohn Marino 
anqp_add_operator_friendly_name(struct hostapd_data * hapd,struct wpabuf * buf)6393ff40c12SJohn Marino static void anqp_add_operator_friendly_name(struct hostapd_data *hapd,
6403ff40c12SJohn Marino 					    struct wpabuf *buf)
6413ff40c12SJohn Marino {
6423ff40c12SJohn Marino 	if (hapd->conf->hs20_oper_friendly_name) {
6433ff40c12SJohn Marino 		u8 *len;
6443ff40c12SJohn Marino 		unsigned int i;
6453ff40c12SJohn Marino 		len = gas_anqp_add_element(buf, ANQP_VENDOR_SPECIFIC);
6463ff40c12SJohn Marino 		wpabuf_put_be24(buf, OUI_WFA);
6473ff40c12SJohn Marino 		wpabuf_put_u8(buf, HS20_ANQP_OUI_TYPE);
6483ff40c12SJohn Marino 		wpabuf_put_u8(buf, HS20_STYPE_OPERATOR_FRIENDLY_NAME);
6493ff40c12SJohn Marino 		wpabuf_put_u8(buf, 0); /* Reserved */
6503ff40c12SJohn Marino 		for (i = 0; i < hapd->conf->hs20_oper_friendly_name_count; i++)
6513ff40c12SJohn Marino 		{
6523ff40c12SJohn Marino 			struct hostapd_lang_string *vn;
6533ff40c12SJohn Marino 			vn = &hapd->conf->hs20_oper_friendly_name[i];
6543ff40c12SJohn Marino 			wpabuf_put_u8(buf, 3 + vn->name_len);
6553ff40c12SJohn Marino 			wpabuf_put_data(buf, vn->lang, 3);
6563ff40c12SJohn Marino 			wpabuf_put_data(buf, vn->name, vn->name_len);
6573ff40c12SJohn Marino 		}
6583ff40c12SJohn Marino 		gas_anqp_set_element_len(buf, len);
6593ff40c12SJohn Marino 	}
6603ff40c12SJohn Marino }
6613ff40c12SJohn Marino 
6623ff40c12SJohn Marino 
anqp_add_wan_metrics(struct hostapd_data * hapd,struct wpabuf * buf)6633ff40c12SJohn Marino static void anqp_add_wan_metrics(struct hostapd_data *hapd,
6643ff40c12SJohn Marino 				 struct wpabuf *buf)
6653ff40c12SJohn Marino {
6663ff40c12SJohn Marino 	if (hapd->conf->hs20_wan_metrics) {
6673ff40c12SJohn Marino 		u8 *len = gas_anqp_add_element(buf, ANQP_VENDOR_SPECIFIC);
6683ff40c12SJohn Marino 		wpabuf_put_be24(buf, OUI_WFA);
6693ff40c12SJohn Marino 		wpabuf_put_u8(buf, HS20_ANQP_OUI_TYPE);
6703ff40c12SJohn Marino 		wpabuf_put_u8(buf, HS20_STYPE_WAN_METRICS);
6713ff40c12SJohn Marino 		wpabuf_put_u8(buf, 0); /* Reserved */
6723ff40c12SJohn Marino 		wpabuf_put_data(buf, hapd->conf->hs20_wan_metrics, 13);
6733ff40c12SJohn Marino 		gas_anqp_set_element_len(buf, len);
6743ff40c12SJohn Marino 	}
6753ff40c12SJohn Marino }
6763ff40c12SJohn Marino 
6773ff40c12SJohn Marino 
anqp_add_connection_capability(struct hostapd_data * hapd,struct wpabuf * buf)6783ff40c12SJohn Marino static void anqp_add_connection_capability(struct hostapd_data *hapd,
6793ff40c12SJohn Marino 					   struct wpabuf *buf)
6803ff40c12SJohn Marino {
6813ff40c12SJohn Marino 	if (hapd->conf->hs20_connection_capability) {
6823ff40c12SJohn Marino 		u8 *len = gas_anqp_add_element(buf, ANQP_VENDOR_SPECIFIC);
6833ff40c12SJohn Marino 		wpabuf_put_be24(buf, OUI_WFA);
6843ff40c12SJohn Marino 		wpabuf_put_u8(buf, HS20_ANQP_OUI_TYPE);
6853ff40c12SJohn Marino 		wpabuf_put_u8(buf, HS20_STYPE_CONNECTION_CAPABILITY);
6863ff40c12SJohn Marino 		wpabuf_put_u8(buf, 0); /* Reserved */
6873ff40c12SJohn Marino 		wpabuf_put_data(buf, hapd->conf->hs20_connection_capability,
6883ff40c12SJohn Marino 				hapd->conf->hs20_connection_capability_len);
6893ff40c12SJohn Marino 		gas_anqp_set_element_len(buf, len);
6903ff40c12SJohn Marino 	}
6913ff40c12SJohn Marino }
6923ff40c12SJohn Marino 
6933ff40c12SJohn Marino 
anqp_add_operating_class(struct hostapd_data * hapd,struct wpabuf * buf)6943ff40c12SJohn Marino static void anqp_add_operating_class(struct hostapd_data *hapd,
6953ff40c12SJohn Marino 				     struct wpabuf *buf)
6963ff40c12SJohn Marino {
6973ff40c12SJohn Marino 	if (hapd->conf->hs20_operating_class) {
6983ff40c12SJohn Marino 		u8 *len = gas_anqp_add_element(buf, ANQP_VENDOR_SPECIFIC);
6993ff40c12SJohn Marino 		wpabuf_put_be24(buf, OUI_WFA);
7003ff40c12SJohn Marino 		wpabuf_put_u8(buf, HS20_ANQP_OUI_TYPE);
7013ff40c12SJohn Marino 		wpabuf_put_u8(buf, HS20_STYPE_OPERATING_CLASS);
7023ff40c12SJohn Marino 		wpabuf_put_u8(buf, 0); /* Reserved */
7033ff40c12SJohn Marino 		wpabuf_put_data(buf, hapd->conf->hs20_operating_class,
7043ff40c12SJohn Marino 				hapd->conf->hs20_operating_class_len);
7053ff40c12SJohn Marino 		gas_anqp_set_element_len(buf, len);
7063ff40c12SJohn Marino 	}
7073ff40c12SJohn Marino }
7083ff40c12SJohn Marino 
709*a1157835SDaniel Fojt 
anqp_add_icon(struct wpabuf * buf,struct hostapd_bss_config * bss,const char * name)710*a1157835SDaniel Fojt static void anqp_add_icon(struct wpabuf *buf, struct hostapd_bss_config *bss,
711*a1157835SDaniel Fojt 			  const char *name)
712*a1157835SDaniel Fojt {
713*a1157835SDaniel Fojt 	size_t j;
714*a1157835SDaniel Fojt 	struct hs20_icon *icon = NULL;
715*a1157835SDaniel Fojt 
716*a1157835SDaniel Fojt 	for (j = 0; j < bss->hs20_icons_count && !icon; j++) {
717*a1157835SDaniel Fojt 		if (os_strcmp(name, bss->hs20_icons[j].name) == 0)
718*a1157835SDaniel Fojt 			icon = &bss->hs20_icons[j];
719*a1157835SDaniel Fojt 	}
720*a1157835SDaniel Fojt 	if (!icon)
721*a1157835SDaniel Fojt 		return; /* icon info not found */
722*a1157835SDaniel Fojt 
723*a1157835SDaniel Fojt 	wpabuf_put_le16(buf, icon->width);
724*a1157835SDaniel Fojt 	wpabuf_put_le16(buf, icon->height);
725*a1157835SDaniel Fojt 	wpabuf_put_data(buf, icon->language, 3);
726*a1157835SDaniel Fojt 	wpabuf_put_u8(buf, os_strlen(icon->type));
727*a1157835SDaniel Fojt 	wpabuf_put_str(buf, icon->type);
728*a1157835SDaniel Fojt 	wpabuf_put_u8(buf, os_strlen(icon->name));
729*a1157835SDaniel Fojt 	wpabuf_put_str(buf, icon->name);
730*a1157835SDaniel Fojt }
731*a1157835SDaniel Fojt 
732*a1157835SDaniel Fojt 
anqp_add_osu_provider(struct wpabuf * buf,struct hostapd_bss_config * bss,struct hs20_osu_provider * p)733*a1157835SDaniel Fojt static void anqp_add_osu_provider(struct wpabuf *buf,
734*a1157835SDaniel Fojt 				  struct hostapd_bss_config *bss,
735*a1157835SDaniel Fojt 				  struct hs20_osu_provider *p)
736*a1157835SDaniel Fojt {
737*a1157835SDaniel Fojt 	u8 *len, *len2, *count;
738*a1157835SDaniel Fojt 	unsigned int i;
739*a1157835SDaniel Fojt 
740*a1157835SDaniel Fojt 	len = wpabuf_put(buf, 2); /* OSU Provider Length to be filled */
741*a1157835SDaniel Fojt 
742*a1157835SDaniel Fojt 	/* OSU Friendly Name Duples */
743*a1157835SDaniel Fojt 	len2 = wpabuf_put(buf, 2);
744*a1157835SDaniel Fojt 	for (i = 0; i < p->friendly_name_count; i++) {
745*a1157835SDaniel Fojt 		struct hostapd_lang_string *s = &p->friendly_name[i];
746*a1157835SDaniel Fojt 		wpabuf_put_u8(buf, 3 + s->name_len);
747*a1157835SDaniel Fojt 		wpabuf_put_data(buf, s->lang, 3);
748*a1157835SDaniel Fojt 		wpabuf_put_data(buf, s->name, s->name_len);
749*a1157835SDaniel Fojt 	}
750*a1157835SDaniel Fojt 	WPA_PUT_LE16(len2, (u8 *) wpabuf_put(buf, 0) - len2 - 2);
751*a1157835SDaniel Fojt 
752*a1157835SDaniel Fojt 	/* OSU Server URI */
753*a1157835SDaniel Fojt 	if (p->server_uri) {
754*a1157835SDaniel Fojt 		wpabuf_put_u8(buf, os_strlen(p->server_uri));
755*a1157835SDaniel Fojt 		wpabuf_put_str(buf, p->server_uri);
756*a1157835SDaniel Fojt 	} else
757*a1157835SDaniel Fojt 		wpabuf_put_u8(buf, 0);
758*a1157835SDaniel Fojt 
759*a1157835SDaniel Fojt 	/* OSU Method List */
760*a1157835SDaniel Fojt 	count = wpabuf_put(buf, 1);
761*a1157835SDaniel Fojt 	for (i = 0; p->method_list && p->method_list[i] >= 0; i++)
762*a1157835SDaniel Fojt 		wpabuf_put_u8(buf, p->method_list[i]);
763*a1157835SDaniel Fojt 	*count = i;
764*a1157835SDaniel Fojt 
765*a1157835SDaniel Fojt 	/* Icons Available */
766*a1157835SDaniel Fojt 	len2 = wpabuf_put(buf, 2);
767*a1157835SDaniel Fojt 	for (i = 0; i < p->icons_count; i++)
768*a1157835SDaniel Fojt 		anqp_add_icon(buf, bss, p->icons[i]);
769*a1157835SDaniel Fojt 	WPA_PUT_LE16(len2, (u8 *) wpabuf_put(buf, 0) - len2 - 2);
770*a1157835SDaniel Fojt 
771*a1157835SDaniel Fojt 	/* OSU_NAI */
772*a1157835SDaniel Fojt 	if (p->osu_nai) {
773*a1157835SDaniel Fojt 		wpabuf_put_u8(buf, os_strlen(p->osu_nai));
774*a1157835SDaniel Fojt 		wpabuf_put_str(buf, p->osu_nai);
775*a1157835SDaniel Fojt 	} else
776*a1157835SDaniel Fojt 		wpabuf_put_u8(buf, 0);
777*a1157835SDaniel Fojt 
778*a1157835SDaniel Fojt 	/* OSU Service Description Duples */
779*a1157835SDaniel Fojt 	len2 = wpabuf_put(buf, 2);
780*a1157835SDaniel Fojt 	for (i = 0; i < p->service_desc_count; i++) {
781*a1157835SDaniel Fojt 		struct hostapd_lang_string *s = &p->service_desc[i];
782*a1157835SDaniel Fojt 		wpabuf_put_u8(buf, 3 + s->name_len);
783*a1157835SDaniel Fojt 		wpabuf_put_data(buf, s->lang, 3);
784*a1157835SDaniel Fojt 		wpabuf_put_data(buf, s->name, s->name_len);
785*a1157835SDaniel Fojt 	}
786*a1157835SDaniel Fojt 	WPA_PUT_LE16(len2, (u8 *) wpabuf_put(buf, 0) - len2 - 2);
787*a1157835SDaniel Fojt 
788*a1157835SDaniel Fojt 	WPA_PUT_LE16(len, (u8 *) wpabuf_put(buf, 0) - len - 2);
789*a1157835SDaniel Fojt }
790*a1157835SDaniel Fojt 
791*a1157835SDaniel Fojt 
anqp_add_osu_providers_list(struct hostapd_data * hapd,struct wpabuf * buf)792*a1157835SDaniel Fojt static void anqp_add_osu_providers_list(struct hostapd_data *hapd,
793*a1157835SDaniel Fojt 					struct wpabuf *buf)
794*a1157835SDaniel Fojt {
795*a1157835SDaniel Fojt 	if (hapd->conf->hs20_osu_providers_count) {
796*a1157835SDaniel Fojt 		size_t i;
797*a1157835SDaniel Fojt 		u8 *len = gas_anqp_add_element(buf, ANQP_VENDOR_SPECIFIC);
798*a1157835SDaniel Fojt 		wpabuf_put_be24(buf, OUI_WFA);
799*a1157835SDaniel Fojt 		wpabuf_put_u8(buf, HS20_ANQP_OUI_TYPE);
800*a1157835SDaniel Fojt 		wpabuf_put_u8(buf, HS20_STYPE_OSU_PROVIDERS_LIST);
801*a1157835SDaniel Fojt 		wpabuf_put_u8(buf, 0); /* Reserved */
802*a1157835SDaniel Fojt 
803*a1157835SDaniel Fojt 		/* OSU SSID */
804*a1157835SDaniel Fojt 		wpabuf_put_u8(buf, hapd->conf->osu_ssid_len);
805*a1157835SDaniel Fojt 		wpabuf_put_data(buf, hapd->conf->osu_ssid,
806*a1157835SDaniel Fojt 				hapd->conf->osu_ssid_len);
807*a1157835SDaniel Fojt 
808*a1157835SDaniel Fojt 		/* Number of OSU Providers */
809*a1157835SDaniel Fojt 		wpabuf_put_u8(buf, hapd->conf->hs20_osu_providers_count);
810*a1157835SDaniel Fojt 
811*a1157835SDaniel Fojt 		for (i = 0; i < hapd->conf->hs20_osu_providers_count; i++) {
812*a1157835SDaniel Fojt 			anqp_add_osu_provider(
813*a1157835SDaniel Fojt 				buf, hapd->conf,
814*a1157835SDaniel Fojt 				&hapd->conf->hs20_osu_providers[i]);
815*a1157835SDaniel Fojt 		}
816*a1157835SDaniel Fojt 
817*a1157835SDaniel Fojt 		gas_anqp_set_element_len(buf, len);
818*a1157835SDaniel Fojt 	}
819*a1157835SDaniel Fojt }
820*a1157835SDaniel Fojt 
821*a1157835SDaniel Fojt 
anqp_add_osu_provider_nai(struct wpabuf * buf,struct hs20_osu_provider * p)822*a1157835SDaniel Fojt static void anqp_add_osu_provider_nai(struct wpabuf *buf,
823*a1157835SDaniel Fojt 				      struct hs20_osu_provider *p)
824*a1157835SDaniel Fojt {
825*a1157835SDaniel Fojt 	/* OSU_NAI for shared BSS (Single SSID) */
826*a1157835SDaniel Fojt 	if (p->osu_nai2) {
827*a1157835SDaniel Fojt 		wpabuf_put_u8(buf, os_strlen(p->osu_nai2));
828*a1157835SDaniel Fojt 		wpabuf_put_str(buf, p->osu_nai2);
829*a1157835SDaniel Fojt 	} else {
830*a1157835SDaniel Fojt 		wpabuf_put_u8(buf, 0);
831*a1157835SDaniel Fojt 	}
832*a1157835SDaniel Fojt }
833*a1157835SDaniel Fojt 
834*a1157835SDaniel Fojt 
anqp_add_osu_providers_nai_list(struct hostapd_data * hapd,struct wpabuf * buf)835*a1157835SDaniel Fojt static void anqp_add_osu_providers_nai_list(struct hostapd_data *hapd,
836*a1157835SDaniel Fojt 					    struct wpabuf *buf)
837*a1157835SDaniel Fojt {
838*a1157835SDaniel Fojt 	if (hapd->conf->hs20_osu_providers_nai_count) {
839*a1157835SDaniel Fojt 		size_t i;
840*a1157835SDaniel Fojt 		u8 *len = gas_anqp_add_element(buf, ANQP_VENDOR_SPECIFIC);
841*a1157835SDaniel Fojt 		wpabuf_put_be24(buf, OUI_WFA);
842*a1157835SDaniel Fojt 		wpabuf_put_u8(buf, HS20_ANQP_OUI_TYPE);
843*a1157835SDaniel Fojt 		wpabuf_put_u8(buf, HS20_STYPE_OSU_PROVIDERS_NAI_LIST);
844*a1157835SDaniel Fojt 		wpabuf_put_u8(buf, 0); /* Reserved */
845*a1157835SDaniel Fojt 
846*a1157835SDaniel Fojt 		for (i = 0; i < hapd->conf->hs20_osu_providers_count; i++) {
847*a1157835SDaniel Fojt 			anqp_add_osu_provider_nai(
848*a1157835SDaniel Fojt 				buf, &hapd->conf->hs20_osu_providers[i]);
849*a1157835SDaniel Fojt 		}
850*a1157835SDaniel Fojt 
851*a1157835SDaniel Fojt 		gas_anqp_set_element_len(buf, len);
852*a1157835SDaniel Fojt 	}
853*a1157835SDaniel Fojt }
854*a1157835SDaniel Fojt 
855*a1157835SDaniel Fojt 
anqp_add_icon_binary_file(struct hostapd_data * hapd,struct wpabuf * buf,const u8 * name,size_t name_len)856*a1157835SDaniel Fojt static void anqp_add_icon_binary_file(struct hostapd_data *hapd,
857*a1157835SDaniel Fojt 				      struct wpabuf *buf,
858*a1157835SDaniel Fojt 				      const u8 *name, size_t name_len)
859*a1157835SDaniel Fojt {
860*a1157835SDaniel Fojt 	struct hs20_icon *icon;
861*a1157835SDaniel Fojt 	size_t i;
862*a1157835SDaniel Fojt 	u8 *len;
863*a1157835SDaniel Fojt 
864*a1157835SDaniel Fojt 	wpa_hexdump_ascii(MSG_DEBUG, "HS 2.0: Requested Icon Filename",
865*a1157835SDaniel Fojt 			  name, name_len);
866*a1157835SDaniel Fojt 	for (i = 0; i < hapd->conf->hs20_icons_count; i++) {
867*a1157835SDaniel Fojt 		icon = &hapd->conf->hs20_icons[i];
868*a1157835SDaniel Fojt 		if (name_len == os_strlen(icon->name) &&
869*a1157835SDaniel Fojt 		    os_memcmp(name, icon->name, name_len) == 0)
870*a1157835SDaniel Fojt 			break;
871*a1157835SDaniel Fojt 	}
872*a1157835SDaniel Fojt 
873*a1157835SDaniel Fojt 	if (i < hapd->conf->hs20_icons_count)
874*a1157835SDaniel Fojt 		icon = &hapd->conf->hs20_icons[i];
875*a1157835SDaniel Fojt 	else
876*a1157835SDaniel Fojt 		icon = NULL;
877*a1157835SDaniel Fojt 
878*a1157835SDaniel Fojt 	len = gas_anqp_add_element(buf, ANQP_VENDOR_SPECIFIC);
879*a1157835SDaniel Fojt 	wpabuf_put_be24(buf, OUI_WFA);
880*a1157835SDaniel Fojt 	wpabuf_put_u8(buf, HS20_ANQP_OUI_TYPE);
881*a1157835SDaniel Fojt 	wpabuf_put_u8(buf, HS20_STYPE_ICON_BINARY_FILE);
882*a1157835SDaniel Fojt 	wpabuf_put_u8(buf, 0); /* Reserved */
883*a1157835SDaniel Fojt 
884*a1157835SDaniel Fojt 	if (icon) {
885*a1157835SDaniel Fojt 		char *data;
886*a1157835SDaniel Fojt 		size_t data_len;
887*a1157835SDaniel Fojt 
888*a1157835SDaniel Fojt 		data = os_readfile(icon->file, &data_len);
889*a1157835SDaniel Fojt 		if (data == NULL || data_len > 65535) {
890*a1157835SDaniel Fojt 			wpabuf_put_u8(buf, 2); /* Download Status:
891*a1157835SDaniel Fojt 						* Unspecified file error */
892*a1157835SDaniel Fojt 			wpabuf_put_u8(buf, 0);
893*a1157835SDaniel Fojt 			wpabuf_put_le16(buf, 0);
894*a1157835SDaniel Fojt 		} else {
895*a1157835SDaniel Fojt 			wpabuf_put_u8(buf, 0); /* Download Status: Success */
896*a1157835SDaniel Fojt 			wpabuf_put_u8(buf, os_strlen(icon->type));
897*a1157835SDaniel Fojt 			wpabuf_put_str(buf, icon->type);
898*a1157835SDaniel Fojt 			wpabuf_put_le16(buf, data_len);
899*a1157835SDaniel Fojt 			wpabuf_put_data(buf, data, data_len);
900*a1157835SDaniel Fojt 		}
901*a1157835SDaniel Fojt 		os_free(data);
902*a1157835SDaniel Fojt 	} else {
903*a1157835SDaniel Fojt 		wpabuf_put_u8(buf, 1); /* Download Status: File not found */
904*a1157835SDaniel Fojt 		wpabuf_put_u8(buf, 0);
905*a1157835SDaniel Fojt 		wpabuf_put_le16(buf, 0);
906*a1157835SDaniel Fojt 	}
907*a1157835SDaniel Fojt 
908*a1157835SDaniel Fojt 	gas_anqp_set_element_len(buf, len);
909*a1157835SDaniel Fojt }
910*a1157835SDaniel Fojt 
911*a1157835SDaniel Fojt 
anqp_add_operator_icon_metadata(struct hostapd_data * hapd,struct wpabuf * buf)912*a1157835SDaniel Fojt static void anqp_add_operator_icon_metadata(struct hostapd_data *hapd,
913*a1157835SDaniel Fojt 					    struct wpabuf *buf)
914*a1157835SDaniel Fojt {
915*a1157835SDaniel Fojt 	struct hostapd_bss_config *bss = hapd->conf;
916*a1157835SDaniel Fojt 	size_t i;
917*a1157835SDaniel Fojt 	u8 *len;
918*a1157835SDaniel Fojt 
919*a1157835SDaniel Fojt 	if (!bss->hs20_operator_icon_count)
920*a1157835SDaniel Fojt 		return;
921*a1157835SDaniel Fojt 
922*a1157835SDaniel Fojt 	len = gas_anqp_add_element(buf, ANQP_VENDOR_SPECIFIC);
923*a1157835SDaniel Fojt 
924*a1157835SDaniel Fojt 	wpabuf_put_be24(buf, OUI_WFA);
925*a1157835SDaniel Fojt 	wpabuf_put_u8(buf, HS20_ANQP_OUI_TYPE);
926*a1157835SDaniel Fojt 	wpabuf_put_u8(buf, HS20_STYPE_OPERATOR_ICON_METADATA);
927*a1157835SDaniel Fojt 	wpabuf_put_u8(buf, 0); /* Reserved */
928*a1157835SDaniel Fojt 
929*a1157835SDaniel Fojt 	for (i = 0; i < bss->hs20_operator_icon_count; i++)
930*a1157835SDaniel Fojt 		anqp_add_icon(buf, bss, bss->hs20_operator_icon[i]);
931*a1157835SDaniel Fojt 
932*a1157835SDaniel Fojt 	gas_anqp_set_element_len(buf, len);
933*a1157835SDaniel Fojt }
934*a1157835SDaniel Fojt 
9353ff40c12SJohn Marino #endif /* CONFIG_HS20 */
9363ff40c12SJohn Marino 
9373ff40c12SJohn Marino 
938*a1157835SDaniel Fojt #ifdef CONFIG_MBO
anqp_add_mbo_cell_data_conn_pref(struct hostapd_data * hapd,struct wpabuf * buf)939*a1157835SDaniel Fojt static void anqp_add_mbo_cell_data_conn_pref(struct hostapd_data *hapd,
940*a1157835SDaniel Fojt 					     struct wpabuf *buf)
941*a1157835SDaniel Fojt {
942*a1157835SDaniel Fojt 	if (hapd->conf->mbo_cell_data_conn_pref >= 0) {
943*a1157835SDaniel Fojt 		u8 *len = gas_anqp_add_element(buf, ANQP_VENDOR_SPECIFIC);
944*a1157835SDaniel Fojt 		wpabuf_put_be24(buf, OUI_WFA);
945*a1157835SDaniel Fojt 		wpabuf_put_u8(buf, MBO_ANQP_OUI_TYPE);
946*a1157835SDaniel Fojt 		wpabuf_put_u8(buf, MBO_ANQP_SUBTYPE_CELL_CONN_PREF);
947*a1157835SDaniel Fojt 		wpabuf_put_u8(buf, hapd->conf->mbo_cell_data_conn_pref);
948*a1157835SDaniel Fojt 		gas_anqp_set_element_len(buf, len);
949*a1157835SDaniel Fojt 	}
950*a1157835SDaniel Fojt }
951*a1157835SDaniel Fojt #endif /* CONFIG_MBO */
952*a1157835SDaniel Fojt 
953*a1157835SDaniel Fojt 
anqp_get_required_len(struct hostapd_data * hapd,const u16 * infoid,unsigned int num_infoid)954*a1157835SDaniel Fojt static size_t anqp_get_required_len(struct hostapd_data *hapd,
955*a1157835SDaniel Fojt 				    const u16 *infoid,
956*a1157835SDaniel Fojt 				    unsigned int num_infoid)
957*a1157835SDaniel Fojt {
958*a1157835SDaniel Fojt 	size_t len = 0;
959*a1157835SDaniel Fojt 	unsigned int i;
960*a1157835SDaniel Fojt 
961*a1157835SDaniel Fojt 	for (i = 0; i < num_infoid; i++) {
962*a1157835SDaniel Fojt 		struct anqp_element *elem = get_anqp_elem(hapd, infoid[i]);
963*a1157835SDaniel Fojt 
964*a1157835SDaniel Fojt 		if (elem)
965*a1157835SDaniel Fojt 			len += 2 + 2 + wpabuf_len(elem->payload);
966*a1157835SDaniel Fojt 	}
967*a1157835SDaniel Fojt 
968*a1157835SDaniel Fojt 	return len;
969*a1157835SDaniel Fojt }
970*a1157835SDaniel Fojt 
971*a1157835SDaniel Fojt 
9723ff40c12SJohn Marino static struct wpabuf *
gas_serv_build_gas_resp_payload(struct hostapd_data * hapd,unsigned int request,const u8 * home_realm,size_t home_realm_len,const u8 * icon_name,size_t icon_name_len,const u16 * extra_req,unsigned int num_extra_req)9733ff40c12SJohn Marino gas_serv_build_gas_resp_payload(struct hostapd_data *hapd,
9743ff40c12SJohn Marino 				unsigned int request,
975*a1157835SDaniel Fojt 				const u8 *home_realm, size_t home_realm_len,
976*a1157835SDaniel Fojt 				const u8 *icon_name, size_t icon_name_len,
977*a1157835SDaniel Fojt 				const u16 *extra_req,
978*a1157835SDaniel Fojt 				unsigned int num_extra_req)
9793ff40c12SJohn Marino {
9803ff40c12SJohn Marino 	struct wpabuf *buf;
981*a1157835SDaniel Fojt 	size_t len;
982*a1157835SDaniel Fojt 	unsigned int i;
9833ff40c12SJohn Marino 
984*a1157835SDaniel Fojt 	len = 1400;
985*a1157835SDaniel Fojt 	if (request & (ANQP_REQ_NAI_REALM | ANQP_REQ_NAI_HOME_REALM))
986*a1157835SDaniel Fojt 		len += 1000;
987*a1157835SDaniel Fojt 	if (request & ANQP_REQ_ICON_REQUEST)
988*a1157835SDaniel Fojt 		len += 65536;
989*a1157835SDaniel Fojt #ifdef CONFIG_FILS
990*a1157835SDaniel Fojt 	if (request & ANQP_FILS_REALM_INFO)
991*a1157835SDaniel Fojt 		len += 2 * dl_list_len(&hapd->conf->fils_realms);
992*a1157835SDaniel Fojt #endif /* CONFIG_FILS */
993*a1157835SDaniel Fojt 	len += anqp_get_required_len(hapd, extra_req, num_extra_req);
994*a1157835SDaniel Fojt 
995*a1157835SDaniel Fojt 	buf = wpabuf_alloc(len);
9963ff40c12SJohn Marino 	if (buf == NULL)
9973ff40c12SJohn Marino 		return NULL;
9983ff40c12SJohn Marino 
9993ff40c12SJohn Marino 	if (request & ANQP_REQ_CAPABILITY_LIST)
10003ff40c12SJohn Marino 		anqp_add_capab_list(hapd, buf);
10013ff40c12SJohn Marino 	if (request & ANQP_REQ_VENUE_NAME)
10023ff40c12SJohn Marino 		anqp_add_venue_name(hapd, buf);
1003*a1157835SDaniel Fojt 	if (request & ANQP_REQ_EMERGENCY_CALL_NUMBER)
1004*a1157835SDaniel Fojt 		anqp_add_elem(hapd, buf, ANQP_EMERGENCY_CALL_NUMBER);
10053ff40c12SJohn Marino 	if (request & ANQP_REQ_NETWORK_AUTH_TYPE)
10063ff40c12SJohn Marino 		anqp_add_network_auth_type(hapd, buf);
10073ff40c12SJohn Marino 	if (request & ANQP_REQ_ROAMING_CONSORTIUM)
10083ff40c12SJohn Marino 		anqp_add_roaming_consortium(hapd, buf);
10093ff40c12SJohn Marino 	if (request & ANQP_REQ_IP_ADDR_TYPE_AVAILABILITY)
10103ff40c12SJohn Marino 		anqp_add_ip_addr_type_availability(hapd, buf);
10113ff40c12SJohn Marino 	if (request & (ANQP_REQ_NAI_REALM | ANQP_REQ_NAI_HOME_REALM))
10123ff40c12SJohn Marino 		anqp_add_nai_realm(hapd, buf, home_realm, home_realm_len,
10133ff40c12SJohn Marino 				   request & ANQP_REQ_NAI_REALM,
10143ff40c12SJohn Marino 				   request & ANQP_REQ_NAI_HOME_REALM);
10153ff40c12SJohn Marino 	if (request & ANQP_REQ_3GPP_CELLULAR_NETWORK)
10163ff40c12SJohn Marino 		anqp_add_3gpp_cellular_network(hapd, buf);
1017*a1157835SDaniel Fojt 	if (request & ANQP_REQ_AP_GEOSPATIAL_LOCATION)
1018*a1157835SDaniel Fojt 		anqp_add_elem(hapd, buf, ANQP_AP_GEOSPATIAL_LOCATION);
1019*a1157835SDaniel Fojt 	if (request & ANQP_REQ_AP_CIVIC_LOCATION)
1020*a1157835SDaniel Fojt 		anqp_add_elem(hapd, buf, ANQP_AP_CIVIC_LOCATION);
1021*a1157835SDaniel Fojt 	if (request & ANQP_REQ_AP_LOCATION_PUBLIC_URI)
1022*a1157835SDaniel Fojt 		anqp_add_elem(hapd, buf, ANQP_AP_LOCATION_PUBLIC_URI);
10233ff40c12SJohn Marino 	if (request & ANQP_REQ_DOMAIN_NAME)
10243ff40c12SJohn Marino 		anqp_add_domain_name(hapd, buf);
1025*a1157835SDaniel Fojt 	if (request & ANQP_REQ_EMERGENCY_ALERT_URI)
1026*a1157835SDaniel Fojt 		anqp_add_elem(hapd, buf, ANQP_EMERGENCY_ALERT_URI);
1027*a1157835SDaniel Fojt 	if (request & ANQP_REQ_TDLS_CAPABILITY)
1028*a1157835SDaniel Fojt 		anqp_add_elem(hapd, buf, ANQP_TDLS_CAPABILITY);
1029*a1157835SDaniel Fojt 	if (request & ANQP_REQ_EMERGENCY_NAI)
1030*a1157835SDaniel Fojt 		anqp_add_elem(hapd, buf, ANQP_EMERGENCY_NAI);
1031*a1157835SDaniel Fojt 
1032*a1157835SDaniel Fojt 	for (i = 0; i < num_extra_req; i++) {
1033*a1157835SDaniel Fojt #ifdef CONFIG_FILS
1034*a1157835SDaniel Fojt 		if (extra_req[i] == ANQP_FILS_REALM_INFO) {
1035*a1157835SDaniel Fojt 			anqp_add_fils_realm_info(hapd, buf);
1036*a1157835SDaniel Fojt 			continue;
1037*a1157835SDaniel Fojt 		}
1038*a1157835SDaniel Fojt #endif /* CONFIG_FILS */
1039*a1157835SDaniel Fojt 		if (extra_req[i] == ANQP_VENUE_URL) {
1040*a1157835SDaniel Fojt 			anqp_add_venue_url(hapd, buf);
1041*a1157835SDaniel Fojt 			continue;
1042*a1157835SDaniel Fojt 		}
1043*a1157835SDaniel Fojt 		anqp_add_elem(hapd, buf, extra_req[i]);
1044*a1157835SDaniel Fojt 	}
10453ff40c12SJohn Marino 
10463ff40c12SJohn Marino #ifdef CONFIG_HS20
10473ff40c12SJohn Marino 	if (request & ANQP_REQ_HS_CAPABILITY_LIST)
10483ff40c12SJohn Marino 		anqp_add_hs_capab_list(hapd, buf);
10493ff40c12SJohn Marino 	if (request & ANQP_REQ_OPERATOR_FRIENDLY_NAME)
10503ff40c12SJohn Marino 		anqp_add_operator_friendly_name(hapd, buf);
10513ff40c12SJohn Marino 	if (request & ANQP_REQ_WAN_METRICS)
10523ff40c12SJohn Marino 		anqp_add_wan_metrics(hapd, buf);
10533ff40c12SJohn Marino 	if (request & ANQP_REQ_CONNECTION_CAPABILITY)
10543ff40c12SJohn Marino 		anqp_add_connection_capability(hapd, buf);
10553ff40c12SJohn Marino 	if (request & ANQP_REQ_OPERATING_CLASS)
10563ff40c12SJohn Marino 		anqp_add_operating_class(hapd, buf);
1057*a1157835SDaniel Fojt 	if (request & ANQP_REQ_OSU_PROVIDERS_LIST)
1058*a1157835SDaniel Fojt 		anqp_add_osu_providers_list(hapd, buf);
1059*a1157835SDaniel Fojt 	if (request & ANQP_REQ_ICON_REQUEST)
1060*a1157835SDaniel Fojt 		anqp_add_icon_binary_file(hapd, buf, icon_name, icon_name_len);
1061*a1157835SDaniel Fojt 	if (request & ANQP_REQ_OPERATOR_ICON_METADATA)
1062*a1157835SDaniel Fojt 		anqp_add_operator_icon_metadata(hapd, buf);
1063*a1157835SDaniel Fojt 	if (request & ANQP_REQ_OSU_PROVIDERS_NAI_LIST)
1064*a1157835SDaniel Fojt 		anqp_add_osu_providers_nai_list(hapd, buf);
10653ff40c12SJohn Marino #endif /* CONFIG_HS20 */
10663ff40c12SJohn Marino 
1067*a1157835SDaniel Fojt #ifdef CONFIG_MBO
1068*a1157835SDaniel Fojt 	if (request & ANQP_REQ_MBO_CELL_DATA_CONN_PREF)
1069*a1157835SDaniel Fojt 		anqp_add_mbo_cell_data_conn_pref(hapd, buf);
1070*a1157835SDaniel Fojt #endif /* CONFIG_MBO */
1071*a1157835SDaniel Fojt 
10723ff40c12SJohn Marino 	return buf;
10733ff40c12SJohn Marino }
10743ff40c12SJohn Marino 
10753ff40c12SJohn Marino 
1076*a1157835SDaniel Fojt #define ANQP_MAX_EXTRA_REQ 20
10773ff40c12SJohn Marino 
10783ff40c12SJohn Marino struct anqp_query_info {
10793ff40c12SJohn Marino 	unsigned int request;
10803ff40c12SJohn Marino 	const u8 *home_realm_query;
10813ff40c12SJohn Marino 	size_t home_realm_query_len;
1082*a1157835SDaniel Fojt 	const u8 *icon_name;
1083*a1157835SDaniel Fojt 	size_t icon_name_len;
1084*a1157835SDaniel Fojt 	int p2p_sd;
1085*a1157835SDaniel Fojt 	u16 extra_req[ANQP_MAX_EXTRA_REQ];
1086*a1157835SDaniel Fojt 	unsigned int num_extra_req;
10873ff40c12SJohn Marino };
10883ff40c12SJohn Marino 
10893ff40c12SJohn Marino 
set_anqp_req(unsigned int bit,const char * name,int local,struct anqp_query_info * qi)10903ff40c12SJohn Marino static void set_anqp_req(unsigned int bit, const char *name, int local,
10913ff40c12SJohn Marino 			 struct anqp_query_info *qi)
10923ff40c12SJohn Marino {
10933ff40c12SJohn Marino 	qi->request |= bit;
10943ff40c12SJohn Marino 	if (local) {
10953ff40c12SJohn Marino 		wpa_printf(MSG_DEBUG, "ANQP: %s (local)", name);
10963ff40c12SJohn Marino 	} else {
10973ff40c12SJohn Marino 		wpa_printf(MSG_DEBUG, "ANQP: %s not available", name);
10983ff40c12SJohn Marino 	}
10993ff40c12SJohn Marino }
11003ff40c12SJohn Marino 
11013ff40c12SJohn Marino 
rx_anqp_query_list_id(struct hostapd_data * hapd,u16 info_id,struct anqp_query_info * qi)11023ff40c12SJohn Marino static void rx_anqp_query_list_id(struct hostapd_data *hapd, u16 info_id,
11033ff40c12SJohn Marino 				  struct anqp_query_info *qi)
11043ff40c12SJohn Marino {
11053ff40c12SJohn Marino 	switch (info_id) {
11063ff40c12SJohn Marino 	case ANQP_CAPABILITY_LIST:
1107*a1157835SDaniel Fojt 		set_anqp_req(ANQP_REQ_CAPABILITY_LIST, "Capability List", 1,
1108*a1157835SDaniel Fojt 			     qi);
11093ff40c12SJohn Marino 		break;
11103ff40c12SJohn Marino 	case ANQP_VENUE_NAME:
11113ff40c12SJohn Marino 		set_anqp_req(ANQP_REQ_VENUE_NAME, "Venue Name",
1112*a1157835SDaniel Fojt 			     hapd->conf->venue_name != NULL, qi);
1113*a1157835SDaniel Fojt 		break;
1114*a1157835SDaniel Fojt 	case ANQP_EMERGENCY_CALL_NUMBER:
1115*a1157835SDaniel Fojt 		set_anqp_req(ANQP_REQ_EMERGENCY_CALL_NUMBER,
1116*a1157835SDaniel Fojt 			     "Emergency Call Number",
1117*a1157835SDaniel Fojt 			     get_anqp_elem(hapd, info_id) != NULL, qi);
11183ff40c12SJohn Marino 		break;
11193ff40c12SJohn Marino 	case ANQP_NETWORK_AUTH_TYPE:
11203ff40c12SJohn Marino 		set_anqp_req(ANQP_REQ_NETWORK_AUTH_TYPE, "Network Auth Type",
1121*a1157835SDaniel Fojt 			     hapd->conf->network_auth_type != NULL, qi);
11223ff40c12SJohn Marino 		break;
11233ff40c12SJohn Marino 	case ANQP_ROAMING_CONSORTIUM:
11243ff40c12SJohn Marino 		set_anqp_req(ANQP_REQ_ROAMING_CONSORTIUM, "Roaming Consortium",
1125*a1157835SDaniel Fojt 			     hapd->conf->roaming_consortium != NULL, qi);
11263ff40c12SJohn Marino 		break;
11273ff40c12SJohn Marino 	case ANQP_IP_ADDR_TYPE_AVAILABILITY:
11283ff40c12SJohn Marino 		set_anqp_req(ANQP_REQ_IP_ADDR_TYPE_AVAILABILITY,
11293ff40c12SJohn Marino 			     "IP Addr Type Availability",
1130*a1157835SDaniel Fojt 			     hapd->conf->ipaddr_type_configured, qi);
11313ff40c12SJohn Marino 		break;
11323ff40c12SJohn Marino 	case ANQP_NAI_REALM:
11333ff40c12SJohn Marino 		set_anqp_req(ANQP_REQ_NAI_REALM, "NAI Realm",
1134*a1157835SDaniel Fojt 			     hapd->conf->nai_realm_data != NULL, qi);
11353ff40c12SJohn Marino 		break;
11363ff40c12SJohn Marino 	case ANQP_3GPP_CELLULAR_NETWORK:
11373ff40c12SJohn Marino 		set_anqp_req(ANQP_REQ_3GPP_CELLULAR_NETWORK,
11383ff40c12SJohn Marino 			     "3GPP Cellular Network",
1139*a1157835SDaniel Fojt 			     hapd->conf->anqp_3gpp_cell_net != NULL, qi);
1140*a1157835SDaniel Fojt 		break;
1141*a1157835SDaniel Fojt 	case ANQP_AP_GEOSPATIAL_LOCATION:
1142*a1157835SDaniel Fojt 		set_anqp_req(ANQP_REQ_AP_GEOSPATIAL_LOCATION,
1143*a1157835SDaniel Fojt 			     "AP Geospatial Location",
1144*a1157835SDaniel Fojt 			     get_anqp_elem(hapd, info_id) != NULL, qi);
1145*a1157835SDaniel Fojt 		break;
1146*a1157835SDaniel Fojt 	case ANQP_AP_CIVIC_LOCATION:
1147*a1157835SDaniel Fojt 		set_anqp_req(ANQP_REQ_AP_CIVIC_LOCATION,
1148*a1157835SDaniel Fojt 			     "AP Civic Location",
1149*a1157835SDaniel Fojt 			     get_anqp_elem(hapd, info_id) != NULL, qi);
1150*a1157835SDaniel Fojt 		break;
1151*a1157835SDaniel Fojt 	case ANQP_AP_LOCATION_PUBLIC_URI:
1152*a1157835SDaniel Fojt 		set_anqp_req(ANQP_REQ_AP_LOCATION_PUBLIC_URI,
1153*a1157835SDaniel Fojt 			     "AP Location Public URI",
1154*a1157835SDaniel Fojt 			     get_anqp_elem(hapd, info_id) != NULL, qi);
11553ff40c12SJohn Marino 		break;
11563ff40c12SJohn Marino 	case ANQP_DOMAIN_NAME:
11573ff40c12SJohn Marino 		set_anqp_req(ANQP_REQ_DOMAIN_NAME, "Domain Name",
1158*a1157835SDaniel Fojt 			     hapd->conf->domain_name != NULL, qi);
1159*a1157835SDaniel Fojt 		break;
1160*a1157835SDaniel Fojt 	case ANQP_EMERGENCY_ALERT_URI:
1161*a1157835SDaniel Fojt 		set_anqp_req(ANQP_REQ_EMERGENCY_ALERT_URI,
1162*a1157835SDaniel Fojt 			     "Emergency Alert URI",
1163*a1157835SDaniel Fojt 			     get_anqp_elem(hapd, info_id) != NULL, qi);
1164*a1157835SDaniel Fojt 		break;
1165*a1157835SDaniel Fojt 	case ANQP_TDLS_CAPABILITY:
1166*a1157835SDaniel Fojt 		set_anqp_req(ANQP_REQ_TDLS_CAPABILITY,
1167*a1157835SDaniel Fojt 			     "TDLS Capability",
1168*a1157835SDaniel Fojt 			     get_anqp_elem(hapd, info_id) != NULL, qi);
1169*a1157835SDaniel Fojt 		break;
1170*a1157835SDaniel Fojt 	case ANQP_EMERGENCY_NAI:
1171*a1157835SDaniel Fojt 		set_anqp_req(ANQP_REQ_EMERGENCY_NAI,
1172*a1157835SDaniel Fojt 			     "Emergency NAI",
1173*a1157835SDaniel Fojt 			     get_anqp_elem(hapd, info_id) != NULL, qi);
11743ff40c12SJohn Marino 		break;
11753ff40c12SJohn Marino 	default:
1176*a1157835SDaniel Fojt #ifdef CONFIG_FILS
1177*a1157835SDaniel Fojt 		if (info_id == ANQP_FILS_REALM_INFO &&
1178*a1157835SDaniel Fojt 		    !dl_list_empty(&hapd->conf->fils_realms)) {
1179*a1157835SDaniel Fojt 			wpa_printf(MSG_DEBUG,
1180*a1157835SDaniel Fojt 				   "ANQP: FILS Realm Information (local)");
1181*a1157835SDaniel Fojt 		} else
1182*a1157835SDaniel Fojt #endif /* CONFIG_FILS */
1183*a1157835SDaniel Fojt 		if (info_id == ANQP_VENUE_URL && hapd->conf->venue_url) {
1184*a1157835SDaniel Fojt 			wpa_printf(MSG_DEBUG,
1185*a1157835SDaniel Fojt 				   "ANQP: Venue URL (local)");
1186*a1157835SDaniel Fojt 		} else if (!get_anqp_elem(hapd, info_id)) {
11873ff40c12SJohn Marino 			wpa_printf(MSG_DEBUG, "ANQP: Unsupported Info Id %u",
11883ff40c12SJohn Marino 				   info_id);
11893ff40c12SJohn Marino 			break;
11903ff40c12SJohn Marino 		}
1191*a1157835SDaniel Fojt 		if (qi->num_extra_req == ANQP_MAX_EXTRA_REQ) {
1192*a1157835SDaniel Fojt 			wpa_printf(MSG_DEBUG,
1193*a1157835SDaniel Fojt 				   "ANQP: No more room for extra requests - ignore Info Id %u",
1194*a1157835SDaniel Fojt 				   info_id);
1195*a1157835SDaniel Fojt 			break;
1196*a1157835SDaniel Fojt 		}
1197*a1157835SDaniel Fojt 		wpa_printf(MSG_DEBUG, "ANQP: Info Id %u (local)", info_id);
1198*a1157835SDaniel Fojt 		qi->extra_req[qi->num_extra_req] = info_id;
1199*a1157835SDaniel Fojt 		qi->num_extra_req++;
1200*a1157835SDaniel Fojt 		break;
1201*a1157835SDaniel Fojt 	}
12023ff40c12SJohn Marino }
12033ff40c12SJohn Marino 
12043ff40c12SJohn Marino 
rx_anqp_query_list(struct hostapd_data * hapd,const u8 * pos,const u8 * end,struct anqp_query_info * qi)12053ff40c12SJohn Marino static void rx_anqp_query_list(struct hostapd_data *hapd,
12063ff40c12SJohn Marino 			       const u8 *pos, const u8 *end,
12073ff40c12SJohn Marino 			       struct anqp_query_info *qi)
12083ff40c12SJohn Marino {
12093ff40c12SJohn Marino 	wpa_printf(MSG_DEBUG, "ANQP: %u Info IDs requested in Query list",
12103ff40c12SJohn Marino 		   (unsigned int) (end - pos) / 2);
12113ff40c12SJohn Marino 
1212*a1157835SDaniel Fojt 	while (end - pos >= 2) {
12133ff40c12SJohn Marino 		rx_anqp_query_list_id(hapd, WPA_GET_LE16(pos), qi);
12143ff40c12SJohn Marino 		pos += 2;
12153ff40c12SJohn Marino 	}
12163ff40c12SJohn Marino }
12173ff40c12SJohn Marino 
12183ff40c12SJohn Marino 
12193ff40c12SJohn Marino #ifdef CONFIG_HS20
12203ff40c12SJohn Marino 
rx_anqp_hs_query_list(struct hostapd_data * hapd,u8 subtype,struct anqp_query_info * qi)12213ff40c12SJohn Marino static void rx_anqp_hs_query_list(struct hostapd_data *hapd, u8 subtype,
12223ff40c12SJohn Marino 				  struct anqp_query_info *qi)
12233ff40c12SJohn Marino {
12243ff40c12SJohn Marino 	switch (subtype) {
12253ff40c12SJohn Marino 	case HS20_STYPE_CAPABILITY_LIST:
12263ff40c12SJohn Marino 		set_anqp_req(ANQP_REQ_HS_CAPABILITY_LIST, "HS Capability List",
1227*a1157835SDaniel Fojt 			     1, qi);
12283ff40c12SJohn Marino 		break;
12293ff40c12SJohn Marino 	case HS20_STYPE_OPERATOR_FRIENDLY_NAME:
12303ff40c12SJohn Marino 		set_anqp_req(ANQP_REQ_OPERATOR_FRIENDLY_NAME,
12313ff40c12SJohn Marino 			     "Operator Friendly Name",
1232*a1157835SDaniel Fojt 			     hapd->conf->hs20_oper_friendly_name != NULL, qi);
12333ff40c12SJohn Marino 		break;
12343ff40c12SJohn Marino 	case HS20_STYPE_WAN_METRICS:
12353ff40c12SJohn Marino 		set_anqp_req(ANQP_REQ_WAN_METRICS, "WAN Metrics",
1236*a1157835SDaniel Fojt 			     hapd->conf->hs20_wan_metrics != NULL, qi);
12373ff40c12SJohn Marino 		break;
12383ff40c12SJohn Marino 	case HS20_STYPE_CONNECTION_CAPABILITY:
12393ff40c12SJohn Marino 		set_anqp_req(ANQP_REQ_CONNECTION_CAPABILITY,
12403ff40c12SJohn Marino 			     "Connection Capability",
12413ff40c12SJohn Marino 			     hapd->conf->hs20_connection_capability != NULL,
1242*a1157835SDaniel Fojt 			     qi);
12433ff40c12SJohn Marino 		break;
12443ff40c12SJohn Marino 	case HS20_STYPE_OPERATING_CLASS:
12453ff40c12SJohn Marino 		set_anqp_req(ANQP_REQ_OPERATING_CLASS, "Operating Class",
1246*a1157835SDaniel Fojt 			     hapd->conf->hs20_operating_class != NULL, qi);
1247*a1157835SDaniel Fojt 		break;
1248*a1157835SDaniel Fojt 	case HS20_STYPE_OSU_PROVIDERS_LIST:
1249*a1157835SDaniel Fojt 		set_anqp_req(ANQP_REQ_OSU_PROVIDERS_LIST, "OSU Providers list",
1250*a1157835SDaniel Fojt 			     hapd->conf->hs20_osu_providers_count, qi);
1251*a1157835SDaniel Fojt 		break;
1252*a1157835SDaniel Fojt 	case HS20_STYPE_OPERATOR_ICON_METADATA:
1253*a1157835SDaniel Fojt 		set_anqp_req(ANQP_REQ_OPERATOR_ICON_METADATA,
1254*a1157835SDaniel Fojt 			     "Operator Icon Metadata",
1255*a1157835SDaniel Fojt 			     hapd->conf->hs20_operator_icon_count, qi);
1256*a1157835SDaniel Fojt 		break;
1257*a1157835SDaniel Fojt 	case HS20_STYPE_OSU_PROVIDERS_NAI_LIST:
1258*a1157835SDaniel Fojt 		set_anqp_req(ANQP_REQ_OSU_PROVIDERS_NAI_LIST,
1259*a1157835SDaniel Fojt 			     "OSU Providers NAI List",
1260*a1157835SDaniel Fojt 			     hapd->conf->hs20_osu_providers_nai_count, qi);
12613ff40c12SJohn Marino 		break;
12623ff40c12SJohn Marino 	default:
12633ff40c12SJohn Marino 		wpa_printf(MSG_DEBUG, "ANQP: Unsupported HS 2.0 subtype %u",
12643ff40c12SJohn Marino 			   subtype);
12653ff40c12SJohn Marino 		break;
12663ff40c12SJohn Marino 	}
12673ff40c12SJohn Marino }
12683ff40c12SJohn Marino 
12693ff40c12SJohn Marino 
rx_anqp_hs_nai_home_realm(struct hostapd_data * hapd,const u8 * pos,const u8 * end,struct anqp_query_info * qi)12703ff40c12SJohn Marino static void rx_anqp_hs_nai_home_realm(struct hostapd_data *hapd,
12713ff40c12SJohn Marino 				      const u8 *pos, const u8 *end,
12723ff40c12SJohn Marino 				      struct anqp_query_info *qi)
12733ff40c12SJohn Marino {
12743ff40c12SJohn Marino 	qi->request |= ANQP_REQ_NAI_HOME_REALM;
12753ff40c12SJohn Marino 	qi->home_realm_query = pos;
12763ff40c12SJohn Marino 	qi->home_realm_query_len = end - pos;
12773ff40c12SJohn Marino 	if (hapd->conf->nai_realm_data != NULL) {
12783ff40c12SJohn Marino 		wpa_printf(MSG_DEBUG, "ANQP: HS 2.0 NAI Home Realm Query "
12793ff40c12SJohn Marino 			   "(local)");
12803ff40c12SJohn Marino 	} else {
12813ff40c12SJohn Marino 		wpa_printf(MSG_DEBUG, "ANQP: HS 2.0 NAI Home Realm Query not "
12823ff40c12SJohn Marino 			   "available");
12833ff40c12SJohn Marino 	}
12843ff40c12SJohn Marino }
12853ff40c12SJohn Marino 
12863ff40c12SJohn Marino 
rx_anqp_hs_icon_request(struct hostapd_data * hapd,const u8 * pos,const u8 * end,struct anqp_query_info * qi)1287*a1157835SDaniel Fojt static void rx_anqp_hs_icon_request(struct hostapd_data *hapd,
12883ff40c12SJohn Marino 				    const u8 *pos, const u8 *end,
12893ff40c12SJohn Marino 				    struct anqp_query_info *qi)
12903ff40c12SJohn Marino {
1291*a1157835SDaniel Fojt 	qi->request |= ANQP_REQ_ICON_REQUEST;
1292*a1157835SDaniel Fojt 	qi->icon_name = pos;
1293*a1157835SDaniel Fojt 	qi->icon_name_len = end - pos;
1294*a1157835SDaniel Fojt 	if (hapd->conf->hs20_icons_count) {
1295*a1157835SDaniel Fojt 		wpa_printf(MSG_DEBUG, "ANQP: HS 2.0 Icon Request Query "
1296*a1157835SDaniel Fojt 			   "(local)");
1297*a1157835SDaniel Fojt 	} else {
1298*a1157835SDaniel Fojt 		wpa_printf(MSG_DEBUG, "ANQP: HS 2.0 Icon Request Query not "
1299*a1157835SDaniel Fojt 			   "available");
1300*a1157835SDaniel Fojt 	}
1301*a1157835SDaniel Fojt }
1302*a1157835SDaniel Fojt 
1303*a1157835SDaniel Fojt 
rx_anqp_vendor_specific_hs20(struct hostapd_data * hapd,const u8 * pos,const u8 * end,struct anqp_query_info * qi)1304*a1157835SDaniel Fojt static void rx_anqp_vendor_specific_hs20(struct hostapd_data *hapd,
1305*a1157835SDaniel Fojt 					 const u8 *pos, const u8 *end,
1306*a1157835SDaniel Fojt 					 struct anqp_query_info *qi)
1307*a1157835SDaniel Fojt {
13083ff40c12SJohn Marino 	u8 subtype;
13093ff40c12SJohn Marino 
1310*a1157835SDaniel Fojt 	if (end - pos <= 1)
13113ff40c12SJohn Marino 		return;
13123ff40c12SJohn Marino 
13133ff40c12SJohn Marino 	subtype = *pos++;
13143ff40c12SJohn Marino 	pos++; /* Reserved */
13153ff40c12SJohn Marino 	switch (subtype) {
13163ff40c12SJohn Marino 	case HS20_STYPE_QUERY_LIST:
13173ff40c12SJohn Marino 		wpa_printf(MSG_DEBUG, "ANQP: HS 2.0 Query List");
13183ff40c12SJohn Marino 		while (pos < end) {
13193ff40c12SJohn Marino 			rx_anqp_hs_query_list(hapd, *pos, qi);
13203ff40c12SJohn Marino 			pos++;
13213ff40c12SJohn Marino 		}
13223ff40c12SJohn Marino 		break;
13233ff40c12SJohn Marino 	case HS20_STYPE_NAI_HOME_REALM_QUERY:
13243ff40c12SJohn Marino 		rx_anqp_hs_nai_home_realm(hapd, pos, end, qi);
13253ff40c12SJohn Marino 		break;
1326*a1157835SDaniel Fojt 	case HS20_STYPE_ICON_REQUEST:
1327*a1157835SDaniel Fojt 		rx_anqp_hs_icon_request(hapd, pos, end, qi);
1328*a1157835SDaniel Fojt 		break;
13293ff40c12SJohn Marino 	default:
13303ff40c12SJohn Marino 		wpa_printf(MSG_DEBUG, "ANQP: Unsupported HS 2.0 query subtype "
13313ff40c12SJohn Marino 			   "%u", subtype);
13323ff40c12SJohn Marino 		break;
13333ff40c12SJohn Marino 	}
13343ff40c12SJohn Marino }
13353ff40c12SJohn Marino 
13363ff40c12SJohn Marino #endif /* CONFIG_HS20 */
13373ff40c12SJohn Marino 
13383ff40c12SJohn Marino 
1339*a1157835SDaniel Fojt #ifdef CONFIG_P2P
rx_anqp_vendor_specific_p2p(struct hostapd_data * hapd,struct anqp_query_info * qi)1340*a1157835SDaniel Fojt static void rx_anqp_vendor_specific_p2p(struct hostapd_data *hapd,
1341*a1157835SDaniel Fojt 					struct anqp_query_info *qi)
1342*a1157835SDaniel Fojt {
1343*a1157835SDaniel Fojt 	/*
1344*a1157835SDaniel Fojt 	 * This is for P2P SD and will be taken care of by the P2P
1345*a1157835SDaniel Fojt 	 * implementation. This query needs to be ignored in the generic
1346*a1157835SDaniel Fojt 	 * GAS server to avoid duplicated response.
1347*a1157835SDaniel Fojt 	 */
1348*a1157835SDaniel Fojt 	wpa_printf(MSG_DEBUG,
1349*a1157835SDaniel Fojt 		   "ANQP: Ignore WFA vendor type %u (P2P SD) in generic GAS server",
1350*a1157835SDaniel Fojt 		   P2P_OUI_TYPE);
1351*a1157835SDaniel Fojt 	qi->p2p_sd = 1;
1352*a1157835SDaniel Fojt 	return;
1353*a1157835SDaniel Fojt }
1354*a1157835SDaniel Fojt #endif /* CONFIG_P2P */
1355*a1157835SDaniel Fojt 
1356*a1157835SDaniel Fojt 
1357*a1157835SDaniel Fojt #ifdef CONFIG_MBO
1358*a1157835SDaniel Fojt 
rx_anqp_mbo_query_list(struct hostapd_data * hapd,u8 subtype,struct anqp_query_info * qi)1359*a1157835SDaniel Fojt static void rx_anqp_mbo_query_list(struct hostapd_data *hapd, u8 subtype,
1360*a1157835SDaniel Fojt 				  struct anqp_query_info *qi)
1361*a1157835SDaniel Fojt {
1362*a1157835SDaniel Fojt 	switch (subtype) {
1363*a1157835SDaniel Fojt 	case MBO_ANQP_SUBTYPE_CELL_CONN_PREF:
1364*a1157835SDaniel Fojt 		set_anqp_req(ANQP_REQ_MBO_CELL_DATA_CONN_PREF,
1365*a1157835SDaniel Fojt 			     "Cellular Data Connection Preference",
1366*a1157835SDaniel Fojt 			     hapd->conf->mbo_cell_data_conn_pref >= 0, qi);
1367*a1157835SDaniel Fojt 		break;
1368*a1157835SDaniel Fojt 	default:
1369*a1157835SDaniel Fojt 		wpa_printf(MSG_DEBUG, "ANQP: Unsupported MBO subtype %u",
1370*a1157835SDaniel Fojt 			   subtype);
1371*a1157835SDaniel Fojt 		break;
1372*a1157835SDaniel Fojt 	}
1373*a1157835SDaniel Fojt }
1374*a1157835SDaniel Fojt 
1375*a1157835SDaniel Fojt 
rx_anqp_vendor_specific_mbo(struct hostapd_data * hapd,const u8 * pos,const u8 * end,struct anqp_query_info * qi)1376*a1157835SDaniel Fojt static void rx_anqp_vendor_specific_mbo(struct hostapd_data *hapd,
1377*a1157835SDaniel Fojt 					const u8 *pos, const u8 *end,
1378*a1157835SDaniel Fojt 					struct anqp_query_info *qi)
1379*a1157835SDaniel Fojt {
1380*a1157835SDaniel Fojt 	u8 subtype;
1381*a1157835SDaniel Fojt 
1382*a1157835SDaniel Fojt 	if (end - pos < 1)
1383*a1157835SDaniel Fojt 		return;
1384*a1157835SDaniel Fojt 
1385*a1157835SDaniel Fojt 	subtype = *pos++;
1386*a1157835SDaniel Fojt 	switch (subtype) {
1387*a1157835SDaniel Fojt 	case MBO_ANQP_SUBTYPE_QUERY_LIST:
1388*a1157835SDaniel Fojt 		wpa_printf(MSG_DEBUG, "ANQP: MBO Query List");
1389*a1157835SDaniel Fojt 		while (pos < end) {
1390*a1157835SDaniel Fojt 			rx_anqp_mbo_query_list(hapd, *pos, qi);
1391*a1157835SDaniel Fojt 			pos++;
1392*a1157835SDaniel Fojt 		}
1393*a1157835SDaniel Fojt 		break;
1394*a1157835SDaniel Fojt 	default:
1395*a1157835SDaniel Fojt 		wpa_printf(MSG_DEBUG, "ANQP: Unsupported MBO query subtype %u",
1396*a1157835SDaniel Fojt 			   subtype);
1397*a1157835SDaniel Fojt 		break;
1398*a1157835SDaniel Fojt 	}
1399*a1157835SDaniel Fojt }
1400*a1157835SDaniel Fojt 
1401*a1157835SDaniel Fojt #endif /* CONFIG_MBO */
1402*a1157835SDaniel Fojt 
1403*a1157835SDaniel Fojt 
rx_anqp_vendor_specific(struct hostapd_data * hapd,const u8 * pos,const u8 * end,struct anqp_query_info * qi)1404*a1157835SDaniel Fojt static void rx_anqp_vendor_specific(struct hostapd_data *hapd,
1405*a1157835SDaniel Fojt 				    const u8 *pos, const u8 *end,
1406*a1157835SDaniel Fojt 				    struct anqp_query_info *qi)
1407*a1157835SDaniel Fojt {
1408*a1157835SDaniel Fojt 	u32 oui;
1409*a1157835SDaniel Fojt 
1410*a1157835SDaniel Fojt 	if (end - pos < 4) {
1411*a1157835SDaniel Fojt 		wpa_printf(MSG_DEBUG, "ANQP: Too short vendor specific ANQP "
1412*a1157835SDaniel Fojt 			   "Query element");
1413*a1157835SDaniel Fojt 		return;
1414*a1157835SDaniel Fojt 	}
1415*a1157835SDaniel Fojt 
1416*a1157835SDaniel Fojt 	oui = WPA_GET_BE24(pos);
1417*a1157835SDaniel Fojt 	pos += 3;
1418*a1157835SDaniel Fojt 	if (oui != OUI_WFA) {
1419*a1157835SDaniel Fojt 		wpa_printf(MSG_DEBUG, "ANQP: Unsupported vendor OUI %06x",
1420*a1157835SDaniel Fojt 			   oui);
1421*a1157835SDaniel Fojt 		return;
1422*a1157835SDaniel Fojt 	}
1423*a1157835SDaniel Fojt 
1424*a1157835SDaniel Fojt 	switch (*pos) {
1425*a1157835SDaniel Fojt #ifdef CONFIG_P2P
1426*a1157835SDaniel Fojt 	case P2P_OUI_TYPE:
1427*a1157835SDaniel Fojt 		rx_anqp_vendor_specific_p2p(hapd, qi);
1428*a1157835SDaniel Fojt 		break;
1429*a1157835SDaniel Fojt #endif /* CONFIG_P2P */
1430*a1157835SDaniel Fojt #ifdef CONFIG_HS20
1431*a1157835SDaniel Fojt 	case HS20_ANQP_OUI_TYPE:
1432*a1157835SDaniel Fojt 		rx_anqp_vendor_specific_hs20(hapd, pos + 1, end, qi);
1433*a1157835SDaniel Fojt 		break;
1434*a1157835SDaniel Fojt #endif /* CONFIG_HS20 */
1435*a1157835SDaniel Fojt #ifdef CONFIG_MBO
1436*a1157835SDaniel Fojt 	case MBO_ANQP_OUI_TYPE:
1437*a1157835SDaniel Fojt 		rx_anqp_vendor_specific_mbo(hapd, pos + 1, end, qi);
1438*a1157835SDaniel Fojt 		break;
1439*a1157835SDaniel Fojt #endif /* CONFIG_MBO */
1440*a1157835SDaniel Fojt 	default:
1441*a1157835SDaniel Fojt 		wpa_printf(MSG_DEBUG, "ANQP: Unsupported WFA vendor type %u",
1442*a1157835SDaniel Fojt 			   *pos);
1443*a1157835SDaniel Fojt 		break;
1444*a1157835SDaniel Fojt 	}
1445*a1157835SDaniel Fojt }
1446*a1157835SDaniel Fojt 
1447*a1157835SDaniel Fojt 
gas_serv_req_local_processing(struct hostapd_data * hapd,const u8 * sa,u8 dialog_token,struct anqp_query_info * qi,int prot,int std_addr3)14483ff40c12SJohn Marino static void gas_serv_req_local_processing(struct hostapd_data *hapd,
14493ff40c12SJohn Marino 					  const u8 *sa, u8 dialog_token,
1450*a1157835SDaniel Fojt 					  struct anqp_query_info *qi, int prot,
1451*a1157835SDaniel Fojt 					  int std_addr3)
14523ff40c12SJohn Marino {
14533ff40c12SJohn Marino 	struct wpabuf *buf, *tx_buf;
14543ff40c12SJohn Marino 
1455*a1157835SDaniel Fojt 	buf = gas_serv_build_gas_resp_payload(hapd, qi->request,
14563ff40c12SJohn Marino 					      qi->home_realm_query,
1457*a1157835SDaniel Fojt 					      qi->home_realm_query_len,
1458*a1157835SDaniel Fojt 					      qi->icon_name, qi->icon_name_len,
1459*a1157835SDaniel Fojt 					      qi->extra_req, qi->num_extra_req);
14603ff40c12SJohn Marino 	wpa_hexdump_buf(MSG_MSGDUMP, "ANQP: Locally generated ANQP responses",
14613ff40c12SJohn Marino 			buf);
14623ff40c12SJohn Marino 	if (!buf)
14633ff40c12SJohn Marino 		return;
1464*a1157835SDaniel Fojt #ifdef CONFIG_P2P
1465*a1157835SDaniel Fojt 	if (wpabuf_len(buf) == 0 && qi->p2p_sd) {
1466*a1157835SDaniel Fojt 		wpa_printf(MSG_DEBUG,
1467*a1157835SDaniel Fojt 			   "ANQP: Do not send response to P2P SD from generic GAS service (P2P SD implementation will process this)");
1468*a1157835SDaniel Fojt 		wpabuf_free(buf);
1469*a1157835SDaniel Fojt 		return;
1470*a1157835SDaniel Fojt 	}
1471*a1157835SDaniel Fojt #endif /* CONFIG_P2P */
14723ff40c12SJohn Marino 
1473*a1157835SDaniel Fojt 	if (wpabuf_len(buf) > hapd->conf->gas_frag_limit ||
14743ff40c12SJohn Marino 	    hapd->conf->gas_comeback_delay) {
14753ff40c12SJohn Marino 		struct gas_dialog_info *di;
14763ff40c12SJohn Marino 		u16 comeback_delay = 1;
14773ff40c12SJohn Marino 
14783ff40c12SJohn Marino 		if (hapd->conf->gas_comeback_delay) {
14793ff40c12SJohn Marino 			/* Testing - allow overriding of the delay value */
14803ff40c12SJohn Marino 			comeback_delay = hapd->conf->gas_comeback_delay;
14813ff40c12SJohn Marino 		}
14823ff40c12SJohn Marino 
14833ff40c12SJohn Marino 		wpa_printf(MSG_DEBUG, "ANQP: Too long response to fit in "
14843ff40c12SJohn Marino 			   "initial response - use GAS comeback");
14853ff40c12SJohn Marino 		di = gas_dialog_create(hapd, sa, dialog_token);
14863ff40c12SJohn Marino 		if (!di) {
14873ff40c12SJohn Marino 			wpa_printf(MSG_INFO, "ANQP: Could not create dialog "
14883ff40c12SJohn Marino 				   "for " MACSTR " (dialog token %u)",
14893ff40c12SJohn Marino 				   MAC2STR(sa), dialog_token);
14903ff40c12SJohn Marino 			wpabuf_free(buf);
1491*a1157835SDaniel Fojt 			tx_buf = gas_anqp_build_initial_resp_buf(
1492*a1157835SDaniel Fojt 				dialog_token, WLAN_STATUS_UNSPECIFIED_FAILURE,
1493*a1157835SDaniel Fojt 				0, NULL);
1494*a1157835SDaniel Fojt 		} else {
14953ff40c12SJohn Marino 			di->prot = prot;
14963ff40c12SJohn Marino 			di->sd_resp = buf;
14973ff40c12SJohn Marino 			di->sd_resp_pos = 0;
14983ff40c12SJohn Marino 			tx_buf = gas_anqp_build_initial_resp_buf(
1499*a1157835SDaniel Fojt 				dialog_token, WLAN_STATUS_SUCCESS,
1500*a1157835SDaniel Fojt 				comeback_delay, NULL);
1501*a1157835SDaniel Fojt 		}
15023ff40c12SJohn Marino 	} else {
15033ff40c12SJohn Marino 		wpa_printf(MSG_DEBUG, "ANQP: Initial response (no comeback)");
15043ff40c12SJohn Marino 		tx_buf = gas_anqp_build_initial_resp_buf(
15053ff40c12SJohn Marino 			dialog_token, WLAN_STATUS_SUCCESS, 0, buf);
15063ff40c12SJohn Marino 		wpabuf_free(buf);
15073ff40c12SJohn Marino 	}
15083ff40c12SJohn Marino 	if (!tx_buf)
15093ff40c12SJohn Marino 		return;
15103ff40c12SJohn Marino 	if (prot)
15113ff40c12SJohn Marino 		convert_to_protected_dual(tx_buf);
1512*a1157835SDaniel Fojt 	if (std_addr3)
15133ff40c12SJohn Marino 		hostapd_drv_send_action(hapd, hapd->iface->freq, 0, sa,
1514*a1157835SDaniel Fojt 					wpabuf_head(tx_buf),
1515*a1157835SDaniel Fojt 					wpabuf_len(tx_buf));
1516*a1157835SDaniel Fojt 	else
1517*a1157835SDaniel Fojt 		hostapd_drv_send_action_addr3_ap(hapd, hapd->iface->freq, 0, sa,
1518*a1157835SDaniel Fojt 						 wpabuf_head(tx_buf),
1519*a1157835SDaniel Fojt 						 wpabuf_len(tx_buf));
15203ff40c12SJohn Marino 	wpabuf_free(tx_buf);
15213ff40c12SJohn Marino }
15223ff40c12SJohn Marino 
15233ff40c12SJohn Marino 
1524*a1157835SDaniel Fojt #ifdef CONFIG_DPP
gas_serv_req_dpp_processing(struct hostapd_data * hapd,const u8 * sa,u8 dialog_token,int prot,struct wpabuf * buf)1525*a1157835SDaniel Fojt void gas_serv_req_dpp_processing(struct hostapd_data *hapd,
1526*a1157835SDaniel Fojt 				 const u8 *sa, u8 dialog_token,
1527*a1157835SDaniel Fojt 				 int prot, struct wpabuf *buf)
1528*a1157835SDaniel Fojt {
1529*a1157835SDaniel Fojt 	struct wpabuf *tx_buf;
1530*a1157835SDaniel Fojt 
1531*a1157835SDaniel Fojt 	if (wpabuf_len(buf) > hapd->conf->gas_frag_limit ||
1532*a1157835SDaniel Fojt 	    hapd->conf->gas_comeback_delay) {
1533*a1157835SDaniel Fojt 		struct gas_dialog_info *di;
1534*a1157835SDaniel Fojt 		u16 comeback_delay = 1;
1535*a1157835SDaniel Fojt 
1536*a1157835SDaniel Fojt 		if (hapd->conf->gas_comeback_delay) {
1537*a1157835SDaniel Fojt 			/* Testing - allow overriding of the delay value */
1538*a1157835SDaniel Fojt 			comeback_delay = hapd->conf->gas_comeback_delay;
1539*a1157835SDaniel Fojt 		}
1540*a1157835SDaniel Fojt 
1541*a1157835SDaniel Fojt 		wpa_printf(MSG_DEBUG,
1542*a1157835SDaniel Fojt 			   "DPP: Too long response to fit in initial response - use GAS comeback");
1543*a1157835SDaniel Fojt 		di = gas_dialog_create(hapd, sa, dialog_token);
1544*a1157835SDaniel Fojt 		if (!di) {
1545*a1157835SDaniel Fojt 			wpa_printf(MSG_INFO, "DPP: Could not create dialog for "
1546*a1157835SDaniel Fojt 				   MACSTR " (dialog token %u)",
1547*a1157835SDaniel Fojt 				   MAC2STR(sa), dialog_token);
1548*a1157835SDaniel Fojt 			wpabuf_free(buf);
1549*a1157835SDaniel Fojt 			tx_buf = gas_build_initial_resp(
1550*a1157835SDaniel Fojt 				dialog_token, WLAN_STATUS_UNSPECIFIED_FAILURE,
1551*a1157835SDaniel Fojt 				0, 10);
1552*a1157835SDaniel Fojt 			if (tx_buf)
1553*a1157835SDaniel Fojt 				gas_serv_write_dpp_adv_proto(tx_buf);
1554*a1157835SDaniel Fojt 		} else {
1555*a1157835SDaniel Fojt 			di->prot = prot;
1556*a1157835SDaniel Fojt 			di->sd_resp = buf;
1557*a1157835SDaniel Fojt 			di->sd_resp_pos = 0;
1558*a1157835SDaniel Fojt 			tx_buf = gas_build_initial_resp(
1559*a1157835SDaniel Fojt 				dialog_token, WLAN_STATUS_SUCCESS,
1560*a1157835SDaniel Fojt 				comeback_delay, 10);
1561*a1157835SDaniel Fojt 			if (tx_buf)
1562*a1157835SDaniel Fojt 				gas_serv_write_dpp_adv_proto(tx_buf);
1563*a1157835SDaniel Fojt 		}
1564*a1157835SDaniel Fojt 	} else {
1565*a1157835SDaniel Fojt 		wpa_printf(MSG_DEBUG,
1566*a1157835SDaniel Fojt 			   "DPP: GAS Initial response (no comeback)");
1567*a1157835SDaniel Fojt 		tx_buf = gas_build_initial_resp(
1568*a1157835SDaniel Fojt 			dialog_token, WLAN_STATUS_SUCCESS, 0,
1569*a1157835SDaniel Fojt 			10 + 2 + wpabuf_len(buf));
1570*a1157835SDaniel Fojt 		if (tx_buf) {
1571*a1157835SDaniel Fojt 			gas_serv_write_dpp_adv_proto(tx_buf);
1572*a1157835SDaniel Fojt 			wpabuf_put_le16(tx_buf, wpabuf_len(buf));
1573*a1157835SDaniel Fojt 			wpabuf_put_buf(tx_buf, buf);
1574*a1157835SDaniel Fojt 			hostapd_dpp_gas_status_handler(hapd, 1);
1575*a1157835SDaniel Fojt 		}
1576*a1157835SDaniel Fojt 		wpabuf_free(buf);
1577*a1157835SDaniel Fojt 	}
1578*a1157835SDaniel Fojt 	if (!tx_buf)
1579*a1157835SDaniel Fojt 		return;
1580*a1157835SDaniel Fojt 	if (prot)
1581*a1157835SDaniel Fojt 		convert_to_protected_dual(tx_buf);
1582*a1157835SDaniel Fojt 	hostapd_drv_send_action(hapd, hapd->iface->freq, 0, sa,
1583*a1157835SDaniel Fojt 				wpabuf_head(tx_buf),
1584*a1157835SDaniel Fojt 				wpabuf_len(tx_buf));
1585*a1157835SDaniel Fojt 	wpabuf_free(tx_buf);
1586*a1157835SDaniel Fojt }
1587*a1157835SDaniel Fojt #endif /* CONFIG_DPP */
1588*a1157835SDaniel Fojt 
1589*a1157835SDaniel Fojt 
gas_serv_rx_gas_initial_req(struct hostapd_data * hapd,const u8 * sa,const u8 * data,size_t len,int prot,int std_addr3)15903ff40c12SJohn Marino static void gas_serv_rx_gas_initial_req(struct hostapd_data *hapd,
15913ff40c12SJohn Marino 					const u8 *sa,
1592*a1157835SDaniel Fojt 					const u8 *data, size_t len, int prot,
1593*a1157835SDaniel Fojt 					int std_addr3)
15943ff40c12SJohn Marino {
15953ff40c12SJohn Marino 	const u8 *pos = data;
15963ff40c12SJohn Marino 	const u8 *end = data + len;
15973ff40c12SJohn Marino 	const u8 *next;
15983ff40c12SJohn Marino 	u8 dialog_token;
15993ff40c12SJohn Marino 	u16 slen;
16003ff40c12SJohn Marino 	struct anqp_query_info qi;
16013ff40c12SJohn Marino 	const u8 *adv_proto;
1602*a1157835SDaniel Fojt #ifdef CONFIG_DPP
1603*a1157835SDaniel Fojt 	int dpp = 0;
1604*a1157835SDaniel Fojt #endif /* CONFIG_DPP */
16053ff40c12SJohn Marino 
16063ff40c12SJohn Marino 	if (len < 1 + 2)
16073ff40c12SJohn Marino 		return;
16083ff40c12SJohn Marino 
16093ff40c12SJohn Marino 	os_memset(&qi, 0, sizeof(qi));
16103ff40c12SJohn Marino 
16113ff40c12SJohn Marino 	dialog_token = *pos++;
16123ff40c12SJohn Marino 	wpa_msg(hapd->msg_ctx, MSG_DEBUG,
16133ff40c12SJohn Marino 		"GAS: GAS Initial Request from " MACSTR " (dialog token %u) ",
16143ff40c12SJohn Marino 		MAC2STR(sa), dialog_token);
16153ff40c12SJohn Marino 
16163ff40c12SJohn Marino 	if (*pos != WLAN_EID_ADV_PROTO) {
16173ff40c12SJohn Marino 		wpa_msg(hapd->msg_ctx, MSG_DEBUG,
16183ff40c12SJohn Marino 			"GAS: Unexpected IE in GAS Initial Request: %u", *pos);
16193ff40c12SJohn Marino 		return;
16203ff40c12SJohn Marino 	}
16213ff40c12SJohn Marino 	adv_proto = pos++;
16223ff40c12SJohn Marino 
16233ff40c12SJohn Marino 	slen = *pos++;
1624*a1157835SDaniel Fojt 	if (slen > end - pos || slen < 2) {
16253ff40c12SJohn Marino 		wpa_msg(hapd->msg_ctx, MSG_DEBUG,
16263ff40c12SJohn Marino 			"GAS: Invalid IE in GAS Initial Request");
16273ff40c12SJohn Marino 		return;
16283ff40c12SJohn Marino 	}
1629*a1157835SDaniel Fojt 	next = pos + slen;
16303ff40c12SJohn Marino 	pos++; /* skip QueryRespLenLimit and PAME-BI */
16313ff40c12SJohn Marino 
1632*a1157835SDaniel Fojt #ifdef CONFIG_DPP
1633*a1157835SDaniel Fojt 	if (slen == 8 && *pos == WLAN_EID_VENDOR_SPECIFIC &&
1634*a1157835SDaniel Fojt 	    pos[1] == 5 && WPA_GET_BE24(&pos[2]) == OUI_WFA &&
1635*a1157835SDaniel Fojt 	    pos[5] == DPP_OUI_TYPE && pos[6] == 0x01) {
1636*a1157835SDaniel Fojt 		wpa_printf(MSG_DEBUG, "DPP: Configuration Request");
1637*a1157835SDaniel Fojt 		dpp = 1;
1638*a1157835SDaniel Fojt 	} else
1639*a1157835SDaniel Fojt #endif /* CONFIG_DPP */
1640*a1157835SDaniel Fojt 
16413ff40c12SJohn Marino 	if (*pos != ACCESS_NETWORK_QUERY_PROTOCOL) {
16423ff40c12SJohn Marino 		struct wpabuf *buf;
16433ff40c12SJohn Marino 		wpa_msg(hapd->msg_ctx, MSG_DEBUG,
16443ff40c12SJohn Marino 			"GAS: Unsupported GAS advertisement protocol id %u",
16453ff40c12SJohn Marino 			*pos);
16463ff40c12SJohn Marino 		if (sa[0] & 0x01)
16473ff40c12SJohn Marino 			return; /* Invalid source address - drop silently */
16483ff40c12SJohn Marino 		buf = gas_build_initial_resp(
16493ff40c12SJohn Marino 			dialog_token, WLAN_STATUS_GAS_ADV_PROTO_NOT_SUPPORTED,
16503ff40c12SJohn Marino 			0, 2 + slen + 2);
16513ff40c12SJohn Marino 		if (buf == NULL)
16523ff40c12SJohn Marino 			return;
16533ff40c12SJohn Marino 		wpabuf_put_data(buf, adv_proto, 2 + slen);
16543ff40c12SJohn Marino 		wpabuf_put_le16(buf, 0); /* Query Response Length */
16553ff40c12SJohn Marino 		if (prot)
16563ff40c12SJohn Marino 			convert_to_protected_dual(buf);
1657*a1157835SDaniel Fojt 		if (std_addr3)
16583ff40c12SJohn Marino 			hostapd_drv_send_action(hapd, hapd->iface->freq, 0, sa,
1659*a1157835SDaniel Fojt 						wpabuf_head(buf),
1660*a1157835SDaniel Fojt 						wpabuf_len(buf));
1661*a1157835SDaniel Fojt 		else
1662*a1157835SDaniel Fojt 			hostapd_drv_send_action_addr3_ap(hapd,
1663*a1157835SDaniel Fojt 							 hapd->iface->freq, 0,
1664*a1157835SDaniel Fojt 							 sa, wpabuf_head(buf),
1665*a1157835SDaniel Fojt 							 wpabuf_len(buf));
16663ff40c12SJohn Marino 		wpabuf_free(buf);
16673ff40c12SJohn Marino 		return;
16683ff40c12SJohn Marino 	}
16693ff40c12SJohn Marino 
16703ff40c12SJohn Marino 	pos = next;
16713ff40c12SJohn Marino 	/* Query Request */
1672*a1157835SDaniel Fojt 	if (end - pos < 2)
16733ff40c12SJohn Marino 		return;
16743ff40c12SJohn Marino 	slen = WPA_GET_LE16(pos);
16753ff40c12SJohn Marino 	pos += 2;
1676*a1157835SDaniel Fojt 	if (slen > end - pos)
16773ff40c12SJohn Marino 		return;
16783ff40c12SJohn Marino 	end = pos + slen;
16793ff40c12SJohn Marino 
1680*a1157835SDaniel Fojt #ifdef CONFIG_DPP
1681*a1157835SDaniel Fojt 	if (dpp) {
1682*a1157835SDaniel Fojt 		struct wpabuf *msg;
1683*a1157835SDaniel Fojt 
1684*a1157835SDaniel Fojt 		msg = hostapd_dpp_gas_req_handler(hapd, sa, pos, slen,
1685*a1157835SDaniel Fojt 						  data, len);
1686*a1157835SDaniel Fojt 		if (!msg)
1687*a1157835SDaniel Fojt 			return;
1688*a1157835SDaniel Fojt 		gas_serv_req_dpp_processing(hapd, sa, dialog_token, prot, msg);
1689*a1157835SDaniel Fojt 		return;
1690*a1157835SDaniel Fojt 	}
1691*a1157835SDaniel Fojt #endif /* CONFIG_DPP */
1692*a1157835SDaniel Fojt 
16933ff40c12SJohn Marino 	/* ANQP Query Request */
16943ff40c12SJohn Marino 	while (pos < end) {
16953ff40c12SJohn Marino 		u16 info_id, elen;
16963ff40c12SJohn Marino 
1697*a1157835SDaniel Fojt 		if (end - pos < 4)
16983ff40c12SJohn Marino 			return;
16993ff40c12SJohn Marino 
17003ff40c12SJohn Marino 		info_id = WPA_GET_LE16(pos);
17013ff40c12SJohn Marino 		pos += 2;
17023ff40c12SJohn Marino 		elen = WPA_GET_LE16(pos);
17033ff40c12SJohn Marino 		pos += 2;
17043ff40c12SJohn Marino 
1705*a1157835SDaniel Fojt 		if (elen > end - pos) {
17063ff40c12SJohn Marino 			wpa_printf(MSG_DEBUG, "ANQP: Invalid Query Request");
17073ff40c12SJohn Marino 			return;
17083ff40c12SJohn Marino 		}
17093ff40c12SJohn Marino 
17103ff40c12SJohn Marino 		switch (info_id) {
17113ff40c12SJohn Marino 		case ANQP_QUERY_LIST:
17123ff40c12SJohn Marino 			rx_anqp_query_list(hapd, pos, pos + elen, &qi);
17133ff40c12SJohn Marino 			break;
17143ff40c12SJohn Marino 		case ANQP_VENDOR_SPECIFIC:
17153ff40c12SJohn Marino 			rx_anqp_vendor_specific(hapd, pos, pos + elen, &qi);
17163ff40c12SJohn Marino 			break;
17173ff40c12SJohn Marino 		default:
17183ff40c12SJohn Marino 			wpa_printf(MSG_DEBUG, "ANQP: Unsupported Query "
17193ff40c12SJohn Marino 				   "Request element %u", info_id);
17203ff40c12SJohn Marino 			break;
17213ff40c12SJohn Marino 		}
17223ff40c12SJohn Marino 
17233ff40c12SJohn Marino 		pos += elen;
17243ff40c12SJohn Marino 	}
17253ff40c12SJohn Marino 
1726*a1157835SDaniel Fojt 	gas_serv_req_local_processing(hapd, sa, dialog_token, &qi, prot,
1727*a1157835SDaniel Fojt 				      std_addr3);
17283ff40c12SJohn Marino }
17293ff40c12SJohn Marino 
17303ff40c12SJohn Marino 
gas_serv_rx_gas_comeback_req(struct hostapd_data * hapd,const u8 * sa,const u8 * data,size_t len,int prot,int std_addr3)17313ff40c12SJohn Marino static void gas_serv_rx_gas_comeback_req(struct hostapd_data *hapd,
17323ff40c12SJohn Marino 					 const u8 *sa,
1733*a1157835SDaniel Fojt 					 const u8 *data, size_t len, int prot,
1734*a1157835SDaniel Fojt 					 int std_addr3)
17353ff40c12SJohn Marino {
17363ff40c12SJohn Marino 	struct gas_dialog_info *dialog;
17373ff40c12SJohn Marino 	struct wpabuf *buf, *tx_buf;
17383ff40c12SJohn Marino 	u8 dialog_token;
17393ff40c12SJohn Marino 	size_t frag_len;
17403ff40c12SJohn Marino 	int more = 0;
17413ff40c12SJohn Marino 
17423ff40c12SJohn Marino 	wpa_hexdump(MSG_DEBUG, "GAS: RX GAS Comeback Request", data, len);
17433ff40c12SJohn Marino 	if (len < 1)
17443ff40c12SJohn Marino 		return;
17453ff40c12SJohn Marino 	dialog_token = *data;
17463ff40c12SJohn Marino 	wpa_msg(hapd->msg_ctx, MSG_DEBUG, "GAS: Dialog Token: %u",
17473ff40c12SJohn Marino 		dialog_token);
17483ff40c12SJohn Marino 
17493ff40c12SJohn Marino 	dialog = gas_serv_dialog_find(hapd, sa, dialog_token);
17503ff40c12SJohn Marino 	if (!dialog) {
17513ff40c12SJohn Marino 		wpa_msg(hapd->msg_ctx, MSG_DEBUG, "GAS: No pending SD "
17523ff40c12SJohn Marino 			"response fragment for " MACSTR " dialog token %u",
17533ff40c12SJohn Marino 			MAC2STR(sa), dialog_token);
17543ff40c12SJohn Marino 
17553ff40c12SJohn Marino 		if (sa[0] & 0x01)
17563ff40c12SJohn Marino 			return; /* Invalid source address - drop silently */
17573ff40c12SJohn Marino 		tx_buf = gas_anqp_build_comeback_resp_buf(
17583ff40c12SJohn Marino 			dialog_token, WLAN_STATUS_NO_OUTSTANDING_GAS_REQ, 0, 0,
17593ff40c12SJohn Marino 			0, NULL);
17603ff40c12SJohn Marino 		if (tx_buf == NULL)
17613ff40c12SJohn Marino 			return;
17623ff40c12SJohn Marino 		goto send_resp;
17633ff40c12SJohn Marino 	}
17643ff40c12SJohn Marino 
17653ff40c12SJohn Marino 	frag_len = wpabuf_len(dialog->sd_resp) - dialog->sd_resp_pos;
1766*a1157835SDaniel Fojt 	if (frag_len > hapd->conf->gas_frag_limit) {
1767*a1157835SDaniel Fojt 		frag_len = hapd->conf->gas_frag_limit;
17683ff40c12SJohn Marino 		more = 1;
17693ff40c12SJohn Marino 	}
17703ff40c12SJohn Marino 	wpa_msg(hapd->msg_ctx, MSG_DEBUG, "GAS: resp frag_len %u",
17713ff40c12SJohn Marino 		(unsigned int) frag_len);
17723ff40c12SJohn Marino 	buf = wpabuf_alloc_copy(wpabuf_head_u8(dialog->sd_resp) +
17733ff40c12SJohn Marino 				dialog->sd_resp_pos, frag_len);
17743ff40c12SJohn Marino 	if (buf == NULL) {
17753ff40c12SJohn Marino 		wpa_msg(hapd->msg_ctx, MSG_DEBUG, "GAS: Failed to allocate "
17763ff40c12SJohn Marino 			"buffer");
1777*a1157835SDaniel Fojt 		gas_serv_dialog_clear(dialog);
1778*a1157835SDaniel Fojt 		return;
17793ff40c12SJohn Marino 	}
1780*a1157835SDaniel Fojt #ifdef CONFIG_DPP
1781*a1157835SDaniel Fojt 	if (dialog->dpp) {
1782*a1157835SDaniel Fojt 		tx_buf = gas_build_comeback_resp(dialog_token,
1783*a1157835SDaniel Fojt 						 WLAN_STATUS_SUCCESS,
1784*a1157835SDaniel Fojt 						 dialog->sd_frag_id, more, 0,
1785*a1157835SDaniel Fojt 						 10 + frag_len);
1786*a1157835SDaniel Fojt 		if (tx_buf) {
1787*a1157835SDaniel Fojt 			gas_serv_write_dpp_adv_proto(tx_buf);
1788*a1157835SDaniel Fojt 			wpabuf_put_buf(tx_buf, buf);
1789*a1157835SDaniel Fojt 		}
1790*a1157835SDaniel Fojt 	} else
1791*a1157835SDaniel Fojt #endif /* CONFIG_DPP */
17923ff40c12SJohn Marino 	tx_buf = gas_anqp_build_comeback_resp_buf(dialog_token,
17933ff40c12SJohn Marino 						  WLAN_STATUS_SUCCESS,
17943ff40c12SJohn Marino 						  dialog->sd_frag_id,
17953ff40c12SJohn Marino 						  more, 0, buf);
17963ff40c12SJohn Marino 	wpabuf_free(buf);
1797*a1157835SDaniel Fojt 	if (tx_buf == NULL) {
1798*a1157835SDaniel Fojt 		gas_serv_dialog_clear(dialog);
1799*a1157835SDaniel Fojt 		return;
1800*a1157835SDaniel Fojt 	}
18013ff40c12SJohn Marino 	wpa_msg(hapd->msg_ctx, MSG_DEBUG, "GAS: Tx GAS Comeback Response "
18023ff40c12SJohn Marino 		"(frag_id %d more=%d frag_len=%d)",
18033ff40c12SJohn Marino 		dialog->sd_frag_id, more, (int) frag_len);
18043ff40c12SJohn Marino 	dialog->sd_frag_id++;
18053ff40c12SJohn Marino 	dialog->sd_resp_pos += frag_len;
18063ff40c12SJohn Marino 
18073ff40c12SJohn Marino 	if (more) {
18083ff40c12SJohn Marino 		wpa_msg(hapd->msg_ctx, MSG_DEBUG, "GAS: %d more bytes remain "
18093ff40c12SJohn Marino 			"to be sent",
18103ff40c12SJohn Marino 			(int) (wpabuf_len(dialog->sd_resp) -
18113ff40c12SJohn Marino 			       dialog->sd_resp_pos));
18123ff40c12SJohn Marino 	} else {
18133ff40c12SJohn Marino 		wpa_msg(hapd->msg_ctx, MSG_DEBUG, "GAS: All fragments of "
18143ff40c12SJohn Marino 			"SD response sent");
1815*a1157835SDaniel Fojt #ifdef CONFIG_DPP
1816*a1157835SDaniel Fojt 		if (dialog->dpp)
1817*a1157835SDaniel Fojt 			hostapd_dpp_gas_status_handler(hapd, 1);
1818*a1157835SDaniel Fojt #endif /* CONFIG_DPP */
18193ff40c12SJohn Marino 		gas_serv_dialog_clear(dialog);
18203ff40c12SJohn Marino 		gas_serv_free_dialogs(hapd, sa);
18213ff40c12SJohn Marino 	}
18223ff40c12SJohn Marino 
18233ff40c12SJohn Marino send_resp:
18243ff40c12SJohn Marino 	if (prot)
18253ff40c12SJohn Marino 		convert_to_protected_dual(tx_buf);
1826*a1157835SDaniel Fojt 	if (std_addr3)
18273ff40c12SJohn Marino 		hostapd_drv_send_action(hapd, hapd->iface->freq, 0, sa,
1828*a1157835SDaniel Fojt 					wpabuf_head(tx_buf),
1829*a1157835SDaniel Fojt 					wpabuf_len(tx_buf));
1830*a1157835SDaniel Fojt 	else
1831*a1157835SDaniel Fojt 		hostapd_drv_send_action_addr3_ap(hapd, hapd->iface->freq, 0, sa,
1832*a1157835SDaniel Fojt 						 wpabuf_head(tx_buf),
1833*a1157835SDaniel Fojt 						 wpabuf_len(tx_buf));
18343ff40c12SJohn Marino 	wpabuf_free(tx_buf);
18353ff40c12SJohn Marino }
18363ff40c12SJohn Marino 
18373ff40c12SJohn Marino 
gas_serv_rx_public_action(void * ctx,const u8 * buf,size_t len,int freq)18383ff40c12SJohn Marino static void gas_serv_rx_public_action(void *ctx, const u8 *buf, size_t len,
18393ff40c12SJohn Marino 				      int freq)
18403ff40c12SJohn Marino {
18413ff40c12SJohn Marino 	struct hostapd_data *hapd = ctx;
18423ff40c12SJohn Marino 	const struct ieee80211_mgmt *mgmt;
18433ff40c12SJohn Marino 	const u8 *sa, *data;
1844*a1157835SDaniel Fojt 	int prot, std_addr3;
18453ff40c12SJohn Marino 
18463ff40c12SJohn Marino 	mgmt = (const struct ieee80211_mgmt *) buf;
1847*a1157835SDaniel Fojt 	if (len < IEEE80211_HDRLEN + 2)
18483ff40c12SJohn Marino 		return;
18493ff40c12SJohn Marino 	if (mgmt->u.action.category != WLAN_ACTION_PUBLIC &&
18503ff40c12SJohn Marino 	    mgmt->u.action.category != WLAN_ACTION_PROTECTED_DUAL)
18513ff40c12SJohn Marino 		return;
18523ff40c12SJohn Marino 	/*
18533ff40c12SJohn Marino 	 * Note: Public Action and Protected Dual of Public Action frames share
18543ff40c12SJohn Marino 	 * the same payload structure, so it is fine to use definitions of
18553ff40c12SJohn Marino 	 * Public Action frames to process both.
18563ff40c12SJohn Marino 	 */
18573ff40c12SJohn Marino 	prot = mgmt->u.action.category == WLAN_ACTION_PROTECTED_DUAL;
18583ff40c12SJohn Marino 	sa = mgmt->sa;
1859*a1157835SDaniel Fojt 	if (hapd->conf->gas_address3 == 1)
1860*a1157835SDaniel Fojt 		std_addr3 = 1;
1861*a1157835SDaniel Fojt 	else if (hapd->conf->gas_address3 == 2)
1862*a1157835SDaniel Fojt 		std_addr3 = 0;
1863*a1157835SDaniel Fojt 	else
1864*a1157835SDaniel Fojt 		std_addr3 = is_broadcast_ether_addr(mgmt->bssid);
1865*a1157835SDaniel Fojt 	len -= IEEE80211_HDRLEN + 1;
1866*a1157835SDaniel Fojt 	data = buf + IEEE80211_HDRLEN + 1;
18673ff40c12SJohn Marino 	switch (data[0]) {
18683ff40c12SJohn Marino 	case WLAN_PA_GAS_INITIAL_REQ:
1869*a1157835SDaniel Fojt 		gas_serv_rx_gas_initial_req(hapd, sa, data + 1, len - 1, prot,
1870*a1157835SDaniel Fojt 					    std_addr3);
18713ff40c12SJohn Marino 		break;
18723ff40c12SJohn Marino 	case WLAN_PA_GAS_COMEBACK_REQ:
1873*a1157835SDaniel Fojt 		gas_serv_rx_gas_comeback_req(hapd, sa, data + 1, len - 1, prot,
1874*a1157835SDaniel Fojt 					     std_addr3);
18753ff40c12SJohn Marino 		break;
18763ff40c12SJohn Marino 	}
18773ff40c12SJohn Marino }
18783ff40c12SJohn Marino 
18793ff40c12SJohn Marino 
gas_serv_init(struct hostapd_data * hapd)18803ff40c12SJohn Marino int gas_serv_init(struct hostapd_data *hapd)
18813ff40c12SJohn Marino {
18823ff40c12SJohn Marino 	hapd->public_action_cb2 = gas_serv_rx_public_action;
18833ff40c12SJohn Marino 	hapd->public_action_cb2_ctx = hapd;
18843ff40c12SJohn Marino 	return 0;
18853ff40c12SJohn Marino }
18863ff40c12SJohn Marino 
18873ff40c12SJohn Marino 
gas_serv_deinit(struct hostapd_data * hapd)18883ff40c12SJohn Marino void gas_serv_deinit(struct hostapd_data *hapd)
18893ff40c12SJohn Marino {
18903ff40c12SJohn Marino }
1891