gas_serv.c revision 1846323989242844f0e857458a8939fa5836429c
104949598a23f501be6eec21697465fd46a28840aDmitry Shmidt/*
204949598a23f501be6eec21697465fd46a28840aDmitry Shmidt * Generic advertisement service (GAS) server
31846323989242844f0e857458a8939fa5836429cDmitry Shmidt * Copyright (c) 2011-2014, 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
221846323989242844f0e857458a8939fa5836429cDmitry Shmidtstatic void convert_to_protected_dual(struct wpabuf *msg)
231846323989242844f0e857458a8939fa5836429cDmitry Shmidt{
241846323989242844f0e857458a8939fa5836429cDmitry Shmidt	u8 *categ = wpabuf_mhead_u8(msg);
251846323989242844f0e857458a8939fa5836429cDmitry Shmidt	*categ = WLAN_ACTION_PROTECTED_DUAL;
261846323989242844f0e857458a8939fa5836429cDmitry Shmidt}
271846323989242844f0e857458a8939fa5836429cDmitry Shmidt
281846323989242844f0e857458a8939fa5836429cDmitry Shmidt
2904949598a23f501be6eec21697465fd46a28840aDmitry Shmidtstatic struct gas_dialog_info *
3004949598a23f501be6eec21697465fd46a28840aDmitry Shmidtgas_dialog_create(struct hostapd_data *hapd, const u8 *addr, u8 dialog_token)
3104949598a23f501be6eec21697465fd46a28840aDmitry Shmidt{
3204949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	struct sta_info *sta;
3304949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	struct gas_dialog_info *dia = NULL;
3404949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	int i, j;
3504949598a23f501be6eec21697465fd46a28840aDmitry Shmidt
3604949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	sta = ap_get_sta(hapd, addr);
3704949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	if (!sta) {
3804949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		/*
3904949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		 * We need a STA entry to be able to maintain state for
4004949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		 * the GAS query.
4104949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		 */
4204949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		wpa_printf(MSG_DEBUG, "ANQP: Add a temporary STA entry for "
4304949598a23f501be6eec21697465fd46a28840aDmitry Shmidt			   "GAS query");
4404949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		sta = ap_sta_add(hapd, addr);
4504949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		if (!sta) {
4604949598a23f501be6eec21697465fd46a28840aDmitry Shmidt			wpa_printf(MSG_DEBUG, "Failed to add STA " MACSTR
4704949598a23f501be6eec21697465fd46a28840aDmitry Shmidt				   " for GAS query", MAC2STR(addr));
4804949598a23f501be6eec21697465fd46a28840aDmitry Shmidt			return NULL;
4904949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		}
5004949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		sta->flags |= WLAN_STA_GAS;
5104949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		/*
5204949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		 * The default inactivity is 300 seconds. We don't need
5304949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		 * it to be that long.
5404949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		 */
5504949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		ap_sta_session_timeout(hapd, sta, 5);
565460547a121207cf7a99eac45e05fcdd83be3161Dmitry Shmidt	} else {
575460547a121207cf7a99eac45e05fcdd83be3161Dmitry Shmidt		ap_sta_replenish_timeout(hapd, sta, 5);
5804949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	}
5904949598a23f501be6eec21697465fd46a28840aDmitry Shmidt
6004949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	if (sta->gas_dialog == NULL) {
6104949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		sta->gas_dialog = os_zalloc(GAS_DIALOG_MAX *
6204949598a23f501be6eec21697465fd46a28840aDmitry Shmidt					    sizeof(struct gas_dialog_info));
6304949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		if (sta->gas_dialog == NULL)
6404949598a23f501be6eec21697465fd46a28840aDmitry Shmidt			return NULL;
6504949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	}
6604949598a23f501be6eec21697465fd46a28840aDmitry Shmidt
6704949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	for (i = sta->gas_dialog_next, j = 0; j < GAS_DIALOG_MAX; i++, j++) {
6804949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		if (i == GAS_DIALOG_MAX)
6904949598a23f501be6eec21697465fd46a28840aDmitry Shmidt			i = 0;
7004949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		if (sta->gas_dialog[i].valid)
7104949598a23f501be6eec21697465fd46a28840aDmitry Shmidt			continue;
7204949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		dia = &sta->gas_dialog[i];
7304949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		dia->valid = 1;
7404949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		dia->index = i;
7504949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		dia->dialog_token = dialog_token;
7604949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		sta->gas_dialog_next = (++i == GAS_DIALOG_MAX) ? 0 : i;
7704949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		return dia;
7804949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	}
7904949598a23f501be6eec21697465fd46a28840aDmitry Shmidt
8004949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	wpa_msg(hapd->msg_ctx, MSG_ERROR, "ANQP: Could not create dialog for "
8104949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		MACSTR " dialog_token %u. Consider increasing "
8204949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		"GAS_DIALOG_MAX.", MAC2STR(addr), dialog_token);
8304949598a23f501be6eec21697465fd46a28840aDmitry Shmidt
8404949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	return NULL;
8504949598a23f501be6eec21697465fd46a28840aDmitry Shmidt}
8604949598a23f501be6eec21697465fd46a28840aDmitry Shmidt
8704949598a23f501be6eec21697465fd46a28840aDmitry Shmidt
8804949598a23f501be6eec21697465fd46a28840aDmitry Shmidtstruct gas_dialog_info *
8904949598a23f501be6eec21697465fd46a28840aDmitry Shmidtgas_serv_dialog_find(struct hostapd_data *hapd, const u8 *addr,
9004949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		     u8 dialog_token)
9104949598a23f501be6eec21697465fd46a28840aDmitry Shmidt{
9204949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	struct sta_info *sta;
9304949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	int i;
9404949598a23f501be6eec21697465fd46a28840aDmitry Shmidt
9504949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	sta = ap_get_sta(hapd, addr);
9604949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	if (!sta) {
9704949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		wpa_printf(MSG_DEBUG, "ANQP: could not find STA " MACSTR,
9804949598a23f501be6eec21697465fd46a28840aDmitry Shmidt			   MAC2STR(addr));
9904949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		return NULL;
10004949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	}
10104949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	for (i = 0; sta->gas_dialog && i < GAS_DIALOG_MAX; i++) {
10204949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		if (sta->gas_dialog[i].dialog_token != dialog_token ||
10304949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		    !sta->gas_dialog[i].valid)
10404949598a23f501be6eec21697465fd46a28840aDmitry Shmidt			continue;
10504949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		return &sta->gas_dialog[i];
10604949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	}
10704949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	wpa_printf(MSG_DEBUG, "ANQP: Could not find dialog for "
10804949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		   MACSTR " dialog_token %u", MAC2STR(addr), dialog_token);
10904949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	return NULL;
11004949598a23f501be6eec21697465fd46a28840aDmitry Shmidt}
11104949598a23f501be6eec21697465fd46a28840aDmitry Shmidt
11204949598a23f501be6eec21697465fd46a28840aDmitry Shmidt
11304949598a23f501be6eec21697465fd46a28840aDmitry Shmidtvoid gas_serv_dialog_clear(struct gas_dialog_info *dia)
11404949598a23f501be6eec21697465fd46a28840aDmitry Shmidt{
11504949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	wpabuf_free(dia->sd_resp);
11604949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	os_memset(dia, 0, sizeof(*dia));
11704949598a23f501be6eec21697465fd46a28840aDmitry Shmidt}
11804949598a23f501be6eec21697465fd46a28840aDmitry Shmidt
11904949598a23f501be6eec21697465fd46a28840aDmitry Shmidt
12004949598a23f501be6eec21697465fd46a28840aDmitry Shmidtstatic void gas_serv_free_dialogs(struct hostapd_data *hapd,
12104949598a23f501be6eec21697465fd46a28840aDmitry Shmidt				  const u8 *sta_addr)
12204949598a23f501be6eec21697465fd46a28840aDmitry Shmidt{
12304949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	struct sta_info *sta;
12404949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	int i;
12504949598a23f501be6eec21697465fd46a28840aDmitry Shmidt
12604949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	sta = ap_get_sta(hapd, sta_addr);
12704949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	if (sta == NULL || sta->gas_dialog == NULL)
12804949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		return;
12904949598a23f501be6eec21697465fd46a28840aDmitry Shmidt
13004949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	for (i = 0; i < GAS_DIALOG_MAX; i++) {
13104949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		if (sta->gas_dialog[i].valid)
13204949598a23f501be6eec21697465fd46a28840aDmitry Shmidt			return;
13304949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	}
13404949598a23f501be6eec21697465fd46a28840aDmitry Shmidt
13504949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	os_free(sta->gas_dialog);
13604949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	sta->gas_dialog = NULL;
13704949598a23f501be6eec21697465fd46a28840aDmitry Shmidt}
13804949598a23f501be6eec21697465fd46a28840aDmitry Shmidt
13904949598a23f501be6eec21697465fd46a28840aDmitry Shmidt
140aa532510a7b8c4da2d7d6e2c11dda5db840894e4Dmitry Shmidt#ifdef CONFIG_HS20
14161d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidtstatic void anqp_add_hs_capab_list(struct hostapd_data *hapd,
14261d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt				   struct wpabuf *buf)
14361d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt{
14461d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	u8 *len;
14561d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt
14661d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	len = gas_anqp_add_element(buf, ANQP_VENDOR_SPECIFIC);
14761d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	wpabuf_put_be24(buf, OUI_WFA);
14861d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	wpabuf_put_u8(buf, HS20_ANQP_OUI_TYPE);
14961d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	wpabuf_put_u8(buf, HS20_STYPE_CAPABILITY_LIST);
15061d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	wpabuf_put_u8(buf, 0); /* Reserved */
15161d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	wpabuf_put_u8(buf, HS20_STYPE_CAPABILITY_LIST);
15261d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	if (hapd->conf->hs20_oper_friendly_name)
15361d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt		wpabuf_put_u8(buf, HS20_STYPE_OPERATOR_FRIENDLY_NAME);
15461d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	if (hapd->conf->hs20_wan_metrics)
15561d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt		wpabuf_put_u8(buf, HS20_STYPE_WAN_METRICS);
15661d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	if (hapd->conf->hs20_connection_capability)
15761d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt		wpabuf_put_u8(buf, HS20_STYPE_CONNECTION_CAPABILITY);
15861d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	if (hapd->conf->nai_realm_data)
15961d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt		wpabuf_put_u8(buf, HS20_STYPE_NAI_HOME_REALM_QUERY);
16061d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	if (hapd->conf->hs20_operating_class)
16161d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt		wpabuf_put_u8(buf, HS20_STYPE_OPERATING_CLASS);
16261d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	gas_anqp_set_element_len(buf, len);
16361d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt}
164aa532510a7b8c4da2d7d6e2c11dda5db840894e4Dmitry Shmidt#endif /* CONFIG_HS20 */
16561d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt
16661d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt
16704949598a23f501be6eec21697465fd46a28840aDmitry Shmidtstatic void anqp_add_capab_list(struct hostapd_data *hapd,
16804949598a23f501be6eec21697465fd46a28840aDmitry Shmidt				struct wpabuf *buf)
16904949598a23f501be6eec21697465fd46a28840aDmitry Shmidt{
17004949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	u8 *len;
17104949598a23f501be6eec21697465fd46a28840aDmitry Shmidt
17204949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	len = gas_anqp_add_element(buf, ANQP_CAPABILITY_LIST);
17304949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	wpabuf_put_le16(buf, ANQP_CAPABILITY_LIST);
17404949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	if (hapd->conf->venue_name)
17504949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		wpabuf_put_le16(buf, ANQP_VENUE_NAME);
17661d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	if (hapd->conf->network_auth_type)
17761d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt		wpabuf_put_le16(buf, ANQP_NETWORK_AUTH_TYPE);
17804949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	if (hapd->conf->roaming_consortium)
17904949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		wpabuf_put_le16(buf, ANQP_ROAMING_CONSORTIUM);
18061d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	if (hapd->conf->ipaddr_type_configured)
18161d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt		wpabuf_put_le16(buf, ANQP_IP_ADDR_TYPE_AVAILABILITY);
18261d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	if (hapd->conf->nai_realm_data)
18361d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt		wpabuf_put_le16(buf, ANQP_NAI_REALM);
18461d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	if (hapd->conf->anqp_3gpp_cell_net)
18561d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt		wpabuf_put_le16(buf, ANQP_3GPP_CELLULAR_NETWORK);
18661d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	if (hapd->conf->domain_name)
18761d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt		wpabuf_put_le16(buf, ANQP_DOMAIN_NAME);
188aa532510a7b8c4da2d7d6e2c11dda5db840894e4Dmitry Shmidt#ifdef CONFIG_HS20
18961d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	anqp_add_hs_capab_list(hapd, buf);
190aa532510a7b8c4da2d7d6e2c11dda5db840894e4Dmitry Shmidt#endif /* CONFIG_HS20 */
19104949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	gas_anqp_set_element_len(buf, len);
19204949598a23f501be6eec21697465fd46a28840aDmitry Shmidt}
19304949598a23f501be6eec21697465fd46a28840aDmitry Shmidt
19404949598a23f501be6eec21697465fd46a28840aDmitry Shmidt
19504949598a23f501be6eec21697465fd46a28840aDmitry Shmidtstatic void anqp_add_venue_name(struct hostapd_data *hapd, struct wpabuf *buf)
19604949598a23f501be6eec21697465fd46a28840aDmitry Shmidt{
19704949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	if (hapd->conf->venue_name) {
19804949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		u8 *len;
19904949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		unsigned int i;
20004949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		len = gas_anqp_add_element(buf, ANQP_VENUE_NAME);
20104949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		wpabuf_put_u8(buf, hapd->conf->venue_group);
20204949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		wpabuf_put_u8(buf, hapd->conf->venue_type);
20304949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		for (i = 0; i < hapd->conf->venue_name_count; i++) {
20461d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt			struct hostapd_lang_string *vn;
20504949598a23f501be6eec21697465fd46a28840aDmitry Shmidt			vn = &hapd->conf->venue_name[i];
20604949598a23f501be6eec21697465fd46a28840aDmitry Shmidt			wpabuf_put_u8(buf, 3 + vn->name_len);
20704949598a23f501be6eec21697465fd46a28840aDmitry Shmidt			wpabuf_put_data(buf, vn->lang, 3);
20804949598a23f501be6eec21697465fd46a28840aDmitry Shmidt			wpabuf_put_data(buf, vn->name, vn->name_len);
20904949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		}
21004949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		gas_anqp_set_element_len(buf, len);
21104949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	}
21204949598a23f501be6eec21697465fd46a28840aDmitry Shmidt}
21304949598a23f501be6eec21697465fd46a28840aDmitry Shmidt
21404949598a23f501be6eec21697465fd46a28840aDmitry Shmidt
21561d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidtstatic void anqp_add_network_auth_type(struct hostapd_data *hapd,
21661d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt				       struct wpabuf *buf)
21761d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt{
21861d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	if (hapd->conf->network_auth_type) {
21961d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt		wpabuf_put_le16(buf, ANQP_NETWORK_AUTH_TYPE);
22061d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt		wpabuf_put_le16(buf, hapd->conf->network_auth_type_len);
22161d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt		wpabuf_put_data(buf, hapd->conf->network_auth_type,
22261d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt				hapd->conf->network_auth_type_len);
22361d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	}
22461d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt}
22561d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt
22661d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt
22704949598a23f501be6eec21697465fd46a28840aDmitry Shmidtstatic void anqp_add_roaming_consortium(struct hostapd_data *hapd,
22804949598a23f501be6eec21697465fd46a28840aDmitry Shmidt					struct wpabuf *buf)
22904949598a23f501be6eec21697465fd46a28840aDmitry Shmidt{
23004949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	unsigned int i;
23104949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	u8 *len;
23204949598a23f501be6eec21697465fd46a28840aDmitry Shmidt
23304949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	len = gas_anqp_add_element(buf, ANQP_ROAMING_CONSORTIUM);
23404949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	for (i = 0; i < hapd->conf->roaming_consortium_count; i++) {
23504949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		struct hostapd_roaming_consortium *rc;
23604949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		rc = &hapd->conf->roaming_consortium[i];
23704949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		wpabuf_put_u8(buf, rc->len);
23804949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		wpabuf_put_data(buf, rc->oi, rc->len);
23904949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	}
24004949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	gas_anqp_set_element_len(buf, len);
24104949598a23f501be6eec21697465fd46a28840aDmitry Shmidt}
24204949598a23f501be6eec21697465fd46a28840aDmitry Shmidt
24304949598a23f501be6eec21697465fd46a28840aDmitry Shmidt
24461d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidtstatic void anqp_add_ip_addr_type_availability(struct hostapd_data *hapd,
24561d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt					       struct wpabuf *buf)
24661d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt{
24761d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	if (hapd->conf->ipaddr_type_configured) {
24861d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt		wpabuf_put_le16(buf, ANQP_IP_ADDR_TYPE_AVAILABILITY);
24961d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt		wpabuf_put_le16(buf, 1);
25061d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt		wpabuf_put_u8(buf, hapd->conf->ipaddr_type_availability);
25161d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	}
25261d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt}
25361d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt
25461d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt
25561d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidtstatic void anqp_add_nai_realm_eap(struct wpabuf *buf,
25661d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt				   struct hostapd_nai_realm_data *realm)
25761d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt{
25861d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	unsigned int i, j;
25961d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt
26061d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	wpabuf_put_u8(buf, realm->eap_method_count);
26161d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt
26261d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	for (i = 0; i < realm->eap_method_count; i++) {
26361d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt		struct hostapd_nai_realm_eap *eap = &realm->eap_method[i];
26461d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt		wpabuf_put_u8(buf, 2 + (3 * eap->num_auths));
26561d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt		wpabuf_put_u8(buf, eap->eap_method);
26661d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt		wpabuf_put_u8(buf, eap->num_auths);
26761d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt		for (j = 0; j < eap->num_auths; j++) {
26861d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt			wpabuf_put_u8(buf, eap->auth_id[j]);
26961d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt			wpabuf_put_u8(buf, 1);
27061d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt			wpabuf_put_u8(buf, eap->auth_val[j]);
27161d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt		}
27261d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	}
27361d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt}
27461d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt
27561d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt
27661d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidtstatic void anqp_add_nai_realm_data(struct wpabuf *buf,
27761d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt				    struct hostapd_nai_realm_data *realm,
27861d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt				    unsigned int realm_idx)
27961d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt{
28061d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	u8 *realm_data_len;
28161d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt
28261d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	wpa_printf(MSG_DEBUG, "realm=%s, len=%d", realm->realm[realm_idx],
28361d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt		   (int) os_strlen(realm->realm[realm_idx]));
28461d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	realm_data_len = wpabuf_put(buf, 2);
28561d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	wpabuf_put_u8(buf, realm->encoding);
28661d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	wpabuf_put_u8(buf, os_strlen(realm->realm[realm_idx]));
28761d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	wpabuf_put_str(buf, realm->realm[realm_idx]);
28861d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	anqp_add_nai_realm_eap(buf, realm);
28961d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	gas_anqp_set_element_len(buf, realm_data_len);
29061d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt}
29161d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt
29261d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt
29361d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidtstatic int hs20_add_nai_home_realm_matches(struct hostapd_data *hapd,
29461d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt					   struct wpabuf *buf,
29561d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt					   const u8 *home_realm,
29661d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt					   size_t home_realm_len)
29761d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt{
29861d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	unsigned int i, j, k;
29961d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	u8 num_realms, num_matching = 0, encoding, realm_len, *realm_list_len;
30061d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	struct hostapd_nai_realm_data *realm;
30161d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	const u8 *pos, *realm_name, *end;
30261d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	struct {
30361d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt		unsigned int realm_data_idx;
30461d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt		unsigned int realm_idx;
30561d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	} matches[10];
30661d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt
30761d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	pos = home_realm;
30861d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	end = pos + home_realm_len;
30961d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	if (pos + 1 > end) {
31061d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt		wpa_hexdump(MSG_DEBUG, "Too short NAI Home Realm Query",
31161d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt			    home_realm, home_realm_len);
31261d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt		return -1;
31361d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	}
31461d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	num_realms = *pos++;
31561d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt
31661d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	for (i = 0; i < num_realms && num_matching < 10; i++) {
31761d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt		if (pos + 2 > end) {
31861d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt			wpa_hexdump(MSG_DEBUG,
31961d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt				    "Truncated NAI Home Realm Query",
32061d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt				    home_realm, home_realm_len);
32161d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt			return -1;
32261d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt		}
32361d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt		encoding = *pos++;
32461d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt		realm_len = *pos++;
32561d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt		if (pos + realm_len > end) {
32661d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt			wpa_hexdump(MSG_DEBUG,
32761d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt				    "Truncated NAI Home Realm Query",
32861d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt				    home_realm, home_realm_len);
32961d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt			return -1;
33061d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt		}
33161d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt		realm_name = pos;
33261d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt		for (j = 0; j < hapd->conf->nai_realm_count &&
33361d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt			     num_matching < 10; j++) {
33461d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt			const u8 *rpos, *rend;
33561d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt			realm = &hapd->conf->nai_realm_data[j];
33661d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt			if (encoding != realm->encoding)
33761d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt				continue;
33861d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt
33961d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt			rpos = realm_name;
34061d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt			while (rpos < realm_name + realm_len &&
34161d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt			       num_matching < 10) {
34261d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt				for (rend = rpos;
34361d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt				     rend < realm_name + realm_len; rend++) {
34461d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt					if (*rend == ';')
34561d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt						break;
34661d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt				}
34761d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt				for (k = 0; k < MAX_NAI_REALMS &&
34861d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt					     realm->realm[k] &&
34961d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt					     num_matching < 10; k++) {
35061d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt					if ((int) os_strlen(realm->realm[k]) !=
35161d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt					    rend - rpos ||
35261d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt					    os_strncmp((char *) rpos,
35361d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt						       realm->realm[k],
35461d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt						       rend - rpos) != 0)
35561d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt						continue;
35661d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt					matches[num_matching].realm_data_idx =
35761d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt						j;
35861d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt					matches[num_matching].realm_idx = k;
35961d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt					num_matching++;
36061d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt				}
36161d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt				rpos = rend + 1;
36261d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt			}
36361d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt		}
36461d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt		pos += realm_len;
36561d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	}
36661d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt
36761d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	realm_list_len = gas_anqp_add_element(buf, ANQP_NAI_REALM);
36861d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	wpabuf_put_le16(buf, num_matching);
36961d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt
37061d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	/*
37161d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	 * There are two ways to format. 1. each realm in a NAI Realm Data unit
37261d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	 * 2. all realms that share the same EAP methods in a NAI Realm Data
37361d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	 * unit. The first format is likely to be bigger in size than the
37461d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	 * second, but may be easier to parse and process by the receiver.
37561d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	 */
37661d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	for (i = 0; i < num_matching; i++) {
37761d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt		wpa_printf(MSG_DEBUG, "realm_idx %d, realm_data_idx %d",
37861d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt			   matches[i].realm_data_idx, matches[i].realm_idx);
37961d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt		realm = &hapd->conf->nai_realm_data[matches[i].realm_data_idx];
38061d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt		anqp_add_nai_realm_data(buf, realm, matches[i].realm_idx);
38161d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	}
38261d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	gas_anqp_set_element_len(buf, realm_list_len);
38361d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	return 0;
38461d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt}
38561d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt
38661d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt
38761d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidtstatic void anqp_add_nai_realm(struct hostapd_data *hapd, struct wpabuf *buf,
38861d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt			       const u8 *home_realm, size_t home_realm_len,
38961d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt			       int nai_realm, int nai_home_realm)
39061d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt{
39161d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	if (nai_realm && hapd->conf->nai_realm_data) {
39261d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt		u8 *len;
39361d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt		unsigned int i, j;
39461d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt		len = gas_anqp_add_element(buf, ANQP_NAI_REALM);
39561d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt		wpabuf_put_le16(buf, hapd->conf->nai_realm_count);
39661d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt		for (i = 0; i < hapd->conf->nai_realm_count; i++) {
39761d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt			u8 *realm_data_len, *realm_len;
39861d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt			struct hostapd_nai_realm_data *realm;
39961d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt
40061d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt			realm = &hapd->conf->nai_realm_data[i];
40161d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt			realm_data_len = wpabuf_put(buf, 2);
40261d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt			wpabuf_put_u8(buf, realm->encoding);
40361d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt			realm_len = wpabuf_put(buf, 1);
40461d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt			for (j = 0; realm->realm[j]; j++) {
40561d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt				if (j > 0)
40661d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt					wpabuf_put_u8(buf, ';');
40761d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt				wpabuf_put_str(buf, realm->realm[j]);
40861d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt			}
40961d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt			*realm_len = (u8 *) wpabuf_put(buf, 0) - realm_len - 1;
41061d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt			anqp_add_nai_realm_eap(buf, realm);
41161d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt			gas_anqp_set_element_len(buf, realm_data_len);
41261d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt		}
41361d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt		gas_anqp_set_element_len(buf, len);
41461d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	} else if (nai_home_realm && hapd->conf->nai_realm_data) {
41561d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt		hs20_add_nai_home_realm_matches(hapd, buf, home_realm,
41661d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt						home_realm_len);
41761d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	}
41861d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt}
41961d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt
42061d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt
42161d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidtstatic void anqp_add_3gpp_cellular_network(struct hostapd_data *hapd,
42261d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt					   struct wpabuf *buf)
42361d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt{
42461d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	if (hapd->conf->anqp_3gpp_cell_net) {
42561d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt		wpabuf_put_le16(buf, ANQP_3GPP_CELLULAR_NETWORK);
42661d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt		wpabuf_put_le16(buf,
42761d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt				hapd->conf->anqp_3gpp_cell_net_len);
42861d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt		wpabuf_put_data(buf, hapd->conf->anqp_3gpp_cell_net,
42961d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt				hapd->conf->anqp_3gpp_cell_net_len);
43061d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	}
43161d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt}
43261d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt
43361d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt
43461d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidtstatic void anqp_add_domain_name(struct hostapd_data *hapd, struct wpabuf *buf)
43561d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt{
43661d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	if (hapd->conf->domain_name) {
43761d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt		wpabuf_put_le16(buf, ANQP_DOMAIN_NAME);
43861d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt		wpabuf_put_le16(buf, hapd->conf->domain_name_len);
43961d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt		wpabuf_put_data(buf, hapd->conf->domain_name,
44061d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt				hapd->conf->domain_name_len);
44161d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	}
44261d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt}
44361d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt
44461d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt
445aa532510a7b8c4da2d7d6e2c11dda5db840894e4Dmitry Shmidt#ifdef CONFIG_HS20
446aa532510a7b8c4da2d7d6e2c11dda5db840894e4Dmitry Shmidt
44761d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidtstatic void anqp_add_operator_friendly_name(struct hostapd_data *hapd,
44861d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt					    struct wpabuf *buf)
44961d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt{
45061d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	if (hapd->conf->hs20_oper_friendly_name) {
45161d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt		u8 *len;
45261d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt		unsigned int i;
45361d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt		len = gas_anqp_add_element(buf, ANQP_VENDOR_SPECIFIC);
45461d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt		wpabuf_put_be24(buf, OUI_WFA);
45561d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt		wpabuf_put_u8(buf, HS20_ANQP_OUI_TYPE);
45661d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt		wpabuf_put_u8(buf, HS20_STYPE_OPERATOR_FRIENDLY_NAME);
45761d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt		wpabuf_put_u8(buf, 0); /* Reserved */
45861d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt		for (i = 0; i < hapd->conf->hs20_oper_friendly_name_count; i++)
45961d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt		{
46061d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt			struct hostapd_lang_string *vn;
46161d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt			vn = &hapd->conf->hs20_oper_friendly_name[i];
46261d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt			wpabuf_put_u8(buf, 3 + vn->name_len);
46361d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt			wpabuf_put_data(buf, vn->lang, 3);
46461d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt			wpabuf_put_data(buf, vn->name, vn->name_len);
46561d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt		}
46661d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt		gas_anqp_set_element_len(buf, len);
46761d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	}
46861d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt}
46961d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt
47061d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt
47161d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidtstatic void anqp_add_wan_metrics(struct hostapd_data *hapd,
47261d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt				 struct wpabuf *buf)
47361d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt{
47461d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	if (hapd->conf->hs20_wan_metrics) {
47561d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt		u8 *len = gas_anqp_add_element(buf, ANQP_VENDOR_SPECIFIC);
47661d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt		wpabuf_put_be24(buf, OUI_WFA);
47761d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt		wpabuf_put_u8(buf, HS20_ANQP_OUI_TYPE);
47861d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt		wpabuf_put_u8(buf, HS20_STYPE_WAN_METRICS);
47961d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt		wpabuf_put_u8(buf, 0); /* Reserved */
48061d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt		wpabuf_put_data(buf, hapd->conf->hs20_wan_metrics, 13);
48161d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt		gas_anqp_set_element_len(buf, len);
48261d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	}
48361d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt}
48461d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt
48561d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt
48661d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidtstatic void anqp_add_connection_capability(struct hostapd_data *hapd,
48761d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt					   struct wpabuf *buf)
48861d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt{
48961d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	if (hapd->conf->hs20_connection_capability) {
49061d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt		u8 *len = gas_anqp_add_element(buf, ANQP_VENDOR_SPECIFIC);
49161d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt		wpabuf_put_be24(buf, OUI_WFA);
49261d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt		wpabuf_put_u8(buf, HS20_ANQP_OUI_TYPE);
49361d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt		wpabuf_put_u8(buf, HS20_STYPE_CONNECTION_CAPABILITY);
49461d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt		wpabuf_put_u8(buf, 0); /* Reserved */
49561d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt		wpabuf_put_data(buf, hapd->conf->hs20_connection_capability,
49661d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt				hapd->conf->hs20_connection_capability_len);
49761d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt		gas_anqp_set_element_len(buf, len);
49861d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	}
49961d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt}
50061d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt
50161d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt
50261d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidtstatic void anqp_add_operating_class(struct hostapd_data *hapd,
50361d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt				     struct wpabuf *buf)
50461d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt{
50561d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	if (hapd->conf->hs20_operating_class) {
50661d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt		u8 *len = gas_anqp_add_element(buf, ANQP_VENDOR_SPECIFIC);
50761d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt		wpabuf_put_be24(buf, OUI_WFA);
50861d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt		wpabuf_put_u8(buf, HS20_ANQP_OUI_TYPE);
50961d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt		wpabuf_put_u8(buf, HS20_STYPE_OPERATING_CLASS);
51061d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt		wpabuf_put_u8(buf, 0); /* Reserved */
51161d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt		wpabuf_put_data(buf, hapd->conf->hs20_operating_class,
51261d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt				hapd->conf->hs20_operating_class_len);
51361d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt		gas_anqp_set_element_len(buf, len);
51461d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	}
51561d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt}
51661d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt
517aa532510a7b8c4da2d7d6e2c11dda5db840894e4Dmitry Shmidt#endif /* CONFIG_HS20 */
518aa532510a7b8c4da2d7d6e2c11dda5db840894e4Dmitry Shmidt
51961d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt
52004949598a23f501be6eec21697465fd46a28840aDmitry Shmidtstatic struct wpabuf *
52104949598a23f501be6eec21697465fd46a28840aDmitry Shmidtgas_serv_build_gas_resp_payload(struct hostapd_data *hapd,
52204949598a23f501be6eec21697465fd46a28840aDmitry Shmidt				unsigned int request,
52361d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt				struct gas_dialog_info *di,
52461d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt				const u8 *home_realm, size_t home_realm_len)
52504949598a23f501be6eec21697465fd46a28840aDmitry Shmidt{
52604949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	struct wpabuf *buf;
52704949598a23f501be6eec21697465fd46a28840aDmitry Shmidt
52804949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	buf = wpabuf_alloc(1400);
52904949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	if (buf == NULL)
53004949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		return NULL;
53104949598a23f501be6eec21697465fd46a28840aDmitry Shmidt
53204949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	if (request & ANQP_REQ_CAPABILITY_LIST)
53304949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		anqp_add_capab_list(hapd, buf);
53404949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	if (request & ANQP_REQ_VENUE_NAME)
53504949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		anqp_add_venue_name(hapd, buf);
53661d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	if (request & ANQP_REQ_NETWORK_AUTH_TYPE)
53761d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt		anqp_add_network_auth_type(hapd, buf);
53804949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	if (request & ANQP_REQ_ROAMING_CONSORTIUM)
53904949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		anqp_add_roaming_consortium(hapd, buf);
54061d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	if (request & ANQP_REQ_IP_ADDR_TYPE_AVAILABILITY)
54161d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt		anqp_add_ip_addr_type_availability(hapd, buf);
54261d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	if (request & (ANQP_REQ_NAI_REALM | ANQP_REQ_NAI_HOME_REALM))
54361d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt		anqp_add_nai_realm(hapd, buf, home_realm, home_realm_len,
54461d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt				   request & ANQP_REQ_NAI_REALM,
54561d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt				   request & ANQP_REQ_NAI_HOME_REALM);
54661d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	if (request & ANQP_REQ_3GPP_CELLULAR_NETWORK)
54761d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt		anqp_add_3gpp_cellular_network(hapd, buf);
54861d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	if (request & ANQP_REQ_DOMAIN_NAME)
54961d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt		anqp_add_domain_name(hapd, buf);
55061d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt
551aa532510a7b8c4da2d7d6e2c11dda5db840894e4Dmitry Shmidt#ifdef CONFIG_HS20
55261d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	if (request & ANQP_REQ_HS_CAPABILITY_LIST)
55361d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt		anqp_add_hs_capab_list(hapd, buf);
55461d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	if (request & ANQP_REQ_OPERATOR_FRIENDLY_NAME)
55561d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt		anqp_add_operator_friendly_name(hapd, buf);
55661d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	if (request & ANQP_REQ_WAN_METRICS)
55761d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt		anqp_add_wan_metrics(hapd, buf);
55861d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	if (request & ANQP_REQ_CONNECTION_CAPABILITY)
55961d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt		anqp_add_connection_capability(hapd, buf);
56061d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	if (request & ANQP_REQ_OPERATING_CLASS)
56161d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt		anqp_add_operating_class(hapd, buf);
562aa532510a7b8c4da2d7d6e2c11dda5db840894e4Dmitry Shmidt#endif /* CONFIG_HS20 */
56304949598a23f501be6eec21697465fd46a28840aDmitry Shmidt
56404949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	return buf;
56504949598a23f501be6eec21697465fd46a28840aDmitry Shmidt}
56604949598a23f501be6eec21697465fd46a28840aDmitry Shmidt
56704949598a23f501be6eec21697465fd46a28840aDmitry Shmidt
56804949598a23f501be6eec21697465fd46a28840aDmitry Shmidtstatic void gas_serv_clear_cached_ies(void *eloop_data, void *user_ctx)
56904949598a23f501be6eec21697465fd46a28840aDmitry Shmidt{
57004949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	struct gas_dialog_info *dia = eloop_data;
57104949598a23f501be6eec21697465fd46a28840aDmitry Shmidt
57204949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	wpa_printf(MSG_DEBUG, "GAS: Timeout triggered, clearing dialog for "
57304949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		   "dialog token %d", dia->dialog_token);
57404949598a23f501be6eec21697465fd46a28840aDmitry Shmidt
57504949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	gas_serv_dialog_clear(dia);
57604949598a23f501be6eec21697465fd46a28840aDmitry Shmidt}
57704949598a23f501be6eec21697465fd46a28840aDmitry Shmidt
57804949598a23f501be6eec21697465fd46a28840aDmitry Shmidt
57904949598a23f501be6eec21697465fd46a28840aDmitry Shmidtstruct anqp_query_info {
58004949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	unsigned int request;
58104949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	unsigned int remote_request;
58261d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	const u8 *home_realm_query;
58361d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	size_t home_realm_query_len;
58404949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	u16 remote_delay;
58504949598a23f501be6eec21697465fd46a28840aDmitry Shmidt};
58604949598a23f501be6eec21697465fd46a28840aDmitry Shmidt
58704949598a23f501be6eec21697465fd46a28840aDmitry Shmidt
58804949598a23f501be6eec21697465fd46a28840aDmitry Shmidtstatic void set_anqp_req(unsigned int bit, const char *name, int local,
58904949598a23f501be6eec21697465fd46a28840aDmitry Shmidt			 unsigned int remote, u16 remote_delay,
59004949598a23f501be6eec21697465fd46a28840aDmitry Shmidt			 struct anqp_query_info *qi)
59104949598a23f501be6eec21697465fd46a28840aDmitry Shmidt{
59204949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	qi->request |= bit;
59304949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	if (local) {
59404949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		wpa_printf(MSG_DEBUG, "ANQP: %s (local)", name);
59504949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	} else if (bit & remote) {
59604949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		wpa_printf(MSG_DEBUG, "ANQP: %s (remote)", name);
59704949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		qi->remote_request |= bit;
59804949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		if (remote_delay > qi->remote_delay)
59904949598a23f501be6eec21697465fd46a28840aDmitry Shmidt			qi->remote_delay = remote_delay;
60004949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	} else {
60104949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		wpa_printf(MSG_DEBUG, "ANQP: %s not available", name);
60204949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	}
60304949598a23f501be6eec21697465fd46a28840aDmitry Shmidt}
60404949598a23f501be6eec21697465fd46a28840aDmitry Shmidt
60504949598a23f501be6eec21697465fd46a28840aDmitry Shmidt
60604949598a23f501be6eec21697465fd46a28840aDmitry Shmidtstatic void rx_anqp_query_list_id(struct hostapd_data *hapd, u16 info_id,
60704949598a23f501be6eec21697465fd46a28840aDmitry Shmidt				  struct anqp_query_info *qi)
60804949598a23f501be6eec21697465fd46a28840aDmitry Shmidt{
60904949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	switch (info_id) {
61004949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	case ANQP_CAPABILITY_LIST:
61104949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		set_anqp_req(ANQP_REQ_CAPABILITY_LIST, "Capability List", 1, 0,
61204949598a23f501be6eec21697465fd46a28840aDmitry Shmidt			     0, qi);
61304949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		break;
61404949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	case ANQP_VENUE_NAME:
61504949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		set_anqp_req(ANQP_REQ_VENUE_NAME, "Venue Name",
61604949598a23f501be6eec21697465fd46a28840aDmitry Shmidt			     hapd->conf->venue_name != NULL, 0, 0, qi);
61704949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		break;
61861d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	case ANQP_NETWORK_AUTH_TYPE:
61961d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt		set_anqp_req(ANQP_REQ_NETWORK_AUTH_TYPE, "Network Auth Type",
62061d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt			     hapd->conf->network_auth_type != NULL,
62161d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt			     0, 0, qi);
62261d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt		break;
62304949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	case ANQP_ROAMING_CONSORTIUM:
62404949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		set_anqp_req(ANQP_REQ_ROAMING_CONSORTIUM, "Roaming Consortium",
62504949598a23f501be6eec21697465fd46a28840aDmitry Shmidt			     hapd->conf->roaming_consortium != NULL, 0, 0, qi);
62604949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		break;
62761d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	case ANQP_IP_ADDR_TYPE_AVAILABILITY:
62861d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt		set_anqp_req(ANQP_REQ_IP_ADDR_TYPE_AVAILABILITY,
62961d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt			     "IP Addr Type Availability",
63061d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt			     hapd->conf->ipaddr_type_configured,
63161d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt			     0, 0, qi);
63261d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt		break;
63361d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	case ANQP_NAI_REALM:
63461d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt		set_anqp_req(ANQP_REQ_NAI_REALM, "NAI Realm",
63561d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt			     hapd->conf->nai_realm_data != NULL,
63661d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt			     0, 0, qi);
63761d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt		break;
63861d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	case ANQP_3GPP_CELLULAR_NETWORK:
63961d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt		set_anqp_req(ANQP_REQ_3GPP_CELLULAR_NETWORK,
64061d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt			     "3GPP Cellular Network",
64161d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt			     hapd->conf->anqp_3gpp_cell_net != NULL,
64261d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt			     0, 0, qi);
64361d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt		break;
64461d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	case ANQP_DOMAIN_NAME:
64561d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt		set_anqp_req(ANQP_REQ_DOMAIN_NAME, "Domain Name",
64661d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt			     hapd->conf->domain_name != NULL,
64761d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt			     0, 0, qi);
64861d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt		break;
64904949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	default:
65004949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		wpa_printf(MSG_DEBUG, "ANQP: Unsupported Info Id %u",
65104949598a23f501be6eec21697465fd46a28840aDmitry Shmidt			   info_id);
65204949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		break;
65304949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	}
65404949598a23f501be6eec21697465fd46a28840aDmitry Shmidt}
65504949598a23f501be6eec21697465fd46a28840aDmitry Shmidt
65604949598a23f501be6eec21697465fd46a28840aDmitry Shmidt
65704949598a23f501be6eec21697465fd46a28840aDmitry Shmidtstatic void rx_anqp_query_list(struct hostapd_data *hapd,
65804949598a23f501be6eec21697465fd46a28840aDmitry Shmidt			       const u8 *pos, const u8 *end,
65904949598a23f501be6eec21697465fd46a28840aDmitry Shmidt			       struct anqp_query_info *qi)
66004949598a23f501be6eec21697465fd46a28840aDmitry Shmidt{
66104949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	wpa_printf(MSG_DEBUG, "ANQP: %u Info IDs requested in Query list",
66204949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		   (unsigned int) (end - pos) / 2);
66304949598a23f501be6eec21697465fd46a28840aDmitry Shmidt
66404949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	while (pos + 2 <= end) {
66504949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		rx_anqp_query_list_id(hapd, WPA_GET_LE16(pos), qi);
66604949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		pos += 2;
66704949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	}
66804949598a23f501be6eec21697465fd46a28840aDmitry Shmidt}
66904949598a23f501be6eec21697465fd46a28840aDmitry Shmidt
67004949598a23f501be6eec21697465fd46a28840aDmitry Shmidt
671aa532510a7b8c4da2d7d6e2c11dda5db840894e4Dmitry Shmidt#ifdef CONFIG_HS20
672aa532510a7b8c4da2d7d6e2c11dda5db840894e4Dmitry Shmidt
67361d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidtstatic void rx_anqp_hs_query_list(struct hostapd_data *hapd, u8 subtype,
67461d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt				  struct anqp_query_info *qi)
67561d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt{
67661d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	switch (subtype) {
67761d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	case HS20_STYPE_CAPABILITY_LIST:
67861d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt		set_anqp_req(ANQP_REQ_HS_CAPABILITY_LIST, "HS Capability List",
67961d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt			     1, 0, 0, qi);
68061d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt		break;
68161d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	case HS20_STYPE_OPERATOR_FRIENDLY_NAME:
68261d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt		set_anqp_req(ANQP_REQ_OPERATOR_FRIENDLY_NAME,
68361d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt			     "Operator Friendly Name",
68461d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt			     hapd->conf->hs20_oper_friendly_name != NULL,
68561d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt			     0, 0, qi);
68661d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt		break;
68761d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	case HS20_STYPE_WAN_METRICS:
68861d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt		set_anqp_req(ANQP_REQ_WAN_METRICS, "WAN Metrics",
68961d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt			     hapd->conf->hs20_wan_metrics != NULL,
69061d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt			     0, 0, qi);
69161d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt		break;
69261d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	case HS20_STYPE_CONNECTION_CAPABILITY:
69361d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt		set_anqp_req(ANQP_REQ_CONNECTION_CAPABILITY,
69461d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt			     "Connection Capability",
69561d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt			     hapd->conf->hs20_connection_capability != NULL,
69661d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt			     0, 0, qi);
69761d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt		break;
69861d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	case HS20_STYPE_OPERATING_CLASS:
69961d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt		set_anqp_req(ANQP_REQ_OPERATING_CLASS, "Operating Class",
70061d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt			     hapd->conf->hs20_operating_class != NULL,
70161d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt			     0, 0, qi);
70261d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt		break;
70361d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	default:
70461d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt		wpa_printf(MSG_DEBUG, "ANQP: Unsupported HS 2.0 subtype %u",
70561d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt			   subtype);
70661d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt		break;
70761d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	}
70861d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt}
70961d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt
71061d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt
71161d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidtstatic void rx_anqp_hs_nai_home_realm(struct hostapd_data *hapd,
71261d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt				      const u8 *pos, const u8 *end,
71361d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt				      struct anqp_query_info *qi)
71461d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt{
71561d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	qi->request |= ANQP_REQ_NAI_HOME_REALM;
71661d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	qi->home_realm_query = pos;
71761d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	qi->home_realm_query_len = end - pos;
71861d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	if (hapd->conf->nai_realm_data != NULL) {
71961d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt		wpa_printf(MSG_DEBUG, "ANQP: HS 2.0 NAI Home Realm Query "
72061d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt			   "(local)");
72161d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	} else {
72261d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt		wpa_printf(MSG_DEBUG, "ANQP: HS 2.0 NAI Home Realm Query not "
72361d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt			   "available");
72461d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	}
72561d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt}
72661d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt
72761d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt
72861d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidtstatic void rx_anqp_vendor_specific(struct hostapd_data *hapd,
72961d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt				    const u8 *pos, const u8 *end,
73061d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt				    struct anqp_query_info *qi)
73161d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt{
73261d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	u32 oui;
73361d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	u8 subtype;
73461d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt
73561d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	if (pos + 4 > end) {
73661d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt		wpa_printf(MSG_DEBUG, "ANQP: Too short vendor specific ANQP "
73761d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt			   "Query element");
73861d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt		return;
73961d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	}
74061d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt
74161d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	oui = WPA_GET_BE24(pos);
74261d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	pos += 3;
74361d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	if (oui != OUI_WFA) {
74461d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt		wpa_printf(MSG_DEBUG, "ANQP: Unsupported vendor OUI %06x",
74561d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt			   oui);
74661d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt		return;
74761d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	}
74861d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt
74961d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	if (*pos != HS20_ANQP_OUI_TYPE) {
75061d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt		wpa_printf(MSG_DEBUG, "ANQP: Unsupported WFA vendor type %u",
75161d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt			   *pos);
75261d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt		return;
75361d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	}
75461d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	pos++;
75561d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt
75661d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	if (pos + 1 >= end)
75761d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt		return;
75861d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt
75961d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	subtype = *pos++;
76061d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	pos++; /* Reserved */
76161d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	switch (subtype) {
76261d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	case HS20_STYPE_QUERY_LIST:
76361d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt		wpa_printf(MSG_DEBUG, "ANQP: HS 2.0 Query List");
76461d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt		while (pos < end) {
76561d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt			rx_anqp_hs_query_list(hapd, *pos, qi);
76661d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt			pos++;
76761d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt		}
76861d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt		break;
76961d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	case HS20_STYPE_NAI_HOME_REALM_QUERY:
77061d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt		rx_anqp_hs_nai_home_realm(hapd, pos, end, qi);
77161d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt		break;
77261d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	default:
77361d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt		wpa_printf(MSG_DEBUG, "ANQP: Unsupported HS 2.0 query subtype "
77461d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt			   "%u", subtype);
77561d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt		break;
77661d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	}
77761d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt}
77861d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt
779aa532510a7b8c4da2d7d6e2c11dda5db840894e4Dmitry Shmidt#endif /* CONFIG_HS20 */
780aa532510a7b8c4da2d7d6e2c11dda5db840894e4Dmitry Shmidt
78161d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt
78204949598a23f501be6eec21697465fd46a28840aDmitry Shmidtstatic void gas_serv_req_local_processing(struct hostapd_data *hapd,
78304949598a23f501be6eec21697465fd46a28840aDmitry Shmidt					  const u8 *sa, u8 dialog_token,
7841846323989242844f0e857458a8939fa5836429cDmitry Shmidt					  struct anqp_query_info *qi, int prot)
78504949598a23f501be6eec21697465fd46a28840aDmitry Shmidt{
78604949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	struct wpabuf *buf, *tx_buf;
78704949598a23f501be6eec21697465fd46a28840aDmitry Shmidt
78861d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	buf = gas_serv_build_gas_resp_payload(hapd, qi->request, NULL,
78961d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt					      qi->home_realm_query,
79061d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt					      qi->home_realm_query_len);
79104949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	wpa_hexdump_buf(MSG_MSGDUMP, "ANQP: Locally generated ANQP responses",
79204949598a23f501be6eec21697465fd46a28840aDmitry Shmidt			buf);
79304949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	if (!buf)
79404949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		return;
79504949598a23f501be6eec21697465fd46a28840aDmitry Shmidt
79604949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	if (wpabuf_len(buf) > hapd->gas_frag_limit ||
79704949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	    hapd->conf->gas_comeback_delay) {
79804949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		struct gas_dialog_info *di;
79904949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		u16 comeback_delay = 1;
80004949598a23f501be6eec21697465fd46a28840aDmitry Shmidt
80104949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		if (hapd->conf->gas_comeback_delay) {
80204949598a23f501be6eec21697465fd46a28840aDmitry Shmidt			/* Testing - allow overriding of the delay value */
80304949598a23f501be6eec21697465fd46a28840aDmitry Shmidt			comeback_delay = hapd->conf->gas_comeback_delay;
80404949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		}
80504949598a23f501be6eec21697465fd46a28840aDmitry Shmidt
80604949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		wpa_printf(MSG_DEBUG, "ANQP: Too long response to fit in "
80704949598a23f501be6eec21697465fd46a28840aDmitry Shmidt			   "initial response - use GAS comeback");
80804949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		di = gas_dialog_create(hapd, sa, dialog_token);
80904949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		if (!di) {
81004949598a23f501be6eec21697465fd46a28840aDmitry Shmidt			wpa_printf(MSG_INFO, "ANQP: Could not create dialog "
81104949598a23f501be6eec21697465fd46a28840aDmitry Shmidt				   "for " MACSTR " (dialog token %u)",
81204949598a23f501be6eec21697465fd46a28840aDmitry Shmidt				   MAC2STR(sa), dialog_token);
81304949598a23f501be6eec21697465fd46a28840aDmitry Shmidt			wpabuf_free(buf);
81404949598a23f501be6eec21697465fd46a28840aDmitry Shmidt			return;
81504949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		}
8161846323989242844f0e857458a8939fa5836429cDmitry Shmidt		di->prot = prot;
81704949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		di->sd_resp = buf;
81804949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		di->sd_resp_pos = 0;
81904949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		tx_buf = gas_anqp_build_initial_resp_buf(
82004949598a23f501be6eec21697465fd46a28840aDmitry Shmidt			dialog_token, WLAN_STATUS_SUCCESS, comeback_delay,
82104949598a23f501be6eec21697465fd46a28840aDmitry Shmidt			NULL);
82204949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	} else {
82304949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		wpa_printf(MSG_DEBUG, "ANQP: Initial response (no comeback)");
82404949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		tx_buf = gas_anqp_build_initial_resp_buf(
82504949598a23f501be6eec21697465fd46a28840aDmitry Shmidt			dialog_token, WLAN_STATUS_SUCCESS, 0, buf);
82604949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		wpabuf_free(buf);
82704949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	}
82804949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	if (!tx_buf)
82904949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		return;
8301846323989242844f0e857458a8939fa5836429cDmitry Shmidt	if (prot)
8311846323989242844f0e857458a8939fa5836429cDmitry Shmidt		convert_to_protected_dual(tx_buf);
83204949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	hostapd_drv_send_action(hapd, hapd->iface->freq, 0, sa,
83304949598a23f501be6eec21697465fd46a28840aDmitry Shmidt				wpabuf_head(tx_buf), wpabuf_len(tx_buf));
83404949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	wpabuf_free(tx_buf);
83504949598a23f501be6eec21697465fd46a28840aDmitry Shmidt}
83604949598a23f501be6eec21697465fd46a28840aDmitry Shmidt
83704949598a23f501be6eec21697465fd46a28840aDmitry Shmidt
83804949598a23f501be6eec21697465fd46a28840aDmitry Shmidtstatic void gas_serv_rx_gas_initial_req(struct hostapd_data *hapd,
83904949598a23f501be6eec21697465fd46a28840aDmitry Shmidt					const u8 *sa,
8401846323989242844f0e857458a8939fa5836429cDmitry Shmidt					const u8 *data, size_t len, int prot)
84104949598a23f501be6eec21697465fd46a28840aDmitry Shmidt{
84204949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	const u8 *pos = data;
84304949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	const u8 *end = data + len;
84404949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	const u8 *next;
84504949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	u8 dialog_token;
84604949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	u16 slen;
84704949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	struct anqp_query_info qi;
84804949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	const u8 *adv_proto;
84904949598a23f501be6eec21697465fd46a28840aDmitry Shmidt
85004949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	if (len < 1 + 2)
85104949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		return;
85204949598a23f501be6eec21697465fd46a28840aDmitry Shmidt
85304949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	os_memset(&qi, 0, sizeof(qi));
85404949598a23f501be6eec21697465fd46a28840aDmitry Shmidt
85504949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	dialog_token = *pos++;
85604949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	wpa_msg(hapd->msg_ctx, MSG_DEBUG,
85704949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		"GAS: GAS Initial Request from " MACSTR " (dialog token %u) ",
85804949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		MAC2STR(sa), dialog_token);
85904949598a23f501be6eec21697465fd46a28840aDmitry Shmidt
86004949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	if (*pos != WLAN_EID_ADV_PROTO) {
86104949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		wpa_msg(hapd->msg_ctx, MSG_DEBUG,
86204949598a23f501be6eec21697465fd46a28840aDmitry Shmidt			"GAS: Unexpected IE in GAS Initial Request: %u", *pos);
86304949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		return;
86404949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	}
86504949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	adv_proto = pos++;
86604949598a23f501be6eec21697465fd46a28840aDmitry Shmidt
86704949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	slen = *pos++;
86804949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	next = pos + slen;
86904949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	if (next > end || slen < 2) {
87004949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		wpa_msg(hapd->msg_ctx, MSG_DEBUG,
87104949598a23f501be6eec21697465fd46a28840aDmitry Shmidt			"GAS: Invalid IE in GAS Initial Request");
87204949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		return;
87304949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	}
87404949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	pos++; /* skip QueryRespLenLimit and PAME-BI */
87504949598a23f501be6eec21697465fd46a28840aDmitry Shmidt
87604949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	if (*pos != ACCESS_NETWORK_QUERY_PROTOCOL) {
87704949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		struct wpabuf *buf;
87804949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		wpa_msg(hapd->msg_ctx, MSG_DEBUG,
87904949598a23f501be6eec21697465fd46a28840aDmitry Shmidt			"GAS: Unsupported GAS advertisement protocol id %u",
88004949598a23f501be6eec21697465fd46a28840aDmitry Shmidt			*pos);
88104949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		if (sa[0] & 0x01)
88204949598a23f501be6eec21697465fd46a28840aDmitry Shmidt			return; /* Invalid source address - drop silently */
88304949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		buf = gas_build_initial_resp(
88404949598a23f501be6eec21697465fd46a28840aDmitry Shmidt			dialog_token, WLAN_STATUS_GAS_ADV_PROTO_NOT_SUPPORTED,
88504949598a23f501be6eec21697465fd46a28840aDmitry Shmidt			0, 2 + slen + 2);
88604949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		if (buf == NULL)
88704949598a23f501be6eec21697465fd46a28840aDmitry Shmidt			return;
88804949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		wpabuf_put_data(buf, adv_proto, 2 + slen);
88904949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		wpabuf_put_le16(buf, 0); /* Query Response Length */
8901846323989242844f0e857458a8939fa5836429cDmitry Shmidt		if (prot)
8911846323989242844f0e857458a8939fa5836429cDmitry Shmidt			convert_to_protected_dual(buf);
89204949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		hostapd_drv_send_action(hapd, hapd->iface->freq, 0, sa,
89304949598a23f501be6eec21697465fd46a28840aDmitry Shmidt					wpabuf_head(buf), wpabuf_len(buf));
89404949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		wpabuf_free(buf);
89504949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		return;
89604949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	}
89704949598a23f501be6eec21697465fd46a28840aDmitry Shmidt
89804949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	pos = next;
89904949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	/* Query Request */
90004949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	if (pos + 2 > end)
90104949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		return;
90204949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	slen = WPA_GET_LE16(pos);
90304949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	pos += 2;
90404949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	if (pos + slen > end)
90504949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		return;
90604949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	end = pos + slen;
90704949598a23f501be6eec21697465fd46a28840aDmitry Shmidt
90804949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	/* ANQP Query Request */
90904949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	while (pos < end) {
91004949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		u16 info_id, elen;
91104949598a23f501be6eec21697465fd46a28840aDmitry Shmidt
91204949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		if (pos + 4 > end)
91304949598a23f501be6eec21697465fd46a28840aDmitry Shmidt			return;
91404949598a23f501be6eec21697465fd46a28840aDmitry Shmidt
91504949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		info_id = WPA_GET_LE16(pos);
91604949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		pos += 2;
91704949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		elen = WPA_GET_LE16(pos);
91804949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		pos += 2;
91904949598a23f501be6eec21697465fd46a28840aDmitry Shmidt
92004949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		if (pos + elen > end) {
92104949598a23f501be6eec21697465fd46a28840aDmitry Shmidt			wpa_printf(MSG_DEBUG, "ANQP: Invalid Query Request");
92204949598a23f501be6eec21697465fd46a28840aDmitry Shmidt			return;
92304949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		}
92404949598a23f501be6eec21697465fd46a28840aDmitry Shmidt
92504949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		switch (info_id) {
92604949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		case ANQP_QUERY_LIST:
92704949598a23f501be6eec21697465fd46a28840aDmitry Shmidt			rx_anqp_query_list(hapd, pos, pos + elen, &qi);
92804949598a23f501be6eec21697465fd46a28840aDmitry Shmidt			break;
929aa532510a7b8c4da2d7d6e2c11dda5db840894e4Dmitry Shmidt#ifdef CONFIG_HS20
93061d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt		case ANQP_VENDOR_SPECIFIC:
93161d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt			rx_anqp_vendor_specific(hapd, pos, pos + elen, &qi);
93261d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt			break;
933aa532510a7b8c4da2d7d6e2c11dda5db840894e4Dmitry Shmidt#endif /* CONFIG_HS20 */
93404949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		default:
93504949598a23f501be6eec21697465fd46a28840aDmitry Shmidt			wpa_printf(MSG_DEBUG, "ANQP: Unsupported Query "
93604949598a23f501be6eec21697465fd46a28840aDmitry Shmidt				   "Request element %u", info_id);
93704949598a23f501be6eec21697465fd46a28840aDmitry Shmidt			break;
93804949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		}
93904949598a23f501be6eec21697465fd46a28840aDmitry Shmidt
94004949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		pos += elen;
94104949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	}
94204949598a23f501be6eec21697465fd46a28840aDmitry Shmidt
9431846323989242844f0e857458a8939fa5836429cDmitry Shmidt	gas_serv_req_local_processing(hapd, sa, dialog_token, &qi, prot);
94404949598a23f501be6eec21697465fd46a28840aDmitry Shmidt}
94504949598a23f501be6eec21697465fd46a28840aDmitry Shmidt
94604949598a23f501be6eec21697465fd46a28840aDmitry Shmidt
94704949598a23f501be6eec21697465fd46a28840aDmitry Shmidtvoid gas_serv_tx_gas_response(struct hostapd_data *hapd, const u8 *dst,
94804949598a23f501be6eec21697465fd46a28840aDmitry Shmidt			      struct gas_dialog_info *dialog)
94904949598a23f501be6eec21697465fd46a28840aDmitry Shmidt{
95004949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	struct wpabuf *buf, *tx_buf;
95104949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	u8 dialog_token = dialog->dialog_token;
95204949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	size_t frag_len;
95304949598a23f501be6eec21697465fd46a28840aDmitry Shmidt
95404949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	if (dialog->sd_resp == NULL) {
95504949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		buf = gas_serv_build_gas_resp_payload(hapd,
95604949598a23f501be6eec21697465fd46a28840aDmitry Shmidt						      dialog->all_requested,
95761d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt						      dialog, NULL, 0);
95804949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		wpa_hexdump_buf(MSG_MSGDUMP, "ANQP: Generated ANQP responses",
95904949598a23f501be6eec21697465fd46a28840aDmitry Shmidt			buf);
96004949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		if (!buf)
96104949598a23f501be6eec21697465fd46a28840aDmitry Shmidt			goto tx_gas_response_done;
96204949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		dialog->sd_resp = buf;
96304949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		dialog->sd_resp_pos = 0;
96404949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	}
96504949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	frag_len = wpabuf_len(dialog->sd_resp) - dialog->sd_resp_pos;
96604949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	if (frag_len > hapd->gas_frag_limit || dialog->comeback_delay ||
96704949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	    hapd->conf->gas_comeback_delay) {
96804949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		u16 comeback_delay_tus = dialog->comeback_delay +
96904949598a23f501be6eec21697465fd46a28840aDmitry Shmidt			GAS_SERV_COMEBACK_DELAY_FUDGE;
97004949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		u32 comeback_delay_secs, comeback_delay_usecs;
97104949598a23f501be6eec21697465fd46a28840aDmitry Shmidt
97204949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		if (hapd->conf->gas_comeback_delay) {
97304949598a23f501be6eec21697465fd46a28840aDmitry Shmidt			/* Testing - allow overriding of the delay value */
97404949598a23f501be6eec21697465fd46a28840aDmitry Shmidt			comeback_delay_tus = hapd->conf->gas_comeback_delay;
97504949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		}
97604949598a23f501be6eec21697465fd46a28840aDmitry Shmidt
97704949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		wpa_printf(MSG_DEBUG, "GAS: Response frag_len %u (frag limit "
97804949598a23f501be6eec21697465fd46a28840aDmitry Shmidt			   "%u) and comeback delay %u, "
97904949598a23f501be6eec21697465fd46a28840aDmitry Shmidt			   "requesting comebacks", (unsigned int) frag_len,
98004949598a23f501be6eec21697465fd46a28840aDmitry Shmidt			   (unsigned int) hapd->gas_frag_limit,
98104949598a23f501be6eec21697465fd46a28840aDmitry Shmidt			   dialog->comeback_delay);
98204949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		tx_buf = gas_anqp_build_initial_resp_buf(dialog_token,
98304949598a23f501be6eec21697465fd46a28840aDmitry Shmidt							 WLAN_STATUS_SUCCESS,
98404949598a23f501be6eec21697465fd46a28840aDmitry Shmidt							 comeback_delay_tus,
98504949598a23f501be6eec21697465fd46a28840aDmitry Shmidt							 NULL);
98604949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		if (tx_buf) {
98704949598a23f501be6eec21697465fd46a28840aDmitry Shmidt			wpa_msg(hapd->msg_ctx, MSG_DEBUG,
98804949598a23f501be6eec21697465fd46a28840aDmitry Shmidt				"GAS: Tx GAS Initial Resp (comeback = 10TU)");
9891846323989242844f0e857458a8939fa5836429cDmitry Shmidt			if (dialog->prot)
9901846323989242844f0e857458a8939fa5836429cDmitry Shmidt				convert_to_protected_dual(tx_buf);
99104949598a23f501be6eec21697465fd46a28840aDmitry Shmidt			hostapd_drv_send_action(hapd, hapd->iface->freq, 0,
99204949598a23f501be6eec21697465fd46a28840aDmitry Shmidt						dst,
99304949598a23f501be6eec21697465fd46a28840aDmitry Shmidt						wpabuf_head(tx_buf),
99404949598a23f501be6eec21697465fd46a28840aDmitry Shmidt						wpabuf_len(tx_buf));
99504949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		}
99604949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		wpabuf_free(tx_buf);
99704949598a23f501be6eec21697465fd46a28840aDmitry Shmidt
99804949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		/* start a timer of 1.5 * comeback-delay */
99904949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		comeback_delay_tus = comeback_delay_tus +
100004949598a23f501be6eec21697465fd46a28840aDmitry Shmidt			(comeback_delay_tus / 2);
100104949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		comeback_delay_secs = (comeback_delay_tus * 1024) / 1000000;
100204949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		comeback_delay_usecs = (comeback_delay_tus * 1024) -
100304949598a23f501be6eec21697465fd46a28840aDmitry Shmidt			(comeback_delay_secs * 1000000);
100404949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		eloop_register_timeout(comeback_delay_secs,
100504949598a23f501be6eec21697465fd46a28840aDmitry Shmidt				       comeback_delay_usecs,
100604949598a23f501be6eec21697465fd46a28840aDmitry Shmidt				       gas_serv_clear_cached_ies, dialog,
100704949598a23f501be6eec21697465fd46a28840aDmitry Shmidt				       NULL);
100804949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		goto tx_gas_response_done;
100904949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	}
101004949598a23f501be6eec21697465fd46a28840aDmitry Shmidt
101104949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	buf = wpabuf_alloc_copy(wpabuf_head_u8(dialog->sd_resp) +
101204949598a23f501be6eec21697465fd46a28840aDmitry Shmidt				dialog->sd_resp_pos, frag_len);
101304949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	if (buf == NULL) {
101404949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		wpa_msg(hapd->msg_ctx, MSG_DEBUG, "GAS: Buffer allocation "
101504949598a23f501be6eec21697465fd46a28840aDmitry Shmidt			"failed");
101604949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		goto tx_gas_response_done;
101704949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	}
101804949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	tx_buf = gas_anqp_build_initial_resp_buf(dialog_token,
101904949598a23f501be6eec21697465fd46a28840aDmitry Shmidt						 WLAN_STATUS_SUCCESS, 0, buf);
102004949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	wpabuf_free(buf);
102104949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	if (tx_buf == NULL)
102204949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		goto tx_gas_response_done;
102304949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	wpa_msg(hapd->msg_ctx, MSG_DEBUG, "GAS: Tx GAS Initial "
102404949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		"Response (frag_id %d frag_len %d)",
102504949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		dialog->sd_frag_id, (int) frag_len);
102604949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	dialog->sd_frag_id++;
102704949598a23f501be6eec21697465fd46a28840aDmitry Shmidt
10281846323989242844f0e857458a8939fa5836429cDmitry Shmidt	if (dialog->prot)
10291846323989242844f0e857458a8939fa5836429cDmitry Shmidt		convert_to_protected_dual(tx_buf);
103004949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	hostapd_drv_send_action(hapd, hapd->iface->freq, 0, dst,
103104949598a23f501be6eec21697465fd46a28840aDmitry Shmidt				wpabuf_head(tx_buf), wpabuf_len(tx_buf));
103204949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	wpabuf_free(tx_buf);
103304949598a23f501be6eec21697465fd46a28840aDmitry Shmidttx_gas_response_done:
103404949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	gas_serv_clear_cached_ies(dialog, NULL);
103504949598a23f501be6eec21697465fd46a28840aDmitry Shmidt}
103604949598a23f501be6eec21697465fd46a28840aDmitry Shmidt
103704949598a23f501be6eec21697465fd46a28840aDmitry Shmidt
103804949598a23f501be6eec21697465fd46a28840aDmitry Shmidtstatic void gas_serv_rx_gas_comeback_req(struct hostapd_data *hapd,
103904949598a23f501be6eec21697465fd46a28840aDmitry Shmidt					 const u8 *sa,
10401846323989242844f0e857458a8939fa5836429cDmitry Shmidt					 const u8 *data, size_t len, int prot)
104104949598a23f501be6eec21697465fd46a28840aDmitry Shmidt{
104204949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	struct gas_dialog_info *dialog;
104304949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	struct wpabuf *buf, *tx_buf;
104404949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	u8 dialog_token;
104504949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	size_t frag_len;
104604949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	int more = 0;
104704949598a23f501be6eec21697465fd46a28840aDmitry Shmidt
104804949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	wpa_hexdump(MSG_DEBUG, "GAS: RX GAS Comeback Request", data, len);
104904949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	if (len < 1)
105004949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		return;
105104949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	dialog_token = *data;
105204949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	wpa_msg(hapd->msg_ctx, MSG_DEBUG, "GAS: Dialog Token: %u",
105304949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		dialog_token);
105404949598a23f501be6eec21697465fd46a28840aDmitry Shmidt
105504949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	dialog = gas_serv_dialog_find(hapd, sa, dialog_token);
105604949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	if (!dialog) {
105704949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		wpa_msg(hapd->msg_ctx, MSG_DEBUG, "GAS: No pending SD "
105804949598a23f501be6eec21697465fd46a28840aDmitry Shmidt			"response fragment for " MACSTR " dialog token %u",
105904949598a23f501be6eec21697465fd46a28840aDmitry Shmidt			MAC2STR(sa), dialog_token);
106004949598a23f501be6eec21697465fd46a28840aDmitry Shmidt
106104949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		if (sa[0] & 0x01)
106204949598a23f501be6eec21697465fd46a28840aDmitry Shmidt			return; /* Invalid source address - drop silently */
106304949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		tx_buf = gas_anqp_build_comeback_resp_buf(
106404949598a23f501be6eec21697465fd46a28840aDmitry Shmidt			dialog_token, WLAN_STATUS_NO_OUTSTANDING_GAS_REQ, 0, 0,
106504949598a23f501be6eec21697465fd46a28840aDmitry Shmidt			0, NULL);
106604949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		if (tx_buf == NULL)
106704949598a23f501be6eec21697465fd46a28840aDmitry Shmidt			return;
106804949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		goto send_resp;
106904949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	}
107004949598a23f501be6eec21697465fd46a28840aDmitry Shmidt
107104949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	if (dialog->sd_resp == NULL) {
107204949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		wpa_printf(MSG_DEBUG, "GAS: Remote request 0x%x received 0x%x",
107304949598a23f501be6eec21697465fd46a28840aDmitry Shmidt			   dialog->requested, dialog->received);
107404949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		if ((dialog->requested & dialog->received) !=
107504949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		    dialog->requested) {
107604949598a23f501be6eec21697465fd46a28840aDmitry Shmidt			wpa_printf(MSG_DEBUG, "GAS: Did not receive response "
107704949598a23f501be6eec21697465fd46a28840aDmitry Shmidt				   "from remote processing");
107804949598a23f501be6eec21697465fd46a28840aDmitry Shmidt			gas_serv_dialog_clear(dialog);
107904949598a23f501be6eec21697465fd46a28840aDmitry Shmidt			tx_buf = gas_anqp_build_comeback_resp_buf(
108004949598a23f501be6eec21697465fd46a28840aDmitry Shmidt				dialog_token,
108104949598a23f501be6eec21697465fd46a28840aDmitry Shmidt				WLAN_STATUS_GAS_RESP_NOT_RECEIVED, 0, 0, 0,
108204949598a23f501be6eec21697465fd46a28840aDmitry Shmidt				NULL);
108304949598a23f501be6eec21697465fd46a28840aDmitry Shmidt			if (tx_buf == NULL)
108404949598a23f501be6eec21697465fd46a28840aDmitry Shmidt				return;
108504949598a23f501be6eec21697465fd46a28840aDmitry Shmidt			goto send_resp;
108604949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		}
108704949598a23f501be6eec21697465fd46a28840aDmitry Shmidt
108804949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		buf = gas_serv_build_gas_resp_payload(hapd,
108904949598a23f501be6eec21697465fd46a28840aDmitry Shmidt						      dialog->all_requested,
109061d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt						      dialog, NULL, 0);
109104949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		wpa_hexdump_buf(MSG_MSGDUMP, "ANQP: Generated ANQP responses",
109204949598a23f501be6eec21697465fd46a28840aDmitry Shmidt			buf);
109304949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		if (!buf)
109404949598a23f501be6eec21697465fd46a28840aDmitry Shmidt			goto rx_gas_comeback_req_done;
109504949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		dialog->sd_resp = buf;
109604949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		dialog->sd_resp_pos = 0;
109704949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	}
109804949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	frag_len = wpabuf_len(dialog->sd_resp) - dialog->sd_resp_pos;
109904949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	if (frag_len > hapd->gas_frag_limit) {
110004949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		frag_len = hapd->gas_frag_limit;
110104949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		more = 1;
110204949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	}
110304949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	wpa_msg(hapd->msg_ctx, MSG_DEBUG, "GAS: resp frag_len %u",
110404949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		(unsigned int) frag_len);
110504949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	buf = wpabuf_alloc_copy(wpabuf_head_u8(dialog->sd_resp) +
110604949598a23f501be6eec21697465fd46a28840aDmitry Shmidt				dialog->sd_resp_pos, frag_len);
110704949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	if (buf == NULL) {
110804949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		wpa_msg(hapd->msg_ctx, MSG_DEBUG, "GAS: Failed to allocate "
110904949598a23f501be6eec21697465fd46a28840aDmitry Shmidt			"buffer");
111004949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		goto rx_gas_comeback_req_done;
111104949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	}
111204949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	tx_buf = gas_anqp_build_comeback_resp_buf(dialog_token,
111304949598a23f501be6eec21697465fd46a28840aDmitry Shmidt						  WLAN_STATUS_SUCCESS,
111404949598a23f501be6eec21697465fd46a28840aDmitry Shmidt						  dialog->sd_frag_id,
111504949598a23f501be6eec21697465fd46a28840aDmitry Shmidt						  more, 0, buf);
111604949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	wpabuf_free(buf);
111704949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	if (tx_buf == NULL)
111804949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		goto rx_gas_comeback_req_done;
111904949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	wpa_msg(hapd->msg_ctx, MSG_DEBUG, "GAS: Tx GAS Comeback Response "
112004949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		"(frag_id %d more=%d frag_len=%d)",
112104949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		dialog->sd_frag_id, more, (int) frag_len);
112204949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	dialog->sd_frag_id++;
112304949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	dialog->sd_resp_pos += frag_len;
112404949598a23f501be6eec21697465fd46a28840aDmitry Shmidt
112504949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	if (more) {
112604949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		wpa_msg(hapd->msg_ctx, MSG_DEBUG, "GAS: %d more bytes remain "
112704949598a23f501be6eec21697465fd46a28840aDmitry Shmidt			"to be sent",
112804949598a23f501be6eec21697465fd46a28840aDmitry Shmidt			(int) (wpabuf_len(dialog->sd_resp) -
112904949598a23f501be6eec21697465fd46a28840aDmitry Shmidt			       dialog->sd_resp_pos));
113004949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	} else {
113104949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		wpa_msg(hapd->msg_ctx, MSG_DEBUG, "GAS: All fragments of "
113204949598a23f501be6eec21697465fd46a28840aDmitry Shmidt			"SD response sent");
113304949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		gas_serv_dialog_clear(dialog);
113404949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		gas_serv_free_dialogs(hapd, sa);
113504949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	}
113604949598a23f501be6eec21697465fd46a28840aDmitry Shmidt
113704949598a23f501be6eec21697465fd46a28840aDmitry Shmidtsend_resp:
11381846323989242844f0e857458a8939fa5836429cDmitry Shmidt	if (prot)
11391846323989242844f0e857458a8939fa5836429cDmitry Shmidt		convert_to_protected_dual(tx_buf);
114004949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	hostapd_drv_send_action(hapd, hapd->iface->freq, 0, sa,
114104949598a23f501be6eec21697465fd46a28840aDmitry Shmidt				wpabuf_head(tx_buf), wpabuf_len(tx_buf));
114204949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	wpabuf_free(tx_buf);
114304949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	return;
114404949598a23f501be6eec21697465fd46a28840aDmitry Shmidt
114504949598a23f501be6eec21697465fd46a28840aDmitry Shmidtrx_gas_comeback_req_done:
114604949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	gas_serv_clear_cached_ies(dialog, NULL);
114704949598a23f501be6eec21697465fd46a28840aDmitry Shmidt}
114804949598a23f501be6eec21697465fd46a28840aDmitry Shmidt
114904949598a23f501be6eec21697465fd46a28840aDmitry Shmidt
115004949598a23f501be6eec21697465fd46a28840aDmitry Shmidtstatic void gas_serv_rx_public_action(void *ctx, const u8 *buf, size_t len,
115104949598a23f501be6eec21697465fd46a28840aDmitry Shmidt				      int freq)
115204949598a23f501be6eec21697465fd46a28840aDmitry Shmidt{
115304949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	struct hostapd_data *hapd = ctx;
115404949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	const struct ieee80211_mgmt *mgmt;
115504949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	size_t hdr_len;
115604949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	const u8 *sa, *data;
11571846323989242844f0e857458a8939fa5836429cDmitry Shmidt	int prot;
115804949598a23f501be6eec21697465fd46a28840aDmitry Shmidt
115904949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	mgmt = (const struct ieee80211_mgmt *) buf;
116004949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	hdr_len = (const u8 *) &mgmt->u.action.u.vs_public_action.action - buf;
116104949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	if (hdr_len > len)
116204949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		return;
11631846323989242844f0e857458a8939fa5836429cDmitry Shmidt	if (mgmt->u.action.category != WLAN_ACTION_PUBLIC &&
11641846323989242844f0e857458a8939fa5836429cDmitry Shmidt	    mgmt->u.action.category != WLAN_ACTION_PROTECTED_DUAL)
116504949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		return;
11661846323989242844f0e857458a8939fa5836429cDmitry Shmidt	/*
11671846323989242844f0e857458a8939fa5836429cDmitry Shmidt	 * Note: Public Action and Protected Dual of Public Action frames share
11681846323989242844f0e857458a8939fa5836429cDmitry Shmidt	 * the same payload structure, so it is fine to use definitions of
11691846323989242844f0e857458a8939fa5836429cDmitry Shmidt	 * Public Action frames to process both.
11701846323989242844f0e857458a8939fa5836429cDmitry Shmidt	 */
11711846323989242844f0e857458a8939fa5836429cDmitry Shmidt	prot = mgmt->u.action.category == WLAN_ACTION_PROTECTED_DUAL;
117204949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	sa = mgmt->sa;
117304949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	len -= hdr_len;
117404949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	data = &mgmt->u.action.u.public_action.action;
117504949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	switch (data[0]) {
117604949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	case WLAN_PA_GAS_INITIAL_REQ:
11771846323989242844f0e857458a8939fa5836429cDmitry Shmidt		gas_serv_rx_gas_initial_req(hapd, sa, data + 1, len - 1, prot);
117804949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		break;
117904949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	case WLAN_PA_GAS_COMEBACK_REQ:
11801846323989242844f0e857458a8939fa5836429cDmitry Shmidt		gas_serv_rx_gas_comeback_req(hapd, sa, data + 1, len - 1, prot);
118104949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		break;
118204949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	}
118304949598a23f501be6eec21697465fd46a28840aDmitry Shmidt}
118404949598a23f501be6eec21697465fd46a28840aDmitry Shmidt
118504949598a23f501be6eec21697465fd46a28840aDmitry Shmidt
118604949598a23f501be6eec21697465fd46a28840aDmitry Shmidtint gas_serv_init(struct hostapd_data *hapd)
118704949598a23f501be6eec21697465fd46a28840aDmitry Shmidt{
11884b9d52f502481b258fec743c03a5e957e5605afcDmitry Shmidt	hapd->public_action_cb2 = gas_serv_rx_public_action;
11894b9d52f502481b258fec743c03a5e957e5605afcDmitry Shmidt	hapd->public_action_cb2_ctx = hapd;
119004949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	hapd->gas_frag_limit = 1400;
119104949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	if (hapd->conf->gas_frag_limit > 0)
119204949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		hapd->gas_frag_limit = hapd->conf->gas_frag_limit;
119304949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	return 0;
119404949598a23f501be6eec21697465fd46a28840aDmitry Shmidt}
119504949598a23f501be6eec21697465fd46a28840aDmitry Shmidt
119604949598a23f501be6eec21697465fd46a28840aDmitry Shmidt
119704949598a23f501be6eec21697465fd46a28840aDmitry Shmidtvoid gas_serv_deinit(struct hostapd_data *hapd)
119804949598a23f501be6eec21697465fd46a28840aDmitry Shmidt{
119904949598a23f501be6eec21697465fd46a28840aDmitry Shmidt}
1200