1f05cddf9SRui Paulo /*
2f05cddf9SRui Paulo * Generic advertisement service (GAS) server
35b9c547cSRui Paulo * Copyright (c) 2011-2014, Qualcomm Atheros, Inc.
4f05cddf9SRui Paulo *
5f05cddf9SRui Paulo * This software may be distributed under the terms of the BSD license.
6f05cddf9SRui Paulo * See README for more details.
7f05cddf9SRui Paulo */
8f05cddf9SRui Paulo
9f05cddf9SRui Paulo #include "includes.h"
10f05cddf9SRui Paulo
11f05cddf9SRui Paulo #include "common.h"
12f05cddf9SRui Paulo #include "common/ieee802_11_defs.h"
13f05cddf9SRui Paulo #include "common/gas.h"
1485732ac8SCy Schubert #include "common/wpa_ctrl.h"
15f05cddf9SRui Paulo #include "utils/eloop.h"
16f05cddf9SRui Paulo #include "hostapd.h"
17f05cddf9SRui Paulo #include "ap_config.h"
18f05cddf9SRui Paulo #include "ap_drv_ops.h"
1985732ac8SCy Schubert #include "dpp_hostapd.h"
20f05cddf9SRui Paulo #include "sta_info.h"
21f05cddf9SRui Paulo #include "gas_serv.h"
22f05cddf9SRui Paulo
23f05cddf9SRui Paulo
2485732ac8SCy Schubert #ifdef CONFIG_DPP
gas_serv_write_dpp_adv_proto(struct wpabuf * buf)2585732ac8SCy Schubert static void gas_serv_write_dpp_adv_proto(struct wpabuf *buf)
2685732ac8SCy Schubert {
2785732ac8SCy Schubert wpabuf_put_u8(buf, WLAN_EID_ADV_PROTO);
2885732ac8SCy Schubert wpabuf_put_u8(buf, 8); /* Length */
2985732ac8SCy Schubert wpabuf_put_u8(buf, 0x7f);
3085732ac8SCy Schubert wpabuf_put_u8(buf, WLAN_EID_VENDOR_SPECIFIC);
3185732ac8SCy Schubert wpabuf_put_u8(buf, 5);
3285732ac8SCy Schubert wpabuf_put_be24(buf, OUI_WFA);
3385732ac8SCy Schubert wpabuf_put_u8(buf, DPP_OUI_TYPE);
3485732ac8SCy Schubert wpabuf_put_u8(buf, 0x01);
3585732ac8SCy Schubert }
3685732ac8SCy Schubert #endif /* CONFIG_DPP */
3785732ac8SCy Schubert
3885732ac8SCy Schubert
convert_to_protected_dual(struct wpabuf * msg)395b9c547cSRui Paulo static void convert_to_protected_dual(struct wpabuf *msg)
405b9c547cSRui Paulo {
415b9c547cSRui Paulo u8 *categ = wpabuf_mhead_u8(msg);
425b9c547cSRui Paulo *categ = WLAN_ACTION_PROTECTED_DUAL;
435b9c547cSRui Paulo }
445b9c547cSRui Paulo
455b9c547cSRui Paulo
46f05cddf9SRui Paulo static struct gas_dialog_info *
gas_dialog_create(struct hostapd_data * hapd,const u8 * addr,u8 dialog_token)47f05cddf9SRui Paulo gas_dialog_create(struct hostapd_data *hapd, const u8 *addr, u8 dialog_token)
48f05cddf9SRui Paulo {
49f05cddf9SRui Paulo struct sta_info *sta;
50f05cddf9SRui Paulo struct gas_dialog_info *dia = NULL;
51f05cddf9SRui Paulo int i, j;
52f05cddf9SRui Paulo
53f05cddf9SRui Paulo sta = ap_get_sta(hapd, addr);
54f05cddf9SRui Paulo if (!sta) {
55f05cddf9SRui Paulo /*
56f05cddf9SRui Paulo * We need a STA entry to be able to maintain state for
57f05cddf9SRui Paulo * the GAS query.
58f05cddf9SRui Paulo */
59f05cddf9SRui Paulo wpa_printf(MSG_DEBUG, "ANQP: Add a temporary STA entry for "
60f05cddf9SRui Paulo "GAS query");
61f05cddf9SRui Paulo sta = ap_sta_add(hapd, addr);
62f05cddf9SRui Paulo if (!sta) {
63f05cddf9SRui Paulo wpa_printf(MSG_DEBUG, "Failed to add STA " MACSTR
64f05cddf9SRui Paulo " for GAS query", MAC2STR(addr));
65f05cddf9SRui Paulo return NULL;
66f05cddf9SRui Paulo }
67f05cddf9SRui Paulo sta->flags |= WLAN_STA_GAS;
68f05cddf9SRui Paulo /*
69f05cddf9SRui Paulo * The default inactivity is 300 seconds. We don't need
7085732ac8SCy Schubert * it to be that long. Use five second timeout and increase this
7185732ac8SCy Schubert * with the comeback_delay for testing cases.
72f05cddf9SRui Paulo */
7385732ac8SCy Schubert ap_sta_session_timeout(hapd, sta,
7485732ac8SCy Schubert hapd->conf->gas_comeback_delay / 1024 +
7585732ac8SCy Schubert 5);
765b9c547cSRui Paulo } else {
775b9c547cSRui Paulo ap_sta_replenish_timeout(hapd, sta, 5);
78f05cddf9SRui Paulo }
79f05cddf9SRui Paulo
80f05cddf9SRui Paulo if (sta->gas_dialog == NULL) {
815b9c547cSRui Paulo sta->gas_dialog = os_calloc(GAS_DIALOG_MAX,
82f05cddf9SRui Paulo sizeof(struct gas_dialog_info));
83f05cddf9SRui Paulo if (sta->gas_dialog == NULL)
84f05cddf9SRui Paulo return NULL;
85f05cddf9SRui Paulo }
86f05cddf9SRui Paulo
87f05cddf9SRui Paulo for (i = sta->gas_dialog_next, j = 0; j < GAS_DIALOG_MAX; i++, j++) {
88f05cddf9SRui Paulo if (i == GAS_DIALOG_MAX)
89f05cddf9SRui Paulo i = 0;
90f05cddf9SRui Paulo if (sta->gas_dialog[i].valid)
91f05cddf9SRui Paulo continue;
92f05cddf9SRui Paulo dia = &sta->gas_dialog[i];
93f05cddf9SRui Paulo dia->valid = 1;
94f05cddf9SRui Paulo dia->dialog_token = dialog_token;
95f05cddf9SRui Paulo sta->gas_dialog_next = (++i == GAS_DIALOG_MAX) ? 0 : i;
96f05cddf9SRui Paulo return dia;
97f05cddf9SRui Paulo }
98f05cddf9SRui Paulo
99f05cddf9SRui Paulo wpa_msg(hapd->msg_ctx, MSG_ERROR, "ANQP: Could not create dialog for "
100f05cddf9SRui Paulo MACSTR " dialog_token %u. Consider increasing "
101f05cddf9SRui Paulo "GAS_DIALOG_MAX.", MAC2STR(addr), dialog_token);
102f05cddf9SRui Paulo
103f05cddf9SRui Paulo return NULL;
104f05cddf9SRui Paulo }
105f05cddf9SRui Paulo
106f05cddf9SRui Paulo
107f05cddf9SRui Paulo struct gas_dialog_info *
gas_serv_dialog_find(struct hostapd_data * hapd,const u8 * addr,u8 dialog_token)108f05cddf9SRui Paulo gas_serv_dialog_find(struct hostapd_data *hapd, const u8 *addr,
109f05cddf9SRui Paulo u8 dialog_token)
110f05cddf9SRui Paulo {
111f05cddf9SRui Paulo struct sta_info *sta;
112f05cddf9SRui Paulo int i;
113f05cddf9SRui Paulo
114f05cddf9SRui Paulo sta = ap_get_sta(hapd, addr);
115f05cddf9SRui Paulo if (!sta) {
116f05cddf9SRui Paulo wpa_printf(MSG_DEBUG, "ANQP: could not find STA " MACSTR,
117f05cddf9SRui Paulo MAC2STR(addr));
118f05cddf9SRui Paulo return NULL;
119f05cddf9SRui Paulo }
120f05cddf9SRui Paulo for (i = 0; sta->gas_dialog && i < GAS_DIALOG_MAX; i++) {
121f05cddf9SRui Paulo if (sta->gas_dialog[i].dialog_token != dialog_token ||
122f05cddf9SRui Paulo !sta->gas_dialog[i].valid)
123f05cddf9SRui Paulo continue;
124780fb4a2SCy Schubert ap_sta_replenish_timeout(hapd, sta, 5);
125f05cddf9SRui Paulo return &sta->gas_dialog[i];
126f05cddf9SRui Paulo }
127f05cddf9SRui Paulo wpa_printf(MSG_DEBUG, "ANQP: Could not find dialog for "
128f05cddf9SRui Paulo MACSTR " dialog_token %u", MAC2STR(addr), dialog_token);
129f05cddf9SRui Paulo return NULL;
130f05cddf9SRui Paulo }
131f05cddf9SRui Paulo
132f05cddf9SRui Paulo
gas_serv_dialog_clear(struct gas_dialog_info * dia)133f05cddf9SRui Paulo void gas_serv_dialog_clear(struct gas_dialog_info *dia)
134f05cddf9SRui Paulo {
135f05cddf9SRui Paulo wpabuf_free(dia->sd_resp);
136f05cddf9SRui Paulo os_memset(dia, 0, sizeof(*dia));
137f05cddf9SRui Paulo }
138f05cddf9SRui Paulo
139f05cddf9SRui Paulo
gas_serv_free_dialogs(struct hostapd_data * hapd,const u8 * sta_addr)140f05cddf9SRui Paulo static void gas_serv_free_dialogs(struct hostapd_data *hapd,
141f05cddf9SRui Paulo const u8 *sta_addr)
142f05cddf9SRui Paulo {
143f05cddf9SRui Paulo struct sta_info *sta;
144f05cddf9SRui Paulo int i;
145f05cddf9SRui Paulo
146f05cddf9SRui Paulo sta = ap_get_sta(hapd, sta_addr);
147f05cddf9SRui Paulo if (sta == NULL || sta->gas_dialog == NULL)
148f05cddf9SRui Paulo return;
149f05cddf9SRui Paulo
150f05cddf9SRui Paulo for (i = 0; i < GAS_DIALOG_MAX; i++) {
151f05cddf9SRui Paulo if (sta->gas_dialog[i].valid)
152f05cddf9SRui Paulo return;
153f05cddf9SRui Paulo }
154f05cddf9SRui Paulo
155f05cddf9SRui Paulo os_free(sta->gas_dialog);
156f05cddf9SRui Paulo sta->gas_dialog = NULL;
157f05cddf9SRui Paulo }
158f05cddf9SRui Paulo
159f05cddf9SRui Paulo
160f05cddf9SRui Paulo #ifdef CONFIG_HS20
anqp_add_hs_capab_list(struct hostapd_data * hapd,struct wpabuf * buf)161f05cddf9SRui Paulo static void anqp_add_hs_capab_list(struct hostapd_data *hapd,
162f05cddf9SRui Paulo struct wpabuf *buf)
163f05cddf9SRui Paulo {
164f05cddf9SRui Paulo u8 *len;
165f05cddf9SRui Paulo
166f05cddf9SRui Paulo len = gas_anqp_add_element(buf, ANQP_VENDOR_SPECIFIC);
167f05cddf9SRui Paulo wpabuf_put_be24(buf, OUI_WFA);
168f05cddf9SRui Paulo wpabuf_put_u8(buf, HS20_ANQP_OUI_TYPE);
169f05cddf9SRui Paulo wpabuf_put_u8(buf, HS20_STYPE_CAPABILITY_LIST);
170f05cddf9SRui Paulo wpabuf_put_u8(buf, 0); /* Reserved */
171f05cddf9SRui Paulo wpabuf_put_u8(buf, HS20_STYPE_CAPABILITY_LIST);
172f05cddf9SRui Paulo if (hapd->conf->hs20_oper_friendly_name)
173f05cddf9SRui Paulo wpabuf_put_u8(buf, HS20_STYPE_OPERATOR_FRIENDLY_NAME);
174f05cddf9SRui Paulo if (hapd->conf->hs20_wan_metrics)
175f05cddf9SRui Paulo wpabuf_put_u8(buf, HS20_STYPE_WAN_METRICS);
176f05cddf9SRui Paulo if (hapd->conf->hs20_connection_capability)
177f05cddf9SRui Paulo wpabuf_put_u8(buf, HS20_STYPE_CONNECTION_CAPABILITY);
178f05cddf9SRui Paulo if (hapd->conf->nai_realm_data)
179f05cddf9SRui Paulo wpabuf_put_u8(buf, HS20_STYPE_NAI_HOME_REALM_QUERY);
180f05cddf9SRui Paulo if (hapd->conf->hs20_operating_class)
181f05cddf9SRui Paulo wpabuf_put_u8(buf, HS20_STYPE_OPERATING_CLASS);
1825b9c547cSRui Paulo if (hapd->conf->hs20_osu_providers_count)
1835b9c547cSRui Paulo wpabuf_put_u8(buf, HS20_STYPE_OSU_PROVIDERS_LIST);
18485732ac8SCy Schubert if (hapd->conf->hs20_osu_providers_nai_count)
18585732ac8SCy Schubert wpabuf_put_u8(buf, HS20_STYPE_OSU_PROVIDERS_NAI_LIST);
1865b9c547cSRui Paulo if (hapd->conf->hs20_icons_count)
1875b9c547cSRui Paulo wpabuf_put_u8(buf, HS20_STYPE_ICON_REQUEST);
18885732ac8SCy Schubert if (hapd->conf->hs20_operator_icon_count)
18985732ac8SCy Schubert wpabuf_put_u8(buf, HS20_STYPE_OPERATOR_ICON_METADATA);
190f05cddf9SRui Paulo gas_anqp_set_element_len(buf, len);
191f05cddf9SRui Paulo }
192f05cddf9SRui Paulo #endif /* CONFIG_HS20 */
193f05cddf9SRui Paulo
194f05cddf9SRui Paulo
get_anqp_elem(struct hostapd_data * hapd,u16 infoid)195780fb4a2SCy Schubert static struct anqp_element * get_anqp_elem(struct hostapd_data *hapd,
196780fb4a2SCy Schubert u16 infoid)
197780fb4a2SCy Schubert {
198780fb4a2SCy Schubert struct anqp_element *elem;
199780fb4a2SCy Schubert
200780fb4a2SCy Schubert dl_list_for_each(elem, &hapd->conf->anqp_elem, struct anqp_element,
201780fb4a2SCy Schubert list) {
202780fb4a2SCy Schubert if (elem->infoid == infoid)
203780fb4a2SCy Schubert return elem;
204780fb4a2SCy Schubert }
205780fb4a2SCy Schubert
206780fb4a2SCy Schubert return NULL;
207780fb4a2SCy Schubert }
208780fb4a2SCy Schubert
209780fb4a2SCy Schubert
anqp_add_elem(struct hostapd_data * hapd,struct wpabuf * buf,u16 infoid)210780fb4a2SCy Schubert static void anqp_add_elem(struct hostapd_data *hapd, struct wpabuf *buf,
211780fb4a2SCy Schubert u16 infoid)
212780fb4a2SCy Schubert {
213780fb4a2SCy Schubert struct anqp_element *elem;
214780fb4a2SCy Schubert
215780fb4a2SCy Schubert elem = get_anqp_elem(hapd, infoid);
216780fb4a2SCy Schubert if (!elem)
217780fb4a2SCy Schubert return;
218780fb4a2SCy Schubert if (wpabuf_tailroom(buf) < 2 + 2 + wpabuf_len(elem->payload)) {
219780fb4a2SCy Schubert wpa_printf(MSG_DEBUG, "ANQP: No room for InfoID %u payload",
220780fb4a2SCy Schubert infoid);
221780fb4a2SCy Schubert return;
222780fb4a2SCy Schubert }
223780fb4a2SCy Schubert
224780fb4a2SCy Schubert wpabuf_put_le16(buf, infoid);
225780fb4a2SCy Schubert wpabuf_put_le16(buf, wpabuf_len(elem->payload));
226780fb4a2SCy Schubert wpabuf_put_buf(buf, elem->payload);
227780fb4a2SCy Schubert }
228780fb4a2SCy Schubert
229780fb4a2SCy Schubert
anqp_add_override(struct hostapd_data * hapd,struct wpabuf * buf,u16 infoid)230780fb4a2SCy Schubert static int anqp_add_override(struct hostapd_data *hapd, struct wpabuf *buf,
231780fb4a2SCy Schubert u16 infoid)
232780fb4a2SCy Schubert {
233780fb4a2SCy Schubert if (get_anqp_elem(hapd, infoid)) {
234780fb4a2SCy Schubert anqp_add_elem(hapd, buf, infoid);
235780fb4a2SCy Schubert return 1;
236780fb4a2SCy Schubert }
237780fb4a2SCy Schubert
238780fb4a2SCy Schubert return 0;
239780fb4a2SCy Schubert }
240780fb4a2SCy Schubert
241780fb4a2SCy Schubert
anqp_add_capab_list(struct hostapd_data * hapd,struct wpabuf * buf)242f05cddf9SRui Paulo static void anqp_add_capab_list(struct hostapd_data *hapd,
243f05cddf9SRui Paulo struct wpabuf *buf)
244f05cddf9SRui Paulo {
245f05cddf9SRui Paulo u8 *len;
246780fb4a2SCy Schubert u16 id;
247780fb4a2SCy Schubert
248780fb4a2SCy Schubert if (anqp_add_override(hapd, buf, ANQP_CAPABILITY_LIST))
249780fb4a2SCy Schubert return;
250f05cddf9SRui Paulo
251f05cddf9SRui Paulo len = gas_anqp_add_element(buf, ANQP_CAPABILITY_LIST);
252f05cddf9SRui Paulo wpabuf_put_le16(buf, ANQP_CAPABILITY_LIST);
253780fb4a2SCy Schubert if (hapd->conf->venue_name || get_anqp_elem(hapd, ANQP_VENUE_NAME))
254f05cddf9SRui Paulo wpabuf_put_le16(buf, ANQP_VENUE_NAME);
255780fb4a2SCy Schubert if (get_anqp_elem(hapd, ANQP_EMERGENCY_CALL_NUMBER))
256780fb4a2SCy Schubert wpabuf_put_le16(buf, ANQP_EMERGENCY_CALL_NUMBER);
257780fb4a2SCy Schubert if (hapd->conf->network_auth_type ||
258780fb4a2SCy Schubert get_anqp_elem(hapd, ANQP_NETWORK_AUTH_TYPE))
259f05cddf9SRui Paulo wpabuf_put_le16(buf, ANQP_NETWORK_AUTH_TYPE);
260780fb4a2SCy Schubert if (hapd->conf->roaming_consortium ||
261780fb4a2SCy Schubert get_anqp_elem(hapd, ANQP_ROAMING_CONSORTIUM))
262f05cddf9SRui Paulo wpabuf_put_le16(buf, ANQP_ROAMING_CONSORTIUM);
263780fb4a2SCy Schubert if (hapd->conf->ipaddr_type_configured ||
264780fb4a2SCy Schubert get_anqp_elem(hapd, ANQP_IP_ADDR_TYPE_AVAILABILITY))
265f05cddf9SRui Paulo wpabuf_put_le16(buf, ANQP_IP_ADDR_TYPE_AVAILABILITY);
266780fb4a2SCy Schubert if (hapd->conf->nai_realm_data ||
267780fb4a2SCy Schubert get_anqp_elem(hapd, ANQP_NAI_REALM))
268f05cddf9SRui Paulo wpabuf_put_le16(buf, ANQP_NAI_REALM);
269780fb4a2SCy Schubert if (hapd->conf->anqp_3gpp_cell_net ||
270780fb4a2SCy Schubert get_anqp_elem(hapd, ANQP_3GPP_CELLULAR_NETWORK))
271f05cddf9SRui Paulo wpabuf_put_le16(buf, ANQP_3GPP_CELLULAR_NETWORK);
272780fb4a2SCy Schubert if (get_anqp_elem(hapd, ANQP_AP_GEOSPATIAL_LOCATION))
273780fb4a2SCy Schubert wpabuf_put_le16(buf, ANQP_AP_GEOSPATIAL_LOCATION);
274780fb4a2SCy Schubert if (get_anqp_elem(hapd, ANQP_AP_CIVIC_LOCATION))
275780fb4a2SCy Schubert wpabuf_put_le16(buf, ANQP_AP_CIVIC_LOCATION);
276780fb4a2SCy Schubert if (get_anqp_elem(hapd, ANQP_AP_LOCATION_PUBLIC_URI))
277780fb4a2SCy Schubert wpabuf_put_le16(buf, ANQP_AP_LOCATION_PUBLIC_URI);
278780fb4a2SCy Schubert if (hapd->conf->domain_name || get_anqp_elem(hapd, ANQP_DOMAIN_NAME))
279f05cddf9SRui Paulo wpabuf_put_le16(buf, ANQP_DOMAIN_NAME);
280780fb4a2SCy Schubert if (get_anqp_elem(hapd, ANQP_EMERGENCY_ALERT_URI))
281780fb4a2SCy Schubert wpabuf_put_le16(buf, ANQP_EMERGENCY_ALERT_URI);
28285732ac8SCy Schubert if (get_anqp_elem(hapd, ANQP_TDLS_CAPABILITY))
28385732ac8SCy Schubert wpabuf_put_le16(buf, ANQP_TDLS_CAPABILITY);
284780fb4a2SCy Schubert if (get_anqp_elem(hapd, ANQP_EMERGENCY_NAI))
285780fb4a2SCy Schubert wpabuf_put_le16(buf, ANQP_EMERGENCY_NAI);
286780fb4a2SCy Schubert if (get_anqp_elem(hapd, ANQP_NEIGHBOR_REPORT))
287780fb4a2SCy Schubert wpabuf_put_le16(buf, ANQP_NEIGHBOR_REPORT);
28885732ac8SCy Schubert #ifdef CONFIG_FILS
28985732ac8SCy Schubert if (!dl_list_empty(&hapd->conf->fils_realms) ||
29085732ac8SCy Schubert get_anqp_elem(hapd, ANQP_FILS_REALM_INFO))
29185732ac8SCy Schubert wpabuf_put_le16(buf, ANQP_FILS_REALM_INFO);
29285732ac8SCy Schubert #endif /* CONFIG_FILS */
29385732ac8SCy Schubert if (get_anqp_elem(hapd, ANQP_CAG))
29485732ac8SCy Schubert wpabuf_put_le16(buf, ANQP_CAG);
29585732ac8SCy Schubert if (hapd->conf->venue_url || get_anqp_elem(hapd, ANQP_VENUE_URL))
296780fb4a2SCy Schubert wpabuf_put_le16(buf, ANQP_VENUE_URL);
297780fb4a2SCy Schubert if (get_anqp_elem(hapd, ANQP_ADVICE_OF_CHARGE))
298780fb4a2SCy Schubert wpabuf_put_le16(buf, ANQP_ADVICE_OF_CHARGE);
299780fb4a2SCy Schubert if (get_anqp_elem(hapd, ANQP_LOCAL_CONTENT))
300780fb4a2SCy Schubert wpabuf_put_le16(buf, ANQP_LOCAL_CONTENT);
30185732ac8SCy Schubert for (id = 280; id < 300; id++) {
30285732ac8SCy Schubert if (get_anqp_elem(hapd, id))
30385732ac8SCy Schubert wpabuf_put_le16(buf, id);
30485732ac8SCy Schubert }
305f05cddf9SRui Paulo #ifdef CONFIG_HS20
306f05cddf9SRui Paulo anqp_add_hs_capab_list(hapd, buf);
307f05cddf9SRui Paulo #endif /* CONFIG_HS20 */
308f05cddf9SRui Paulo gas_anqp_set_element_len(buf, len);
309f05cddf9SRui Paulo }
310f05cddf9SRui Paulo
311f05cddf9SRui Paulo
anqp_add_venue_name(struct hostapd_data * hapd,struct wpabuf * buf)312f05cddf9SRui Paulo static void anqp_add_venue_name(struct hostapd_data *hapd, struct wpabuf *buf)
313f05cddf9SRui Paulo {
314780fb4a2SCy Schubert if (anqp_add_override(hapd, buf, ANQP_VENUE_NAME))
315780fb4a2SCy Schubert return;
316780fb4a2SCy Schubert
317f05cddf9SRui Paulo if (hapd->conf->venue_name) {
318f05cddf9SRui Paulo u8 *len;
319f05cddf9SRui Paulo unsigned int i;
320f05cddf9SRui Paulo len = gas_anqp_add_element(buf, ANQP_VENUE_NAME);
321f05cddf9SRui Paulo wpabuf_put_u8(buf, hapd->conf->venue_group);
322f05cddf9SRui Paulo wpabuf_put_u8(buf, hapd->conf->venue_type);
323f05cddf9SRui Paulo for (i = 0; i < hapd->conf->venue_name_count; i++) {
324f05cddf9SRui Paulo struct hostapd_lang_string *vn;
325f05cddf9SRui Paulo vn = &hapd->conf->venue_name[i];
326f05cddf9SRui Paulo wpabuf_put_u8(buf, 3 + vn->name_len);
327f05cddf9SRui Paulo wpabuf_put_data(buf, vn->lang, 3);
328f05cddf9SRui Paulo wpabuf_put_data(buf, vn->name, vn->name_len);
329f05cddf9SRui Paulo }
330f05cddf9SRui Paulo gas_anqp_set_element_len(buf, len);
331f05cddf9SRui Paulo }
332f05cddf9SRui Paulo }
333f05cddf9SRui Paulo
334f05cddf9SRui Paulo
anqp_add_venue_url(struct hostapd_data * hapd,struct wpabuf * buf)33585732ac8SCy Schubert static void anqp_add_venue_url(struct hostapd_data *hapd, struct wpabuf *buf)
33685732ac8SCy Schubert {
33785732ac8SCy Schubert if (anqp_add_override(hapd, buf, ANQP_VENUE_URL))
33885732ac8SCy Schubert return;
33985732ac8SCy Schubert
34085732ac8SCy Schubert if (hapd->conf->venue_url) {
34185732ac8SCy Schubert u8 *len;
34285732ac8SCy Schubert unsigned int i;
34385732ac8SCy Schubert
34485732ac8SCy Schubert len = gas_anqp_add_element(buf, ANQP_VENUE_URL);
34585732ac8SCy Schubert for (i = 0; i < hapd->conf->venue_url_count; i++) {
34685732ac8SCy Schubert struct hostapd_venue_url *url;
34785732ac8SCy Schubert
34885732ac8SCy Schubert url = &hapd->conf->venue_url[i];
34985732ac8SCy Schubert wpabuf_put_u8(buf, 1 + url->url_len);
35085732ac8SCy Schubert wpabuf_put_u8(buf, url->venue_number);
35185732ac8SCy Schubert wpabuf_put_data(buf, url->url, url->url_len);
35285732ac8SCy Schubert }
35385732ac8SCy Schubert gas_anqp_set_element_len(buf, len);
35485732ac8SCy Schubert }
35585732ac8SCy Schubert }
35685732ac8SCy Schubert
35785732ac8SCy Schubert
anqp_add_network_auth_type(struct hostapd_data * hapd,struct wpabuf * buf)358f05cddf9SRui Paulo static void anqp_add_network_auth_type(struct hostapd_data *hapd,
359f05cddf9SRui Paulo struct wpabuf *buf)
360f05cddf9SRui Paulo {
361780fb4a2SCy Schubert if (anqp_add_override(hapd, buf, ANQP_NETWORK_AUTH_TYPE))
362780fb4a2SCy Schubert return;
363780fb4a2SCy Schubert
364f05cddf9SRui Paulo if (hapd->conf->network_auth_type) {
365f05cddf9SRui Paulo wpabuf_put_le16(buf, ANQP_NETWORK_AUTH_TYPE);
366f05cddf9SRui Paulo wpabuf_put_le16(buf, hapd->conf->network_auth_type_len);
367f05cddf9SRui Paulo wpabuf_put_data(buf, hapd->conf->network_auth_type,
368f05cddf9SRui Paulo hapd->conf->network_auth_type_len);
369f05cddf9SRui Paulo }
370f05cddf9SRui Paulo }
371f05cddf9SRui Paulo
372f05cddf9SRui Paulo
anqp_add_roaming_consortium(struct hostapd_data * hapd,struct wpabuf * buf)373f05cddf9SRui Paulo static void anqp_add_roaming_consortium(struct hostapd_data *hapd,
374f05cddf9SRui Paulo struct wpabuf *buf)
375f05cddf9SRui Paulo {
376f05cddf9SRui Paulo unsigned int i;
377f05cddf9SRui Paulo u8 *len;
378f05cddf9SRui Paulo
379780fb4a2SCy Schubert if (anqp_add_override(hapd, buf, ANQP_ROAMING_CONSORTIUM))
380780fb4a2SCy Schubert return;
381780fb4a2SCy Schubert
382f05cddf9SRui Paulo len = gas_anqp_add_element(buf, ANQP_ROAMING_CONSORTIUM);
383f05cddf9SRui Paulo for (i = 0; i < hapd->conf->roaming_consortium_count; i++) {
384f05cddf9SRui Paulo struct hostapd_roaming_consortium *rc;
385f05cddf9SRui Paulo rc = &hapd->conf->roaming_consortium[i];
386f05cddf9SRui Paulo wpabuf_put_u8(buf, rc->len);
387f05cddf9SRui Paulo wpabuf_put_data(buf, rc->oi, rc->len);
388f05cddf9SRui Paulo }
389f05cddf9SRui Paulo gas_anqp_set_element_len(buf, len);
390f05cddf9SRui Paulo }
391f05cddf9SRui Paulo
392f05cddf9SRui Paulo
anqp_add_ip_addr_type_availability(struct hostapd_data * hapd,struct wpabuf * buf)393f05cddf9SRui Paulo static void anqp_add_ip_addr_type_availability(struct hostapd_data *hapd,
394f05cddf9SRui Paulo struct wpabuf *buf)
395f05cddf9SRui Paulo {
396780fb4a2SCy Schubert if (anqp_add_override(hapd, buf, ANQP_IP_ADDR_TYPE_AVAILABILITY))
397780fb4a2SCy Schubert return;
398780fb4a2SCy Schubert
399f05cddf9SRui Paulo if (hapd->conf->ipaddr_type_configured) {
400f05cddf9SRui Paulo wpabuf_put_le16(buf, ANQP_IP_ADDR_TYPE_AVAILABILITY);
401f05cddf9SRui Paulo wpabuf_put_le16(buf, 1);
402f05cddf9SRui Paulo wpabuf_put_u8(buf, hapd->conf->ipaddr_type_availability);
403f05cddf9SRui Paulo }
404f05cddf9SRui Paulo }
405f05cddf9SRui Paulo
406f05cddf9SRui Paulo
anqp_add_nai_realm_eap(struct wpabuf * buf,struct hostapd_nai_realm_data * realm)407f05cddf9SRui Paulo static void anqp_add_nai_realm_eap(struct wpabuf *buf,
408f05cddf9SRui Paulo struct hostapd_nai_realm_data *realm)
409f05cddf9SRui Paulo {
410f05cddf9SRui Paulo unsigned int i, j;
411f05cddf9SRui Paulo
412f05cddf9SRui Paulo wpabuf_put_u8(buf, realm->eap_method_count);
413f05cddf9SRui Paulo
414f05cddf9SRui Paulo for (i = 0; i < realm->eap_method_count; i++) {
415f05cddf9SRui Paulo struct hostapd_nai_realm_eap *eap = &realm->eap_method[i];
416f05cddf9SRui Paulo wpabuf_put_u8(buf, 2 + (3 * eap->num_auths));
417f05cddf9SRui Paulo wpabuf_put_u8(buf, eap->eap_method);
418f05cddf9SRui Paulo wpabuf_put_u8(buf, eap->num_auths);
419f05cddf9SRui Paulo for (j = 0; j < eap->num_auths; j++) {
420f05cddf9SRui Paulo wpabuf_put_u8(buf, eap->auth_id[j]);
421f05cddf9SRui Paulo wpabuf_put_u8(buf, 1);
422f05cddf9SRui Paulo wpabuf_put_u8(buf, eap->auth_val[j]);
423f05cddf9SRui Paulo }
424f05cddf9SRui Paulo }
425f05cddf9SRui Paulo }
426f05cddf9SRui Paulo
427f05cddf9SRui Paulo
anqp_add_nai_realm_data(struct wpabuf * buf,struct hostapd_nai_realm_data * realm,unsigned int realm_idx)428f05cddf9SRui Paulo static void anqp_add_nai_realm_data(struct wpabuf *buf,
429f05cddf9SRui Paulo struct hostapd_nai_realm_data *realm,
430f05cddf9SRui Paulo unsigned int realm_idx)
431f05cddf9SRui Paulo {
432f05cddf9SRui Paulo u8 *realm_data_len;
433f05cddf9SRui Paulo
434f05cddf9SRui Paulo wpa_printf(MSG_DEBUG, "realm=%s, len=%d", realm->realm[realm_idx],
435f05cddf9SRui Paulo (int) os_strlen(realm->realm[realm_idx]));
436f05cddf9SRui Paulo realm_data_len = wpabuf_put(buf, 2);
437f05cddf9SRui Paulo wpabuf_put_u8(buf, realm->encoding);
438f05cddf9SRui Paulo wpabuf_put_u8(buf, os_strlen(realm->realm[realm_idx]));
439f05cddf9SRui Paulo wpabuf_put_str(buf, realm->realm[realm_idx]);
440f05cddf9SRui Paulo anqp_add_nai_realm_eap(buf, realm);
441f05cddf9SRui Paulo gas_anqp_set_element_len(buf, realm_data_len);
442f05cddf9SRui Paulo }
443f05cddf9SRui Paulo
444f05cddf9SRui Paulo
hs20_add_nai_home_realm_matches(struct hostapd_data * hapd,struct wpabuf * buf,const u8 * home_realm,size_t home_realm_len)445f05cddf9SRui Paulo static int hs20_add_nai_home_realm_matches(struct hostapd_data *hapd,
446f05cddf9SRui Paulo struct wpabuf *buf,
447f05cddf9SRui Paulo const u8 *home_realm,
448f05cddf9SRui Paulo size_t home_realm_len)
449f05cddf9SRui Paulo {
450f05cddf9SRui Paulo unsigned int i, j, k;
451f05cddf9SRui Paulo u8 num_realms, num_matching = 0, encoding, realm_len, *realm_list_len;
452f05cddf9SRui Paulo struct hostapd_nai_realm_data *realm;
453f05cddf9SRui Paulo const u8 *pos, *realm_name, *end;
454f05cddf9SRui Paulo struct {
455f05cddf9SRui Paulo unsigned int realm_data_idx;
456f05cddf9SRui Paulo unsigned int realm_idx;
457f05cddf9SRui Paulo } matches[10];
458f05cddf9SRui Paulo
459f05cddf9SRui Paulo pos = home_realm;
460f05cddf9SRui Paulo end = pos + home_realm_len;
461780fb4a2SCy Schubert if (end - pos < 1) {
462f05cddf9SRui Paulo wpa_hexdump(MSG_DEBUG, "Too short NAI Home Realm Query",
463f05cddf9SRui Paulo home_realm, home_realm_len);
464f05cddf9SRui Paulo return -1;
465f05cddf9SRui Paulo }
466f05cddf9SRui Paulo num_realms = *pos++;
467f05cddf9SRui Paulo
468f05cddf9SRui Paulo for (i = 0; i < num_realms && num_matching < 10; i++) {
469780fb4a2SCy Schubert if (end - pos < 2) {
470f05cddf9SRui Paulo wpa_hexdump(MSG_DEBUG,
471f05cddf9SRui Paulo "Truncated NAI Home Realm Query",
472f05cddf9SRui Paulo home_realm, home_realm_len);
473f05cddf9SRui Paulo return -1;
474f05cddf9SRui Paulo }
475f05cddf9SRui Paulo encoding = *pos++;
476f05cddf9SRui Paulo realm_len = *pos++;
477780fb4a2SCy Schubert if (realm_len > end - pos) {
478f05cddf9SRui Paulo wpa_hexdump(MSG_DEBUG,
479f05cddf9SRui Paulo "Truncated NAI Home Realm Query",
480f05cddf9SRui Paulo home_realm, home_realm_len);
481f05cddf9SRui Paulo return -1;
482f05cddf9SRui Paulo }
483f05cddf9SRui Paulo realm_name = pos;
484f05cddf9SRui Paulo for (j = 0; j < hapd->conf->nai_realm_count &&
485f05cddf9SRui Paulo num_matching < 10; j++) {
486f05cddf9SRui Paulo const u8 *rpos, *rend;
487f05cddf9SRui Paulo realm = &hapd->conf->nai_realm_data[j];
488f05cddf9SRui Paulo if (encoding != realm->encoding)
489f05cddf9SRui Paulo continue;
490f05cddf9SRui Paulo
491f05cddf9SRui Paulo rpos = realm_name;
492f05cddf9SRui Paulo while (rpos < realm_name + realm_len &&
493f05cddf9SRui Paulo num_matching < 10) {
494f05cddf9SRui Paulo for (rend = rpos;
495f05cddf9SRui Paulo rend < realm_name + realm_len; rend++) {
496f05cddf9SRui Paulo if (*rend == ';')
497f05cddf9SRui Paulo break;
498f05cddf9SRui Paulo }
499f05cddf9SRui Paulo for (k = 0; k < MAX_NAI_REALMS &&
500f05cddf9SRui Paulo realm->realm[k] &&
501f05cddf9SRui Paulo num_matching < 10; k++) {
502f05cddf9SRui Paulo if ((int) os_strlen(realm->realm[k]) !=
503f05cddf9SRui Paulo rend - rpos ||
504f05cddf9SRui Paulo os_strncmp((char *) rpos,
505f05cddf9SRui Paulo realm->realm[k],
506f05cddf9SRui Paulo rend - rpos) != 0)
507f05cddf9SRui Paulo continue;
508f05cddf9SRui Paulo matches[num_matching].realm_data_idx =
509f05cddf9SRui Paulo j;
510f05cddf9SRui Paulo matches[num_matching].realm_idx = k;
511f05cddf9SRui Paulo num_matching++;
512f05cddf9SRui Paulo }
513f05cddf9SRui Paulo rpos = rend + 1;
514f05cddf9SRui Paulo }
515f05cddf9SRui Paulo }
516f05cddf9SRui Paulo pos += realm_len;
517f05cddf9SRui Paulo }
518f05cddf9SRui Paulo
519f05cddf9SRui Paulo realm_list_len = gas_anqp_add_element(buf, ANQP_NAI_REALM);
520f05cddf9SRui Paulo wpabuf_put_le16(buf, num_matching);
521f05cddf9SRui Paulo
522f05cddf9SRui Paulo /*
523f05cddf9SRui Paulo * There are two ways to format. 1. each realm in a NAI Realm Data unit
524f05cddf9SRui Paulo * 2. all realms that share the same EAP methods in a NAI Realm Data
525f05cddf9SRui Paulo * unit. The first format is likely to be bigger in size than the
526f05cddf9SRui Paulo * second, but may be easier to parse and process by the receiver.
527f05cddf9SRui Paulo */
528f05cddf9SRui Paulo for (i = 0; i < num_matching; i++) {
529f05cddf9SRui Paulo wpa_printf(MSG_DEBUG, "realm_idx %d, realm_data_idx %d",
530f05cddf9SRui Paulo matches[i].realm_data_idx, matches[i].realm_idx);
531f05cddf9SRui Paulo realm = &hapd->conf->nai_realm_data[matches[i].realm_data_idx];
532f05cddf9SRui Paulo anqp_add_nai_realm_data(buf, realm, matches[i].realm_idx);
533f05cddf9SRui Paulo }
534f05cddf9SRui Paulo gas_anqp_set_element_len(buf, realm_list_len);
535f05cddf9SRui Paulo return 0;
536f05cddf9SRui Paulo }
537f05cddf9SRui Paulo
538f05cddf9SRui Paulo
anqp_add_nai_realm(struct hostapd_data * hapd,struct wpabuf * buf,const u8 * home_realm,size_t home_realm_len,int nai_realm,int nai_home_realm)539f05cddf9SRui Paulo static void anqp_add_nai_realm(struct hostapd_data *hapd, struct wpabuf *buf,
540f05cddf9SRui Paulo const u8 *home_realm, size_t home_realm_len,
541f05cddf9SRui Paulo int nai_realm, int nai_home_realm)
542f05cddf9SRui Paulo {
543780fb4a2SCy Schubert if (nai_realm && !nai_home_realm &&
544780fb4a2SCy Schubert anqp_add_override(hapd, buf, ANQP_NAI_REALM))
545780fb4a2SCy Schubert return;
546780fb4a2SCy Schubert
547f05cddf9SRui Paulo if (nai_realm && hapd->conf->nai_realm_data) {
548f05cddf9SRui Paulo u8 *len;
549f05cddf9SRui Paulo unsigned int i, j;
550f05cddf9SRui Paulo len = gas_anqp_add_element(buf, ANQP_NAI_REALM);
551f05cddf9SRui Paulo wpabuf_put_le16(buf, hapd->conf->nai_realm_count);
552f05cddf9SRui Paulo for (i = 0; i < hapd->conf->nai_realm_count; i++) {
553f05cddf9SRui Paulo u8 *realm_data_len, *realm_len;
554f05cddf9SRui Paulo struct hostapd_nai_realm_data *realm;
555f05cddf9SRui Paulo
556f05cddf9SRui Paulo realm = &hapd->conf->nai_realm_data[i];
557f05cddf9SRui Paulo realm_data_len = wpabuf_put(buf, 2);
558f05cddf9SRui Paulo wpabuf_put_u8(buf, realm->encoding);
559f05cddf9SRui Paulo realm_len = wpabuf_put(buf, 1);
560f05cddf9SRui Paulo for (j = 0; realm->realm[j]; j++) {
561f05cddf9SRui Paulo if (j > 0)
562f05cddf9SRui Paulo wpabuf_put_u8(buf, ';');
563f05cddf9SRui Paulo wpabuf_put_str(buf, realm->realm[j]);
564f05cddf9SRui Paulo }
565f05cddf9SRui Paulo *realm_len = (u8 *) wpabuf_put(buf, 0) - realm_len - 1;
566f05cddf9SRui Paulo anqp_add_nai_realm_eap(buf, realm);
567f05cddf9SRui Paulo gas_anqp_set_element_len(buf, realm_data_len);
568f05cddf9SRui Paulo }
569f05cddf9SRui Paulo gas_anqp_set_element_len(buf, len);
5705b9c547cSRui Paulo } else if (nai_home_realm && hapd->conf->nai_realm_data && home_realm) {
571f05cddf9SRui Paulo hs20_add_nai_home_realm_matches(hapd, buf, home_realm,
572f05cddf9SRui Paulo home_realm_len);
573f05cddf9SRui Paulo }
574f05cddf9SRui Paulo }
575f05cddf9SRui Paulo
576f05cddf9SRui Paulo
anqp_add_3gpp_cellular_network(struct hostapd_data * hapd,struct wpabuf * buf)577f05cddf9SRui Paulo static void anqp_add_3gpp_cellular_network(struct hostapd_data *hapd,
578f05cddf9SRui Paulo struct wpabuf *buf)
579f05cddf9SRui Paulo {
580780fb4a2SCy Schubert if (anqp_add_override(hapd, buf, ANQP_3GPP_CELLULAR_NETWORK))
581780fb4a2SCy Schubert return;
582780fb4a2SCy Schubert
583f05cddf9SRui Paulo if (hapd->conf->anqp_3gpp_cell_net) {
584f05cddf9SRui Paulo wpabuf_put_le16(buf, ANQP_3GPP_CELLULAR_NETWORK);
585f05cddf9SRui Paulo wpabuf_put_le16(buf,
586f05cddf9SRui Paulo hapd->conf->anqp_3gpp_cell_net_len);
587f05cddf9SRui Paulo wpabuf_put_data(buf, hapd->conf->anqp_3gpp_cell_net,
588f05cddf9SRui Paulo hapd->conf->anqp_3gpp_cell_net_len);
589f05cddf9SRui Paulo }
590f05cddf9SRui Paulo }
591f05cddf9SRui Paulo
592f05cddf9SRui Paulo
anqp_add_domain_name(struct hostapd_data * hapd,struct wpabuf * buf)593f05cddf9SRui Paulo static void anqp_add_domain_name(struct hostapd_data *hapd, struct wpabuf *buf)
594f05cddf9SRui Paulo {
595780fb4a2SCy Schubert if (anqp_add_override(hapd, buf, ANQP_DOMAIN_NAME))
596780fb4a2SCy Schubert return;
597780fb4a2SCy Schubert
598f05cddf9SRui Paulo if (hapd->conf->domain_name) {
599f05cddf9SRui Paulo wpabuf_put_le16(buf, ANQP_DOMAIN_NAME);
600f05cddf9SRui Paulo wpabuf_put_le16(buf, hapd->conf->domain_name_len);
601f05cddf9SRui Paulo wpabuf_put_data(buf, hapd->conf->domain_name,
602f05cddf9SRui Paulo hapd->conf->domain_name_len);
603f05cddf9SRui Paulo }
604f05cddf9SRui Paulo }
605f05cddf9SRui Paulo
606f05cddf9SRui Paulo
60785732ac8SCy Schubert #ifdef CONFIG_FILS
anqp_add_fils_realm_info(struct hostapd_data * hapd,struct wpabuf * buf)60885732ac8SCy Schubert static void anqp_add_fils_realm_info(struct hostapd_data *hapd,
60985732ac8SCy Schubert struct wpabuf *buf)
61085732ac8SCy Schubert {
61185732ac8SCy Schubert size_t count;
61285732ac8SCy Schubert
61385732ac8SCy Schubert if (anqp_add_override(hapd, buf, ANQP_FILS_REALM_INFO))
61485732ac8SCy Schubert return;
61585732ac8SCy Schubert
61685732ac8SCy Schubert count = dl_list_len(&hapd->conf->fils_realms);
61785732ac8SCy Schubert if (count > 10000)
61885732ac8SCy Schubert count = 10000;
61985732ac8SCy Schubert if (count) {
62085732ac8SCy Schubert struct fils_realm *realm;
62185732ac8SCy Schubert
62285732ac8SCy Schubert wpabuf_put_le16(buf, ANQP_FILS_REALM_INFO);
62385732ac8SCy Schubert wpabuf_put_le16(buf, 2 * count);
62485732ac8SCy Schubert
62585732ac8SCy Schubert dl_list_for_each(realm, &hapd->conf->fils_realms,
62685732ac8SCy Schubert struct fils_realm, list) {
62785732ac8SCy Schubert if (count == 0)
62885732ac8SCy Schubert break;
62985732ac8SCy Schubert wpabuf_put_data(buf, realm->hash, 2);
63085732ac8SCy Schubert count--;
63185732ac8SCy Schubert }
63285732ac8SCy Schubert }
63385732ac8SCy Schubert }
63485732ac8SCy Schubert #endif /* CONFIG_FILS */
63585732ac8SCy Schubert
63685732ac8SCy Schubert
637f05cddf9SRui Paulo #ifdef CONFIG_HS20
638f05cddf9SRui Paulo
anqp_add_operator_friendly_name(struct hostapd_data * hapd,struct wpabuf * buf)639f05cddf9SRui Paulo static void anqp_add_operator_friendly_name(struct hostapd_data *hapd,
640f05cddf9SRui Paulo struct wpabuf *buf)
641f05cddf9SRui Paulo {
642f05cddf9SRui Paulo if (hapd->conf->hs20_oper_friendly_name) {
643f05cddf9SRui Paulo u8 *len;
644f05cddf9SRui Paulo unsigned int i;
645f05cddf9SRui Paulo len = gas_anqp_add_element(buf, ANQP_VENDOR_SPECIFIC);
646f05cddf9SRui Paulo wpabuf_put_be24(buf, OUI_WFA);
647f05cddf9SRui Paulo wpabuf_put_u8(buf, HS20_ANQP_OUI_TYPE);
648f05cddf9SRui Paulo wpabuf_put_u8(buf, HS20_STYPE_OPERATOR_FRIENDLY_NAME);
649f05cddf9SRui Paulo wpabuf_put_u8(buf, 0); /* Reserved */
650f05cddf9SRui Paulo for (i = 0; i < hapd->conf->hs20_oper_friendly_name_count; i++)
651f05cddf9SRui Paulo {
652f05cddf9SRui Paulo struct hostapd_lang_string *vn;
653f05cddf9SRui Paulo vn = &hapd->conf->hs20_oper_friendly_name[i];
654f05cddf9SRui Paulo wpabuf_put_u8(buf, 3 + vn->name_len);
655f05cddf9SRui Paulo wpabuf_put_data(buf, vn->lang, 3);
656f05cddf9SRui Paulo wpabuf_put_data(buf, vn->name, vn->name_len);
657f05cddf9SRui Paulo }
658f05cddf9SRui Paulo gas_anqp_set_element_len(buf, len);
659f05cddf9SRui Paulo }
660f05cddf9SRui Paulo }
661f05cddf9SRui Paulo
662f05cddf9SRui Paulo
anqp_add_wan_metrics(struct hostapd_data * hapd,struct wpabuf * buf)663f05cddf9SRui Paulo static void anqp_add_wan_metrics(struct hostapd_data *hapd,
664f05cddf9SRui Paulo struct wpabuf *buf)
665f05cddf9SRui Paulo {
666f05cddf9SRui Paulo if (hapd->conf->hs20_wan_metrics) {
667f05cddf9SRui Paulo u8 *len = gas_anqp_add_element(buf, ANQP_VENDOR_SPECIFIC);
668f05cddf9SRui Paulo wpabuf_put_be24(buf, OUI_WFA);
669f05cddf9SRui Paulo wpabuf_put_u8(buf, HS20_ANQP_OUI_TYPE);
670f05cddf9SRui Paulo wpabuf_put_u8(buf, HS20_STYPE_WAN_METRICS);
671f05cddf9SRui Paulo wpabuf_put_u8(buf, 0); /* Reserved */
672f05cddf9SRui Paulo wpabuf_put_data(buf, hapd->conf->hs20_wan_metrics, 13);
673f05cddf9SRui Paulo gas_anqp_set_element_len(buf, len);
674f05cddf9SRui Paulo }
675f05cddf9SRui Paulo }
676f05cddf9SRui Paulo
677f05cddf9SRui Paulo
anqp_add_connection_capability(struct hostapd_data * hapd,struct wpabuf * buf)678f05cddf9SRui Paulo static void anqp_add_connection_capability(struct hostapd_data *hapd,
679f05cddf9SRui Paulo struct wpabuf *buf)
680f05cddf9SRui Paulo {
681f05cddf9SRui Paulo if (hapd->conf->hs20_connection_capability) {
682f05cddf9SRui Paulo u8 *len = gas_anqp_add_element(buf, ANQP_VENDOR_SPECIFIC);
683f05cddf9SRui Paulo wpabuf_put_be24(buf, OUI_WFA);
684f05cddf9SRui Paulo wpabuf_put_u8(buf, HS20_ANQP_OUI_TYPE);
685f05cddf9SRui Paulo wpabuf_put_u8(buf, HS20_STYPE_CONNECTION_CAPABILITY);
686f05cddf9SRui Paulo wpabuf_put_u8(buf, 0); /* Reserved */
687f05cddf9SRui Paulo wpabuf_put_data(buf, hapd->conf->hs20_connection_capability,
688f05cddf9SRui Paulo hapd->conf->hs20_connection_capability_len);
689f05cddf9SRui Paulo gas_anqp_set_element_len(buf, len);
690f05cddf9SRui Paulo }
691f05cddf9SRui Paulo }
692f05cddf9SRui Paulo
693f05cddf9SRui Paulo
anqp_add_operating_class(struct hostapd_data * hapd,struct wpabuf * buf)694f05cddf9SRui Paulo static void anqp_add_operating_class(struct hostapd_data *hapd,
695f05cddf9SRui Paulo struct wpabuf *buf)
696f05cddf9SRui Paulo {
697f05cddf9SRui Paulo if (hapd->conf->hs20_operating_class) {
698f05cddf9SRui Paulo u8 *len = gas_anqp_add_element(buf, ANQP_VENDOR_SPECIFIC);
699f05cddf9SRui Paulo wpabuf_put_be24(buf, OUI_WFA);
700f05cddf9SRui Paulo wpabuf_put_u8(buf, HS20_ANQP_OUI_TYPE);
701f05cddf9SRui Paulo wpabuf_put_u8(buf, HS20_STYPE_OPERATING_CLASS);
702f05cddf9SRui Paulo wpabuf_put_u8(buf, 0); /* Reserved */
703f05cddf9SRui Paulo wpabuf_put_data(buf, hapd->conf->hs20_operating_class,
704f05cddf9SRui Paulo hapd->conf->hs20_operating_class_len);
705f05cddf9SRui Paulo gas_anqp_set_element_len(buf, len);
706f05cddf9SRui Paulo }
707f05cddf9SRui Paulo }
708f05cddf9SRui Paulo
7095b9c547cSRui Paulo
anqp_add_icon(struct wpabuf * buf,struct hostapd_bss_config * bss,const char * name)71085732ac8SCy Schubert static void anqp_add_icon(struct wpabuf *buf, struct hostapd_bss_config *bss,
71185732ac8SCy Schubert const char *name)
71285732ac8SCy Schubert {
71385732ac8SCy Schubert size_t j;
71485732ac8SCy Schubert struct hs20_icon *icon = NULL;
71585732ac8SCy Schubert
71685732ac8SCy Schubert for (j = 0; j < bss->hs20_icons_count && !icon; j++) {
71785732ac8SCy Schubert if (os_strcmp(name, bss->hs20_icons[j].name) == 0)
71885732ac8SCy Schubert icon = &bss->hs20_icons[j];
71985732ac8SCy Schubert }
72085732ac8SCy Schubert if (!icon)
72185732ac8SCy Schubert return; /* icon info not found */
72285732ac8SCy Schubert
72385732ac8SCy Schubert wpabuf_put_le16(buf, icon->width);
72485732ac8SCy Schubert wpabuf_put_le16(buf, icon->height);
72585732ac8SCy Schubert wpabuf_put_data(buf, icon->language, 3);
72685732ac8SCy Schubert wpabuf_put_u8(buf, os_strlen(icon->type));
72785732ac8SCy Schubert wpabuf_put_str(buf, icon->type);
72885732ac8SCy Schubert wpabuf_put_u8(buf, os_strlen(icon->name));
72985732ac8SCy Schubert wpabuf_put_str(buf, icon->name);
73085732ac8SCy Schubert }
73185732ac8SCy Schubert
73285732ac8SCy Schubert
anqp_add_osu_provider(struct wpabuf * buf,struct hostapd_bss_config * bss,struct hs20_osu_provider * p)7335b9c547cSRui Paulo static void anqp_add_osu_provider(struct wpabuf *buf,
7345b9c547cSRui Paulo struct hostapd_bss_config *bss,
7355b9c547cSRui Paulo struct hs20_osu_provider *p)
7365b9c547cSRui Paulo {
7375b9c547cSRui Paulo u8 *len, *len2, *count;
7385b9c547cSRui Paulo unsigned int i;
7395b9c547cSRui Paulo
7405b9c547cSRui Paulo len = wpabuf_put(buf, 2); /* OSU Provider Length to be filled */
7415b9c547cSRui Paulo
7425b9c547cSRui Paulo /* OSU Friendly Name Duples */
7435b9c547cSRui Paulo len2 = wpabuf_put(buf, 2);
7445b9c547cSRui Paulo for (i = 0; i < p->friendly_name_count; i++) {
7455b9c547cSRui Paulo struct hostapd_lang_string *s = &p->friendly_name[i];
7465b9c547cSRui Paulo wpabuf_put_u8(buf, 3 + s->name_len);
7475b9c547cSRui Paulo wpabuf_put_data(buf, s->lang, 3);
7485b9c547cSRui Paulo wpabuf_put_data(buf, s->name, s->name_len);
7495b9c547cSRui Paulo }
7505b9c547cSRui Paulo WPA_PUT_LE16(len2, (u8 *) wpabuf_put(buf, 0) - len2 - 2);
7515b9c547cSRui Paulo
7525b9c547cSRui Paulo /* OSU Server URI */
7535b9c547cSRui Paulo if (p->server_uri) {
7545b9c547cSRui Paulo wpabuf_put_u8(buf, os_strlen(p->server_uri));
7555b9c547cSRui Paulo wpabuf_put_str(buf, p->server_uri);
7565b9c547cSRui Paulo } else
7575b9c547cSRui Paulo wpabuf_put_u8(buf, 0);
7585b9c547cSRui Paulo
7595b9c547cSRui Paulo /* OSU Method List */
7605b9c547cSRui Paulo count = wpabuf_put(buf, 1);
76185732ac8SCy Schubert for (i = 0; p->method_list && p->method_list[i] >= 0; i++)
7625b9c547cSRui Paulo wpabuf_put_u8(buf, p->method_list[i]);
7635b9c547cSRui Paulo *count = i;
7645b9c547cSRui Paulo
7655b9c547cSRui Paulo /* Icons Available */
7665b9c547cSRui Paulo len2 = wpabuf_put(buf, 2);
76785732ac8SCy Schubert for (i = 0; i < p->icons_count; i++)
76885732ac8SCy Schubert anqp_add_icon(buf, bss, p->icons[i]);
7695b9c547cSRui Paulo WPA_PUT_LE16(len2, (u8 *) wpabuf_put(buf, 0) - len2 - 2);
7705b9c547cSRui Paulo
7715b9c547cSRui Paulo /* OSU_NAI */
7725b9c547cSRui Paulo if (p->osu_nai) {
7735b9c547cSRui Paulo wpabuf_put_u8(buf, os_strlen(p->osu_nai));
7745b9c547cSRui Paulo wpabuf_put_str(buf, p->osu_nai);
7755b9c547cSRui Paulo } else
7765b9c547cSRui Paulo wpabuf_put_u8(buf, 0);
7775b9c547cSRui Paulo
7785b9c547cSRui Paulo /* OSU Service Description Duples */
7795b9c547cSRui Paulo len2 = wpabuf_put(buf, 2);
7805b9c547cSRui Paulo for (i = 0; i < p->service_desc_count; i++) {
7815b9c547cSRui Paulo struct hostapd_lang_string *s = &p->service_desc[i];
7825b9c547cSRui Paulo wpabuf_put_u8(buf, 3 + s->name_len);
7835b9c547cSRui Paulo wpabuf_put_data(buf, s->lang, 3);
7845b9c547cSRui Paulo wpabuf_put_data(buf, s->name, s->name_len);
7855b9c547cSRui Paulo }
7865b9c547cSRui Paulo WPA_PUT_LE16(len2, (u8 *) wpabuf_put(buf, 0) - len2 - 2);
7875b9c547cSRui Paulo
7885b9c547cSRui Paulo WPA_PUT_LE16(len, (u8 *) wpabuf_put(buf, 0) - len - 2);
7895b9c547cSRui Paulo }
7905b9c547cSRui Paulo
7915b9c547cSRui Paulo
anqp_add_osu_providers_list(struct hostapd_data * hapd,struct wpabuf * buf)7925b9c547cSRui Paulo static void anqp_add_osu_providers_list(struct hostapd_data *hapd,
7935b9c547cSRui Paulo struct wpabuf *buf)
7945b9c547cSRui Paulo {
7955b9c547cSRui Paulo if (hapd->conf->hs20_osu_providers_count) {
7965b9c547cSRui Paulo size_t i;
7975b9c547cSRui Paulo u8 *len = gas_anqp_add_element(buf, ANQP_VENDOR_SPECIFIC);
7985b9c547cSRui Paulo wpabuf_put_be24(buf, OUI_WFA);
7995b9c547cSRui Paulo wpabuf_put_u8(buf, HS20_ANQP_OUI_TYPE);
8005b9c547cSRui Paulo wpabuf_put_u8(buf, HS20_STYPE_OSU_PROVIDERS_LIST);
8015b9c547cSRui Paulo wpabuf_put_u8(buf, 0); /* Reserved */
8025b9c547cSRui Paulo
8035b9c547cSRui Paulo /* OSU SSID */
8045b9c547cSRui Paulo wpabuf_put_u8(buf, hapd->conf->osu_ssid_len);
8055b9c547cSRui Paulo wpabuf_put_data(buf, hapd->conf->osu_ssid,
8065b9c547cSRui Paulo hapd->conf->osu_ssid_len);
8075b9c547cSRui Paulo
8085b9c547cSRui Paulo /* Number of OSU Providers */
8095b9c547cSRui Paulo wpabuf_put_u8(buf, hapd->conf->hs20_osu_providers_count);
8105b9c547cSRui Paulo
8115b9c547cSRui Paulo for (i = 0; i < hapd->conf->hs20_osu_providers_count; i++) {
8125b9c547cSRui Paulo anqp_add_osu_provider(
8135b9c547cSRui Paulo buf, hapd->conf,
8145b9c547cSRui Paulo &hapd->conf->hs20_osu_providers[i]);
8155b9c547cSRui Paulo }
8165b9c547cSRui Paulo
8175b9c547cSRui Paulo gas_anqp_set_element_len(buf, len);
8185b9c547cSRui Paulo }
8195b9c547cSRui Paulo }
8205b9c547cSRui Paulo
8215b9c547cSRui Paulo
anqp_add_osu_provider_nai(struct wpabuf * buf,struct hs20_osu_provider * p)82285732ac8SCy Schubert static void anqp_add_osu_provider_nai(struct wpabuf *buf,
82385732ac8SCy Schubert struct hs20_osu_provider *p)
82485732ac8SCy Schubert {
82585732ac8SCy Schubert /* OSU_NAI for shared BSS (Single SSID) */
82685732ac8SCy Schubert if (p->osu_nai2) {
82785732ac8SCy Schubert wpabuf_put_u8(buf, os_strlen(p->osu_nai2));
82885732ac8SCy Schubert wpabuf_put_str(buf, p->osu_nai2);
82985732ac8SCy Schubert } else {
83085732ac8SCy Schubert wpabuf_put_u8(buf, 0);
83185732ac8SCy Schubert }
83285732ac8SCy Schubert }
83385732ac8SCy Schubert
83485732ac8SCy Schubert
anqp_add_osu_providers_nai_list(struct hostapd_data * hapd,struct wpabuf * buf)83585732ac8SCy Schubert static void anqp_add_osu_providers_nai_list(struct hostapd_data *hapd,
83685732ac8SCy Schubert struct wpabuf *buf)
83785732ac8SCy Schubert {
83885732ac8SCy Schubert if (hapd->conf->hs20_osu_providers_nai_count) {
83985732ac8SCy Schubert size_t i;
84085732ac8SCy Schubert u8 *len = gas_anqp_add_element(buf, ANQP_VENDOR_SPECIFIC);
84185732ac8SCy Schubert wpabuf_put_be24(buf, OUI_WFA);
84285732ac8SCy Schubert wpabuf_put_u8(buf, HS20_ANQP_OUI_TYPE);
84385732ac8SCy Schubert wpabuf_put_u8(buf, HS20_STYPE_OSU_PROVIDERS_NAI_LIST);
84485732ac8SCy Schubert wpabuf_put_u8(buf, 0); /* Reserved */
84585732ac8SCy Schubert
84685732ac8SCy Schubert for (i = 0; i < hapd->conf->hs20_osu_providers_count; i++) {
84785732ac8SCy Schubert anqp_add_osu_provider_nai(
84885732ac8SCy Schubert buf, &hapd->conf->hs20_osu_providers[i]);
84985732ac8SCy Schubert }
85085732ac8SCy Schubert
85185732ac8SCy Schubert gas_anqp_set_element_len(buf, len);
85285732ac8SCy Schubert }
85385732ac8SCy Schubert }
85485732ac8SCy Schubert
85585732ac8SCy Schubert
anqp_add_icon_binary_file(struct hostapd_data * hapd,struct wpabuf * buf,const u8 * name,size_t name_len)8565b9c547cSRui Paulo static void anqp_add_icon_binary_file(struct hostapd_data *hapd,
8575b9c547cSRui Paulo struct wpabuf *buf,
8585b9c547cSRui Paulo const u8 *name, size_t name_len)
8595b9c547cSRui Paulo {
8605b9c547cSRui Paulo struct hs20_icon *icon;
8615b9c547cSRui Paulo size_t i;
8625b9c547cSRui Paulo u8 *len;
8635b9c547cSRui Paulo
8645b9c547cSRui Paulo wpa_hexdump_ascii(MSG_DEBUG, "HS 2.0: Requested Icon Filename",
8655b9c547cSRui Paulo name, name_len);
8665b9c547cSRui Paulo for (i = 0; i < hapd->conf->hs20_icons_count; i++) {
8675b9c547cSRui Paulo icon = &hapd->conf->hs20_icons[i];
8685b9c547cSRui Paulo if (name_len == os_strlen(icon->name) &&
8695b9c547cSRui Paulo os_memcmp(name, icon->name, name_len) == 0)
8705b9c547cSRui Paulo break;
8715b9c547cSRui Paulo }
8725b9c547cSRui Paulo
8735b9c547cSRui Paulo if (i < hapd->conf->hs20_icons_count)
8745b9c547cSRui Paulo icon = &hapd->conf->hs20_icons[i];
8755b9c547cSRui Paulo else
8765b9c547cSRui Paulo icon = NULL;
8775b9c547cSRui Paulo
8785b9c547cSRui Paulo len = gas_anqp_add_element(buf, ANQP_VENDOR_SPECIFIC);
8795b9c547cSRui Paulo wpabuf_put_be24(buf, OUI_WFA);
8805b9c547cSRui Paulo wpabuf_put_u8(buf, HS20_ANQP_OUI_TYPE);
8815b9c547cSRui Paulo wpabuf_put_u8(buf, HS20_STYPE_ICON_BINARY_FILE);
8825b9c547cSRui Paulo wpabuf_put_u8(buf, 0); /* Reserved */
8835b9c547cSRui Paulo
8845b9c547cSRui Paulo if (icon) {
8855b9c547cSRui Paulo char *data;
8865b9c547cSRui Paulo size_t data_len;
8875b9c547cSRui Paulo
8885b9c547cSRui Paulo data = os_readfile(icon->file, &data_len);
8895b9c547cSRui Paulo if (data == NULL || data_len > 65535) {
8905b9c547cSRui Paulo wpabuf_put_u8(buf, 2); /* Download Status:
8915b9c547cSRui Paulo * Unspecified file error */
8925b9c547cSRui Paulo wpabuf_put_u8(buf, 0);
8935b9c547cSRui Paulo wpabuf_put_le16(buf, 0);
8945b9c547cSRui Paulo } else {
8955b9c547cSRui Paulo wpabuf_put_u8(buf, 0); /* Download Status: Success */
8965b9c547cSRui Paulo wpabuf_put_u8(buf, os_strlen(icon->type));
8975b9c547cSRui Paulo wpabuf_put_str(buf, icon->type);
8985b9c547cSRui Paulo wpabuf_put_le16(buf, data_len);
8995b9c547cSRui Paulo wpabuf_put_data(buf, data, data_len);
9005b9c547cSRui Paulo }
9015b9c547cSRui Paulo os_free(data);
9025b9c547cSRui Paulo } else {
9035b9c547cSRui Paulo wpabuf_put_u8(buf, 1); /* Download Status: File not found */
9045b9c547cSRui Paulo wpabuf_put_u8(buf, 0);
9055b9c547cSRui Paulo wpabuf_put_le16(buf, 0);
9065b9c547cSRui Paulo }
9075b9c547cSRui Paulo
9085b9c547cSRui Paulo gas_anqp_set_element_len(buf, len);
9095b9c547cSRui Paulo }
9105b9c547cSRui Paulo
91185732ac8SCy Schubert
anqp_add_operator_icon_metadata(struct hostapd_data * hapd,struct wpabuf * buf)91285732ac8SCy Schubert static void anqp_add_operator_icon_metadata(struct hostapd_data *hapd,
91385732ac8SCy Schubert struct wpabuf *buf)
91485732ac8SCy Schubert {
91585732ac8SCy Schubert struct hostapd_bss_config *bss = hapd->conf;
91685732ac8SCy Schubert size_t i;
91785732ac8SCy Schubert u8 *len;
91885732ac8SCy Schubert
91985732ac8SCy Schubert if (!bss->hs20_operator_icon_count)
92085732ac8SCy Schubert return;
92185732ac8SCy Schubert
92285732ac8SCy Schubert len = gas_anqp_add_element(buf, ANQP_VENDOR_SPECIFIC);
92385732ac8SCy Schubert
92485732ac8SCy Schubert wpabuf_put_be24(buf, OUI_WFA);
92585732ac8SCy Schubert wpabuf_put_u8(buf, HS20_ANQP_OUI_TYPE);
92685732ac8SCy Schubert wpabuf_put_u8(buf, HS20_STYPE_OPERATOR_ICON_METADATA);
92785732ac8SCy Schubert wpabuf_put_u8(buf, 0); /* Reserved */
92885732ac8SCy Schubert
92985732ac8SCy Schubert for (i = 0; i < bss->hs20_operator_icon_count; i++)
93085732ac8SCy Schubert anqp_add_icon(buf, bss, bss->hs20_operator_icon[i]);
93185732ac8SCy Schubert
93285732ac8SCy Schubert gas_anqp_set_element_len(buf, len);
93385732ac8SCy Schubert }
93485732ac8SCy Schubert
935f05cddf9SRui Paulo #endif /* CONFIG_HS20 */
936f05cddf9SRui Paulo
937f05cddf9SRui Paulo
93885732ac8SCy Schubert #ifdef CONFIG_MBO
anqp_add_mbo_cell_data_conn_pref(struct hostapd_data * hapd,struct wpabuf * buf)93985732ac8SCy Schubert static void anqp_add_mbo_cell_data_conn_pref(struct hostapd_data *hapd,
94085732ac8SCy Schubert struct wpabuf *buf)
94185732ac8SCy Schubert {
94285732ac8SCy Schubert if (hapd->conf->mbo_cell_data_conn_pref >= 0) {
94385732ac8SCy Schubert u8 *len = gas_anqp_add_element(buf, ANQP_VENDOR_SPECIFIC);
94485732ac8SCy Schubert wpabuf_put_be24(buf, OUI_WFA);
94585732ac8SCy Schubert wpabuf_put_u8(buf, MBO_ANQP_OUI_TYPE);
94685732ac8SCy Schubert wpabuf_put_u8(buf, MBO_ANQP_SUBTYPE_CELL_CONN_PREF);
94785732ac8SCy Schubert wpabuf_put_u8(buf, hapd->conf->mbo_cell_data_conn_pref);
94885732ac8SCy Schubert gas_anqp_set_element_len(buf, len);
94985732ac8SCy Schubert }
95085732ac8SCy Schubert }
95185732ac8SCy Schubert #endif /* CONFIG_MBO */
95285732ac8SCy Schubert
95385732ac8SCy Schubert
anqp_get_required_len(struct hostapd_data * hapd,const u16 * infoid,unsigned int num_infoid)954780fb4a2SCy Schubert static size_t anqp_get_required_len(struct hostapd_data *hapd,
955780fb4a2SCy Schubert const u16 *infoid,
956780fb4a2SCy Schubert unsigned int num_infoid)
957780fb4a2SCy Schubert {
958780fb4a2SCy Schubert size_t len = 0;
959780fb4a2SCy Schubert unsigned int i;
960780fb4a2SCy Schubert
961780fb4a2SCy Schubert for (i = 0; i < num_infoid; i++) {
962780fb4a2SCy Schubert struct anqp_element *elem = get_anqp_elem(hapd, infoid[i]);
963780fb4a2SCy Schubert
964780fb4a2SCy Schubert if (elem)
965780fb4a2SCy Schubert len += 2 + 2 + wpabuf_len(elem->payload);
966780fb4a2SCy Schubert }
967780fb4a2SCy Schubert
968780fb4a2SCy Schubert return len;
969780fb4a2SCy Schubert }
970780fb4a2SCy Schubert
971780fb4a2SCy Schubert
972f05cddf9SRui Paulo static struct wpabuf *
gas_serv_build_gas_resp_payload(struct hostapd_data * hapd,unsigned int request,const u8 * home_realm,size_t home_realm_len,const u8 * icon_name,size_t icon_name_len,const u16 * extra_req,unsigned int num_extra_req)973f05cddf9SRui Paulo gas_serv_build_gas_resp_payload(struct hostapd_data *hapd,
974f05cddf9SRui Paulo unsigned int request,
9755b9c547cSRui Paulo const u8 *home_realm, size_t home_realm_len,
976780fb4a2SCy Schubert const u8 *icon_name, size_t icon_name_len,
977780fb4a2SCy Schubert const u16 *extra_req,
978780fb4a2SCy Schubert unsigned int num_extra_req)
979f05cddf9SRui Paulo {
980f05cddf9SRui Paulo struct wpabuf *buf;
9815b9c547cSRui Paulo size_t len;
982780fb4a2SCy Schubert unsigned int i;
983f05cddf9SRui Paulo
9845b9c547cSRui Paulo len = 1400;
9855b9c547cSRui Paulo if (request & (ANQP_REQ_NAI_REALM | ANQP_REQ_NAI_HOME_REALM))
9865b9c547cSRui Paulo len += 1000;
9875b9c547cSRui Paulo if (request & ANQP_REQ_ICON_REQUEST)
9885b9c547cSRui Paulo len += 65536;
98985732ac8SCy Schubert #ifdef CONFIG_FILS
99085732ac8SCy Schubert if (request & ANQP_FILS_REALM_INFO)
99185732ac8SCy Schubert len += 2 * dl_list_len(&hapd->conf->fils_realms);
99285732ac8SCy Schubert #endif /* CONFIG_FILS */
993780fb4a2SCy Schubert len += anqp_get_required_len(hapd, extra_req, num_extra_req);
9945b9c547cSRui Paulo
9955b9c547cSRui Paulo buf = wpabuf_alloc(len);
996f05cddf9SRui Paulo if (buf == NULL)
997f05cddf9SRui Paulo return NULL;
998f05cddf9SRui Paulo
999f05cddf9SRui Paulo if (request & ANQP_REQ_CAPABILITY_LIST)
1000f05cddf9SRui Paulo anqp_add_capab_list(hapd, buf);
1001f05cddf9SRui Paulo if (request & ANQP_REQ_VENUE_NAME)
1002f05cddf9SRui Paulo anqp_add_venue_name(hapd, buf);
1003780fb4a2SCy Schubert if (request & ANQP_REQ_EMERGENCY_CALL_NUMBER)
1004780fb4a2SCy Schubert anqp_add_elem(hapd, buf, ANQP_EMERGENCY_CALL_NUMBER);
1005f05cddf9SRui Paulo if (request & ANQP_REQ_NETWORK_AUTH_TYPE)
1006f05cddf9SRui Paulo anqp_add_network_auth_type(hapd, buf);
1007f05cddf9SRui Paulo if (request & ANQP_REQ_ROAMING_CONSORTIUM)
1008f05cddf9SRui Paulo anqp_add_roaming_consortium(hapd, buf);
1009f05cddf9SRui Paulo if (request & ANQP_REQ_IP_ADDR_TYPE_AVAILABILITY)
1010f05cddf9SRui Paulo anqp_add_ip_addr_type_availability(hapd, buf);
1011f05cddf9SRui Paulo if (request & (ANQP_REQ_NAI_REALM | ANQP_REQ_NAI_HOME_REALM))
1012f05cddf9SRui Paulo anqp_add_nai_realm(hapd, buf, home_realm, home_realm_len,
1013f05cddf9SRui Paulo request & ANQP_REQ_NAI_REALM,
1014f05cddf9SRui Paulo request & ANQP_REQ_NAI_HOME_REALM);
1015f05cddf9SRui Paulo if (request & ANQP_REQ_3GPP_CELLULAR_NETWORK)
1016f05cddf9SRui Paulo anqp_add_3gpp_cellular_network(hapd, buf);
1017780fb4a2SCy Schubert if (request & ANQP_REQ_AP_GEOSPATIAL_LOCATION)
1018780fb4a2SCy Schubert anqp_add_elem(hapd, buf, ANQP_AP_GEOSPATIAL_LOCATION);
1019780fb4a2SCy Schubert if (request & ANQP_REQ_AP_CIVIC_LOCATION)
1020780fb4a2SCy Schubert anqp_add_elem(hapd, buf, ANQP_AP_CIVIC_LOCATION);
1021780fb4a2SCy Schubert if (request & ANQP_REQ_AP_LOCATION_PUBLIC_URI)
1022780fb4a2SCy Schubert anqp_add_elem(hapd, buf, ANQP_AP_LOCATION_PUBLIC_URI);
1023f05cddf9SRui Paulo if (request & ANQP_REQ_DOMAIN_NAME)
1024f05cddf9SRui Paulo anqp_add_domain_name(hapd, buf);
1025780fb4a2SCy Schubert if (request & ANQP_REQ_EMERGENCY_ALERT_URI)
1026780fb4a2SCy Schubert anqp_add_elem(hapd, buf, ANQP_EMERGENCY_ALERT_URI);
1027780fb4a2SCy Schubert if (request & ANQP_REQ_TDLS_CAPABILITY)
1028780fb4a2SCy Schubert anqp_add_elem(hapd, buf, ANQP_TDLS_CAPABILITY);
1029780fb4a2SCy Schubert if (request & ANQP_REQ_EMERGENCY_NAI)
1030780fb4a2SCy Schubert anqp_add_elem(hapd, buf, ANQP_EMERGENCY_NAI);
1031780fb4a2SCy Schubert
103285732ac8SCy Schubert for (i = 0; i < num_extra_req; i++) {
103385732ac8SCy Schubert #ifdef CONFIG_FILS
103485732ac8SCy Schubert if (extra_req[i] == ANQP_FILS_REALM_INFO) {
103585732ac8SCy Schubert anqp_add_fils_realm_info(hapd, buf);
103685732ac8SCy Schubert continue;
103785732ac8SCy Schubert }
103885732ac8SCy Schubert #endif /* CONFIG_FILS */
103985732ac8SCy Schubert if (extra_req[i] == ANQP_VENUE_URL) {
104085732ac8SCy Schubert anqp_add_venue_url(hapd, buf);
104185732ac8SCy Schubert continue;
104285732ac8SCy Schubert }
1043780fb4a2SCy Schubert anqp_add_elem(hapd, buf, extra_req[i]);
104485732ac8SCy Schubert }
1045f05cddf9SRui Paulo
1046f05cddf9SRui Paulo #ifdef CONFIG_HS20
1047f05cddf9SRui Paulo if (request & ANQP_REQ_HS_CAPABILITY_LIST)
1048f05cddf9SRui Paulo anqp_add_hs_capab_list(hapd, buf);
1049f05cddf9SRui Paulo if (request & ANQP_REQ_OPERATOR_FRIENDLY_NAME)
1050f05cddf9SRui Paulo anqp_add_operator_friendly_name(hapd, buf);
1051f05cddf9SRui Paulo if (request & ANQP_REQ_WAN_METRICS)
1052f05cddf9SRui Paulo anqp_add_wan_metrics(hapd, buf);
1053f05cddf9SRui Paulo if (request & ANQP_REQ_CONNECTION_CAPABILITY)
1054f05cddf9SRui Paulo anqp_add_connection_capability(hapd, buf);
1055f05cddf9SRui Paulo if (request & ANQP_REQ_OPERATING_CLASS)
1056f05cddf9SRui Paulo anqp_add_operating_class(hapd, buf);
10575b9c547cSRui Paulo if (request & ANQP_REQ_OSU_PROVIDERS_LIST)
10585b9c547cSRui Paulo anqp_add_osu_providers_list(hapd, buf);
10595b9c547cSRui Paulo if (request & ANQP_REQ_ICON_REQUEST)
10605b9c547cSRui Paulo anqp_add_icon_binary_file(hapd, buf, icon_name, icon_name_len);
106185732ac8SCy Schubert if (request & ANQP_REQ_OPERATOR_ICON_METADATA)
106285732ac8SCy Schubert anqp_add_operator_icon_metadata(hapd, buf);
106385732ac8SCy Schubert if (request & ANQP_REQ_OSU_PROVIDERS_NAI_LIST)
106485732ac8SCy Schubert anqp_add_osu_providers_nai_list(hapd, buf);
1065f05cddf9SRui Paulo #endif /* CONFIG_HS20 */
1066f05cddf9SRui Paulo
106785732ac8SCy Schubert #ifdef CONFIG_MBO
106885732ac8SCy Schubert if (request & ANQP_REQ_MBO_CELL_DATA_CONN_PREF)
106985732ac8SCy Schubert anqp_add_mbo_cell_data_conn_pref(hapd, buf);
107085732ac8SCy Schubert #endif /* CONFIG_MBO */
107185732ac8SCy Schubert
1072f05cddf9SRui Paulo return buf;
1073f05cddf9SRui Paulo }
1074f05cddf9SRui Paulo
1075f05cddf9SRui Paulo
1076780fb4a2SCy Schubert #define ANQP_MAX_EXTRA_REQ 20
1077780fb4a2SCy Schubert
1078f05cddf9SRui Paulo struct anqp_query_info {
1079f05cddf9SRui Paulo unsigned int request;
1080f05cddf9SRui Paulo const u8 *home_realm_query;
1081f05cddf9SRui Paulo size_t home_realm_query_len;
10825b9c547cSRui Paulo const u8 *icon_name;
10835b9c547cSRui Paulo size_t icon_name_len;
10845b9c547cSRui Paulo int p2p_sd;
1085780fb4a2SCy Schubert u16 extra_req[ANQP_MAX_EXTRA_REQ];
1086780fb4a2SCy Schubert unsigned int num_extra_req;
1087f05cddf9SRui Paulo };
1088f05cddf9SRui Paulo
1089f05cddf9SRui Paulo
set_anqp_req(unsigned int bit,const char * name,int local,struct anqp_query_info * qi)1090f05cddf9SRui Paulo static void set_anqp_req(unsigned int bit, const char *name, int local,
1091f05cddf9SRui Paulo struct anqp_query_info *qi)
1092f05cddf9SRui Paulo {
1093f05cddf9SRui Paulo qi->request |= bit;
1094f05cddf9SRui Paulo if (local) {
1095f05cddf9SRui Paulo wpa_printf(MSG_DEBUG, "ANQP: %s (local)", name);
1096f05cddf9SRui Paulo } else {
1097f05cddf9SRui Paulo wpa_printf(MSG_DEBUG, "ANQP: %s not available", name);
1098f05cddf9SRui Paulo }
1099f05cddf9SRui Paulo }
1100f05cddf9SRui Paulo
1101f05cddf9SRui Paulo
rx_anqp_query_list_id(struct hostapd_data * hapd,u16 info_id,struct anqp_query_info * qi)1102f05cddf9SRui Paulo static void rx_anqp_query_list_id(struct hostapd_data *hapd, u16 info_id,
1103f05cddf9SRui Paulo struct anqp_query_info *qi)
1104f05cddf9SRui Paulo {
1105f05cddf9SRui Paulo switch (info_id) {
1106f05cddf9SRui Paulo case ANQP_CAPABILITY_LIST:
11075b9c547cSRui Paulo set_anqp_req(ANQP_REQ_CAPABILITY_LIST, "Capability List", 1,
11085b9c547cSRui Paulo qi);
1109f05cddf9SRui Paulo break;
1110f05cddf9SRui Paulo case ANQP_VENUE_NAME:
1111f05cddf9SRui Paulo set_anqp_req(ANQP_REQ_VENUE_NAME, "Venue Name",
11125b9c547cSRui Paulo hapd->conf->venue_name != NULL, qi);
1113f05cddf9SRui Paulo break;
1114780fb4a2SCy Schubert case ANQP_EMERGENCY_CALL_NUMBER:
1115780fb4a2SCy Schubert set_anqp_req(ANQP_REQ_EMERGENCY_CALL_NUMBER,
1116780fb4a2SCy Schubert "Emergency Call Number",
1117780fb4a2SCy Schubert get_anqp_elem(hapd, info_id) != NULL, qi);
1118780fb4a2SCy Schubert break;
1119f05cddf9SRui Paulo case ANQP_NETWORK_AUTH_TYPE:
1120f05cddf9SRui Paulo set_anqp_req(ANQP_REQ_NETWORK_AUTH_TYPE, "Network Auth Type",
11215b9c547cSRui Paulo hapd->conf->network_auth_type != NULL, qi);
1122f05cddf9SRui Paulo break;
1123f05cddf9SRui Paulo case ANQP_ROAMING_CONSORTIUM:
1124f05cddf9SRui Paulo set_anqp_req(ANQP_REQ_ROAMING_CONSORTIUM, "Roaming Consortium",
11255b9c547cSRui Paulo hapd->conf->roaming_consortium != NULL, qi);
1126f05cddf9SRui Paulo break;
1127f05cddf9SRui Paulo case ANQP_IP_ADDR_TYPE_AVAILABILITY:
1128f05cddf9SRui Paulo set_anqp_req(ANQP_REQ_IP_ADDR_TYPE_AVAILABILITY,
1129f05cddf9SRui Paulo "IP Addr Type Availability",
11305b9c547cSRui Paulo hapd->conf->ipaddr_type_configured, qi);
1131f05cddf9SRui Paulo break;
1132f05cddf9SRui Paulo case ANQP_NAI_REALM:
1133f05cddf9SRui Paulo set_anqp_req(ANQP_REQ_NAI_REALM, "NAI Realm",
11345b9c547cSRui Paulo hapd->conf->nai_realm_data != NULL, qi);
1135f05cddf9SRui Paulo break;
1136f05cddf9SRui Paulo case ANQP_3GPP_CELLULAR_NETWORK:
1137f05cddf9SRui Paulo set_anqp_req(ANQP_REQ_3GPP_CELLULAR_NETWORK,
1138f05cddf9SRui Paulo "3GPP Cellular Network",
11395b9c547cSRui Paulo hapd->conf->anqp_3gpp_cell_net != NULL, qi);
1140f05cddf9SRui Paulo break;
1141780fb4a2SCy Schubert case ANQP_AP_GEOSPATIAL_LOCATION:
1142780fb4a2SCy Schubert set_anqp_req(ANQP_REQ_AP_GEOSPATIAL_LOCATION,
1143780fb4a2SCy Schubert "AP Geospatial Location",
1144780fb4a2SCy Schubert get_anqp_elem(hapd, info_id) != NULL, qi);
1145780fb4a2SCy Schubert break;
1146780fb4a2SCy Schubert case ANQP_AP_CIVIC_LOCATION:
1147780fb4a2SCy Schubert set_anqp_req(ANQP_REQ_AP_CIVIC_LOCATION,
1148780fb4a2SCy Schubert "AP Civic Location",
1149780fb4a2SCy Schubert get_anqp_elem(hapd, info_id) != NULL, qi);
1150780fb4a2SCy Schubert break;
1151780fb4a2SCy Schubert case ANQP_AP_LOCATION_PUBLIC_URI:
1152780fb4a2SCy Schubert set_anqp_req(ANQP_REQ_AP_LOCATION_PUBLIC_URI,
1153780fb4a2SCy Schubert "AP Location Public URI",
1154780fb4a2SCy Schubert get_anqp_elem(hapd, info_id) != NULL, qi);
1155780fb4a2SCy Schubert break;
1156f05cddf9SRui Paulo case ANQP_DOMAIN_NAME:
1157f05cddf9SRui Paulo set_anqp_req(ANQP_REQ_DOMAIN_NAME, "Domain Name",
11585b9c547cSRui Paulo hapd->conf->domain_name != NULL, qi);
1159f05cddf9SRui Paulo break;
1160780fb4a2SCy Schubert case ANQP_EMERGENCY_ALERT_URI:
1161780fb4a2SCy Schubert set_anqp_req(ANQP_REQ_EMERGENCY_ALERT_URI,
1162780fb4a2SCy Schubert "Emergency Alert URI",
1163780fb4a2SCy Schubert get_anqp_elem(hapd, info_id) != NULL, qi);
1164780fb4a2SCy Schubert break;
1165780fb4a2SCy Schubert case ANQP_TDLS_CAPABILITY:
1166780fb4a2SCy Schubert set_anqp_req(ANQP_REQ_TDLS_CAPABILITY,
1167780fb4a2SCy Schubert "TDLS Capability",
1168780fb4a2SCy Schubert get_anqp_elem(hapd, info_id) != NULL, qi);
1169780fb4a2SCy Schubert break;
1170780fb4a2SCy Schubert case ANQP_EMERGENCY_NAI:
1171780fb4a2SCy Schubert set_anqp_req(ANQP_REQ_EMERGENCY_NAI,
1172780fb4a2SCy Schubert "Emergency NAI",
1173780fb4a2SCy Schubert get_anqp_elem(hapd, info_id) != NULL, qi);
1174780fb4a2SCy Schubert break;
1175f05cddf9SRui Paulo default:
117685732ac8SCy Schubert #ifdef CONFIG_FILS
117785732ac8SCy Schubert if (info_id == ANQP_FILS_REALM_INFO &&
117885732ac8SCy Schubert !dl_list_empty(&hapd->conf->fils_realms)) {
117985732ac8SCy Schubert wpa_printf(MSG_DEBUG,
118085732ac8SCy Schubert "ANQP: FILS Realm Information (local)");
118185732ac8SCy Schubert } else
118285732ac8SCy Schubert #endif /* CONFIG_FILS */
118385732ac8SCy Schubert if (info_id == ANQP_VENUE_URL && hapd->conf->venue_url) {
118485732ac8SCy Schubert wpa_printf(MSG_DEBUG,
118585732ac8SCy Schubert "ANQP: Venue URL (local)");
118685732ac8SCy Schubert } else if (!get_anqp_elem(hapd, info_id)) {
1187f05cddf9SRui Paulo wpa_printf(MSG_DEBUG, "ANQP: Unsupported Info Id %u",
1188f05cddf9SRui Paulo info_id);
1189f05cddf9SRui Paulo break;
1190f05cddf9SRui Paulo }
1191780fb4a2SCy Schubert if (qi->num_extra_req == ANQP_MAX_EXTRA_REQ) {
1192780fb4a2SCy Schubert wpa_printf(MSG_DEBUG,
1193780fb4a2SCy Schubert "ANQP: No more room for extra requests - ignore Info Id %u",
1194780fb4a2SCy Schubert info_id);
1195780fb4a2SCy Schubert break;
1196780fb4a2SCy Schubert }
1197780fb4a2SCy Schubert wpa_printf(MSG_DEBUG, "ANQP: Info Id %u (local)", info_id);
1198780fb4a2SCy Schubert qi->extra_req[qi->num_extra_req] = info_id;
1199780fb4a2SCy Schubert qi->num_extra_req++;
1200780fb4a2SCy Schubert break;
1201780fb4a2SCy Schubert }
1202f05cddf9SRui Paulo }
1203f05cddf9SRui Paulo
1204f05cddf9SRui Paulo
rx_anqp_query_list(struct hostapd_data * hapd,const u8 * pos,const u8 * end,struct anqp_query_info * qi)1205f05cddf9SRui Paulo static void rx_anqp_query_list(struct hostapd_data *hapd,
1206f05cddf9SRui Paulo const u8 *pos, const u8 *end,
1207f05cddf9SRui Paulo struct anqp_query_info *qi)
1208f05cddf9SRui Paulo {
1209f05cddf9SRui Paulo wpa_printf(MSG_DEBUG, "ANQP: %u Info IDs requested in Query list",
1210f05cddf9SRui Paulo (unsigned int) (end - pos) / 2);
1211f05cddf9SRui Paulo
1212780fb4a2SCy Schubert while (end - pos >= 2) {
1213f05cddf9SRui Paulo rx_anqp_query_list_id(hapd, WPA_GET_LE16(pos), qi);
1214f05cddf9SRui Paulo pos += 2;
1215f05cddf9SRui Paulo }
1216f05cddf9SRui Paulo }
1217f05cddf9SRui Paulo
1218f05cddf9SRui Paulo
1219f05cddf9SRui Paulo #ifdef CONFIG_HS20
1220f05cddf9SRui Paulo
rx_anqp_hs_query_list(struct hostapd_data * hapd,u8 subtype,struct anqp_query_info * qi)1221f05cddf9SRui Paulo static void rx_anqp_hs_query_list(struct hostapd_data *hapd, u8 subtype,
1222f05cddf9SRui Paulo struct anqp_query_info *qi)
1223f05cddf9SRui Paulo {
1224f05cddf9SRui Paulo switch (subtype) {
1225f05cddf9SRui Paulo case HS20_STYPE_CAPABILITY_LIST:
1226f05cddf9SRui Paulo set_anqp_req(ANQP_REQ_HS_CAPABILITY_LIST, "HS Capability List",
12275b9c547cSRui Paulo 1, qi);
1228f05cddf9SRui Paulo break;
1229f05cddf9SRui Paulo case HS20_STYPE_OPERATOR_FRIENDLY_NAME:
1230f05cddf9SRui Paulo set_anqp_req(ANQP_REQ_OPERATOR_FRIENDLY_NAME,
1231f05cddf9SRui Paulo "Operator Friendly Name",
12325b9c547cSRui Paulo hapd->conf->hs20_oper_friendly_name != NULL, qi);
1233f05cddf9SRui Paulo break;
1234f05cddf9SRui Paulo case HS20_STYPE_WAN_METRICS:
1235f05cddf9SRui Paulo set_anqp_req(ANQP_REQ_WAN_METRICS, "WAN Metrics",
12365b9c547cSRui Paulo hapd->conf->hs20_wan_metrics != NULL, qi);
1237f05cddf9SRui Paulo break;
1238f05cddf9SRui Paulo case HS20_STYPE_CONNECTION_CAPABILITY:
1239f05cddf9SRui Paulo set_anqp_req(ANQP_REQ_CONNECTION_CAPABILITY,
1240f05cddf9SRui Paulo "Connection Capability",
1241f05cddf9SRui Paulo hapd->conf->hs20_connection_capability != NULL,
12425b9c547cSRui Paulo qi);
1243f05cddf9SRui Paulo break;
1244f05cddf9SRui Paulo case HS20_STYPE_OPERATING_CLASS:
1245f05cddf9SRui Paulo set_anqp_req(ANQP_REQ_OPERATING_CLASS, "Operating Class",
12465b9c547cSRui Paulo hapd->conf->hs20_operating_class != NULL, qi);
12475b9c547cSRui Paulo break;
12485b9c547cSRui Paulo case HS20_STYPE_OSU_PROVIDERS_LIST:
12495b9c547cSRui Paulo set_anqp_req(ANQP_REQ_OSU_PROVIDERS_LIST, "OSU Providers list",
12505b9c547cSRui Paulo hapd->conf->hs20_osu_providers_count, qi);
1251f05cddf9SRui Paulo break;
125285732ac8SCy Schubert case HS20_STYPE_OPERATOR_ICON_METADATA:
125385732ac8SCy Schubert set_anqp_req(ANQP_REQ_OPERATOR_ICON_METADATA,
125485732ac8SCy Schubert "Operator Icon Metadata",
125585732ac8SCy Schubert hapd->conf->hs20_operator_icon_count, qi);
125685732ac8SCy Schubert break;
125785732ac8SCy Schubert case HS20_STYPE_OSU_PROVIDERS_NAI_LIST:
125885732ac8SCy Schubert set_anqp_req(ANQP_REQ_OSU_PROVIDERS_NAI_LIST,
125985732ac8SCy Schubert "OSU Providers NAI List",
126085732ac8SCy Schubert hapd->conf->hs20_osu_providers_nai_count, qi);
126185732ac8SCy Schubert break;
1262f05cddf9SRui Paulo default:
1263f05cddf9SRui Paulo wpa_printf(MSG_DEBUG, "ANQP: Unsupported HS 2.0 subtype %u",
1264f05cddf9SRui Paulo subtype);
1265f05cddf9SRui Paulo break;
1266f05cddf9SRui Paulo }
1267f05cddf9SRui Paulo }
1268f05cddf9SRui Paulo
1269f05cddf9SRui Paulo
rx_anqp_hs_nai_home_realm(struct hostapd_data * hapd,const u8 * pos,const u8 * end,struct anqp_query_info * qi)1270f05cddf9SRui Paulo static void rx_anqp_hs_nai_home_realm(struct hostapd_data *hapd,
1271f05cddf9SRui Paulo const u8 *pos, const u8 *end,
1272f05cddf9SRui Paulo struct anqp_query_info *qi)
1273f05cddf9SRui Paulo {
1274f05cddf9SRui Paulo qi->request |= ANQP_REQ_NAI_HOME_REALM;
1275f05cddf9SRui Paulo qi->home_realm_query = pos;
1276f05cddf9SRui Paulo qi->home_realm_query_len = end - pos;
1277f05cddf9SRui Paulo if (hapd->conf->nai_realm_data != NULL) {
1278f05cddf9SRui Paulo wpa_printf(MSG_DEBUG, "ANQP: HS 2.0 NAI Home Realm Query "
1279f05cddf9SRui Paulo "(local)");
1280f05cddf9SRui Paulo } else {
1281f05cddf9SRui Paulo wpa_printf(MSG_DEBUG, "ANQP: HS 2.0 NAI Home Realm Query not "
1282f05cddf9SRui Paulo "available");
1283f05cddf9SRui Paulo }
1284f05cddf9SRui Paulo }
1285f05cddf9SRui Paulo
1286f05cddf9SRui Paulo
rx_anqp_hs_icon_request(struct hostapd_data * hapd,const u8 * pos,const u8 * end,struct anqp_query_info * qi)12875b9c547cSRui Paulo static void rx_anqp_hs_icon_request(struct hostapd_data *hapd,
12885b9c547cSRui Paulo const u8 *pos, const u8 *end,
12895b9c547cSRui Paulo struct anqp_query_info *qi)
12905b9c547cSRui Paulo {
12915b9c547cSRui Paulo qi->request |= ANQP_REQ_ICON_REQUEST;
12925b9c547cSRui Paulo qi->icon_name = pos;
12935b9c547cSRui Paulo qi->icon_name_len = end - pos;
12945b9c547cSRui Paulo if (hapd->conf->hs20_icons_count) {
12955b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "ANQP: HS 2.0 Icon Request Query "
12965b9c547cSRui Paulo "(local)");
12975b9c547cSRui Paulo } else {
12985b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "ANQP: HS 2.0 Icon Request Query not "
12995b9c547cSRui Paulo "available");
13005b9c547cSRui Paulo }
13015b9c547cSRui Paulo }
13025b9c547cSRui Paulo
13035b9c547cSRui Paulo
rx_anqp_vendor_specific_hs20(struct hostapd_data * hapd,const u8 * pos,const u8 * end,struct anqp_query_info * qi)130485732ac8SCy Schubert static void rx_anqp_vendor_specific_hs20(struct hostapd_data *hapd,
1305f05cddf9SRui Paulo const u8 *pos, const u8 *end,
1306f05cddf9SRui Paulo struct anqp_query_info *qi)
1307f05cddf9SRui Paulo {
1308f05cddf9SRui Paulo u8 subtype;
1309f05cddf9SRui Paulo
1310780fb4a2SCy Schubert if (end - pos <= 1)
1311f05cddf9SRui Paulo return;
1312f05cddf9SRui Paulo
1313f05cddf9SRui Paulo subtype = *pos++;
1314f05cddf9SRui Paulo pos++; /* Reserved */
1315f05cddf9SRui Paulo switch (subtype) {
1316f05cddf9SRui Paulo case HS20_STYPE_QUERY_LIST:
1317f05cddf9SRui Paulo wpa_printf(MSG_DEBUG, "ANQP: HS 2.0 Query List");
1318f05cddf9SRui Paulo while (pos < end) {
1319f05cddf9SRui Paulo rx_anqp_hs_query_list(hapd, *pos, qi);
1320f05cddf9SRui Paulo pos++;
1321f05cddf9SRui Paulo }
1322f05cddf9SRui Paulo break;
1323f05cddf9SRui Paulo case HS20_STYPE_NAI_HOME_REALM_QUERY:
1324f05cddf9SRui Paulo rx_anqp_hs_nai_home_realm(hapd, pos, end, qi);
1325f05cddf9SRui Paulo break;
13265b9c547cSRui Paulo case HS20_STYPE_ICON_REQUEST:
13275b9c547cSRui Paulo rx_anqp_hs_icon_request(hapd, pos, end, qi);
13285b9c547cSRui Paulo break;
1329f05cddf9SRui Paulo default:
1330f05cddf9SRui Paulo wpa_printf(MSG_DEBUG, "ANQP: Unsupported HS 2.0 query subtype "
1331f05cddf9SRui Paulo "%u", subtype);
1332f05cddf9SRui Paulo break;
1333f05cddf9SRui Paulo }
1334f05cddf9SRui Paulo }
1335f05cddf9SRui Paulo
1336f05cddf9SRui Paulo #endif /* CONFIG_HS20 */
1337f05cddf9SRui Paulo
1338f05cddf9SRui Paulo
133985732ac8SCy Schubert #ifdef CONFIG_P2P
rx_anqp_vendor_specific_p2p(struct hostapd_data * hapd,struct anqp_query_info * qi)134085732ac8SCy Schubert static void rx_anqp_vendor_specific_p2p(struct hostapd_data *hapd,
134185732ac8SCy Schubert struct anqp_query_info *qi)
134285732ac8SCy Schubert {
134385732ac8SCy Schubert /*
134485732ac8SCy Schubert * This is for P2P SD and will be taken care of by the P2P
134585732ac8SCy Schubert * implementation. This query needs to be ignored in the generic
134685732ac8SCy Schubert * GAS server to avoid duplicated response.
134785732ac8SCy Schubert */
134885732ac8SCy Schubert wpa_printf(MSG_DEBUG,
134985732ac8SCy Schubert "ANQP: Ignore WFA vendor type %u (P2P SD) in generic GAS server",
135085732ac8SCy Schubert P2P_OUI_TYPE);
135185732ac8SCy Schubert qi->p2p_sd = 1;
135285732ac8SCy Schubert return;
135385732ac8SCy Schubert }
135485732ac8SCy Schubert #endif /* CONFIG_P2P */
135585732ac8SCy Schubert
135685732ac8SCy Schubert
135785732ac8SCy Schubert #ifdef CONFIG_MBO
135885732ac8SCy Schubert
rx_anqp_mbo_query_list(struct hostapd_data * hapd,u8 subtype,struct anqp_query_info * qi)135985732ac8SCy Schubert static void rx_anqp_mbo_query_list(struct hostapd_data *hapd, u8 subtype,
136085732ac8SCy Schubert struct anqp_query_info *qi)
136185732ac8SCy Schubert {
136285732ac8SCy Schubert switch (subtype) {
136385732ac8SCy Schubert case MBO_ANQP_SUBTYPE_CELL_CONN_PREF:
136485732ac8SCy Schubert set_anqp_req(ANQP_REQ_MBO_CELL_DATA_CONN_PREF,
136585732ac8SCy Schubert "Cellular Data Connection Preference",
136685732ac8SCy Schubert hapd->conf->mbo_cell_data_conn_pref >= 0, qi);
136785732ac8SCy Schubert break;
136885732ac8SCy Schubert default:
136985732ac8SCy Schubert wpa_printf(MSG_DEBUG, "ANQP: Unsupported MBO subtype %u",
137085732ac8SCy Schubert subtype);
137185732ac8SCy Schubert break;
137285732ac8SCy Schubert }
137385732ac8SCy Schubert }
137485732ac8SCy Schubert
137585732ac8SCy Schubert
rx_anqp_vendor_specific_mbo(struct hostapd_data * hapd,const u8 * pos,const u8 * end,struct anqp_query_info * qi)137685732ac8SCy Schubert static void rx_anqp_vendor_specific_mbo(struct hostapd_data *hapd,
137785732ac8SCy Schubert const u8 *pos, const u8 *end,
137885732ac8SCy Schubert struct anqp_query_info *qi)
137985732ac8SCy Schubert {
138085732ac8SCy Schubert u8 subtype;
138185732ac8SCy Schubert
138285732ac8SCy Schubert if (end - pos < 1)
138385732ac8SCy Schubert return;
138485732ac8SCy Schubert
138585732ac8SCy Schubert subtype = *pos++;
138685732ac8SCy Schubert switch (subtype) {
138785732ac8SCy Schubert case MBO_ANQP_SUBTYPE_QUERY_LIST:
138885732ac8SCy Schubert wpa_printf(MSG_DEBUG, "ANQP: MBO Query List");
138985732ac8SCy Schubert while (pos < end) {
139085732ac8SCy Schubert rx_anqp_mbo_query_list(hapd, *pos, qi);
139185732ac8SCy Schubert pos++;
139285732ac8SCy Schubert }
139385732ac8SCy Schubert break;
139485732ac8SCy Schubert default:
139585732ac8SCy Schubert wpa_printf(MSG_DEBUG, "ANQP: Unsupported MBO query subtype %u",
139685732ac8SCy Schubert subtype);
139785732ac8SCy Schubert break;
139885732ac8SCy Schubert }
139985732ac8SCy Schubert }
140085732ac8SCy Schubert
140185732ac8SCy Schubert #endif /* CONFIG_MBO */
140285732ac8SCy Schubert
140385732ac8SCy Schubert
rx_anqp_vendor_specific(struct hostapd_data * hapd,const u8 * pos,const u8 * end,struct anqp_query_info * qi)140485732ac8SCy Schubert static void rx_anqp_vendor_specific(struct hostapd_data *hapd,
140585732ac8SCy Schubert const u8 *pos, const u8 *end,
140685732ac8SCy Schubert struct anqp_query_info *qi)
140785732ac8SCy Schubert {
140885732ac8SCy Schubert u32 oui;
140985732ac8SCy Schubert
141085732ac8SCy Schubert if (end - pos < 4) {
141185732ac8SCy Schubert wpa_printf(MSG_DEBUG, "ANQP: Too short vendor specific ANQP "
141285732ac8SCy Schubert "Query element");
141385732ac8SCy Schubert return;
141485732ac8SCy Schubert }
141585732ac8SCy Schubert
141685732ac8SCy Schubert oui = WPA_GET_BE24(pos);
141785732ac8SCy Schubert pos += 3;
141885732ac8SCy Schubert if (oui != OUI_WFA) {
141985732ac8SCy Schubert wpa_printf(MSG_DEBUG, "ANQP: Unsupported vendor OUI %06x",
142085732ac8SCy Schubert oui);
142185732ac8SCy Schubert return;
142285732ac8SCy Schubert }
142385732ac8SCy Schubert
142485732ac8SCy Schubert switch (*pos) {
142585732ac8SCy Schubert #ifdef CONFIG_P2P
142685732ac8SCy Schubert case P2P_OUI_TYPE:
142785732ac8SCy Schubert rx_anqp_vendor_specific_p2p(hapd, qi);
142885732ac8SCy Schubert break;
142985732ac8SCy Schubert #endif /* CONFIG_P2P */
143085732ac8SCy Schubert #ifdef CONFIG_HS20
143185732ac8SCy Schubert case HS20_ANQP_OUI_TYPE:
143285732ac8SCy Schubert rx_anqp_vendor_specific_hs20(hapd, pos + 1, end, qi);
143385732ac8SCy Schubert break;
143485732ac8SCy Schubert #endif /* CONFIG_HS20 */
143585732ac8SCy Schubert #ifdef CONFIG_MBO
143685732ac8SCy Schubert case MBO_ANQP_OUI_TYPE:
143785732ac8SCy Schubert rx_anqp_vendor_specific_mbo(hapd, pos + 1, end, qi);
143885732ac8SCy Schubert break;
143985732ac8SCy Schubert #endif /* CONFIG_MBO */
144085732ac8SCy Schubert default:
144185732ac8SCy Schubert wpa_printf(MSG_DEBUG, "ANQP: Unsupported WFA vendor type %u",
144285732ac8SCy Schubert *pos);
144385732ac8SCy Schubert break;
144485732ac8SCy Schubert }
144585732ac8SCy Schubert }
144685732ac8SCy Schubert
144785732ac8SCy Schubert
gas_serv_req_local_processing(struct hostapd_data * hapd,const u8 * sa,u8 dialog_token,struct anqp_query_info * qi,int prot,int std_addr3)1448f05cddf9SRui Paulo static void gas_serv_req_local_processing(struct hostapd_data *hapd,
1449f05cddf9SRui Paulo const u8 *sa, u8 dialog_token,
1450780fb4a2SCy Schubert struct anqp_query_info *qi, int prot,
1451780fb4a2SCy Schubert int std_addr3)
1452f05cddf9SRui Paulo {
1453f05cddf9SRui Paulo struct wpabuf *buf, *tx_buf;
1454f05cddf9SRui Paulo
14555b9c547cSRui Paulo buf = gas_serv_build_gas_resp_payload(hapd, qi->request,
1456f05cddf9SRui Paulo qi->home_realm_query,
14575b9c547cSRui Paulo qi->home_realm_query_len,
1458780fb4a2SCy Schubert qi->icon_name, qi->icon_name_len,
1459780fb4a2SCy Schubert qi->extra_req, qi->num_extra_req);
1460f05cddf9SRui Paulo wpa_hexdump_buf(MSG_MSGDUMP, "ANQP: Locally generated ANQP responses",
1461f05cddf9SRui Paulo buf);
1462f05cddf9SRui Paulo if (!buf)
1463f05cddf9SRui Paulo return;
14645b9c547cSRui Paulo #ifdef CONFIG_P2P
14655b9c547cSRui Paulo if (wpabuf_len(buf) == 0 && qi->p2p_sd) {
14665b9c547cSRui Paulo wpa_printf(MSG_DEBUG,
14675b9c547cSRui Paulo "ANQP: Do not send response to P2P SD from generic GAS service (P2P SD implementation will process this)");
14685b9c547cSRui Paulo wpabuf_free(buf);
14695b9c547cSRui Paulo return;
14705b9c547cSRui Paulo }
14715b9c547cSRui Paulo #endif /* CONFIG_P2P */
1472f05cddf9SRui Paulo
147385732ac8SCy Schubert if (wpabuf_len(buf) > hapd->conf->gas_frag_limit ||
1474f05cddf9SRui Paulo hapd->conf->gas_comeback_delay) {
1475f05cddf9SRui Paulo struct gas_dialog_info *di;
1476f05cddf9SRui Paulo u16 comeback_delay = 1;
1477f05cddf9SRui Paulo
1478f05cddf9SRui Paulo if (hapd->conf->gas_comeback_delay) {
1479f05cddf9SRui Paulo /* Testing - allow overriding of the delay value */
1480f05cddf9SRui Paulo comeback_delay = hapd->conf->gas_comeback_delay;
1481f05cddf9SRui Paulo }
1482f05cddf9SRui Paulo
1483f05cddf9SRui Paulo wpa_printf(MSG_DEBUG, "ANQP: Too long response to fit in "
1484f05cddf9SRui Paulo "initial response - use GAS comeback");
1485f05cddf9SRui Paulo di = gas_dialog_create(hapd, sa, dialog_token);
1486f05cddf9SRui Paulo if (!di) {
1487f05cddf9SRui Paulo wpa_printf(MSG_INFO, "ANQP: Could not create dialog "
1488f05cddf9SRui Paulo "for " MACSTR " (dialog token %u)",
1489f05cddf9SRui Paulo MAC2STR(sa), dialog_token);
1490f05cddf9SRui Paulo wpabuf_free(buf);
14915b9c547cSRui Paulo tx_buf = gas_anqp_build_initial_resp_buf(
14925b9c547cSRui Paulo dialog_token, WLAN_STATUS_UNSPECIFIED_FAILURE,
14935b9c547cSRui Paulo 0, NULL);
14945b9c547cSRui Paulo } else {
14955b9c547cSRui Paulo di->prot = prot;
1496f05cddf9SRui Paulo di->sd_resp = buf;
1497f05cddf9SRui Paulo di->sd_resp_pos = 0;
1498f05cddf9SRui Paulo tx_buf = gas_anqp_build_initial_resp_buf(
14995b9c547cSRui Paulo dialog_token, WLAN_STATUS_SUCCESS,
15005b9c547cSRui Paulo comeback_delay, NULL);
15015b9c547cSRui Paulo }
1502f05cddf9SRui Paulo } else {
1503f05cddf9SRui Paulo wpa_printf(MSG_DEBUG, "ANQP: Initial response (no comeback)");
1504f05cddf9SRui Paulo tx_buf = gas_anqp_build_initial_resp_buf(
1505f05cddf9SRui Paulo dialog_token, WLAN_STATUS_SUCCESS, 0, buf);
1506f05cddf9SRui Paulo wpabuf_free(buf);
1507f05cddf9SRui Paulo }
1508f05cddf9SRui Paulo if (!tx_buf)
1509f05cddf9SRui Paulo return;
15105b9c547cSRui Paulo if (prot)
15115b9c547cSRui Paulo convert_to_protected_dual(tx_buf);
1512780fb4a2SCy Schubert if (std_addr3)
1513f05cddf9SRui Paulo hostapd_drv_send_action(hapd, hapd->iface->freq, 0, sa,
1514780fb4a2SCy Schubert wpabuf_head(tx_buf),
1515780fb4a2SCy Schubert wpabuf_len(tx_buf));
1516780fb4a2SCy Schubert else
1517780fb4a2SCy Schubert hostapd_drv_send_action_addr3_ap(hapd, hapd->iface->freq, 0, sa,
1518780fb4a2SCy Schubert wpabuf_head(tx_buf),
1519780fb4a2SCy Schubert wpabuf_len(tx_buf));
1520f05cddf9SRui Paulo wpabuf_free(tx_buf);
1521f05cddf9SRui Paulo }
1522f05cddf9SRui Paulo
1523f05cddf9SRui Paulo
152485732ac8SCy Schubert #ifdef CONFIG_DPP
gas_serv_req_dpp_processing(struct hostapd_data * hapd,const u8 * sa,u8 dialog_token,int prot,struct wpabuf * buf,int freq)1525206b73d0SCy Schubert void gas_serv_req_dpp_processing(struct hostapd_data *hapd,
152685732ac8SCy Schubert const u8 *sa, u8 dialog_token,
1527*a90b9d01SCy Schubert int prot, struct wpabuf *buf, int freq)
152885732ac8SCy Schubert {
152985732ac8SCy Schubert struct wpabuf *tx_buf;
153085732ac8SCy Schubert
153185732ac8SCy Schubert if (wpabuf_len(buf) > hapd->conf->gas_frag_limit ||
153285732ac8SCy Schubert hapd->conf->gas_comeback_delay) {
153385732ac8SCy Schubert struct gas_dialog_info *di;
153485732ac8SCy Schubert u16 comeback_delay = 1;
153585732ac8SCy Schubert
153685732ac8SCy Schubert if (hapd->conf->gas_comeback_delay) {
153785732ac8SCy Schubert /* Testing - allow overriding of the delay value */
153885732ac8SCy Schubert comeback_delay = hapd->conf->gas_comeback_delay;
153985732ac8SCy Schubert }
154085732ac8SCy Schubert
154185732ac8SCy Schubert wpa_printf(MSG_DEBUG,
154285732ac8SCy Schubert "DPP: Too long response to fit in initial response - use GAS comeback");
154385732ac8SCy Schubert di = gas_dialog_create(hapd, sa, dialog_token);
154485732ac8SCy Schubert if (!di) {
154585732ac8SCy Schubert wpa_printf(MSG_INFO, "DPP: Could not create dialog for "
154685732ac8SCy Schubert MACSTR " (dialog token %u)",
154785732ac8SCy Schubert MAC2STR(sa), dialog_token);
154885732ac8SCy Schubert wpabuf_free(buf);
154985732ac8SCy Schubert tx_buf = gas_build_initial_resp(
155085732ac8SCy Schubert dialog_token, WLAN_STATUS_UNSPECIFIED_FAILURE,
155185732ac8SCy Schubert 0, 10);
155285732ac8SCy Schubert if (tx_buf)
155385732ac8SCy Schubert gas_serv_write_dpp_adv_proto(tx_buf);
155485732ac8SCy Schubert } else {
155585732ac8SCy Schubert di->prot = prot;
155685732ac8SCy Schubert di->sd_resp = buf;
155785732ac8SCy Schubert di->sd_resp_pos = 0;
1558c1d255d3SCy Schubert di->dpp = 1;
155985732ac8SCy Schubert tx_buf = gas_build_initial_resp(
156085732ac8SCy Schubert dialog_token, WLAN_STATUS_SUCCESS,
1561c1d255d3SCy Schubert comeback_delay, 10 + 2);
1562c1d255d3SCy Schubert if (tx_buf) {
156385732ac8SCy Schubert gas_serv_write_dpp_adv_proto(tx_buf);
1564c1d255d3SCy Schubert wpabuf_put_le16(tx_buf, 0);
1565c1d255d3SCy Schubert }
156685732ac8SCy Schubert }
156785732ac8SCy Schubert } else {
156885732ac8SCy Schubert wpa_printf(MSG_DEBUG,
156985732ac8SCy Schubert "DPP: GAS Initial response (no comeback)");
157085732ac8SCy Schubert tx_buf = gas_build_initial_resp(
157185732ac8SCy Schubert dialog_token, WLAN_STATUS_SUCCESS, 0,
157285732ac8SCy Schubert 10 + 2 + wpabuf_len(buf));
157385732ac8SCy Schubert if (tx_buf) {
157485732ac8SCy Schubert gas_serv_write_dpp_adv_proto(tx_buf);
157585732ac8SCy Schubert wpabuf_put_le16(tx_buf, wpabuf_len(buf));
157685732ac8SCy Schubert wpabuf_put_buf(tx_buf, buf);
157785732ac8SCy Schubert hostapd_dpp_gas_status_handler(hapd, 1);
157885732ac8SCy Schubert }
157985732ac8SCy Schubert wpabuf_free(buf);
158085732ac8SCy Schubert }
158185732ac8SCy Schubert if (!tx_buf)
158285732ac8SCy Schubert return;
158385732ac8SCy Schubert if (prot)
158485732ac8SCy Schubert convert_to_protected_dual(tx_buf);
1585*a90b9d01SCy Schubert hostapd_drv_send_action(hapd, freq ? freq : hapd->iface->freq, 0, sa,
158685732ac8SCy Schubert wpabuf_head(tx_buf),
158785732ac8SCy Schubert wpabuf_len(tx_buf));
158885732ac8SCy Schubert wpabuf_free(tx_buf);
158985732ac8SCy Schubert }
159085732ac8SCy Schubert #endif /* CONFIG_DPP */
159185732ac8SCy Schubert
159285732ac8SCy Schubert
gas_serv_rx_gas_initial_req(struct hostapd_data * hapd,const u8 * sa,const u8 * data,size_t len,int prot,int std_addr3,int freq)1593f05cddf9SRui Paulo static void gas_serv_rx_gas_initial_req(struct hostapd_data *hapd,
1594f05cddf9SRui Paulo const u8 *sa,
1595780fb4a2SCy Schubert const u8 *data, size_t len, int prot,
1596*a90b9d01SCy Schubert int std_addr3, int freq)
1597f05cddf9SRui Paulo {
1598f05cddf9SRui Paulo const u8 *pos = data;
1599f05cddf9SRui Paulo const u8 *end = data + len;
1600f05cddf9SRui Paulo const u8 *next;
1601f05cddf9SRui Paulo u8 dialog_token;
1602f05cddf9SRui Paulo u16 slen;
1603f05cddf9SRui Paulo struct anqp_query_info qi;
1604f05cddf9SRui Paulo const u8 *adv_proto;
160585732ac8SCy Schubert #ifdef CONFIG_DPP
160685732ac8SCy Schubert int dpp = 0;
160785732ac8SCy Schubert #endif /* CONFIG_DPP */
1608f05cddf9SRui Paulo
1609f05cddf9SRui Paulo if (len < 1 + 2)
1610f05cddf9SRui Paulo return;
1611f05cddf9SRui Paulo
1612f05cddf9SRui Paulo os_memset(&qi, 0, sizeof(qi));
1613f05cddf9SRui Paulo
1614f05cddf9SRui Paulo dialog_token = *pos++;
1615f05cddf9SRui Paulo wpa_msg(hapd->msg_ctx, MSG_DEBUG,
1616f05cddf9SRui Paulo "GAS: GAS Initial Request from " MACSTR " (dialog token %u) ",
1617f05cddf9SRui Paulo MAC2STR(sa), dialog_token);
1618f05cddf9SRui Paulo
1619f05cddf9SRui Paulo if (*pos != WLAN_EID_ADV_PROTO) {
1620f05cddf9SRui Paulo wpa_msg(hapd->msg_ctx, MSG_DEBUG,
1621f05cddf9SRui Paulo "GAS: Unexpected IE in GAS Initial Request: %u", *pos);
1622f05cddf9SRui Paulo return;
1623f05cddf9SRui Paulo }
1624f05cddf9SRui Paulo adv_proto = pos++;
1625f05cddf9SRui Paulo
1626f05cddf9SRui Paulo slen = *pos++;
1627780fb4a2SCy Schubert if (slen > end - pos || slen < 2) {
1628f05cddf9SRui Paulo wpa_msg(hapd->msg_ctx, MSG_DEBUG,
1629f05cddf9SRui Paulo "GAS: Invalid IE in GAS Initial Request");
1630f05cddf9SRui Paulo return;
1631f05cddf9SRui Paulo }
1632780fb4a2SCy Schubert next = pos + slen;
1633f05cddf9SRui Paulo pos++; /* skip QueryRespLenLimit and PAME-BI */
1634f05cddf9SRui Paulo
163585732ac8SCy Schubert #ifdef CONFIG_DPP
163685732ac8SCy Schubert if (slen == 8 && *pos == WLAN_EID_VENDOR_SPECIFIC &&
163785732ac8SCy Schubert pos[1] == 5 && WPA_GET_BE24(&pos[2]) == OUI_WFA &&
163885732ac8SCy Schubert pos[5] == DPP_OUI_TYPE && pos[6] == 0x01) {
163985732ac8SCy Schubert wpa_printf(MSG_DEBUG, "DPP: Configuration Request");
164085732ac8SCy Schubert dpp = 1;
164185732ac8SCy Schubert } else
164285732ac8SCy Schubert #endif /* CONFIG_DPP */
164385732ac8SCy Schubert
1644f05cddf9SRui Paulo if (*pos != ACCESS_NETWORK_QUERY_PROTOCOL) {
1645f05cddf9SRui Paulo struct wpabuf *buf;
1646f05cddf9SRui Paulo wpa_msg(hapd->msg_ctx, MSG_DEBUG,
1647f05cddf9SRui Paulo "GAS: Unsupported GAS advertisement protocol id %u",
1648f05cddf9SRui Paulo *pos);
1649f05cddf9SRui Paulo if (sa[0] & 0x01)
1650f05cddf9SRui Paulo return; /* Invalid source address - drop silently */
1651f05cddf9SRui Paulo buf = gas_build_initial_resp(
1652f05cddf9SRui Paulo dialog_token, WLAN_STATUS_GAS_ADV_PROTO_NOT_SUPPORTED,
1653f05cddf9SRui Paulo 0, 2 + slen + 2);
1654f05cddf9SRui Paulo if (buf == NULL)
1655f05cddf9SRui Paulo return;
1656f05cddf9SRui Paulo wpabuf_put_data(buf, adv_proto, 2 + slen);
1657f05cddf9SRui Paulo wpabuf_put_le16(buf, 0); /* Query Response Length */
16585b9c547cSRui Paulo if (prot)
16595b9c547cSRui Paulo convert_to_protected_dual(buf);
1660780fb4a2SCy Schubert if (std_addr3)
1661f05cddf9SRui Paulo hostapd_drv_send_action(hapd, hapd->iface->freq, 0, sa,
1662780fb4a2SCy Schubert wpabuf_head(buf),
1663780fb4a2SCy Schubert wpabuf_len(buf));
1664780fb4a2SCy Schubert else
1665780fb4a2SCy Schubert hostapd_drv_send_action_addr3_ap(hapd,
1666780fb4a2SCy Schubert hapd->iface->freq, 0,
1667780fb4a2SCy Schubert sa, wpabuf_head(buf),
1668780fb4a2SCy Schubert wpabuf_len(buf));
1669f05cddf9SRui Paulo wpabuf_free(buf);
1670f05cddf9SRui Paulo return;
1671f05cddf9SRui Paulo }
1672f05cddf9SRui Paulo
1673f05cddf9SRui Paulo pos = next;
1674f05cddf9SRui Paulo /* Query Request */
1675780fb4a2SCy Schubert if (end - pos < 2)
1676f05cddf9SRui Paulo return;
1677f05cddf9SRui Paulo slen = WPA_GET_LE16(pos);
1678f05cddf9SRui Paulo pos += 2;
1679780fb4a2SCy Schubert if (slen > end - pos)
1680f05cddf9SRui Paulo return;
1681f05cddf9SRui Paulo end = pos + slen;
1682f05cddf9SRui Paulo
168385732ac8SCy Schubert #ifdef CONFIG_DPP
168485732ac8SCy Schubert if (dpp) {
168585732ac8SCy Schubert struct wpabuf *msg;
168685732ac8SCy Schubert
1687206b73d0SCy Schubert msg = hostapd_dpp_gas_req_handler(hapd, sa, pos, slen,
1688206b73d0SCy Schubert data, len);
168985732ac8SCy Schubert if (!msg)
169085732ac8SCy Schubert return;
1691*a90b9d01SCy Schubert gas_serv_req_dpp_processing(hapd, sa, dialog_token, prot, msg,
1692*a90b9d01SCy Schubert freq);
169385732ac8SCy Schubert return;
169485732ac8SCy Schubert }
169585732ac8SCy Schubert #endif /* CONFIG_DPP */
169685732ac8SCy Schubert
1697f05cddf9SRui Paulo /* ANQP Query Request */
1698f05cddf9SRui Paulo while (pos < end) {
1699f05cddf9SRui Paulo u16 info_id, elen;
1700f05cddf9SRui Paulo
1701780fb4a2SCy Schubert if (end - pos < 4)
1702f05cddf9SRui Paulo return;
1703f05cddf9SRui Paulo
1704f05cddf9SRui Paulo info_id = WPA_GET_LE16(pos);
1705f05cddf9SRui Paulo pos += 2;
1706f05cddf9SRui Paulo elen = WPA_GET_LE16(pos);
1707f05cddf9SRui Paulo pos += 2;
1708f05cddf9SRui Paulo
1709780fb4a2SCy Schubert if (elen > end - pos) {
1710f05cddf9SRui Paulo wpa_printf(MSG_DEBUG, "ANQP: Invalid Query Request");
1711f05cddf9SRui Paulo return;
1712f05cddf9SRui Paulo }
1713f05cddf9SRui Paulo
1714f05cddf9SRui Paulo switch (info_id) {
1715f05cddf9SRui Paulo case ANQP_QUERY_LIST:
1716f05cddf9SRui Paulo rx_anqp_query_list(hapd, pos, pos + elen, &qi);
1717f05cddf9SRui Paulo break;
1718f05cddf9SRui Paulo case ANQP_VENDOR_SPECIFIC:
1719f05cddf9SRui Paulo rx_anqp_vendor_specific(hapd, pos, pos + elen, &qi);
1720f05cddf9SRui Paulo break;
1721f05cddf9SRui Paulo default:
1722f05cddf9SRui Paulo wpa_printf(MSG_DEBUG, "ANQP: Unsupported Query "
1723f05cddf9SRui Paulo "Request element %u", info_id);
1724f05cddf9SRui Paulo break;
1725f05cddf9SRui Paulo }
1726f05cddf9SRui Paulo
1727f05cddf9SRui Paulo pos += elen;
1728f05cddf9SRui Paulo }
1729f05cddf9SRui Paulo
1730780fb4a2SCy Schubert gas_serv_req_local_processing(hapd, sa, dialog_token, &qi, prot,
1731780fb4a2SCy Schubert std_addr3);
1732f05cddf9SRui Paulo }
1733f05cddf9SRui Paulo
1734f05cddf9SRui Paulo
gas_serv_rx_gas_comeback_req(struct hostapd_data * hapd,const u8 * sa,const u8 * data,size_t len,int prot,int std_addr3)1735f05cddf9SRui Paulo static void gas_serv_rx_gas_comeback_req(struct hostapd_data *hapd,
1736f05cddf9SRui Paulo const u8 *sa,
1737780fb4a2SCy Schubert const u8 *data, size_t len, int prot,
1738780fb4a2SCy Schubert int std_addr3)
1739f05cddf9SRui Paulo {
1740f05cddf9SRui Paulo struct gas_dialog_info *dialog;
1741f05cddf9SRui Paulo struct wpabuf *buf, *tx_buf;
1742f05cddf9SRui Paulo u8 dialog_token;
1743f05cddf9SRui Paulo size_t frag_len;
1744f05cddf9SRui Paulo int more = 0;
1745f05cddf9SRui Paulo
1746f05cddf9SRui Paulo wpa_hexdump(MSG_DEBUG, "GAS: RX GAS Comeback Request", data, len);
1747f05cddf9SRui Paulo if (len < 1)
1748f05cddf9SRui Paulo return;
1749f05cddf9SRui Paulo dialog_token = *data;
1750f05cddf9SRui Paulo wpa_msg(hapd->msg_ctx, MSG_DEBUG, "GAS: Dialog Token: %u",
1751f05cddf9SRui Paulo dialog_token);
1752f05cddf9SRui Paulo
1753f05cddf9SRui Paulo dialog = gas_serv_dialog_find(hapd, sa, dialog_token);
1754f05cddf9SRui Paulo if (!dialog) {
1755f05cddf9SRui Paulo wpa_msg(hapd->msg_ctx, MSG_DEBUG, "GAS: No pending SD "
1756f05cddf9SRui Paulo "response fragment for " MACSTR " dialog token %u",
1757f05cddf9SRui Paulo MAC2STR(sa), dialog_token);
1758f05cddf9SRui Paulo
1759f05cddf9SRui Paulo if (sa[0] & 0x01)
1760f05cddf9SRui Paulo return; /* Invalid source address - drop silently */
1761f05cddf9SRui Paulo tx_buf = gas_anqp_build_comeback_resp_buf(
1762f05cddf9SRui Paulo dialog_token, WLAN_STATUS_NO_OUTSTANDING_GAS_REQ, 0, 0,
1763f05cddf9SRui Paulo 0, NULL);
1764f05cddf9SRui Paulo if (tx_buf == NULL)
1765f05cddf9SRui Paulo return;
1766f05cddf9SRui Paulo goto send_resp;
1767f05cddf9SRui Paulo }
1768f05cddf9SRui Paulo
1769f05cddf9SRui Paulo frag_len = wpabuf_len(dialog->sd_resp) - dialog->sd_resp_pos;
177085732ac8SCy Schubert if (frag_len > hapd->conf->gas_frag_limit) {
177185732ac8SCy Schubert frag_len = hapd->conf->gas_frag_limit;
1772f05cddf9SRui Paulo more = 1;
1773f05cddf9SRui Paulo }
1774f05cddf9SRui Paulo wpa_msg(hapd->msg_ctx, MSG_DEBUG, "GAS: resp frag_len %u",
1775f05cddf9SRui Paulo (unsigned int) frag_len);
1776f05cddf9SRui Paulo buf = wpabuf_alloc_copy(wpabuf_head_u8(dialog->sd_resp) +
1777f05cddf9SRui Paulo dialog->sd_resp_pos, frag_len);
1778f05cddf9SRui Paulo if (buf == NULL) {
1779f05cddf9SRui Paulo wpa_msg(hapd->msg_ctx, MSG_DEBUG, "GAS: Failed to allocate "
1780f05cddf9SRui Paulo "buffer");
17815b9c547cSRui Paulo gas_serv_dialog_clear(dialog);
17825b9c547cSRui Paulo return;
1783f05cddf9SRui Paulo }
178485732ac8SCy Schubert #ifdef CONFIG_DPP
178585732ac8SCy Schubert if (dialog->dpp) {
178685732ac8SCy Schubert tx_buf = gas_build_comeback_resp(dialog_token,
178785732ac8SCy Schubert WLAN_STATUS_SUCCESS,
178885732ac8SCy Schubert dialog->sd_frag_id, more, 0,
1789c1d255d3SCy Schubert 10 + 2 + frag_len);
179085732ac8SCy Schubert if (tx_buf) {
179185732ac8SCy Schubert gas_serv_write_dpp_adv_proto(tx_buf);
1792c1d255d3SCy Schubert wpabuf_put_le16(tx_buf, frag_len);
179385732ac8SCy Schubert wpabuf_put_buf(tx_buf, buf);
179485732ac8SCy Schubert }
179585732ac8SCy Schubert } else
179685732ac8SCy Schubert #endif /* CONFIG_DPP */
1797f05cddf9SRui Paulo tx_buf = gas_anqp_build_comeback_resp_buf(dialog_token,
1798f05cddf9SRui Paulo WLAN_STATUS_SUCCESS,
1799f05cddf9SRui Paulo dialog->sd_frag_id,
1800f05cddf9SRui Paulo more, 0, buf);
1801f05cddf9SRui Paulo wpabuf_free(buf);
18025b9c547cSRui Paulo if (tx_buf == NULL) {
18035b9c547cSRui Paulo gas_serv_dialog_clear(dialog);
18045b9c547cSRui Paulo return;
18055b9c547cSRui Paulo }
1806f05cddf9SRui Paulo wpa_msg(hapd->msg_ctx, MSG_DEBUG, "GAS: Tx GAS Comeback Response "
1807f05cddf9SRui Paulo "(frag_id %d more=%d frag_len=%d)",
1808f05cddf9SRui Paulo dialog->sd_frag_id, more, (int) frag_len);
1809f05cddf9SRui Paulo dialog->sd_frag_id++;
1810f05cddf9SRui Paulo dialog->sd_resp_pos += frag_len;
1811f05cddf9SRui Paulo
1812f05cddf9SRui Paulo if (more) {
1813f05cddf9SRui Paulo wpa_msg(hapd->msg_ctx, MSG_DEBUG, "GAS: %d more bytes remain "
1814f05cddf9SRui Paulo "to be sent",
1815f05cddf9SRui Paulo (int) (wpabuf_len(dialog->sd_resp) -
1816f05cddf9SRui Paulo dialog->sd_resp_pos));
1817f05cddf9SRui Paulo } else {
1818f05cddf9SRui Paulo wpa_msg(hapd->msg_ctx, MSG_DEBUG, "GAS: All fragments of "
1819f05cddf9SRui Paulo "SD response sent");
182085732ac8SCy Schubert #ifdef CONFIG_DPP
182185732ac8SCy Schubert if (dialog->dpp)
182285732ac8SCy Schubert hostapd_dpp_gas_status_handler(hapd, 1);
182385732ac8SCy Schubert #endif /* CONFIG_DPP */
1824f05cddf9SRui Paulo gas_serv_dialog_clear(dialog);
1825f05cddf9SRui Paulo gas_serv_free_dialogs(hapd, sa);
1826f05cddf9SRui Paulo }
1827f05cddf9SRui Paulo
1828f05cddf9SRui Paulo send_resp:
18295b9c547cSRui Paulo if (prot)
18305b9c547cSRui Paulo convert_to_protected_dual(tx_buf);
1831780fb4a2SCy Schubert if (std_addr3)
1832f05cddf9SRui Paulo hostapd_drv_send_action(hapd, hapd->iface->freq, 0, sa,
1833780fb4a2SCy Schubert wpabuf_head(tx_buf),
1834780fb4a2SCy Schubert wpabuf_len(tx_buf));
1835780fb4a2SCy Schubert else
1836780fb4a2SCy Schubert hostapd_drv_send_action_addr3_ap(hapd, hapd->iface->freq, 0, sa,
1837780fb4a2SCy Schubert wpabuf_head(tx_buf),
1838780fb4a2SCy Schubert wpabuf_len(tx_buf));
1839f05cddf9SRui Paulo wpabuf_free(tx_buf);
1840f05cddf9SRui Paulo }
1841f05cddf9SRui Paulo
1842f05cddf9SRui Paulo
gas_serv_rx_public_action(void * ctx,const u8 * buf,size_t len,int freq)1843f05cddf9SRui Paulo static void gas_serv_rx_public_action(void *ctx, const u8 *buf, size_t len,
1844f05cddf9SRui Paulo int freq)
1845f05cddf9SRui Paulo {
1846f05cddf9SRui Paulo struct hostapd_data *hapd = ctx;
1847f05cddf9SRui Paulo const struct ieee80211_mgmt *mgmt;
1848f05cddf9SRui Paulo const u8 *sa, *data;
1849780fb4a2SCy Schubert int prot, std_addr3;
1850f05cddf9SRui Paulo
1851f05cddf9SRui Paulo mgmt = (const struct ieee80211_mgmt *) buf;
18525b9c547cSRui Paulo if (len < IEEE80211_HDRLEN + 2)
1853f05cddf9SRui Paulo return;
18545b9c547cSRui Paulo if (mgmt->u.action.category != WLAN_ACTION_PUBLIC &&
18555b9c547cSRui Paulo mgmt->u.action.category != WLAN_ACTION_PROTECTED_DUAL)
1856f05cddf9SRui Paulo return;
18575b9c547cSRui Paulo /*
18585b9c547cSRui Paulo * Note: Public Action and Protected Dual of Public Action frames share
18595b9c547cSRui Paulo * the same payload structure, so it is fine to use definitions of
18605b9c547cSRui Paulo * Public Action frames to process both.
18615b9c547cSRui Paulo */
18625b9c547cSRui Paulo prot = mgmt->u.action.category == WLAN_ACTION_PROTECTED_DUAL;
1863f05cddf9SRui Paulo sa = mgmt->sa;
1864780fb4a2SCy Schubert if (hapd->conf->gas_address3 == 1)
1865780fb4a2SCy Schubert std_addr3 = 1;
1866780fb4a2SCy Schubert else if (hapd->conf->gas_address3 == 2)
1867780fb4a2SCy Schubert std_addr3 = 0;
1868780fb4a2SCy Schubert else
1869780fb4a2SCy Schubert std_addr3 = is_broadcast_ether_addr(mgmt->bssid);
18705b9c547cSRui Paulo len -= IEEE80211_HDRLEN + 1;
18715b9c547cSRui Paulo data = buf + IEEE80211_HDRLEN + 1;
1872f05cddf9SRui Paulo switch (data[0]) {
1873f05cddf9SRui Paulo case WLAN_PA_GAS_INITIAL_REQ:
1874780fb4a2SCy Schubert gas_serv_rx_gas_initial_req(hapd, sa, data + 1, len - 1, prot,
1875*a90b9d01SCy Schubert std_addr3, freq);
1876f05cddf9SRui Paulo break;
1877f05cddf9SRui Paulo case WLAN_PA_GAS_COMEBACK_REQ:
1878780fb4a2SCy Schubert gas_serv_rx_gas_comeback_req(hapd, sa, data + 1, len - 1, prot,
1879780fb4a2SCy Schubert std_addr3);
1880f05cddf9SRui Paulo break;
1881f05cddf9SRui Paulo }
1882f05cddf9SRui Paulo }
1883f05cddf9SRui Paulo
1884f05cddf9SRui Paulo
gas_serv_init(struct hostapd_data * hapd)1885f05cddf9SRui Paulo int gas_serv_init(struct hostapd_data *hapd)
1886f05cddf9SRui Paulo {
18875b9c547cSRui Paulo hapd->public_action_cb2 = gas_serv_rx_public_action;
18885b9c547cSRui Paulo hapd->public_action_cb2_ctx = hapd;
1889f05cddf9SRui Paulo return 0;
1890f05cddf9SRui Paulo }
1891f05cddf9SRui Paulo
1892f05cddf9SRui Paulo
gas_serv_deinit(struct hostapd_data * hapd)1893f05cddf9SRui Paulo void gas_serv_deinit(struct hostapd_data *hapd)
1894f05cddf9SRui Paulo {
1895f05cddf9SRui Paulo }
1896