104949598a23f501be6eec21697465fd46a28840aDmitry Shmidt/*
204949598a23f501be6eec21697465fd46a28840aDmitry Shmidt * Generic advertisement service (GAS) server
304949598a23f501be6eec21697465fd46a28840aDmitry Shmidt * Copyright (c) 2011-2012, Qualcomm Atheros, Inc.
404949598a23f501be6eec21697465fd46a28840aDmitry Shmidt *
504949598a23f501be6eec21697465fd46a28840aDmitry Shmidt * This software may be distributed under the terms of the BSD license.
604949598a23f501be6eec21697465fd46a28840aDmitry Shmidt * See README for more details.
704949598a23f501be6eec21697465fd46a28840aDmitry Shmidt */
804949598a23f501be6eec21697465fd46a28840aDmitry Shmidt
904949598a23f501be6eec21697465fd46a28840aDmitry Shmidt#include "includes.h"
1004949598a23f501be6eec21697465fd46a28840aDmitry Shmidt
1104949598a23f501be6eec21697465fd46a28840aDmitry Shmidt#include "common.h"
1204949598a23f501be6eec21697465fd46a28840aDmitry Shmidt#include "common/ieee802_11_defs.h"
1304949598a23f501be6eec21697465fd46a28840aDmitry Shmidt#include "common/gas.h"
1404949598a23f501be6eec21697465fd46a28840aDmitry Shmidt#include "utils/eloop.h"
1504949598a23f501be6eec21697465fd46a28840aDmitry Shmidt#include "hostapd.h"
1604949598a23f501be6eec21697465fd46a28840aDmitry Shmidt#include "ap_config.h"
1704949598a23f501be6eec21697465fd46a28840aDmitry Shmidt#include "ap_drv_ops.h"
1804949598a23f501be6eec21697465fd46a28840aDmitry Shmidt#include "sta_info.h"
1904949598a23f501be6eec21697465fd46a28840aDmitry Shmidt#include "gas_serv.h"
2004949598a23f501be6eec21697465fd46a28840aDmitry Shmidt
2104949598a23f501be6eec21697465fd46a28840aDmitry Shmidt
2204949598a23f501be6eec21697465fd46a28840aDmitry Shmidtstatic struct gas_dialog_info *
2304949598a23f501be6eec21697465fd46a28840aDmitry Shmidtgas_dialog_create(struct hostapd_data *hapd, const u8 *addr, u8 dialog_token)
2404949598a23f501be6eec21697465fd46a28840aDmitry Shmidt{
2504949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	struct sta_info *sta;
2604949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	struct gas_dialog_info *dia = NULL;
2704949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	int i, j;
2804949598a23f501be6eec21697465fd46a28840aDmitry Shmidt
2904949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	sta = ap_get_sta(hapd, addr);
3004949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	if (!sta) {
3104949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		/*
3204949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		 * We need a STA entry to be able to maintain state for
3304949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		 * the GAS query.
3404949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		 */
3504949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		wpa_printf(MSG_DEBUG, "ANQP: Add a temporary STA entry for "
3604949598a23f501be6eec21697465fd46a28840aDmitry Shmidt			   "GAS query");
3704949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		sta = ap_sta_add(hapd, addr);
3804949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		if (!sta) {
3904949598a23f501be6eec21697465fd46a28840aDmitry Shmidt			wpa_printf(MSG_DEBUG, "Failed to add STA " MACSTR
4004949598a23f501be6eec21697465fd46a28840aDmitry Shmidt				   " for GAS query", MAC2STR(addr));
4104949598a23f501be6eec21697465fd46a28840aDmitry Shmidt			return NULL;
4204949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		}
4304949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		sta->flags |= WLAN_STA_GAS;
4404949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		/*
4504949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		 * The default inactivity is 300 seconds. We don't need
4604949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		 * it to be that long.
4704949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		 */
4804949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		ap_sta_session_timeout(hapd, sta, 5);
4904949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	}
5004949598a23f501be6eec21697465fd46a28840aDmitry Shmidt
5104949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	if (sta->gas_dialog == NULL) {
5204949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		sta->gas_dialog = os_zalloc(GAS_DIALOG_MAX *
5304949598a23f501be6eec21697465fd46a28840aDmitry Shmidt					    sizeof(struct gas_dialog_info));
5404949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		if (sta->gas_dialog == NULL)
5504949598a23f501be6eec21697465fd46a28840aDmitry Shmidt			return NULL;
5604949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	}
5704949598a23f501be6eec21697465fd46a28840aDmitry Shmidt
5804949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	for (i = sta->gas_dialog_next, j = 0; j < GAS_DIALOG_MAX; i++, j++) {
5904949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		if (i == GAS_DIALOG_MAX)
6004949598a23f501be6eec21697465fd46a28840aDmitry Shmidt			i = 0;
6104949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		if (sta->gas_dialog[i].valid)
6204949598a23f501be6eec21697465fd46a28840aDmitry Shmidt			continue;
6304949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		dia = &sta->gas_dialog[i];
6404949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		dia->valid = 1;
6504949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		dia->index = i;
6604949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		dia->dialog_token = dialog_token;
6704949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		sta->gas_dialog_next = (++i == GAS_DIALOG_MAX) ? 0 : i;
6804949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		return dia;
6904949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	}
7004949598a23f501be6eec21697465fd46a28840aDmitry Shmidt
7104949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	wpa_msg(hapd->msg_ctx, MSG_ERROR, "ANQP: Could not create dialog for "
7204949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		MACSTR " dialog_token %u. Consider increasing "
7304949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		"GAS_DIALOG_MAX.", MAC2STR(addr), dialog_token);
7404949598a23f501be6eec21697465fd46a28840aDmitry Shmidt
7504949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	return NULL;
7604949598a23f501be6eec21697465fd46a28840aDmitry Shmidt}
7704949598a23f501be6eec21697465fd46a28840aDmitry Shmidt
7804949598a23f501be6eec21697465fd46a28840aDmitry Shmidt
7904949598a23f501be6eec21697465fd46a28840aDmitry Shmidtstruct gas_dialog_info *
8004949598a23f501be6eec21697465fd46a28840aDmitry Shmidtgas_serv_dialog_find(struct hostapd_data *hapd, const u8 *addr,
8104949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		     u8 dialog_token)
8204949598a23f501be6eec21697465fd46a28840aDmitry Shmidt{
8304949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	struct sta_info *sta;
8404949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	int i;
8504949598a23f501be6eec21697465fd46a28840aDmitry Shmidt
8604949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	sta = ap_get_sta(hapd, addr);
8704949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	if (!sta) {
8804949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		wpa_printf(MSG_DEBUG, "ANQP: could not find STA " MACSTR,
8904949598a23f501be6eec21697465fd46a28840aDmitry Shmidt			   MAC2STR(addr));
9004949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		return NULL;
9104949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	}
9204949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	for (i = 0; sta->gas_dialog && i < GAS_DIALOG_MAX; i++) {
9304949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		if (sta->gas_dialog[i].dialog_token != dialog_token ||
9404949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		    !sta->gas_dialog[i].valid)
9504949598a23f501be6eec21697465fd46a28840aDmitry Shmidt			continue;
9604949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		return &sta->gas_dialog[i];
9704949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	}
9804949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	wpa_printf(MSG_DEBUG, "ANQP: Could not find dialog for "
9904949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		   MACSTR " dialog_token %u", MAC2STR(addr), dialog_token);
10004949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	return NULL;
10104949598a23f501be6eec21697465fd46a28840aDmitry Shmidt}
10204949598a23f501be6eec21697465fd46a28840aDmitry Shmidt
10304949598a23f501be6eec21697465fd46a28840aDmitry Shmidt
10404949598a23f501be6eec21697465fd46a28840aDmitry Shmidtvoid gas_serv_dialog_clear(struct gas_dialog_info *dia)
10504949598a23f501be6eec21697465fd46a28840aDmitry Shmidt{
10604949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	wpabuf_free(dia->sd_resp);
10704949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	os_memset(dia, 0, sizeof(*dia));
10804949598a23f501be6eec21697465fd46a28840aDmitry Shmidt}
10904949598a23f501be6eec21697465fd46a28840aDmitry Shmidt
11004949598a23f501be6eec21697465fd46a28840aDmitry Shmidt
11104949598a23f501be6eec21697465fd46a28840aDmitry Shmidtstatic void gas_serv_free_dialogs(struct hostapd_data *hapd,
11204949598a23f501be6eec21697465fd46a28840aDmitry Shmidt				  const u8 *sta_addr)
11304949598a23f501be6eec21697465fd46a28840aDmitry Shmidt{
11404949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	struct sta_info *sta;
11504949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	int i;
11604949598a23f501be6eec21697465fd46a28840aDmitry Shmidt
11704949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	sta = ap_get_sta(hapd, sta_addr);
11804949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	if (sta == NULL || sta->gas_dialog == NULL)
11904949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		return;
12004949598a23f501be6eec21697465fd46a28840aDmitry Shmidt
12104949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	for (i = 0; i < GAS_DIALOG_MAX; i++) {
12204949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		if (sta->gas_dialog[i].valid)
12304949598a23f501be6eec21697465fd46a28840aDmitry Shmidt			return;
12404949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	}
12504949598a23f501be6eec21697465fd46a28840aDmitry Shmidt
12604949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	os_free(sta->gas_dialog);
12704949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	sta->gas_dialog = NULL;
12804949598a23f501be6eec21697465fd46a28840aDmitry Shmidt}
12904949598a23f501be6eec21697465fd46a28840aDmitry Shmidt
13004949598a23f501be6eec21697465fd46a28840aDmitry Shmidt
131aa532510a7b8c4da2d7d6e2c11dda5db840894e4Dmitry Shmidt#ifdef CONFIG_HS20
13261d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidtstatic void anqp_add_hs_capab_list(struct hostapd_data *hapd,
13361d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt				   struct wpabuf *buf)
13461d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt{
13561d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	u8 *len;
13661d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt
13761d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	len = gas_anqp_add_element(buf, ANQP_VENDOR_SPECIFIC);
13861d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	wpabuf_put_be24(buf, OUI_WFA);
13961d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	wpabuf_put_u8(buf, HS20_ANQP_OUI_TYPE);
14061d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	wpabuf_put_u8(buf, HS20_STYPE_CAPABILITY_LIST);
14161d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	wpabuf_put_u8(buf, 0); /* Reserved */
14261d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	wpabuf_put_u8(buf, HS20_STYPE_CAPABILITY_LIST);
14361d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	if (hapd->conf->hs20_oper_friendly_name)
14461d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt		wpabuf_put_u8(buf, HS20_STYPE_OPERATOR_FRIENDLY_NAME);
14561d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	if (hapd->conf->hs20_wan_metrics)
14661d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt		wpabuf_put_u8(buf, HS20_STYPE_WAN_METRICS);
14761d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	if (hapd->conf->hs20_connection_capability)
14861d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt		wpabuf_put_u8(buf, HS20_STYPE_CONNECTION_CAPABILITY);
14961d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	if (hapd->conf->nai_realm_data)
15061d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt		wpabuf_put_u8(buf, HS20_STYPE_NAI_HOME_REALM_QUERY);
15161d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	if (hapd->conf->hs20_operating_class)
15261d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt		wpabuf_put_u8(buf, HS20_STYPE_OPERATING_CLASS);
15361d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	gas_anqp_set_element_len(buf, len);
15461d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt}
155aa532510a7b8c4da2d7d6e2c11dda5db840894e4Dmitry Shmidt#endif /* CONFIG_HS20 */
15661d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt
15761d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt
15804949598a23f501be6eec21697465fd46a28840aDmitry Shmidtstatic void anqp_add_capab_list(struct hostapd_data *hapd,
15904949598a23f501be6eec21697465fd46a28840aDmitry Shmidt				struct wpabuf *buf)
16004949598a23f501be6eec21697465fd46a28840aDmitry Shmidt{
16104949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	u8 *len;
16204949598a23f501be6eec21697465fd46a28840aDmitry Shmidt
16304949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	len = gas_anqp_add_element(buf, ANQP_CAPABILITY_LIST);
16404949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	wpabuf_put_le16(buf, ANQP_CAPABILITY_LIST);
16504949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	if (hapd->conf->venue_name)
16604949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		wpabuf_put_le16(buf, ANQP_VENUE_NAME);
16761d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	if (hapd->conf->network_auth_type)
16861d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt		wpabuf_put_le16(buf, ANQP_NETWORK_AUTH_TYPE);
16904949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	if (hapd->conf->roaming_consortium)
17004949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		wpabuf_put_le16(buf, ANQP_ROAMING_CONSORTIUM);
17161d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	if (hapd->conf->ipaddr_type_configured)
17261d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt		wpabuf_put_le16(buf, ANQP_IP_ADDR_TYPE_AVAILABILITY);
17361d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	if (hapd->conf->nai_realm_data)
17461d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt		wpabuf_put_le16(buf, ANQP_NAI_REALM);
17561d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	if (hapd->conf->anqp_3gpp_cell_net)
17661d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt		wpabuf_put_le16(buf, ANQP_3GPP_CELLULAR_NETWORK);
17761d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	if (hapd->conf->domain_name)
17861d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt		wpabuf_put_le16(buf, ANQP_DOMAIN_NAME);
179aa532510a7b8c4da2d7d6e2c11dda5db840894e4Dmitry Shmidt#ifdef CONFIG_HS20
18061d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	anqp_add_hs_capab_list(hapd, buf);
181aa532510a7b8c4da2d7d6e2c11dda5db840894e4Dmitry Shmidt#endif /* CONFIG_HS20 */
18204949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	gas_anqp_set_element_len(buf, len);
18304949598a23f501be6eec21697465fd46a28840aDmitry Shmidt}
18404949598a23f501be6eec21697465fd46a28840aDmitry Shmidt
18504949598a23f501be6eec21697465fd46a28840aDmitry Shmidt
18604949598a23f501be6eec21697465fd46a28840aDmitry Shmidtstatic void anqp_add_venue_name(struct hostapd_data *hapd, struct wpabuf *buf)
18704949598a23f501be6eec21697465fd46a28840aDmitry Shmidt{
18804949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	if (hapd->conf->venue_name) {
18904949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		u8 *len;
19004949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		unsigned int i;
19104949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		len = gas_anqp_add_element(buf, ANQP_VENUE_NAME);
19204949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		wpabuf_put_u8(buf, hapd->conf->venue_group);
19304949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		wpabuf_put_u8(buf, hapd->conf->venue_type);
19404949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		for (i = 0; i < hapd->conf->venue_name_count; i++) {
19561d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt			struct hostapd_lang_string *vn;
19604949598a23f501be6eec21697465fd46a28840aDmitry Shmidt			vn = &hapd->conf->venue_name[i];
19704949598a23f501be6eec21697465fd46a28840aDmitry Shmidt			wpabuf_put_u8(buf, 3 + vn->name_len);
19804949598a23f501be6eec21697465fd46a28840aDmitry Shmidt			wpabuf_put_data(buf, vn->lang, 3);
19904949598a23f501be6eec21697465fd46a28840aDmitry Shmidt			wpabuf_put_data(buf, vn->name, vn->name_len);
20004949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		}
20104949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		gas_anqp_set_element_len(buf, len);
20204949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	}
20304949598a23f501be6eec21697465fd46a28840aDmitry Shmidt}
20404949598a23f501be6eec21697465fd46a28840aDmitry Shmidt
20504949598a23f501be6eec21697465fd46a28840aDmitry Shmidt
20661d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidtstatic void anqp_add_network_auth_type(struct hostapd_data *hapd,
20761d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt				       struct wpabuf *buf)
20861d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt{
20961d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	if (hapd->conf->network_auth_type) {
21061d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt		wpabuf_put_le16(buf, ANQP_NETWORK_AUTH_TYPE);
21161d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt		wpabuf_put_le16(buf, hapd->conf->network_auth_type_len);
21261d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt		wpabuf_put_data(buf, hapd->conf->network_auth_type,
21361d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt				hapd->conf->network_auth_type_len);
21461d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	}
21561d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt}
21661d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt
21761d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt
21804949598a23f501be6eec21697465fd46a28840aDmitry Shmidtstatic void anqp_add_roaming_consortium(struct hostapd_data *hapd,
21904949598a23f501be6eec21697465fd46a28840aDmitry Shmidt					struct wpabuf *buf)
22004949598a23f501be6eec21697465fd46a28840aDmitry Shmidt{
22104949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	unsigned int i;
22204949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	u8 *len;
22304949598a23f501be6eec21697465fd46a28840aDmitry Shmidt
22404949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	len = gas_anqp_add_element(buf, ANQP_ROAMING_CONSORTIUM);
22504949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	for (i = 0; i < hapd->conf->roaming_consortium_count; i++) {
22604949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		struct hostapd_roaming_consortium *rc;
22704949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		rc = &hapd->conf->roaming_consortium[i];
22804949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		wpabuf_put_u8(buf, rc->len);
22904949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		wpabuf_put_data(buf, rc->oi, rc->len);
23004949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	}
23104949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	gas_anqp_set_element_len(buf, len);
23204949598a23f501be6eec21697465fd46a28840aDmitry Shmidt}
23304949598a23f501be6eec21697465fd46a28840aDmitry Shmidt
23404949598a23f501be6eec21697465fd46a28840aDmitry Shmidt
23561d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidtstatic void anqp_add_ip_addr_type_availability(struct hostapd_data *hapd,
23661d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt					       struct wpabuf *buf)
23761d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt{
23861d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	if (hapd->conf->ipaddr_type_configured) {
23961d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt		wpabuf_put_le16(buf, ANQP_IP_ADDR_TYPE_AVAILABILITY);
24061d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt		wpabuf_put_le16(buf, 1);
24161d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt		wpabuf_put_u8(buf, hapd->conf->ipaddr_type_availability);
24261d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	}
24361d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt}
24461d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt
24561d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt
24661d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidtstatic void anqp_add_nai_realm_eap(struct wpabuf *buf,
24761d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt				   struct hostapd_nai_realm_data *realm)
24861d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt{
24961d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	unsigned int i, j;
25061d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt
25161d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	wpabuf_put_u8(buf, realm->eap_method_count);
25261d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt
25361d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	for (i = 0; i < realm->eap_method_count; i++) {
25461d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt		struct hostapd_nai_realm_eap *eap = &realm->eap_method[i];
25561d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt		wpabuf_put_u8(buf, 2 + (3 * eap->num_auths));
25661d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt		wpabuf_put_u8(buf, eap->eap_method);
25761d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt		wpabuf_put_u8(buf, eap->num_auths);
25861d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt		for (j = 0; j < eap->num_auths; j++) {
25961d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt			wpabuf_put_u8(buf, eap->auth_id[j]);
26061d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt			wpabuf_put_u8(buf, 1);
26161d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt			wpabuf_put_u8(buf, eap->auth_val[j]);
26261d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt		}
26361d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	}
26461d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt}
26561d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt
26661d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt
26761d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidtstatic void anqp_add_nai_realm_data(struct wpabuf *buf,
26861d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt				    struct hostapd_nai_realm_data *realm,
26961d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt				    unsigned int realm_idx)
27061d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt{
27161d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	u8 *realm_data_len;
27261d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt
27361d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	wpa_printf(MSG_DEBUG, "realm=%s, len=%d", realm->realm[realm_idx],
27461d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt		   (int) os_strlen(realm->realm[realm_idx]));
27561d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	realm_data_len = wpabuf_put(buf, 2);
27661d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	wpabuf_put_u8(buf, realm->encoding);
27761d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	wpabuf_put_u8(buf, os_strlen(realm->realm[realm_idx]));
27861d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	wpabuf_put_str(buf, realm->realm[realm_idx]);
27961d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	anqp_add_nai_realm_eap(buf, realm);
28061d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	gas_anqp_set_element_len(buf, realm_data_len);
28161d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt}
28261d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt
28361d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt
28461d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidtstatic int hs20_add_nai_home_realm_matches(struct hostapd_data *hapd,
28561d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt					   struct wpabuf *buf,
28661d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt					   const u8 *home_realm,
28761d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt					   size_t home_realm_len)
28861d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt{
28961d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	unsigned int i, j, k;
29061d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	u8 num_realms, num_matching = 0, encoding, realm_len, *realm_list_len;
29161d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	struct hostapd_nai_realm_data *realm;
29261d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	const u8 *pos, *realm_name, *end;
29361d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	struct {
29461d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt		unsigned int realm_data_idx;
29561d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt		unsigned int realm_idx;
29661d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	} matches[10];
29761d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt
29861d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	pos = home_realm;
29961d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	end = pos + home_realm_len;
30061d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	if (pos + 1 > end) {
30161d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt		wpa_hexdump(MSG_DEBUG, "Too short NAI Home Realm Query",
30261d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt			    home_realm, home_realm_len);
30361d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt		return -1;
30461d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	}
30561d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	num_realms = *pos++;
30661d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt
30761d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	for (i = 0; i < num_realms && num_matching < 10; i++) {
30861d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt		if (pos + 2 > end) {
30961d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt			wpa_hexdump(MSG_DEBUG,
31061d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt				    "Truncated NAI Home Realm Query",
31161d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt				    home_realm, home_realm_len);
31261d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt			return -1;
31361d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt		}
31461d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt		encoding = *pos++;
31561d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt		realm_len = *pos++;
31661d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt		if (pos + realm_len > end) {
31761d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt			wpa_hexdump(MSG_DEBUG,
31861d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt				    "Truncated NAI Home Realm Query",
31961d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt				    home_realm, home_realm_len);
32061d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt			return -1;
32161d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt		}
32261d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt		realm_name = pos;
32361d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt		for (j = 0; j < hapd->conf->nai_realm_count &&
32461d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt			     num_matching < 10; j++) {
32561d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt			const u8 *rpos, *rend;
32661d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt			realm = &hapd->conf->nai_realm_data[j];
32761d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt			if (encoding != realm->encoding)
32861d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt				continue;
32961d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt
33061d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt			rpos = realm_name;
33161d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt			while (rpos < realm_name + realm_len &&
33261d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt			       num_matching < 10) {
33361d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt				for (rend = rpos;
33461d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt				     rend < realm_name + realm_len; rend++) {
33561d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt					if (*rend == ';')
33661d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt						break;
33761d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt				}
33861d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt				for (k = 0; k < MAX_NAI_REALMS &&
33961d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt					     realm->realm[k] &&
34061d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt					     num_matching < 10; k++) {
34161d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt					if ((int) os_strlen(realm->realm[k]) !=
34261d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt					    rend - rpos ||
34361d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt					    os_strncmp((char *) rpos,
34461d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt						       realm->realm[k],
34561d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt						       rend - rpos) != 0)
34661d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt						continue;
34761d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt					matches[num_matching].realm_data_idx =
34861d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt						j;
34961d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt					matches[num_matching].realm_idx = k;
35061d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt					num_matching++;
35161d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt				}
35261d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt				rpos = rend + 1;
35361d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt			}
35461d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt		}
35561d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt		pos += realm_len;
35661d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	}
35761d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt
35861d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	realm_list_len = gas_anqp_add_element(buf, ANQP_NAI_REALM);
35961d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	wpabuf_put_le16(buf, num_matching);
36061d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt
36161d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	/*
36261d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	 * There are two ways to format. 1. each realm in a NAI Realm Data unit
36361d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	 * 2. all realms that share the same EAP methods in a NAI Realm Data
36461d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	 * unit. The first format is likely to be bigger in size than the
36561d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	 * second, but may be easier to parse and process by the receiver.
36661d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	 */
36761d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	for (i = 0; i < num_matching; i++) {
36861d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt		wpa_printf(MSG_DEBUG, "realm_idx %d, realm_data_idx %d",
36961d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt			   matches[i].realm_data_idx, matches[i].realm_idx);
37061d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt		realm = &hapd->conf->nai_realm_data[matches[i].realm_data_idx];
37161d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt		anqp_add_nai_realm_data(buf, realm, matches[i].realm_idx);
37261d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	}
37361d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	gas_anqp_set_element_len(buf, realm_list_len);
37461d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	return 0;
37561d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt}
37661d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt
37761d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt
37861d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidtstatic void anqp_add_nai_realm(struct hostapd_data *hapd, struct wpabuf *buf,
37961d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt			       const u8 *home_realm, size_t home_realm_len,
38061d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt			       int nai_realm, int nai_home_realm)
38161d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt{
38261d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	if (nai_realm && hapd->conf->nai_realm_data) {
38361d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt		u8 *len;
38461d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt		unsigned int i, j;
38561d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt		len = gas_anqp_add_element(buf, ANQP_NAI_REALM);
38661d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt		wpabuf_put_le16(buf, hapd->conf->nai_realm_count);
38761d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt		for (i = 0; i < hapd->conf->nai_realm_count; i++) {
38861d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt			u8 *realm_data_len, *realm_len;
38961d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt			struct hostapd_nai_realm_data *realm;
39061d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt
39161d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt			realm = &hapd->conf->nai_realm_data[i];
39261d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt			realm_data_len = wpabuf_put(buf, 2);
39361d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt			wpabuf_put_u8(buf, realm->encoding);
39461d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt			realm_len = wpabuf_put(buf, 1);
39561d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt			for (j = 0; realm->realm[j]; j++) {
39661d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt				if (j > 0)
39761d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt					wpabuf_put_u8(buf, ';');
39861d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt				wpabuf_put_str(buf, realm->realm[j]);
39961d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt			}
40061d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt			*realm_len = (u8 *) wpabuf_put(buf, 0) - realm_len - 1;
40161d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt			anqp_add_nai_realm_eap(buf, realm);
40261d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt			gas_anqp_set_element_len(buf, realm_data_len);
40361d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt		}
40461d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt		gas_anqp_set_element_len(buf, len);
40561d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	} else if (nai_home_realm && hapd->conf->nai_realm_data) {
40661d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt		hs20_add_nai_home_realm_matches(hapd, buf, home_realm,
40761d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt						home_realm_len);
40861d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	}
40961d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt}
41061d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt
41161d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt
41261d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidtstatic void anqp_add_3gpp_cellular_network(struct hostapd_data *hapd,
41361d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt					   struct wpabuf *buf)
41461d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt{
41561d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	if (hapd->conf->anqp_3gpp_cell_net) {
41661d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt		wpabuf_put_le16(buf, ANQP_3GPP_CELLULAR_NETWORK);
41761d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt		wpabuf_put_le16(buf,
41861d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt				hapd->conf->anqp_3gpp_cell_net_len);
41961d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt		wpabuf_put_data(buf, hapd->conf->anqp_3gpp_cell_net,
42061d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt				hapd->conf->anqp_3gpp_cell_net_len);
42161d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	}
42261d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt}
42361d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt
42461d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt
42561d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidtstatic void anqp_add_domain_name(struct hostapd_data *hapd, struct wpabuf *buf)
42661d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt{
42761d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	if (hapd->conf->domain_name) {
42861d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt		wpabuf_put_le16(buf, ANQP_DOMAIN_NAME);
42961d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt		wpabuf_put_le16(buf, hapd->conf->domain_name_len);
43061d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt		wpabuf_put_data(buf, hapd->conf->domain_name,
43161d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt				hapd->conf->domain_name_len);
43261d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	}
43361d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt}
43461d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt
43561d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt
436aa532510a7b8c4da2d7d6e2c11dda5db840894e4Dmitry Shmidt#ifdef CONFIG_HS20
437aa532510a7b8c4da2d7d6e2c11dda5db840894e4Dmitry Shmidt
43861d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidtstatic void anqp_add_operator_friendly_name(struct hostapd_data *hapd,
43961d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt					    struct wpabuf *buf)
44061d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt{
44161d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	if (hapd->conf->hs20_oper_friendly_name) {
44261d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt		u8 *len;
44361d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt		unsigned int i;
44461d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt		len = gas_anqp_add_element(buf, ANQP_VENDOR_SPECIFIC);
44561d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt		wpabuf_put_be24(buf, OUI_WFA);
44661d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt		wpabuf_put_u8(buf, HS20_ANQP_OUI_TYPE);
44761d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt		wpabuf_put_u8(buf, HS20_STYPE_OPERATOR_FRIENDLY_NAME);
44861d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt		wpabuf_put_u8(buf, 0); /* Reserved */
44961d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt		for (i = 0; i < hapd->conf->hs20_oper_friendly_name_count; i++)
45061d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt		{
45161d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt			struct hostapd_lang_string *vn;
45261d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt			vn = &hapd->conf->hs20_oper_friendly_name[i];
45361d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt			wpabuf_put_u8(buf, 3 + vn->name_len);
45461d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt			wpabuf_put_data(buf, vn->lang, 3);
45561d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt			wpabuf_put_data(buf, vn->name, vn->name_len);
45661d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt		}
45761d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt		gas_anqp_set_element_len(buf, len);
45861d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	}
45961d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt}
46061d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt
46161d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt
46261d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidtstatic void anqp_add_wan_metrics(struct hostapd_data *hapd,
46361d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt				 struct wpabuf *buf)
46461d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt{
46561d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	if (hapd->conf->hs20_wan_metrics) {
46661d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt		u8 *len = gas_anqp_add_element(buf, ANQP_VENDOR_SPECIFIC);
46761d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt		wpabuf_put_be24(buf, OUI_WFA);
46861d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt		wpabuf_put_u8(buf, HS20_ANQP_OUI_TYPE);
46961d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt		wpabuf_put_u8(buf, HS20_STYPE_WAN_METRICS);
47061d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt		wpabuf_put_u8(buf, 0); /* Reserved */
47161d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt		wpabuf_put_data(buf, hapd->conf->hs20_wan_metrics, 13);
47261d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt		gas_anqp_set_element_len(buf, len);
47361d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	}
47461d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt}
47561d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt
47661d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt
47761d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidtstatic void anqp_add_connection_capability(struct hostapd_data *hapd,
47861d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt					   struct wpabuf *buf)
47961d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt{
48061d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	if (hapd->conf->hs20_connection_capability) {
48161d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt		u8 *len = gas_anqp_add_element(buf, ANQP_VENDOR_SPECIFIC);
48261d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt		wpabuf_put_be24(buf, OUI_WFA);
48361d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt		wpabuf_put_u8(buf, HS20_ANQP_OUI_TYPE);
48461d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt		wpabuf_put_u8(buf, HS20_STYPE_CONNECTION_CAPABILITY);
48561d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt		wpabuf_put_u8(buf, 0); /* Reserved */
48661d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt		wpabuf_put_data(buf, hapd->conf->hs20_connection_capability,
48761d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt				hapd->conf->hs20_connection_capability_len);
48861d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt		gas_anqp_set_element_len(buf, len);
48961d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	}
49061d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt}
49161d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt
49261d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt
49361d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidtstatic void anqp_add_operating_class(struct hostapd_data *hapd,
49461d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt				     struct wpabuf *buf)
49561d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt{
49661d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	if (hapd->conf->hs20_operating_class) {
49761d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt		u8 *len = gas_anqp_add_element(buf, ANQP_VENDOR_SPECIFIC);
49861d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt		wpabuf_put_be24(buf, OUI_WFA);
49961d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt		wpabuf_put_u8(buf, HS20_ANQP_OUI_TYPE);
50061d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt		wpabuf_put_u8(buf, HS20_STYPE_OPERATING_CLASS);
50161d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt		wpabuf_put_u8(buf, 0); /* Reserved */
50261d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt		wpabuf_put_data(buf, hapd->conf->hs20_operating_class,
50361d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt				hapd->conf->hs20_operating_class_len);
50461d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt		gas_anqp_set_element_len(buf, len);
50561d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	}
50661d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt}
50761d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt
508aa532510a7b8c4da2d7d6e2c11dda5db840894e4Dmitry Shmidt#endif /* CONFIG_HS20 */
509aa532510a7b8c4da2d7d6e2c11dda5db840894e4Dmitry Shmidt
51061d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt
51104949598a23f501be6eec21697465fd46a28840aDmitry Shmidtstatic struct wpabuf *
51204949598a23f501be6eec21697465fd46a28840aDmitry Shmidtgas_serv_build_gas_resp_payload(struct hostapd_data *hapd,
51304949598a23f501be6eec21697465fd46a28840aDmitry Shmidt				unsigned int request,
51461d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt				struct gas_dialog_info *di,
51561d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt				const u8 *home_realm, size_t home_realm_len)
51604949598a23f501be6eec21697465fd46a28840aDmitry Shmidt{
51704949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	struct wpabuf *buf;
51804949598a23f501be6eec21697465fd46a28840aDmitry Shmidt
51904949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	buf = wpabuf_alloc(1400);
52004949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	if (buf == NULL)
52104949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		return NULL;
52204949598a23f501be6eec21697465fd46a28840aDmitry Shmidt
52304949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	if (request & ANQP_REQ_CAPABILITY_LIST)
52404949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		anqp_add_capab_list(hapd, buf);
52504949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	if (request & ANQP_REQ_VENUE_NAME)
52604949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		anqp_add_venue_name(hapd, buf);
52761d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	if (request & ANQP_REQ_NETWORK_AUTH_TYPE)
52861d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt		anqp_add_network_auth_type(hapd, buf);
52904949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	if (request & ANQP_REQ_ROAMING_CONSORTIUM)
53004949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		anqp_add_roaming_consortium(hapd, buf);
53161d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	if (request & ANQP_REQ_IP_ADDR_TYPE_AVAILABILITY)
53261d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt		anqp_add_ip_addr_type_availability(hapd, buf);
53361d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	if (request & (ANQP_REQ_NAI_REALM | ANQP_REQ_NAI_HOME_REALM))
53461d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt		anqp_add_nai_realm(hapd, buf, home_realm, home_realm_len,
53561d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt				   request & ANQP_REQ_NAI_REALM,
53661d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt				   request & ANQP_REQ_NAI_HOME_REALM);
53761d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	if (request & ANQP_REQ_3GPP_CELLULAR_NETWORK)
53861d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt		anqp_add_3gpp_cellular_network(hapd, buf);
53961d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	if (request & ANQP_REQ_DOMAIN_NAME)
54061d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt		anqp_add_domain_name(hapd, buf);
54161d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt
542aa532510a7b8c4da2d7d6e2c11dda5db840894e4Dmitry Shmidt#ifdef CONFIG_HS20
54361d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	if (request & ANQP_REQ_HS_CAPABILITY_LIST)
54461d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt		anqp_add_hs_capab_list(hapd, buf);
54561d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	if (request & ANQP_REQ_OPERATOR_FRIENDLY_NAME)
54661d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt		anqp_add_operator_friendly_name(hapd, buf);
54761d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	if (request & ANQP_REQ_WAN_METRICS)
54861d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt		anqp_add_wan_metrics(hapd, buf);
54961d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	if (request & ANQP_REQ_CONNECTION_CAPABILITY)
55061d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt		anqp_add_connection_capability(hapd, buf);
55161d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	if (request & ANQP_REQ_OPERATING_CLASS)
55261d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt		anqp_add_operating_class(hapd, buf);
553aa532510a7b8c4da2d7d6e2c11dda5db840894e4Dmitry Shmidt#endif /* CONFIG_HS20 */
55404949598a23f501be6eec21697465fd46a28840aDmitry Shmidt
55504949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	return buf;
55604949598a23f501be6eec21697465fd46a28840aDmitry Shmidt}
55704949598a23f501be6eec21697465fd46a28840aDmitry Shmidt
55804949598a23f501be6eec21697465fd46a28840aDmitry Shmidt
55904949598a23f501be6eec21697465fd46a28840aDmitry Shmidtstatic void gas_serv_clear_cached_ies(void *eloop_data, void *user_ctx)
56004949598a23f501be6eec21697465fd46a28840aDmitry Shmidt{
56104949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	struct gas_dialog_info *dia = eloop_data;
56204949598a23f501be6eec21697465fd46a28840aDmitry Shmidt
56304949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	wpa_printf(MSG_DEBUG, "GAS: Timeout triggered, clearing dialog for "
56404949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		   "dialog token %d", dia->dialog_token);
56504949598a23f501be6eec21697465fd46a28840aDmitry Shmidt
56604949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	gas_serv_dialog_clear(dia);
56704949598a23f501be6eec21697465fd46a28840aDmitry Shmidt}
56804949598a23f501be6eec21697465fd46a28840aDmitry Shmidt
56904949598a23f501be6eec21697465fd46a28840aDmitry Shmidt
57004949598a23f501be6eec21697465fd46a28840aDmitry Shmidtstruct anqp_query_info {
57104949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	unsigned int request;
57204949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	unsigned int remote_request;
57361d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	const u8 *home_realm_query;
57461d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	size_t home_realm_query_len;
57504949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	u16 remote_delay;
57604949598a23f501be6eec21697465fd46a28840aDmitry Shmidt};
57704949598a23f501be6eec21697465fd46a28840aDmitry Shmidt
57804949598a23f501be6eec21697465fd46a28840aDmitry Shmidt
57904949598a23f501be6eec21697465fd46a28840aDmitry Shmidtstatic void set_anqp_req(unsigned int bit, const char *name, int local,
58004949598a23f501be6eec21697465fd46a28840aDmitry Shmidt			 unsigned int remote, u16 remote_delay,
58104949598a23f501be6eec21697465fd46a28840aDmitry Shmidt			 struct anqp_query_info *qi)
58204949598a23f501be6eec21697465fd46a28840aDmitry Shmidt{
58304949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	qi->request |= bit;
58404949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	if (local) {
58504949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		wpa_printf(MSG_DEBUG, "ANQP: %s (local)", name);
58604949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	} else if (bit & remote) {
58704949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		wpa_printf(MSG_DEBUG, "ANQP: %s (remote)", name);
58804949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		qi->remote_request |= bit;
58904949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		if (remote_delay > qi->remote_delay)
59004949598a23f501be6eec21697465fd46a28840aDmitry Shmidt			qi->remote_delay = remote_delay;
59104949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	} else {
59204949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		wpa_printf(MSG_DEBUG, "ANQP: %s not available", name);
59304949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	}
59404949598a23f501be6eec21697465fd46a28840aDmitry Shmidt}
59504949598a23f501be6eec21697465fd46a28840aDmitry Shmidt
59604949598a23f501be6eec21697465fd46a28840aDmitry Shmidt
59704949598a23f501be6eec21697465fd46a28840aDmitry Shmidtstatic void rx_anqp_query_list_id(struct hostapd_data *hapd, u16 info_id,
59804949598a23f501be6eec21697465fd46a28840aDmitry Shmidt				  struct anqp_query_info *qi)
59904949598a23f501be6eec21697465fd46a28840aDmitry Shmidt{
60004949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	switch (info_id) {
60104949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	case ANQP_CAPABILITY_LIST:
60204949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		set_anqp_req(ANQP_REQ_CAPABILITY_LIST, "Capability List", 1, 0,
60304949598a23f501be6eec21697465fd46a28840aDmitry Shmidt			     0, qi);
60404949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		break;
60504949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	case ANQP_VENUE_NAME:
60604949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		set_anqp_req(ANQP_REQ_VENUE_NAME, "Venue Name",
60704949598a23f501be6eec21697465fd46a28840aDmitry Shmidt			     hapd->conf->venue_name != NULL, 0, 0, qi);
60804949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		break;
60961d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	case ANQP_NETWORK_AUTH_TYPE:
61061d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt		set_anqp_req(ANQP_REQ_NETWORK_AUTH_TYPE, "Network Auth Type",
61161d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt			     hapd->conf->network_auth_type != NULL,
61261d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt			     0, 0, qi);
61361d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt		break;
61404949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	case ANQP_ROAMING_CONSORTIUM:
61504949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		set_anqp_req(ANQP_REQ_ROAMING_CONSORTIUM, "Roaming Consortium",
61604949598a23f501be6eec21697465fd46a28840aDmitry Shmidt			     hapd->conf->roaming_consortium != NULL, 0, 0, qi);
61704949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		break;
61861d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	case ANQP_IP_ADDR_TYPE_AVAILABILITY:
61961d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt		set_anqp_req(ANQP_REQ_IP_ADDR_TYPE_AVAILABILITY,
62061d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt			     "IP Addr Type Availability",
62161d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt			     hapd->conf->ipaddr_type_configured,
62261d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt			     0, 0, qi);
62361d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt		break;
62461d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	case ANQP_NAI_REALM:
62561d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt		set_anqp_req(ANQP_REQ_NAI_REALM, "NAI Realm",
62661d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt			     hapd->conf->nai_realm_data != NULL,
62761d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt			     0, 0, qi);
62861d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt		break;
62961d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	case ANQP_3GPP_CELLULAR_NETWORK:
63061d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt		set_anqp_req(ANQP_REQ_3GPP_CELLULAR_NETWORK,
63161d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt			     "3GPP Cellular Network",
63261d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt			     hapd->conf->anqp_3gpp_cell_net != NULL,
63361d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt			     0, 0, qi);
63461d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt		break;
63561d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	case ANQP_DOMAIN_NAME:
63661d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt		set_anqp_req(ANQP_REQ_DOMAIN_NAME, "Domain Name",
63761d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt			     hapd->conf->domain_name != NULL,
63861d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt			     0, 0, qi);
63961d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt		break;
64004949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	default:
64104949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		wpa_printf(MSG_DEBUG, "ANQP: Unsupported Info Id %u",
64204949598a23f501be6eec21697465fd46a28840aDmitry Shmidt			   info_id);
64304949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		break;
64404949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	}
64504949598a23f501be6eec21697465fd46a28840aDmitry Shmidt}
64604949598a23f501be6eec21697465fd46a28840aDmitry Shmidt
64704949598a23f501be6eec21697465fd46a28840aDmitry Shmidt
64804949598a23f501be6eec21697465fd46a28840aDmitry Shmidtstatic void rx_anqp_query_list(struct hostapd_data *hapd,
64904949598a23f501be6eec21697465fd46a28840aDmitry Shmidt			       const u8 *pos, const u8 *end,
65004949598a23f501be6eec21697465fd46a28840aDmitry Shmidt			       struct anqp_query_info *qi)
65104949598a23f501be6eec21697465fd46a28840aDmitry Shmidt{
65204949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	wpa_printf(MSG_DEBUG, "ANQP: %u Info IDs requested in Query list",
65304949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		   (unsigned int) (end - pos) / 2);
65404949598a23f501be6eec21697465fd46a28840aDmitry Shmidt
65504949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	while (pos + 2 <= end) {
65604949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		rx_anqp_query_list_id(hapd, WPA_GET_LE16(pos), qi);
65704949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		pos += 2;
65804949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	}
65904949598a23f501be6eec21697465fd46a28840aDmitry Shmidt}
66004949598a23f501be6eec21697465fd46a28840aDmitry Shmidt
66104949598a23f501be6eec21697465fd46a28840aDmitry Shmidt
662aa532510a7b8c4da2d7d6e2c11dda5db840894e4Dmitry Shmidt#ifdef CONFIG_HS20
663aa532510a7b8c4da2d7d6e2c11dda5db840894e4Dmitry Shmidt
66461d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidtstatic void rx_anqp_hs_query_list(struct hostapd_data *hapd, u8 subtype,
66561d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt				  struct anqp_query_info *qi)
66661d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt{
66761d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	switch (subtype) {
66861d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	case HS20_STYPE_CAPABILITY_LIST:
66961d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt		set_anqp_req(ANQP_REQ_HS_CAPABILITY_LIST, "HS Capability List",
67061d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt			     1, 0, 0, qi);
67161d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt		break;
67261d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	case HS20_STYPE_OPERATOR_FRIENDLY_NAME:
67361d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt		set_anqp_req(ANQP_REQ_OPERATOR_FRIENDLY_NAME,
67461d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt			     "Operator Friendly Name",
67561d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt			     hapd->conf->hs20_oper_friendly_name != NULL,
67661d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt			     0, 0, qi);
67761d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt		break;
67861d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	case HS20_STYPE_WAN_METRICS:
67961d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt		set_anqp_req(ANQP_REQ_WAN_METRICS, "WAN Metrics",
68061d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt			     hapd->conf->hs20_wan_metrics != NULL,
68161d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt			     0, 0, qi);
68261d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt		break;
68361d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	case HS20_STYPE_CONNECTION_CAPABILITY:
68461d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt		set_anqp_req(ANQP_REQ_CONNECTION_CAPABILITY,
68561d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt			     "Connection Capability",
68661d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt			     hapd->conf->hs20_connection_capability != NULL,
68761d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt			     0, 0, qi);
68861d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt		break;
68961d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	case HS20_STYPE_OPERATING_CLASS:
69061d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt		set_anqp_req(ANQP_REQ_OPERATING_CLASS, "Operating Class",
69161d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt			     hapd->conf->hs20_operating_class != NULL,
69261d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt			     0, 0, qi);
69361d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt		break;
69461d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	default:
69561d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt		wpa_printf(MSG_DEBUG, "ANQP: Unsupported HS 2.0 subtype %u",
69661d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt			   subtype);
69761d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt		break;
69861d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	}
69961d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt}
70061d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt
70161d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt
70261d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidtstatic void rx_anqp_hs_nai_home_realm(struct hostapd_data *hapd,
70361d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt				      const u8 *pos, const u8 *end,
70461d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt				      struct anqp_query_info *qi)
70561d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt{
70661d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	qi->request |= ANQP_REQ_NAI_HOME_REALM;
70761d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	qi->home_realm_query = pos;
70861d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	qi->home_realm_query_len = end - pos;
70961d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	if (hapd->conf->nai_realm_data != NULL) {
71061d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt		wpa_printf(MSG_DEBUG, "ANQP: HS 2.0 NAI Home Realm Query "
71161d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt			   "(local)");
71261d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	} else {
71361d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt		wpa_printf(MSG_DEBUG, "ANQP: HS 2.0 NAI Home Realm Query not "
71461d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt			   "available");
71561d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	}
71661d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt}
71761d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt
71861d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt
71961d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidtstatic void rx_anqp_vendor_specific(struct hostapd_data *hapd,
72061d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt				    const u8 *pos, const u8 *end,
72161d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt				    struct anqp_query_info *qi)
72261d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt{
72361d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	u32 oui;
72461d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	u8 subtype;
72561d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt
72661d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	if (pos + 4 > end) {
72761d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt		wpa_printf(MSG_DEBUG, "ANQP: Too short vendor specific ANQP "
72861d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt			   "Query element");
72961d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt		return;
73061d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	}
73161d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt
73261d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	oui = WPA_GET_BE24(pos);
73361d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	pos += 3;
73461d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	if (oui != OUI_WFA) {
73561d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt		wpa_printf(MSG_DEBUG, "ANQP: Unsupported vendor OUI %06x",
73661d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt			   oui);
73761d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt		return;
73861d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	}
73961d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt
74061d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	if (*pos != HS20_ANQP_OUI_TYPE) {
74161d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt		wpa_printf(MSG_DEBUG, "ANQP: Unsupported WFA vendor type %u",
74261d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt			   *pos);
74361d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt		return;
74461d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	}
74561d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	pos++;
74661d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt
74761d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	if (pos + 1 >= end)
74861d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt		return;
74961d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt
75061d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	subtype = *pos++;
75161d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	pos++; /* Reserved */
75261d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	switch (subtype) {
75361d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	case HS20_STYPE_QUERY_LIST:
75461d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt		wpa_printf(MSG_DEBUG, "ANQP: HS 2.0 Query List");
75561d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt		while (pos < end) {
75661d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt			rx_anqp_hs_query_list(hapd, *pos, qi);
75761d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt			pos++;
75861d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt		}
75961d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt		break;
76061d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	case HS20_STYPE_NAI_HOME_REALM_QUERY:
76161d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt		rx_anqp_hs_nai_home_realm(hapd, pos, end, qi);
76261d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt		break;
76361d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	default:
76461d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt		wpa_printf(MSG_DEBUG, "ANQP: Unsupported HS 2.0 query subtype "
76561d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt			   "%u", subtype);
76661d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt		break;
76761d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	}
76861d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt}
76961d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt
770aa532510a7b8c4da2d7d6e2c11dda5db840894e4Dmitry Shmidt#endif /* CONFIG_HS20 */
771aa532510a7b8c4da2d7d6e2c11dda5db840894e4Dmitry Shmidt
77261d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt
77304949598a23f501be6eec21697465fd46a28840aDmitry Shmidtstatic void gas_serv_req_local_processing(struct hostapd_data *hapd,
77404949598a23f501be6eec21697465fd46a28840aDmitry Shmidt					  const u8 *sa, u8 dialog_token,
77504949598a23f501be6eec21697465fd46a28840aDmitry Shmidt					  struct anqp_query_info *qi)
77604949598a23f501be6eec21697465fd46a28840aDmitry Shmidt{
77704949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	struct wpabuf *buf, *tx_buf;
77804949598a23f501be6eec21697465fd46a28840aDmitry Shmidt
77961d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	buf = gas_serv_build_gas_resp_payload(hapd, qi->request, NULL,
78061d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt					      qi->home_realm_query,
78161d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt					      qi->home_realm_query_len);
78204949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	wpa_hexdump_buf(MSG_MSGDUMP, "ANQP: Locally generated ANQP responses",
78304949598a23f501be6eec21697465fd46a28840aDmitry Shmidt			buf);
78404949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	if (!buf)
78504949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		return;
78604949598a23f501be6eec21697465fd46a28840aDmitry Shmidt
78704949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	if (wpabuf_len(buf) > hapd->gas_frag_limit ||
78804949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	    hapd->conf->gas_comeback_delay) {
78904949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		struct gas_dialog_info *di;
79004949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		u16 comeback_delay = 1;
79104949598a23f501be6eec21697465fd46a28840aDmitry Shmidt
79204949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		if (hapd->conf->gas_comeback_delay) {
79304949598a23f501be6eec21697465fd46a28840aDmitry Shmidt			/* Testing - allow overriding of the delay value */
79404949598a23f501be6eec21697465fd46a28840aDmitry Shmidt			comeback_delay = hapd->conf->gas_comeback_delay;
79504949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		}
79604949598a23f501be6eec21697465fd46a28840aDmitry Shmidt
79704949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		wpa_printf(MSG_DEBUG, "ANQP: Too long response to fit in "
79804949598a23f501be6eec21697465fd46a28840aDmitry Shmidt			   "initial response - use GAS comeback");
79904949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		di = gas_dialog_create(hapd, sa, dialog_token);
80004949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		if (!di) {
80104949598a23f501be6eec21697465fd46a28840aDmitry Shmidt			wpa_printf(MSG_INFO, "ANQP: Could not create dialog "
80204949598a23f501be6eec21697465fd46a28840aDmitry Shmidt				   "for " MACSTR " (dialog token %u)",
80304949598a23f501be6eec21697465fd46a28840aDmitry Shmidt				   MAC2STR(sa), dialog_token);
80404949598a23f501be6eec21697465fd46a28840aDmitry Shmidt			wpabuf_free(buf);
80504949598a23f501be6eec21697465fd46a28840aDmitry Shmidt			return;
80604949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		}
80704949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		di->sd_resp = buf;
80804949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		di->sd_resp_pos = 0;
80904949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		tx_buf = gas_anqp_build_initial_resp_buf(
81004949598a23f501be6eec21697465fd46a28840aDmitry Shmidt			dialog_token, WLAN_STATUS_SUCCESS, comeback_delay,
81104949598a23f501be6eec21697465fd46a28840aDmitry Shmidt			NULL);
81204949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	} else {
81304949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		wpa_printf(MSG_DEBUG, "ANQP: Initial response (no comeback)");
81404949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		tx_buf = gas_anqp_build_initial_resp_buf(
81504949598a23f501be6eec21697465fd46a28840aDmitry Shmidt			dialog_token, WLAN_STATUS_SUCCESS, 0, buf);
81604949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		wpabuf_free(buf);
81704949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	}
81804949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	if (!tx_buf)
81904949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		return;
82004949598a23f501be6eec21697465fd46a28840aDmitry Shmidt
82104949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	hostapd_drv_send_action(hapd, hapd->iface->freq, 0, sa,
82204949598a23f501be6eec21697465fd46a28840aDmitry Shmidt				wpabuf_head(tx_buf), wpabuf_len(tx_buf));
82304949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	wpabuf_free(tx_buf);
82404949598a23f501be6eec21697465fd46a28840aDmitry Shmidt}
82504949598a23f501be6eec21697465fd46a28840aDmitry Shmidt
82604949598a23f501be6eec21697465fd46a28840aDmitry Shmidt
82704949598a23f501be6eec21697465fd46a28840aDmitry Shmidtstatic void gas_serv_rx_gas_initial_req(struct hostapd_data *hapd,
82804949598a23f501be6eec21697465fd46a28840aDmitry Shmidt					const u8 *sa,
82904949598a23f501be6eec21697465fd46a28840aDmitry Shmidt					const u8 *data, size_t len)
83004949598a23f501be6eec21697465fd46a28840aDmitry Shmidt{
83104949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	const u8 *pos = data;
83204949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	const u8 *end = data + len;
83304949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	const u8 *next;
83404949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	u8 dialog_token;
83504949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	u16 slen;
83604949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	struct anqp_query_info qi;
83704949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	const u8 *adv_proto;
83804949598a23f501be6eec21697465fd46a28840aDmitry Shmidt
83904949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	if (len < 1 + 2)
84004949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		return;
84104949598a23f501be6eec21697465fd46a28840aDmitry Shmidt
84204949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	os_memset(&qi, 0, sizeof(qi));
84304949598a23f501be6eec21697465fd46a28840aDmitry Shmidt
84404949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	dialog_token = *pos++;
84504949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	wpa_msg(hapd->msg_ctx, MSG_DEBUG,
84604949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		"GAS: GAS Initial Request from " MACSTR " (dialog token %u) ",
84704949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		MAC2STR(sa), dialog_token);
84804949598a23f501be6eec21697465fd46a28840aDmitry Shmidt
84904949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	if (*pos != WLAN_EID_ADV_PROTO) {
85004949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		wpa_msg(hapd->msg_ctx, MSG_DEBUG,
85104949598a23f501be6eec21697465fd46a28840aDmitry Shmidt			"GAS: Unexpected IE in GAS Initial Request: %u", *pos);
85204949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		return;
85304949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	}
85404949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	adv_proto = pos++;
85504949598a23f501be6eec21697465fd46a28840aDmitry Shmidt
85604949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	slen = *pos++;
85704949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	next = pos + slen;
85804949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	if (next > end || slen < 2) {
85904949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		wpa_msg(hapd->msg_ctx, MSG_DEBUG,
86004949598a23f501be6eec21697465fd46a28840aDmitry Shmidt			"GAS: Invalid IE in GAS Initial Request");
86104949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		return;
86204949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	}
86304949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	pos++; /* skip QueryRespLenLimit and PAME-BI */
86404949598a23f501be6eec21697465fd46a28840aDmitry Shmidt
86504949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	if (*pos != ACCESS_NETWORK_QUERY_PROTOCOL) {
86604949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		struct wpabuf *buf;
86704949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		wpa_msg(hapd->msg_ctx, MSG_DEBUG,
86804949598a23f501be6eec21697465fd46a28840aDmitry Shmidt			"GAS: Unsupported GAS advertisement protocol id %u",
86904949598a23f501be6eec21697465fd46a28840aDmitry Shmidt			*pos);
87004949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		if (sa[0] & 0x01)
87104949598a23f501be6eec21697465fd46a28840aDmitry Shmidt			return; /* Invalid source address - drop silently */
87204949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		buf = gas_build_initial_resp(
87304949598a23f501be6eec21697465fd46a28840aDmitry Shmidt			dialog_token, WLAN_STATUS_GAS_ADV_PROTO_NOT_SUPPORTED,
87404949598a23f501be6eec21697465fd46a28840aDmitry Shmidt			0, 2 + slen + 2);
87504949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		if (buf == NULL)
87604949598a23f501be6eec21697465fd46a28840aDmitry Shmidt			return;
87704949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		wpabuf_put_data(buf, adv_proto, 2 + slen);
87804949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		wpabuf_put_le16(buf, 0); /* Query Response Length */
87904949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		hostapd_drv_send_action(hapd, hapd->iface->freq, 0, sa,
88004949598a23f501be6eec21697465fd46a28840aDmitry Shmidt					wpabuf_head(buf), wpabuf_len(buf));
88104949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		wpabuf_free(buf);
88204949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		return;
88304949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	}
88404949598a23f501be6eec21697465fd46a28840aDmitry Shmidt
88504949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	pos = next;
88604949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	/* Query Request */
88704949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	if (pos + 2 > end)
88804949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		return;
88904949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	slen = WPA_GET_LE16(pos);
89004949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	pos += 2;
89104949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	if (pos + slen > end)
89204949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		return;
89304949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	end = pos + slen;
89404949598a23f501be6eec21697465fd46a28840aDmitry Shmidt
89504949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	/* ANQP Query Request */
89604949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	while (pos < end) {
89704949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		u16 info_id, elen;
89804949598a23f501be6eec21697465fd46a28840aDmitry Shmidt
89904949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		if (pos + 4 > end)
90004949598a23f501be6eec21697465fd46a28840aDmitry Shmidt			return;
90104949598a23f501be6eec21697465fd46a28840aDmitry Shmidt
90204949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		info_id = WPA_GET_LE16(pos);
90304949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		pos += 2;
90404949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		elen = WPA_GET_LE16(pos);
90504949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		pos += 2;
90604949598a23f501be6eec21697465fd46a28840aDmitry Shmidt
90704949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		if (pos + elen > end) {
90804949598a23f501be6eec21697465fd46a28840aDmitry Shmidt			wpa_printf(MSG_DEBUG, "ANQP: Invalid Query Request");
90904949598a23f501be6eec21697465fd46a28840aDmitry Shmidt			return;
91004949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		}
91104949598a23f501be6eec21697465fd46a28840aDmitry Shmidt
91204949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		switch (info_id) {
91304949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		case ANQP_QUERY_LIST:
91404949598a23f501be6eec21697465fd46a28840aDmitry Shmidt			rx_anqp_query_list(hapd, pos, pos + elen, &qi);
91504949598a23f501be6eec21697465fd46a28840aDmitry Shmidt			break;
916aa532510a7b8c4da2d7d6e2c11dda5db840894e4Dmitry Shmidt#ifdef CONFIG_HS20
91761d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt		case ANQP_VENDOR_SPECIFIC:
91861d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt			rx_anqp_vendor_specific(hapd, pos, pos + elen, &qi);
91961d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt			break;
920aa532510a7b8c4da2d7d6e2c11dda5db840894e4Dmitry Shmidt#endif /* CONFIG_HS20 */
92104949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		default:
92204949598a23f501be6eec21697465fd46a28840aDmitry Shmidt			wpa_printf(MSG_DEBUG, "ANQP: Unsupported Query "
92304949598a23f501be6eec21697465fd46a28840aDmitry Shmidt				   "Request element %u", info_id);
92404949598a23f501be6eec21697465fd46a28840aDmitry Shmidt			break;
92504949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		}
92604949598a23f501be6eec21697465fd46a28840aDmitry Shmidt
92704949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		pos += elen;
92804949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	}
92904949598a23f501be6eec21697465fd46a28840aDmitry Shmidt
93004949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	gas_serv_req_local_processing(hapd, sa, dialog_token, &qi);
93104949598a23f501be6eec21697465fd46a28840aDmitry Shmidt}
93204949598a23f501be6eec21697465fd46a28840aDmitry Shmidt
93304949598a23f501be6eec21697465fd46a28840aDmitry Shmidt
93404949598a23f501be6eec21697465fd46a28840aDmitry Shmidtvoid gas_serv_tx_gas_response(struct hostapd_data *hapd, const u8 *dst,
93504949598a23f501be6eec21697465fd46a28840aDmitry Shmidt			      struct gas_dialog_info *dialog)
93604949598a23f501be6eec21697465fd46a28840aDmitry Shmidt{
93704949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	struct wpabuf *buf, *tx_buf;
93804949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	u8 dialog_token = dialog->dialog_token;
93904949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	size_t frag_len;
94004949598a23f501be6eec21697465fd46a28840aDmitry Shmidt
94104949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	if (dialog->sd_resp == NULL) {
94204949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		buf = gas_serv_build_gas_resp_payload(hapd,
94304949598a23f501be6eec21697465fd46a28840aDmitry Shmidt						      dialog->all_requested,
94461d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt						      dialog, NULL, 0);
94504949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		wpa_hexdump_buf(MSG_MSGDUMP, "ANQP: Generated ANQP responses",
94604949598a23f501be6eec21697465fd46a28840aDmitry Shmidt			buf);
94704949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		if (!buf)
94804949598a23f501be6eec21697465fd46a28840aDmitry Shmidt			goto tx_gas_response_done;
94904949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		dialog->sd_resp = buf;
95004949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		dialog->sd_resp_pos = 0;
95104949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	}
95204949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	frag_len = wpabuf_len(dialog->sd_resp) - dialog->sd_resp_pos;
95304949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	if (frag_len > hapd->gas_frag_limit || dialog->comeback_delay ||
95404949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	    hapd->conf->gas_comeback_delay) {
95504949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		u16 comeback_delay_tus = dialog->comeback_delay +
95604949598a23f501be6eec21697465fd46a28840aDmitry Shmidt			GAS_SERV_COMEBACK_DELAY_FUDGE;
95704949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		u32 comeback_delay_secs, comeback_delay_usecs;
95804949598a23f501be6eec21697465fd46a28840aDmitry Shmidt
95904949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		if (hapd->conf->gas_comeback_delay) {
96004949598a23f501be6eec21697465fd46a28840aDmitry Shmidt			/* Testing - allow overriding of the delay value */
96104949598a23f501be6eec21697465fd46a28840aDmitry Shmidt			comeback_delay_tus = hapd->conf->gas_comeback_delay;
96204949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		}
96304949598a23f501be6eec21697465fd46a28840aDmitry Shmidt
96404949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		wpa_printf(MSG_DEBUG, "GAS: Response frag_len %u (frag limit "
96504949598a23f501be6eec21697465fd46a28840aDmitry Shmidt			   "%u) and comeback delay %u, "
96604949598a23f501be6eec21697465fd46a28840aDmitry Shmidt			   "requesting comebacks", (unsigned int) frag_len,
96704949598a23f501be6eec21697465fd46a28840aDmitry Shmidt			   (unsigned int) hapd->gas_frag_limit,
96804949598a23f501be6eec21697465fd46a28840aDmitry Shmidt			   dialog->comeback_delay);
96904949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		tx_buf = gas_anqp_build_initial_resp_buf(dialog_token,
97004949598a23f501be6eec21697465fd46a28840aDmitry Shmidt							 WLAN_STATUS_SUCCESS,
97104949598a23f501be6eec21697465fd46a28840aDmitry Shmidt							 comeback_delay_tus,
97204949598a23f501be6eec21697465fd46a28840aDmitry Shmidt							 NULL);
97304949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		if (tx_buf) {
97404949598a23f501be6eec21697465fd46a28840aDmitry Shmidt			wpa_msg(hapd->msg_ctx, MSG_DEBUG,
97504949598a23f501be6eec21697465fd46a28840aDmitry Shmidt				"GAS: Tx GAS Initial Resp (comeback = 10TU)");
97604949598a23f501be6eec21697465fd46a28840aDmitry Shmidt			hostapd_drv_send_action(hapd, hapd->iface->freq, 0,
97704949598a23f501be6eec21697465fd46a28840aDmitry Shmidt						dst,
97804949598a23f501be6eec21697465fd46a28840aDmitry Shmidt						wpabuf_head(tx_buf),
97904949598a23f501be6eec21697465fd46a28840aDmitry Shmidt						wpabuf_len(tx_buf));
98004949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		}
98104949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		wpabuf_free(tx_buf);
98204949598a23f501be6eec21697465fd46a28840aDmitry Shmidt
98304949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		/* start a timer of 1.5 * comeback-delay */
98404949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		comeback_delay_tus = comeback_delay_tus +
98504949598a23f501be6eec21697465fd46a28840aDmitry Shmidt			(comeback_delay_tus / 2);
98604949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		comeback_delay_secs = (comeback_delay_tus * 1024) / 1000000;
98704949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		comeback_delay_usecs = (comeback_delay_tus * 1024) -
98804949598a23f501be6eec21697465fd46a28840aDmitry Shmidt			(comeback_delay_secs * 1000000);
98904949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		eloop_register_timeout(comeback_delay_secs,
99004949598a23f501be6eec21697465fd46a28840aDmitry Shmidt				       comeback_delay_usecs,
99104949598a23f501be6eec21697465fd46a28840aDmitry Shmidt				       gas_serv_clear_cached_ies, dialog,
99204949598a23f501be6eec21697465fd46a28840aDmitry Shmidt				       NULL);
99304949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		goto tx_gas_response_done;
99404949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	}
99504949598a23f501be6eec21697465fd46a28840aDmitry Shmidt
99604949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	buf = wpabuf_alloc_copy(wpabuf_head_u8(dialog->sd_resp) +
99704949598a23f501be6eec21697465fd46a28840aDmitry Shmidt				dialog->sd_resp_pos, frag_len);
99804949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	if (buf == NULL) {
99904949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		wpa_msg(hapd->msg_ctx, MSG_DEBUG, "GAS: Buffer allocation "
100004949598a23f501be6eec21697465fd46a28840aDmitry Shmidt			"failed");
100104949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		goto tx_gas_response_done;
100204949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	}
100304949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	tx_buf = gas_anqp_build_initial_resp_buf(dialog_token,
100404949598a23f501be6eec21697465fd46a28840aDmitry Shmidt						 WLAN_STATUS_SUCCESS, 0, buf);
100504949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	wpabuf_free(buf);
100604949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	if (tx_buf == NULL)
100704949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		goto tx_gas_response_done;
100804949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	wpa_msg(hapd->msg_ctx, MSG_DEBUG, "GAS: Tx GAS Initial "
100904949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		"Response (frag_id %d frag_len %d)",
101004949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		dialog->sd_frag_id, (int) frag_len);
101104949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	dialog->sd_frag_id++;
101204949598a23f501be6eec21697465fd46a28840aDmitry Shmidt
101304949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	hostapd_drv_send_action(hapd, hapd->iface->freq, 0, dst,
101404949598a23f501be6eec21697465fd46a28840aDmitry Shmidt				wpabuf_head(tx_buf), wpabuf_len(tx_buf));
101504949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	wpabuf_free(tx_buf);
101604949598a23f501be6eec21697465fd46a28840aDmitry Shmidttx_gas_response_done:
101704949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	gas_serv_clear_cached_ies(dialog, NULL);
101804949598a23f501be6eec21697465fd46a28840aDmitry Shmidt}
101904949598a23f501be6eec21697465fd46a28840aDmitry Shmidt
102004949598a23f501be6eec21697465fd46a28840aDmitry Shmidt
102104949598a23f501be6eec21697465fd46a28840aDmitry Shmidtstatic void gas_serv_rx_gas_comeback_req(struct hostapd_data *hapd,
102204949598a23f501be6eec21697465fd46a28840aDmitry Shmidt					 const u8 *sa,
102304949598a23f501be6eec21697465fd46a28840aDmitry Shmidt					 const u8 *data, size_t len)
102404949598a23f501be6eec21697465fd46a28840aDmitry Shmidt{
102504949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	struct gas_dialog_info *dialog;
102604949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	struct wpabuf *buf, *tx_buf;
102704949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	u8 dialog_token;
102804949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	size_t frag_len;
102904949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	int more = 0;
103004949598a23f501be6eec21697465fd46a28840aDmitry Shmidt
103104949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	wpa_hexdump(MSG_DEBUG, "GAS: RX GAS Comeback Request", data, len);
103204949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	if (len < 1)
103304949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		return;
103404949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	dialog_token = *data;
103504949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	wpa_msg(hapd->msg_ctx, MSG_DEBUG, "GAS: Dialog Token: %u",
103604949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		dialog_token);
103704949598a23f501be6eec21697465fd46a28840aDmitry Shmidt
103804949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	dialog = gas_serv_dialog_find(hapd, sa, dialog_token);
103904949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	if (!dialog) {
104004949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		wpa_msg(hapd->msg_ctx, MSG_DEBUG, "GAS: No pending SD "
104104949598a23f501be6eec21697465fd46a28840aDmitry Shmidt			"response fragment for " MACSTR " dialog token %u",
104204949598a23f501be6eec21697465fd46a28840aDmitry Shmidt			MAC2STR(sa), dialog_token);
104304949598a23f501be6eec21697465fd46a28840aDmitry Shmidt
104404949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		if (sa[0] & 0x01)
104504949598a23f501be6eec21697465fd46a28840aDmitry Shmidt			return; /* Invalid source address - drop silently */
104604949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		tx_buf = gas_anqp_build_comeback_resp_buf(
104704949598a23f501be6eec21697465fd46a28840aDmitry Shmidt			dialog_token, WLAN_STATUS_NO_OUTSTANDING_GAS_REQ, 0, 0,
104804949598a23f501be6eec21697465fd46a28840aDmitry Shmidt			0, NULL);
104904949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		if (tx_buf == NULL)
105004949598a23f501be6eec21697465fd46a28840aDmitry Shmidt			return;
105104949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		goto send_resp;
105204949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	}
105304949598a23f501be6eec21697465fd46a28840aDmitry Shmidt
105404949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	if (dialog->sd_resp == NULL) {
105504949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		wpa_printf(MSG_DEBUG, "GAS: Remote request 0x%x received 0x%x",
105604949598a23f501be6eec21697465fd46a28840aDmitry Shmidt			   dialog->requested, dialog->received);
105704949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		if ((dialog->requested & dialog->received) !=
105804949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		    dialog->requested) {
105904949598a23f501be6eec21697465fd46a28840aDmitry Shmidt			wpa_printf(MSG_DEBUG, "GAS: Did not receive response "
106004949598a23f501be6eec21697465fd46a28840aDmitry Shmidt				   "from remote processing");
106104949598a23f501be6eec21697465fd46a28840aDmitry Shmidt			gas_serv_dialog_clear(dialog);
106204949598a23f501be6eec21697465fd46a28840aDmitry Shmidt			tx_buf = gas_anqp_build_comeback_resp_buf(
106304949598a23f501be6eec21697465fd46a28840aDmitry Shmidt				dialog_token,
106404949598a23f501be6eec21697465fd46a28840aDmitry Shmidt				WLAN_STATUS_GAS_RESP_NOT_RECEIVED, 0, 0, 0,
106504949598a23f501be6eec21697465fd46a28840aDmitry Shmidt				NULL);
106604949598a23f501be6eec21697465fd46a28840aDmitry Shmidt			if (tx_buf == NULL)
106704949598a23f501be6eec21697465fd46a28840aDmitry Shmidt				return;
106804949598a23f501be6eec21697465fd46a28840aDmitry Shmidt			goto send_resp;
106904949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		}
107004949598a23f501be6eec21697465fd46a28840aDmitry Shmidt
107104949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		buf = gas_serv_build_gas_resp_payload(hapd,
107204949598a23f501be6eec21697465fd46a28840aDmitry Shmidt						      dialog->all_requested,
107361d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt						      dialog, NULL, 0);
107404949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		wpa_hexdump_buf(MSG_MSGDUMP, "ANQP: Generated ANQP responses",
107504949598a23f501be6eec21697465fd46a28840aDmitry Shmidt			buf);
107604949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		if (!buf)
107704949598a23f501be6eec21697465fd46a28840aDmitry Shmidt			goto rx_gas_comeback_req_done;
107804949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		dialog->sd_resp = buf;
107904949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		dialog->sd_resp_pos = 0;
108004949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	}
108104949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	frag_len = wpabuf_len(dialog->sd_resp) - dialog->sd_resp_pos;
108204949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	if (frag_len > hapd->gas_frag_limit) {
108304949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		frag_len = hapd->gas_frag_limit;
108404949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		more = 1;
108504949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	}
108604949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	wpa_msg(hapd->msg_ctx, MSG_DEBUG, "GAS: resp frag_len %u",
108704949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		(unsigned int) frag_len);
108804949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	buf = wpabuf_alloc_copy(wpabuf_head_u8(dialog->sd_resp) +
108904949598a23f501be6eec21697465fd46a28840aDmitry Shmidt				dialog->sd_resp_pos, frag_len);
109004949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	if (buf == NULL) {
109104949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		wpa_msg(hapd->msg_ctx, MSG_DEBUG, "GAS: Failed to allocate "
109204949598a23f501be6eec21697465fd46a28840aDmitry Shmidt			"buffer");
109304949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		goto rx_gas_comeback_req_done;
109404949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	}
109504949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	tx_buf = gas_anqp_build_comeback_resp_buf(dialog_token,
109604949598a23f501be6eec21697465fd46a28840aDmitry Shmidt						  WLAN_STATUS_SUCCESS,
109704949598a23f501be6eec21697465fd46a28840aDmitry Shmidt						  dialog->sd_frag_id,
109804949598a23f501be6eec21697465fd46a28840aDmitry Shmidt						  more, 0, buf);
109904949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	wpabuf_free(buf);
110004949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	if (tx_buf == NULL)
110104949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		goto rx_gas_comeback_req_done;
110204949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	wpa_msg(hapd->msg_ctx, MSG_DEBUG, "GAS: Tx GAS Comeback Response "
110304949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		"(frag_id %d more=%d frag_len=%d)",
110404949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		dialog->sd_frag_id, more, (int) frag_len);
110504949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	dialog->sd_frag_id++;
110604949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	dialog->sd_resp_pos += frag_len;
110704949598a23f501be6eec21697465fd46a28840aDmitry Shmidt
110804949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	if (more) {
110904949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		wpa_msg(hapd->msg_ctx, MSG_DEBUG, "GAS: %d more bytes remain "
111004949598a23f501be6eec21697465fd46a28840aDmitry Shmidt			"to be sent",
111104949598a23f501be6eec21697465fd46a28840aDmitry Shmidt			(int) (wpabuf_len(dialog->sd_resp) -
111204949598a23f501be6eec21697465fd46a28840aDmitry Shmidt			       dialog->sd_resp_pos));
111304949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	} else {
111404949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		wpa_msg(hapd->msg_ctx, MSG_DEBUG, "GAS: All fragments of "
111504949598a23f501be6eec21697465fd46a28840aDmitry Shmidt			"SD response sent");
111604949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		gas_serv_dialog_clear(dialog);
111704949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		gas_serv_free_dialogs(hapd, sa);
111804949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	}
111904949598a23f501be6eec21697465fd46a28840aDmitry Shmidt
112004949598a23f501be6eec21697465fd46a28840aDmitry Shmidtsend_resp:
112104949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	hostapd_drv_send_action(hapd, hapd->iface->freq, 0, sa,
112204949598a23f501be6eec21697465fd46a28840aDmitry Shmidt				wpabuf_head(tx_buf), wpabuf_len(tx_buf));
112304949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	wpabuf_free(tx_buf);
112404949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	return;
112504949598a23f501be6eec21697465fd46a28840aDmitry Shmidt
112604949598a23f501be6eec21697465fd46a28840aDmitry Shmidtrx_gas_comeback_req_done:
112704949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	gas_serv_clear_cached_ies(dialog, NULL);
112804949598a23f501be6eec21697465fd46a28840aDmitry Shmidt}
112904949598a23f501be6eec21697465fd46a28840aDmitry Shmidt
113004949598a23f501be6eec21697465fd46a28840aDmitry Shmidt
113104949598a23f501be6eec21697465fd46a28840aDmitry Shmidtstatic void gas_serv_rx_public_action(void *ctx, const u8 *buf, size_t len,
113204949598a23f501be6eec21697465fd46a28840aDmitry Shmidt				      int freq)
113304949598a23f501be6eec21697465fd46a28840aDmitry Shmidt{
113404949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	struct hostapd_data *hapd = ctx;
113504949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	const struct ieee80211_mgmt *mgmt;
113604949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	size_t hdr_len;
113704949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	const u8 *sa, *data;
113804949598a23f501be6eec21697465fd46a28840aDmitry Shmidt
113904949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	mgmt = (const struct ieee80211_mgmt *) buf;
114004949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	hdr_len = (const u8 *) &mgmt->u.action.u.vs_public_action.action - buf;
114104949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	if (hdr_len > len)
114204949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		return;
114304949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	if (mgmt->u.action.category != WLAN_ACTION_PUBLIC)
114404949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		return;
114504949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	sa = mgmt->sa;
114604949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	len -= hdr_len;
114704949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	data = &mgmt->u.action.u.public_action.action;
114804949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	switch (data[0]) {
114904949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	case WLAN_PA_GAS_INITIAL_REQ:
115004949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		gas_serv_rx_gas_initial_req(hapd, sa, data + 1, len - 1);
115104949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		break;
115204949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	case WLAN_PA_GAS_COMEBACK_REQ:
115304949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		gas_serv_rx_gas_comeback_req(hapd, sa, data + 1, len - 1);
115404949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		break;
115504949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	}
115604949598a23f501be6eec21697465fd46a28840aDmitry Shmidt}
115704949598a23f501be6eec21697465fd46a28840aDmitry Shmidt
115804949598a23f501be6eec21697465fd46a28840aDmitry Shmidt
115904949598a23f501be6eec21697465fd46a28840aDmitry Shmidtint gas_serv_init(struct hostapd_data *hapd)
116004949598a23f501be6eec21697465fd46a28840aDmitry Shmidt{
11614b9d52f502481b258fec743c03a5e957e5605afcDmitry Shmidt	hapd->public_action_cb2 = gas_serv_rx_public_action;
11624b9d52f502481b258fec743c03a5e957e5605afcDmitry Shmidt	hapd->public_action_cb2_ctx = hapd;
116304949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	hapd->gas_frag_limit = 1400;
116404949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	if (hapd->conf->gas_frag_limit > 0)
116504949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		hapd->gas_frag_limit = hapd->conf->gas_frag_limit;
116604949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	return 0;
116704949598a23f501be6eec21697465fd46a28840aDmitry Shmidt}
116804949598a23f501be6eec21697465fd46a28840aDmitry Shmidt
116904949598a23f501be6eec21697465fd46a28840aDmitry Shmidt
117004949598a23f501be6eec21697465fd46a28840aDmitry Shmidtvoid gas_serv_deinit(struct hostapd_data *hapd)
117104949598a23f501be6eec21697465fd46a28840aDmitry Shmidt{
117204949598a23f501be6eec21697465fd46a28840aDmitry Shmidt}
1173