blob: 13cf766c50380a4615923b7bf9a85bb908fec4df [file] [log] [blame]
Dmitry Shmidt04949592012-07-19 12:16:46 -07001/*
2 * Generic advertisement service (GAS) server
Dmitry Shmidt18463232014-01-24 12:29:41 -08003 * Copyright (c) 2011-2014, Qualcomm Atheros, Inc.
Dmitry Shmidt04949592012-07-19 12:16:46 -07004 *
5 * This software may be distributed under the terms of the BSD license.
6 * See README for more details.
7 */
8
9#include "includes.h"
10
11#include "common.h"
12#include "common/ieee802_11_defs.h"
13#include "common/gas.h"
Dmitry Shmidtd2986c22017-10-23 14:22:09 -070014#include "common/wpa_ctrl.h"
Dmitry Shmidt04949592012-07-19 12:16:46 -070015#include "utils/eloop.h"
16#include "hostapd.h"
17#include "ap_config.h"
18#include "ap_drv_ops.h"
Dmitry Shmidtd2986c22017-10-23 14:22:09 -070019#include "dpp_hostapd.h"
Dmitry Shmidt04949592012-07-19 12:16:46 -070020#include "sta_info.h"
21#include "gas_serv.h"
22
23
Dmitry Shmidtd2986c22017-10-23 14:22:09 -070024#ifdef CONFIG_DPP
25static void gas_serv_write_dpp_adv_proto(struct wpabuf *buf)
26{
27 wpabuf_put_u8(buf, WLAN_EID_ADV_PROTO);
28 wpabuf_put_u8(buf, 8); /* Length */
29 wpabuf_put_u8(buf, 0x7f);
30 wpabuf_put_u8(buf, WLAN_EID_VENDOR_SPECIFIC);
31 wpabuf_put_u8(buf, 5);
32 wpabuf_put_be24(buf, OUI_WFA);
33 wpabuf_put_u8(buf, DPP_OUI_TYPE);
34 wpabuf_put_u8(buf, 0x01);
35}
36#endif /* CONFIG_DPP */
37
38
Dmitry Shmidt18463232014-01-24 12:29:41 -080039static void convert_to_protected_dual(struct wpabuf *msg)
40{
41 u8 *categ = wpabuf_mhead_u8(msg);
42 *categ = WLAN_ACTION_PROTECTED_DUAL;
43}
44
45
Dmitry Shmidt04949592012-07-19 12:16:46 -070046static struct gas_dialog_info *
47gas_dialog_create(struct hostapd_data *hapd, const u8 *addr, u8 dialog_token)
48{
49 struct sta_info *sta;
50 struct gas_dialog_info *dia = NULL;
51 int i, j;
52
53 sta = ap_get_sta(hapd, addr);
54 if (!sta) {
55 /*
56 * We need a STA entry to be able to maintain state for
57 * the GAS query.
58 */
59 wpa_printf(MSG_DEBUG, "ANQP: Add a temporary STA entry for "
60 "GAS query");
61 sta = ap_sta_add(hapd, addr);
62 if (!sta) {
63 wpa_printf(MSG_DEBUG, "Failed to add STA " MACSTR
64 " for GAS query", MAC2STR(addr));
65 return NULL;
66 }
67 sta->flags |= WLAN_STA_GAS;
68 /*
69 * The default inactivity is 300 seconds. We don't need
Dmitry Shmidtebd93af2017-02-21 13:40:44 -080070 * it to be that long. Use five second timeout and increase this
71 * with the comeback_delay for testing cases.
Dmitry Shmidt04949592012-07-19 12:16:46 -070072 */
Dmitry Shmidtebd93af2017-02-21 13:40:44 -080073 ap_sta_session_timeout(hapd, sta,
74 hapd->conf->gas_comeback_delay / 1024 +
75 5);
Dmitry Shmidt54605472013-11-08 11:10:19 -080076 } else {
77 ap_sta_replenish_timeout(hapd, sta, 5);
Dmitry Shmidt04949592012-07-19 12:16:46 -070078 }
79
80 if (sta->gas_dialog == NULL) {
Dmitry Shmidtfb45fd52015-01-05 13:08:17 -080081 sta->gas_dialog = os_calloc(GAS_DIALOG_MAX,
Dmitry Shmidt04949592012-07-19 12:16:46 -070082 sizeof(struct gas_dialog_info));
83 if (sta->gas_dialog == NULL)
84 return NULL;
85 }
86
87 for (i = sta->gas_dialog_next, j = 0; j < GAS_DIALOG_MAX; i++, j++) {
88 if (i == GAS_DIALOG_MAX)
89 i = 0;
90 if (sta->gas_dialog[i].valid)
91 continue;
92 dia = &sta->gas_dialog[i];
93 dia->valid = 1;
Dmitry Shmidt04949592012-07-19 12:16:46 -070094 dia->dialog_token = dialog_token;
95 sta->gas_dialog_next = (++i == GAS_DIALOG_MAX) ? 0 : i;
96 return dia;
97 }
98
99 wpa_msg(hapd->msg_ctx, MSG_ERROR, "ANQP: Could not create dialog for "
100 MACSTR " dialog_token %u. Consider increasing "
101 "GAS_DIALOG_MAX.", MAC2STR(addr), dialog_token);
102
103 return NULL;
104}
105
106
107struct gas_dialog_info *
108gas_serv_dialog_find(struct hostapd_data *hapd, const u8 *addr,
109 u8 dialog_token)
110{
111 struct sta_info *sta;
112 int i;
113
114 sta = ap_get_sta(hapd, addr);
115 if (!sta) {
116 wpa_printf(MSG_DEBUG, "ANQP: could not find STA " MACSTR,
117 MAC2STR(addr));
118 return NULL;
119 }
120 for (i = 0; sta->gas_dialog && i < GAS_DIALOG_MAX; i++) {
121 if (sta->gas_dialog[i].dialog_token != dialog_token ||
122 !sta->gas_dialog[i].valid)
123 continue;
Dmitry Shmidt7d56b752015-12-22 10:59:44 -0800124 ap_sta_replenish_timeout(hapd, sta, 5);
Dmitry Shmidt04949592012-07-19 12:16:46 -0700125 return &sta->gas_dialog[i];
126 }
127 wpa_printf(MSG_DEBUG, "ANQP: Could not find dialog for "
128 MACSTR " dialog_token %u", MAC2STR(addr), dialog_token);
129 return NULL;
130}
131
132
133void gas_serv_dialog_clear(struct gas_dialog_info *dia)
134{
135 wpabuf_free(dia->sd_resp);
136 os_memset(dia, 0, sizeof(*dia));
137}
138
139
140static void gas_serv_free_dialogs(struct hostapd_data *hapd,
141 const u8 *sta_addr)
142{
143 struct sta_info *sta;
144 int i;
145
146 sta = ap_get_sta(hapd, sta_addr);
147 if (sta == NULL || sta->gas_dialog == NULL)
148 return;
149
150 for (i = 0; i < GAS_DIALOG_MAX; i++) {
151 if (sta->gas_dialog[i].valid)
152 return;
153 }
154
155 os_free(sta->gas_dialog);
156 sta->gas_dialog = NULL;
157}
158
159
Dmitry Shmidtaa532512012-09-24 10:35:31 -0700160#ifdef CONFIG_HS20
Dmitry Shmidt61d9df32012-08-29 16:22:06 -0700161static void anqp_add_hs_capab_list(struct hostapd_data *hapd,
162 struct wpabuf *buf)
163{
164 u8 *len;
165
166 len = gas_anqp_add_element(buf, ANQP_VENDOR_SPECIFIC);
167 wpabuf_put_be24(buf, OUI_WFA);
168 wpabuf_put_u8(buf, HS20_ANQP_OUI_TYPE);
169 wpabuf_put_u8(buf, HS20_STYPE_CAPABILITY_LIST);
170 wpabuf_put_u8(buf, 0); /* Reserved */
171 wpabuf_put_u8(buf, HS20_STYPE_CAPABILITY_LIST);
172 if (hapd->conf->hs20_oper_friendly_name)
173 wpabuf_put_u8(buf, HS20_STYPE_OPERATOR_FRIENDLY_NAME);
174 if (hapd->conf->hs20_wan_metrics)
175 wpabuf_put_u8(buf, HS20_STYPE_WAN_METRICS);
176 if (hapd->conf->hs20_connection_capability)
177 wpabuf_put_u8(buf, HS20_STYPE_CONNECTION_CAPABILITY);
178 if (hapd->conf->nai_realm_data)
179 wpabuf_put_u8(buf, HS20_STYPE_NAI_HOME_REALM_QUERY);
180 if (hapd->conf->hs20_operating_class)
181 wpabuf_put_u8(buf, HS20_STYPE_OPERATING_CLASS);
182 gas_anqp_set_element_len(buf, len);
183}
Dmitry Shmidtaa532512012-09-24 10:35:31 -0700184#endif /* CONFIG_HS20 */
Dmitry Shmidt61d9df32012-08-29 16:22:06 -0700185
186
Dmitry Shmidtd80a4012015-11-05 16:35:40 -0800187static struct anqp_element * get_anqp_elem(struct hostapd_data *hapd,
188 u16 infoid)
189{
190 struct anqp_element *elem;
191
192 dl_list_for_each(elem, &hapd->conf->anqp_elem, struct anqp_element,
193 list) {
194 if (elem->infoid == infoid)
195 return elem;
196 }
197
198 return NULL;
199}
200
201
202static void anqp_add_elem(struct hostapd_data *hapd, struct wpabuf *buf,
203 u16 infoid)
204{
205 struct anqp_element *elem;
206
207 elem = get_anqp_elem(hapd, infoid);
208 if (!elem)
209 return;
210 if (wpabuf_tailroom(buf) < 2 + 2 + wpabuf_len(elem->payload)) {
211 wpa_printf(MSG_DEBUG, "ANQP: No room for InfoID %u payload",
212 infoid);
213 return;
214 }
215
216 wpabuf_put_le16(buf, infoid);
217 wpabuf_put_le16(buf, wpabuf_len(elem->payload));
218 wpabuf_put_buf(buf, elem->payload);
219}
220
221
222static int anqp_add_override(struct hostapd_data *hapd, struct wpabuf *buf,
223 u16 infoid)
224{
225 if (get_anqp_elem(hapd, infoid)) {
226 anqp_add_elem(hapd, buf, infoid);
227 return 1;
228 }
229
230 return 0;
231}
232
233
Dmitry Shmidt04949592012-07-19 12:16:46 -0700234static void anqp_add_capab_list(struct hostapd_data *hapd,
235 struct wpabuf *buf)
236{
237 u8 *len;
Dmitry Shmidtd80a4012015-11-05 16:35:40 -0800238 u16 id;
239
240 if (anqp_add_override(hapd, buf, ANQP_CAPABILITY_LIST))
241 return;
Dmitry Shmidt04949592012-07-19 12:16:46 -0700242
243 len = gas_anqp_add_element(buf, ANQP_CAPABILITY_LIST);
244 wpabuf_put_le16(buf, ANQP_CAPABILITY_LIST);
Dmitry Shmidtd80a4012015-11-05 16:35:40 -0800245 if (hapd->conf->venue_name || get_anqp_elem(hapd, ANQP_VENUE_NAME))
Dmitry Shmidt04949592012-07-19 12:16:46 -0700246 wpabuf_put_le16(buf, ANQP_VENUE_NAME);
Dmitry Shmidtd80a4012015-11-05 16:35:40 -0800247 if (get_anqp_elem(hapd, ANQP_EMERGENCY_CALL_NUMBER))
248 wpabuf_put_le16(buf, ANQP_EMERGENCY_CALL_NUMBER);
249 if (hapd->conf->network_auth_type ||
250 get_anqp_elem(hapd, ANQP_NETWORK_AUTH_TYPE))
Dmitry Shmidt61d9df32012-08-29 16:22:06 -0700251 wpabuf_put_le16(buf, ANQP_NETWORK_AUTH_TYPE);
Dmitry Shmidtd80a4012015-11-05 16:35:40 -0800252 if (hapd->conf->roaming_consortium ||
253 get_anqp_elem(hapd, ANQP_ROAMING_CONSORTIUM))
Dmitry Shmidt04949592012-07-19 12:16:46 -0700254 wpabuf_put_le16(buf, ANQP_ROAMING_CONSORTIUM);
Dmitry Shmidtd80a4012015-11-05 16:35:40 -0800255 if (hapd->conf->ipaddr_type_configured ||
256 get_anqp_elem(hapd, ANQP_IP_ADDR_TYPE_AVAILABILITY))
Dmitry Shmidt61d9df32012-08-29 16:22:06 -0700257 wpabuf_put_le16(buf, ANQP_IP_ADDR_TYPE_AVAILABILITY);
Dmitry Shmidtd80a4012015-11-05 16:35:40 -0800258 if (hapd->conf->nai_realm_data ||
259 get_anqp_elem(hapd, ANQP_NAI_REALM))
Dmitry Shmidt61d9df32012-08-29 16:22:06 -0700260 wpabuf_put_le16(buf, ANQP_NAI_REALM);
Dmitry Shmidtd80a4012015-11-05 16:35:40 -0800261 if (hapd->conf->anqp_3gpp_cell_net ||
262 get_anqp_elem(hapd, ANQP_3GPP_CELLULAR_NETWORK))
Dmitry Shmidt61d9df32012-08-29 16:22:06 -0700263 wpabuf_put_le16(buf, ANQP_3GPP_CELLULAR_NETWORK);
Dmitry Shmidtd80a4012015-11-05 16:35:40 -0800264 if (get_anqp_elem(hapd, ANQP_AP_GEOSPATIAL_LOCATION))
265 wpabuf_put_le16(buf, ANQP_AP_GEOSPATIAL_LOCATION);
266 if (get_anqp_elem(hapd, ANQP_AP_CIVIC_LOCATION))
267 wpabuf_put_le16(buf, ANQP_AP_CIVIC_LOCATION);
268 if (get_anqp_elem(hapd, ANQP_AP_LOCATION_PUBLIC_URI))
269 wpabuf_put_le16(buf, ANQP_AP_LOCATION_PUBLIC_URI);
270 if (hapd->conf->domain_name || get_anqp_elem(hapd, ANQP_DOMAIN_NAME))
Dmitry Shmidt61d9df32012-08-29 16:22:06 -0700271 wpabuf_put_le16(buf, ANQP_DOMAIN_NAME);
Dmitry Shmidtd80a4012015-11-05 16:35:40 -0800272 if (get_anqp_elem(hapd, ANQP_EMERGENCY_ALERT_URI))
273 wpabuf_put_le16(buf, ANQP_EMERGENCY_ALERT_URI);
Dmitry Shmidtabb90a32016-12-05 15:34:39 -0800274 if (get_anqp_elem(hapd, ANQP_TDLS_CAPABILITY))
275 wpabuf_put_le16(buf, ANQP_TDLS_CAPABILITY);
Dmitry Shmidtd80a4012015-11-05 16:35:40 -0800276 if (get_anqp_elem(hapd, ANQP_EMERGENCY_NAI))
277 wpabuf_put_le16(buf, ANQP_EMERGENCY_NAI);
278 if (get_anqp_elem(hapd, ANQP_NEIGHBOR_REPORT))
279 wpabuf_put_le16(buf, ANQP_NEIGHBOR_REPORT);
Dmitry Shmidt29333592017-01-09 12:27:11 -0800280#ifdef CONFIG_FILS
281 if (!dl_list_empty(&hapd->conf->fils_realms) ||
282 get_anqp_elem(hapd, ANQP_FILS_REALM_INFO))
283 wpabuf_put_le16(buf, ANQP_FILS_REALM_INFO);
284#endif /* CONFIG_FILS */
285 if (get_anqp_elem(hapd, ANQP_CAG))
286 wpabuf_put_le16(buf, ANQP_CAG);
Roshan Pius3a1667e2018-07-03 15:17:14 -0700287 if (hapd->conf->venue_url || get_anqp_elem(hapd, ANQP_VENUE_URL))
Dmitry Shmidtd80a4012015-11-05 16:35:40 -0800288 wpabuf_put_le16(buf, ANQP_VENUE_URL);
289 if (get_anqp_elem(hapd, ANQP_ADVICE_OF_CHARGE))
290 wpabuf_put_le16(buf, ANQP_ADVICE_OF_CHARGE);
291 if (get_anqp_elem(hapd, ANQP_LOCAL_CONTENT))
292 wpabuf_put_le16(buf, ANQP_LOCAL_CONTENT);
Dmitry Shmidtabb90a32016-12-05 15:34:39 -0800293 for (id = 280; id < 300; id++) {
294 if (get_anqp_elem(hapd, id))
295 wpabuf_put_le16(buf, id);
296 }
Dmitry Shmidtaa532512012-09-24 10:35:31 -0700297#ifdef CONFIG_HS20
Dmitry Shmidt61d9df32012-08-29 16:22:06 -0700298 anqp_add_hs_capab_list(hapd, buf);
Dmitry Shmidtaa532512012-09-24 10:35:31 -0700299#endif /* CONFIG_HS20 */
Dmitry Shmidt04949592012-07-19 12:16:46 -0700300 gas_anqp_set_element_len(buf, len);
301}
302
303
304static void anqp_add_venue_name(struct hostapd_data *hapd, struct wpabuf *buf)
305{
Dmitry Shmidtd80a4012015-11-05 16:35:40 -0800306 if (anqp_add_override(hapd, buf, ANQP_VENUE_NAME))
307 return;
308
Dmitry Shmidt04949592012-07-19 12:16:46 -0700309 if (hapd->conf->venue_name) {
310 u8 *len;
311 unsigned int i;
312 len = gas_anqp_add_element(buf, ANQP_VENUE_NAME);
313 wpabuf_put_u8(buf, hapd->conf->venue_group);
314 wpabuf_put_u8(buf, hapd->conf->venue_type);
315 for (i = 0; i < hapd->conf->venue_name_count; i++) {
Dmitry Shmidt61d9df32012-08-29 16:22:06 -0700316 struct hostapd_lang_string *vn;
Dmitry Shmidt04949592012-07-19 12:16:46 -0700317 vn = &hapd->conf->venue_name[i];
318 wpabuf_put_u8(buf, 3 + vn->name_len);
319 wpabuf_put_data(buf, vn->lang, 3);
320 wpabuf_put_data(buf, vn->name, vn->name_len);
321 }
322 gas_anqp_set_element_len(buf, len);
323 }
324}
325
326
Roshan Pius3a1667e2018-07-03 15:17:14 -0700327static void anqp_add_venue_url(struct hostapd_data *hapd, struct wpabuf *buf)
328{
329 if (anqp_add_override(hapd, buf, ANQP_VENUE_URL))
330 return;
331
332 if (hapd->conf->venue_url) {
333 u8 *len;
334 unsigned int i;
335
336 len = gas_anqp_add_element(buf, ANQP_VENUE_URL);
337 for (i = 0; i < hapd->conf->venue_url_count; i++) {
338 struct hostapd_venue_url *url;
339
340 url = &hapd->conf->venue_url[i];
341 wpabuf_put_u8(buf, 1 + url->url_len);
342 wpabuf_put_u8(buf, url->venue_number);
343 wpabuf_put_data(buf, url->url, url->url_len);
344 }
345 gas_anqp_set_element_len(buf, len);
346 }
347}
348
349
Dmitry Shmidt61d9df32012-08-29 16:22:06 -0700350static void anqp_add_network_auth_type(struct hostapd_data *hapd,
351 struct wpabuf *buf)
352{
Dmitry Shmidtd80a4012015-11-05 16:35:40 -0800353 if (anqp_add_override(hapd, buf, ANQP_NETWORK_AUTH_TYPE))
354 return;
355
Dmitry Shmidt61d9df32012-08-29 16:22:06 -0700356 if (hapd->conf->network_auth_type) {
357 wpabuf_put_le16(buf, ANQP_NETWORK_AUTH_TYPE);
358 wpabuf_put_le16(buf, hapd->conf->network_auth_type_len);
359 wpabuf_put_data(buf, hapd->conf->network_auth_type,
360 hapd->conf->network_auth_type_len);
361 }
362}
363
364
Dmitry Shmidt04949592012-07-19 12:16:46 -0700365static void anqp_add_roaming_consortium(struct hostapd_data *hapd,
366 struct wpabuf *buf)
367{
368 unsigned int i;
369 u8 *len;
370
Dmitry Shmidtd80a4012015-11-05 16:35:40 -0800371 if (anqp_add_override(hapd, buf, ANQP_ROAMING_CONSORTIUM))
372 return;
373
Dmitry Shmidt04949592012-07-19 12:16:46 -0700374 len = gas_anqp_add_element(buf, ANQP_ROAMING_CONSORTIUM);
375 for (i = 0; i < hapd->conf->roaming_consortium_count; i++) {
376 struct hostapd_roaming_consortium *rc;
377 rc = &hapd->conf->roaming_consortium[i];
378 wpabuf_put_u8(buf, rc->len);
379 wpabuf_put_data(buf, rc->oi, rc->len);
380 }
381 gas_anqp_set_element_len(buf, len);
382}
383
384
Dmitry Shmidt61d9df32012-08-29 16:22:06 -0700385static void anqp_add_ip_addr_type_availability(struct hostapd_data *hapd,
386 struct wpabuf *buf)
387{
Dmitry Shmidtd80a4012015-11-05 16:35:40 -0800388 if (anqp_add_override(hapd, buf, ANQP_IP_ADDR_TYPE_AVAILABILITY))
389 return;
390
Dmitry Shmidt61d9df32012-08-29 16:22:06 -0700391 if (hapd->conf->ipaddr_type_configured) {
392 wpabuf_put_le16(buf, ANQP_IP_ADDR_TYPE_AVAILABILITY);
393 wpabuf_put_le16(buf, 1);
394 wpabuf_put_u8(buf, hapd->conf->ipaddr_type_availability);
395 }
396}
397
398
399static void anqp_add_nai_realm_eap(struct wpabuf *buf,
400 struct hostapd_nai_realm_data *realm)
401{
402 unsigned int i, j;
403
404 wpabuf_put_u8(buf, realm->eap_method_count);
405
406 for (i = 0; i < realm->eap_method_count; i++) {
407 struct hostapd_nai_realm_eap *eap = &realm->eap_method[i];
408 wpabuf_put_u8(buf, 2 + (3 * eap->num_auths));
409 wpabuf_put_u8(buf, eap->eap_method);
410 wpabuf_put_u8(buf, eap->num_auths);
411 for (j = 0; j < eap->num_auths; j++) {
412 wpabuf_put_u8(buf, eap->auth_id[j]);
413 wpabuf_put_u8(buf, 1);
414 wpabuf_put_u8(buf, eap->auth_val[j]);
415 }
416 }
417}
418
419
420static void anqp_add_nai_realm_data(struct wpabuf *buf,
421 struct hostapd_nai_realm_data *realm,
422 unsigned int realm_idx)
423{
424 u8 *realm_data_len;
425
426 wpa_printf(MSG_DEBUG, "realm=%s, len=%d", realm->realm[realm_idx],
427 (int) os_strlen(realm->realm[realm_idx]));
428 realm_data_len = wpabuf_put(buf, 2);
429 wpabuf_put_u8(buf, realm->encoding);
430 wpabuf_put_u8(buf, os_strlen(realm->realm[realm_idx]));
431 wpabuf_put_str(buf, realm->realm[realm_idx]);
432 anqp_add_nai_realm_eap(buf, realm);
433 gas_anqp_set_element_len(buf, realm_data_len);
434}
435
436
437static int hs20_add_nai_home_realm_matches(struct hostapd_data *hapd,
438 struct wpabuf *buf,
439 const u8 *home_realm,
440 size_t home_realm_len)
441{
442 unsigned int i, j, k;
443 u8 num_realms, num_matching = 0, encoding, realm_len, *realm_list_len;
444 struct hostapd_nai_realm_data *realm;
445 const u8 *pos, *realm_name, *end;
446 struct {
447 unsigned int realm_data_idx;
448 unsigned int realm_idx;
449 } matches[10];
450
451 pos = home_realm;
452 end = pos + home_realm_len;
Dmitry Shmidtd80a4012015-11-05 16:35:40 -0800453 if (end - pos < 1) {
Dmitry Shmidt61d9df32012-08-29 16:22:06 -0700454 wpa_hexdump(MSG_DEBUG, "Too short NAI Home Realm Query",
455 home_realm, home_realm_len);
456 return -1;
457 }
458 num_realms = *pos++;
459
460 for (i = 0; i < num_realms && num_matching < 10; i++) {
Dmitry Shmidtd80a4012015-11-05 16:35:40 -0800461 if (end - pos < 2) {
Dmitry Shmidt61d9df32012-08-29 16:22:06 -0700462 wpa_hexdump(MSG_DEBUG,
463 "Truncated NAI Home Realm Query",
464 home_realm, home_realm_len);
465 return -1;
466 }
467 encoding = *pos++;
468 realm_len = *pos++;
Dmitry Shmidtd80a4012015-11-05 16:35:40 -0800469 if (realm_len > end - pos) {
Dmitry Shmidt61d9df32012-08-29 16:22:06 -0700470 wpa_hexdump(MSG_DEBUG,
471 "Truncated NAI Home Realm Query",
472 home_realm, home_realm_len);
473 return -1;
474 }
475 realm_name = pos;
476 for (j = 0; j < hapd->conf->nai_realm_count &&
477 num_matching < 10; j++) {
478 const u8 *rpos, *rend;
479 realm = &hapd->conf->nai_realm_data[j];
480 if (encoding != realm->encoding)
481 continue;
482
483 rpos = realm_name;
484 while (rpos < realm_name + realm_len &&
485 num_matching < 10) {
486 for (rend = rpos;
487 rend < realm_name + realm_len; rend++) {
488 if (*rend == ';')
489 break;
490 }
491 for (k = 0; k < MAX_NAI_REALMS &&
492 realm->realm[k] &&
493 num_matching < 10; k++) {
494 if ((int) os_strlen(realm->realm[k]) !=
495 rend - rpos ||
496 os_strncmp((char *) rpos,
497 realm->realm[k],
498 rend - rpos) != 0)
499 continue;
500 matches[num_matching].realm_data_idx =
501 j;
502 matches[num_matching].realm_idx = k;
503 num_matching++;
504 }
505 rpos = rend + 1;
506 }
507 }
508 pos += realm_len;
509 }
510
511 realm_list_len = gas_anqp_add_element(buf, ANQP_NAI_REALM);
512 wpabuf_put_le16(buf, num_matching);
513
514 /*
515 * There are two ways to format. 1. each realm in a NAI Realm Data unit
516 * 2. all realms that share the same EAP methods in a NAI Realm Data
517 * unit. The first format is likely to be bigger in size than the
518 * second, but may be easier to parse and process by the receiver.
519 */
520 for (i = 0; i < num_matching; i++) {
521 wpa_printf(MSG_DEBUG, "realm_idx %d, realm_data_idx %d",
522 matches[i].realm_data_idx, matches[i].realm_idx);
523 realm = &hapd->conf->nai_realm_data[matches[i].realm_data_idx];
524 anqp_add_nai_realm_data(buf, realm, matches[i].realm_idx);
525 }
526 gas_anqp_set_element_len(buf, realm_list_len);
527 return 0;
528}
529
530
531static void anqp_add_nai_realm(struct hostapd_data *hapd, struct wpabuf *buf,
532 const u8 *home_realm, size_t home_realm_len,
533 int nai_realm, int nai_home_realm)
534{
Dmitry Shmidtd80a4012015-11-05 16:35:40 -0800535 if (nai_realm && !nai_home_realm &&
536 anqp_add_override(hapd, buf, ANQP_NAI_REALM))
537 return;
538
Dmitry Shmidt61d9df32012-08-29 16:22:06 -0700539 if (nai_realm && hapd->conf->nai_realm_data) {
540 u8 *len;
541 unsigned int i, j;
542 len = gas_anqp_add_element(buf, ANQP_NAI_REALM);
543 wpabuf_put_le16(buf, hapd->conf->nai_realm_count);
544 for (i = 0; i < hapd->conf->nai_realm_count; i++) {
545 u8 *realm_data_len, *realm_len;
546 struct hostapd_nai_realm_data *realm;
547
548 realm = &hapd->conf->nai_realm_data[i];
549 realm_data_len = wpabuf_put(buf, 2);
550 wpabuf_put_u8(buf, realm->encoding);
551 realm_len = wpabuf_put(buf, 1);
552 for (j = 0; realm->realm[j]; j++) {
553 if (j > 0)
554 wpabuf_put_u8(buf, ';');
555 wpabuf_put_str(buf, realm->realm[j]);
556 }
557 *realm_len = (u8 *) wpabuf_put(buf, 0) - realm_len - 1;
558 anqp_add_nai_realm_eap(buf, realm);
559 gas_anqp_set_element_len(buf, realm_data_len);
560 }
561 gas_anqp_set_element_len(buf, len);
Dmitry Shmidt09f57ba2014-06-10 16:07:13 -0700562 } else if (nai_home_realm && hapd->conf->nai_realm_data && home_realm) {
Dmitry Shmidt61d9df32012-08-29 16:22:06 -0700563 hs20_add_nai_home_realm_matches(hapd, buf, home_realm,
564 home_realm_len);
565 }
566}
567
568
569static void anqp_add_3gpp_cellular_network(struct hostapd_data *hapd,
570 struct wpabuf *buf)
571{
Dmitry Shmidtd80a4012015-11-05 16:35:40 -0800572 if (anqp_add_override(hapd, buf, ANQP_3GPP_CELLULAR_NETWORK))
573 return;
574
Dmitry Shmidt61d9df32012-08-29 16:22:06 -0700575 if (hapd->conf->anqp_3gpp_cell_net) {
576 wpabuf_put_le16(buf, ANQP_3GPP_CELLULAR_NETWORK);
577 wpabuf_put_le16(buf,
578 hapd->conf->anqp_3gpp_cell_net_len);
579 wpabuf_put_data(buf, hapd->conf->anqp_3gpp_cell_net,
580 hapd->conf->anqp_3gpp_cell_net_len);
581 }
582}
583
584
585static void anqp_add_domain_name(struct hostapd_data *hapd, struct wpabuf *buf)
586{
Dmitry Shmidtd80a4012015-11-05 16:35:40 -0800587 if (anqp_add_override(hapd, buf, ANQP_DOMAIN_NAME))
588 return;
589
Dmitry Shmidt61d9df32012-08-29 16:22:06 -0700590 if (hapd->conf->domain_name) {
591 wpabuf_put_le16(buf, ANQP_DOMAIN_NAME);
592 wpabuf_put_le16(buf, hapd->conf->domain_name_len);
593 wpabuf_put_data(buf, hapd->conf->domain_name,
594 hapd->conf->domain_name_len);
595 }
596}
597
598
Dmitry Shmidt29333592017-01-09 12:27:11 -0800599#ifdef CONFIG_FILS
600static void anqp_add_fils_realm_info(struct hostapd_data *hapd,
601 struct wpabuf *buf)
602{
603 size_t count;
604
605 if (anqp_add_override(hapd, buf, ANQP_FILS_REALM_INFO))
606 return;
607
608 count = dl_list_len(&hapd->conf->fils_realms);
609 if (count > 10000)
610 count = 10000;
611 if (count) {
612 struct fils_realm *realm;
613
614 wpabuf_put_le16(buf, ANQP_FILS_REALM_INFO);
615 wpabuf_put_le16(buf, 2 * count);
616
617 dl_list_for_each(realm, &hapd->conf->fils_realms,
618 struct fils_realm, list) {
619 if (count == 0)
620 break;
621 wpabuf_put_data(buf, realm->hash, 2);
622 count--;
623 }
624 }
625}
626#endif /* CONFIG_FILS */
627
628
Dmitry Shmidtaa532512012-09-24 10:35:31 -0700629#ifdef CONFIG_HS20
630
Dmitry Shmidt61d9df32012-08-29 16:22:06 -0700631static void anqp_add_operator_friendly_name(struct hostapd_data *hapd,
632 struct wpabuf *buf)
633{
634 if (hapd->conf->hs20_oper_friendly_name) {
635 u8 *len;
636 unsigned int i;
637 len = gas_anqp_add_element(buf, ANQP_VENDOR_SPECIFIC);
638 wpabuf_put_be24(buf, OUI_WFA);
639 wpabuf_put_u8(buf, HS20_ANQP_OUI_TYPE);
640 wpabuf_put_u8(buf, HS20_STYPE_OPERATOR_FRIENDLY_NAME);
641 wpabuf_put_u8(buf, 0); /* Reserved */
642 for (i = 0; i < hapd->conf->hs20_oper_friendly_name_count; i++)
643 {
644 struct hostapd_lang_string *vn;
645 vn = &hapd->conf->hs20_oper_friendly_name[i];
646 wpabuf_put_u8(buf, 3 + vn->name_len);
647 wpabuf_put_data(buf, vn->lang, 3);
648 wpabuf_put_data(buf, vn->name, vn->name_len);
649 }
650 gas_anqp_set_element_len(buf, len);
651 }
652}
653
654
655static void anqp_add_wan_metrics(struct hostapd_data *hapd,
656 struct wpabuf *buf)
657{
658 if (hapd->conf->hs20_wan_metrics) {
659 u8 *len = gas_anqp_add_element(buf, ANQP_VENDOR_SPECIFIC);
660 wpabuf_put_be24(buf, OUI_WFA);
661 wpabuf_put_u8(buf, HS20_ANQP_OUI_TYPE);
662 wpabuf_put_u8(buf, HS20_STYPE_WAN_METRICS);
663 wpabuf_put_u8(buf, 0); /* Reserved */
664 wpabuf_put_data(buf, hapd->conf->hs20_wan_metrics, 13);
665 gas_anqp_set_element_len(buf, len);
666 }
667}
668
669
670static void anqp_add_connection_capability(struct hostapd_data *hapd,
671 struct wpabuf *buf)
672{
673 if (hapd->conf->hs20_connection_capability) {
674 u8 *len = gas_anqp_add_element(buf, ANQP_VENDOR_SPECIFIC);
675 wpabuf_put_be24(buf, OUI_WFA);
676 wpabuf_put_u8(buf, HS20_ANQP_OUI_TYPE);
677 wpabuf_put_u8(buf, HS20_STYPE_CONNECTION_CAPABILITY);
678 wpabuf_put_u8(buf, 0); /* Reserved */
679 wpabuf_put_data(buf, hapd->conf->hs20_connection_capability,
680 hapd->conf->hs20_connection_capability_len);
681 gas_anqp_set_element_len(buf, len);
682 }
683}
684
685
686static void anqp_add_operating_class(struct hostapd_data *hapd,
687 struct wpabuf *buf)
688{
689 if (hapd->conf->hs20_operating_class) {
690 u8 *len = gas_anqp_add_element(buf, ANQP_VENDOR_SPECIFIC);
691 wpabuf_put_be24(buf, OUI_WFA);
692 wpabuf_put_u8(buf, HS20_ANQP_OUI_TYPE);
693 wpabuf_put_u8(buf, HS20_STYPE_OPERATING_CLASS);
694 wpabuf_put_u8(buf, 0); /* Reserved */
695 wpabuf_put_data(buf, hapd->conf->hs20_operating_class,
696 hapd->conf->hs20_operating_class_len);
697 gas_anqp_set_element_len(buf, len);
698 }
699}
700
Dmitry Shmidtaa532512012-09-24 10:35:31 -0700701#endif /* CONFIG_HS20 */
702
Dmitry Shmidt61d9df32012-08-29 16:22:06 -0700703
Dmitry Shmidtd2986c22017-10-23 14:22:09 -0700704#ifdef CONFIG_MBO
705static void anqp_add_mbo_cell_data_conn_pref(struct hostapd_data *hapd,
706 struct wpabuf *buf)
707{
708 if (hapd->conf->mbo_cell_data_conn_pref >= 0) {
709 u8 *len = gas_anqp_add_element(buf, ANQP_VENDOR_SPECIFIC);
710 wpabuf_put_be24(buf, OUI_WFA);
711 wpabuf_put_u8(buf, MBO_ANQP_OUI_TYPE);
712 wpabuf_put_u8(buf, MBO_ANQP_SUBTYPE_CELL_CONN_PREF);
713 wpabuf_put_u8(buf, hapd->conf->mbo_cell_data_conn_pref);
714 gas_anqp_set_element_len(buf, len);
715 }
716}
717#endif /* CONFIG_MBO */
718
719
Dmitry Shmidtb97e4282016-02-08 10:16:07 -0800720static size_t anqp_get_required_len(struct hostapd_data *hapd,
721 const u16 *infoid,
722 unsigned int num_infoid)
723{
724 size_t len = 0;
725 unsigned int i;
726
727 for (i = 0; i < num_infoid; i++) {
728 struct anqp_element *elem = get_anqp_elem(hapd, infoid[i]);
729
730 if (elem)
731 len += 2 + 2 + wpabuf_len(elem->payload);
732 }
733
734 return len;
735}
736
737
Dmitry Shmidt04949592012-07-19 12:16:46 -0700738static struct wpabuf *
739gas_serv_build_gas_resp_payload(struct hostapd_data *hapd,
740 unsigned int request,
Dmitry Shmidtf21452a2014-02-26 10:55:25 -0800741 const u8 *home_realm, size_t home_realm_len,
Dmitry Shmidtd80a4012015-11-05 16:35:40 -0800742 const u16 *extra_req,
743 unsigned int num_extra_req)
Dmitry Shmidt04949592012-07-19 12:16:46 -0700744{
745 struct wpabuf *buf;
Dmitry Shmidtf21452a2014-02-26 10:55:25 -0800746 size_t len;
Dmitry Shmidtd80a4012015-11-05 16:35:40 -0800747 unsigned int i;
Dmitry Shmidt04949592012-07-19 12:16:46 -0700748
Dmitry Shmidtf21452a2014-02-26 10:55:25 -0800749 len = 1400;
750 if (request & (ANQP_REQ_NAI_REALM | ANQP_REQ_NAI_HOME_REALM))
751 len += 1000;
Dmitry Shmidt29333592017-01-09 12:27:11 -0800752#ifdef CONFIG_FILS
753 if (request & ANQP_FILS_REALM_INFO)
754 len += 2 * dl_list_len(&hapd->conf->fils_realms);
755#endif /* CONFIG_FILS */
Dmitry Shmidtb97e4282016-02-08 10:16:07 -0800756 len += anqp_get_required_len(hapd, extra_req, num_extra_req);
Dmitry Shmidtf21452a2014-02-26 10:55:25 -0800757
758 buf = wpabuf_alloc(len);
Dmitry Shmidt04949592012-07-19 12:16:46 -0700759 if (buf == NULL)
760 return NULL;
761
762 if (request & ANQP_REQ_CAPABILITY_LIST)
763 anqp_add_capab_list(hapd, buf);
764 if (request & ANQP_REQ_VENUE_NAME)
765 anqp_add_venue_name(hapd, buf);
Dmitry Shmidtd80a4012015-11-05 16:35:40 -0800766 if (request & ANQP_REQ_EMERGENCY_CALL_NUMBER)
767 anqp_add_elem(hapd, buf, ANQP_EMERGENCY_CALL_NUMBER);
Dmitry Shmidt61d9df32012-08-29 16:22:06 -0700768 if (request & ANQP_REQ_NETWORK_AUTH_TYPE)
769 anqp_add_network_auth_type(hapd, buf);
Dmitry Shmidt04949592012-07-19 12:16:46 -0700770 if (request & ANQP_REQ_ROAMING_CONSORTIUM)
771 anqp_add_roaming_consortium(hapd, buf);
Dmitry Shmidt61d9df32012-08-29 16:22:06 -0700772 if (request & ANQP_REQ_IP_ADDR_TYPE_AVAILABILITY)
773 anqp_add_ip_addr_type_availability(hapd, buf);
774 if (request & (ANQP_REQ_NAI_REALM | ANQP_REQ_NAI_HOME_REALM))
775 anqp_add_nai_realm(hapd, buf, home_realm, home_realm_len,
776 request & ANQP_REQ_NAI_REALM,
777 request & ANQP_REQ_NAI_HOME_REALM);
778 if (request & ANQP_REQ_3GPP_CELLULAR_NETWORK)
779 anqp_add_3gpp_cellular_network(hapd, buf);
Dmitry Shmidtd80a4012015-11-05 16:35:40 -0800780 if (request & ANQP_REQ_AP_GEOSPATIAL_LOCATION)
781 anqp_add_elem(hapd, buf, ANQP_AP_GEOSPATIAL_LOCATION);
782 if (request & ANQP_REQ_AP_CIVIC_LOCATION)
783 anqp_add_elem(hapd, buf, ANQP_AP_CIVIC_LOCATION);
784 if (request & ANQP_REQ_AP_LOCATION_PUBLIC_URI)
785 anqp_add_elem(hapd, buf, ANQP_AP_LOCATION_PUBLIC_URI);
Dmitry Shmidt61d9df32012-08-29 16:22:06 -0700786 if (request & ANQP_REQ_DOMAIN_NAME)
787 anqp_add_domain_name(hapd, buf);
Dmitry Shmidtd80a4012015-11-05 16:35:40 -0800788 if (request & ANQP_REQ_EMERGENCY_ALERT_URI)
789 anqp_add_elem(hapd, buf, ANQP_EMERGENCY_ALERT_URI);
790 if (request & ANQP_REQ_TDLS_CAPABILITY)
791 anqp_add_elem(hapd, buf, ANQP_TDLS_CAPABILITY);
792 if (request & ANQP_REQ_EMERGENCY_NAI)
793 anqp_add_elem(hapd, buf, ANQP_EMERGENCY_NAI);
794
Dmitry Shmidt29333592017-01-09 12:27:11 -0800795 for (i = 0; i < num_extra_req; i++) {
796#ifdef CONFIG_FILS
797 if (extra_req[i] == ANQP_FILS_REALM_INFO) {
798 anqp_add_fils_realm_info(hapd, buf);
799 continue;
800 }
801#endif /* CONFIG_FILS */
Roshan Pius3a1667e2018-07-03 15:17:14 -0700802 if (extra_req[i] == ANQP_VENUE_URL) {
803 anqp_add_venue_url(hapd, buf);
804 continue;
805 }
Dmitry Shmidtd80a4012015-11-05 16:35:40 -0800806 anqp_add_elem(hapd, buf, extra_req[i]);
Dmitry Shmidt29333592017-01-09 12:27:11 -0800807 }
Dmitry Shmidt61d9df32012-08-29 16:22:06 -0700808
Dmitry Shmidtaa532512012-09-24 10:35:31 -0700809#ifdef CONFIG_HS20
Dmitry Shmidt61d9df32012-08-29 16:22:06 -0700810 if (request & ANQP_REQ_HS_CAPABILITY_LIST)
811 anqp_add_hs_capab_list(hapd, buf);
812 if (request & ANQP_REQ_OPERATOR_FRIENDLY_NAME)
813 anqp_add_operator_friendly_name(hapd, buf);
814 if (request & ANQP_REQ_WAN_METRICS)
815 anqp_add_wan_metrics(hapd, buf);
816 if (request & ANQP_REQ_CONNECTION_CAPABILITY)
817 anqp_add_connection_capability(hapd, buf);
818 if (request & ANQP_REQ_OPERATING_CLASS)
819 anqp_add_operating_class(hapd, buf);
Dmitry Shmidtaa532512012-09-24 10:35:31 -0700820#endif /* CONFIG_HS20 */
Dmitry Shmidt04949592012-07-19 12:16:46 -0700821
Dmitry Shmidtd2986c22017-10-23 14:22:09 -0700822#ifdef CONFIG_MBO
823 if (request & ANQP_REQ_MBO_CELL_DATA_CONN_PREF)
824 anqp_add_mbo_cell_data_conn_pref(hapd, buf);
825#endif /* CONFIG_MBO */
826
Dmitry Shmidt04949592012-07-19 12:16:46 -0700827 return buf;
828}
829
830
Dmitry Shmidtd80a4012015-11-05 16:35:40 -0800831#define ANQP_MAX_EXTRA_REQ 20
832
Dmitry Shmidt04949592012-07-19 12:16:46 -0700833struct anqp_query_info {
834 unsigned int request;
Dmitry Shmidt61d9df32012-08-29 16:22:06 -0700835 const u8 *home_realm_query;
836 size_t home_realm_query_len;
Dmitry Shmidtfb45fd52015-01-05 13:08:17 -0800837 int p2p_sd;
Dmitry Shmidtd80a4012015-11-05 16:35:40 -0800838 u16 extra_req[ANQP_MAX_EXTRA_REQ];
839 unsigned int num_extra_req;
Dmitry Shmidt04949592012-07-19 12:16:46 -0700840};
841
842
843static void set_anqp_req(unsigned int bit, const char *name, int local,
Dmitry Shmidt04949592012-07-19 12:16:46 -0700844 struct anqp_query_info *qi)
845{
846 qi->request |= bit;
847 if (local) {
848 wpa_printf(MSG_DEBUG, "ANQP: %s (local)", name);
Dmitry Shmidt04949592012-07-19 12:16:46 -0700849 } else {
850 wpa_printf(MSG_DEBUG, "ANQP: %s not available", name);
851 }
852}
853
854
855static void rx_anqp_query_list_id(struct hostapd_data *hapd, u16 info_id,
856 struct anqp_query_info *qi)
857{
858 switch (info_id) {
859 case ANQP_CAPABILITY_LIST:
Dmitry Shmidtb36ed7c2014-03-17 10:57:26 -0700860 set_anqp_req(ANQP_REQ_CAPABILITY_LIST, "Capability List", 1,
861 qi);
Dmitry Shmidt04949592012-07-19 12:16:46 -0700862 break;
863 case ANQP_VENUE_NAME:
864 set_anqp_req(ANQP_REQ_VENUE_NAME, "Venue Name",
Dmitry Shmidtb36ed7c2014-03-17 10:57:26 -0700865 hapd->conf->venue_name != NULL, qi);
Dmitry Shmidt04949592012-07-19 12:16:46 -0700866 break;
Dmitry Shmidtd80a4012015-11-05 16:35:40 -0800867 case ANQP_EMERGENCY_CALL_NUMBER:
868 set_anqp_req(ANQP_REQ_EMERGENCY_CALL_NUMBER,
869 "Emergency Call Number",
870 get_anqp_elem(hapd, info_id) != NULL, qi);
871 break;
Dmitry Shmidt61d9df32012-08-29 16:22:06 -0700872 case ANQP_NETWORK_AUTH_TYPE:
873 set_anqp_req(ANQP_REQ_NETWORK_AUTH_TYPE, "Network Auth Type",
Dmitry Shmidtb36ed7c2014-03-17 10:57:26 -0700874 hapd->conf->network_auth_type != NULL, qi);
Dmitry Shmidt61d9df32012-08-29 16:22:06 -0700875 break;
Dmitry Shmidt04949592012-07-19 12:16:46 -0700876 case ANQP_ROAMING_CONSORTIUM:
877 set_anqp_req(ANQP_REQ_ROAMING_CONSORTIUM, "Roaming Consortium",
Dmitry Shmidtb36ed7c2014-03-17 10:57:26 -0700878 hapd->conf->roaming_consortium != NULL, qi);
Dmitry Shmidt04949592012-07-19 12:16:46 -0700879 break;
Dmitry Shmidt61d9df32012-08-29 16:22:06 -0700880 case ANQP_IP_ADDR_TYPE_AVAILABILITY:
881 set_anqp_req(ANQP_REQ_IP_ADDR_TYPE_AVAILABILITY,
882 "IP Addr Type Availability",
Dmitry Shmidtb36ed7c2014-03-17 10:57:26 -0700883 hapd->conf->ipaddr_type_configured, qi);
Dmitry Shmidt61d9df32012-08-29 16:22:06 -0700884 break;
885 case ANQP_NAI_REALM:
886 set_anqp_req(ANQP_REQ_NAI_REALM, "NAI Realm",
Dmitry Shmidtb36ed7c2014-03-17 10:57:26 -0700887 hapd->conf->nai_realm_data != NULL, qi);
Dmitry Shmidt61d9df32012-08-29 16:22:06 -0700888 break;
889 case ANQP_3GPP_CELLULAR_NETWORK:
890 set_anqp_req(ANQP_REQ_3GPP_CELLULAR_NETWORK,
891 "3GPP Cellular Network",
Dmitry Shmidtb36ed7c2014-03-17 10:57:26 -0700892 hapd->conf->anqp_3gpp_cell_net != NULL, qi);
Dmitry Shmidt61d9df32012-08-29 16:22:06 -0700893 break;
Dmitry Shmidtd80a4012015-11-05 16:35:40 -0800894 case ANQP_AP_GEOSPATIAL_LOCATION:
895 set_anqp_req(ANQP_REQ_AP_GEOSPATIAL_LOCATION,
896 "AP Geospatial Location",
897 get_anqp_elem(hapd, info_id) != NULL, qi);
898 break;
899 case ANQP_AP_CIVIC_LOCATION:
900 set_anqp_req(ANQP_REQ_AP_CIVIC_LOCATION,
901 "AP Civic Location",
902 get_anqp_elem(hapd, info_id) != NULL, qi);
903 break;
904 case ANQP_AP_LOCATION_PUBLIC_URI:
905 set_anqp_req(ANQP_REQ_AP_LOCATION_PUBLIC_URI,
906 "AP Location Public URI",
907 get_anqp_elem(hapd, info_id) != NULL, qi);
908 break;
Dmitry Shmidt61d9df32012-08-29 16:22:06 -0700909 case ANQP_DOMAIN_NAME:
910 set_anqp_req(ANQP_REQ_DOMAIN_NAME, "Domain Name",
Dmitry Shmidtb36ed7c2014-03-17 10:57:26 -0700911 hapd->conf->domain_name != NULL, qi);
Dmitry Shmidt61d9df32012-08-29 16:22:06 -0700912 break;
Dmitry Shmidtd80a4012015-11-05 16:35:40 -0800913 case ANQP_EMERGENCY_ALERT_URI:
914 set_anqp_req(ANQP_REQ_EMERGENCY_ALERT_URI,
915 "Emergency Alert URI",
916 get_anqp_elem(hapd, info_id) != NULL, qi);
917 break;
918 case ANQP_TDLS_CAPABILITY:
919 set_anqp_req(ANQP_REQ_TDLS_CAPABILITY,
920 "TDLS Capability",
921 get_anqp_elem(hapd, info_id) != NULL, qi);
922 break;
923 case ANQP_EMERGENCY_NAI:
924 set_anqp_req(ANQP_REQ_EMERGENCY_NAI,
925 "Emergency NAI",
926 get_anqp_elem(hapd, info_id) != NULL, qi);
927 break;
Dmitry Shmidt04949592012-07-19 12:16:46 -0700928 default:
Dmitry Shmidt29333592017-01-09 12:27:11 -0800929#ifdef CONFIG_FILS
930 if (info_id == ANQP_FILS_REALM_INFO &&
931 !dl_list_empty(&hapd->conf->fils_realms)) {
932 wpa_printf(MSG_DEBUG,
933 "ANQP: FILS Realm Information (local)");
934 } else
935#endif /* CONFIG_FILS */
Roshan Pius3a1667e2018-07-03 15:17:14 -0700936 if (info_id == ANQP_VENUE_URL && hapd->conf->venue_url) {
937 wpa_printf(MSG_DEBUG,
938 "ANQP: Venue URL (local)");
939 } else if (!get_anqp_elem(hapd, info_id)) {
Dmitry Shmidtd80a4012015-11-05 16:35:40 -0800940 wpa_printf(MSG_DEBUG, "ANQP: Unsupported Info Id %u",
941 info_id);
942 break;
943 }
944 if (qi->num_extra_req == ANQP_MAX_EXTRA_REQ) {
945 wpa_printf(MSG_DEBUG,
946 "ANQP: No more room for extra requests - ignore Info Id %u",
947 info_id);
948 break;
949 }
950 wpa_printf(MSG_DEBUG, "ANQP: Info Id %u (local)", info_id);
951 qi->extra_req[qi->num_extra_req] = info_id;
952 qi->num_extra_req++;
Dmitry Shmidt04949592012-07-19 12:16:46 -0700953 break;
954 }
955}
956
957
958static void rx_anqp_query_list(struct hostapd_data *hapd,
959 const u8 *pos, const u8 *end,
960 struct anqp_query_info *qi)
961{
962 wpa_printf(MSG_DEBUG, "ANQP: %u Info IDs requested in Query list",
963 (unsigned int) (end - pos) / 2);
964
Dmitry Shmidtd80a4012015-11-05 16:35:40 -0800965 while (end - pos >= 2) {
Dmitry Shmidt04949592012-07-19 12:16:46 -0700966 rx_anqp_query_list_id(hapd, WPA_GET_LE16(pos), qi);
967 pos += 2;
968 }
969}
970
971
Dmitry Shmidtaa532512012-09-24 10:35:31 -0700972#ifdef CONFIG_HS20
973
Dmitry Shmidt61d9df32012-08-29 16:22:06 -0700974static void rx_anqp_hs_query_list(struct hostapd_data *hapd, u8 subtype,
975 struct anqp_query_info *qi)
976{
977 switch (subtype) {
978 case HS20_STYPE_CAPABILITY_LIST:
979 set_anqp_req(ANQP_REQ_HS_CAPABILITY_LIST, "HS Capability List",
Dmitry Shmidtb36ed7c2014-03-17 10:57:26 -0700980 1, qi);
Dmitry Shmidt61d9df32012-08-29 16:22:06 -0700981 break;
982 case HS20_STYPE_OPERATOR_FRIENDLY_NAME:
983 set_anqp_req(ANQP_REQ_OPERATOR_FRIENDLY_NAME,
984 "Operator Friendly Name",
Dmitry Shmidtb36ed7c2014-03-17 10:57:26 -0700985 hapd->conf->hs20_oper_friendly_name != NULL, qi);
Dmitry Shmidt61d9df32012-08-29 16:22:06 -0700986 break;
987 case HS20_STYPE_WAN_METRICS:
988 set_anqp_req(ANQP_REQ_WAN_METRICS, "WAN Metrics",
Dmitry Shmidtb36ed7c2014-03-17 10:57:26 -0700989 hapd->conf->hs20_wan_metrics != NULL, qi);
Dmitry Shmidt61d9df32012-08-29 16:22:06 -0700990 break;
991 case HS20_STYPE_CONNECTION_CAPABILITY:
992 set_anqp_req(ANQP_REQ_CONNECTION_CAPABILITY,
993 "Connection Capability",
994 hapd->conf->hs20_connection_capability != NULL,
Dmitry Shmidtb36ed7c2014-03-17 10:57:26 -0700995 qi);
Dmitry Shmidt61d9df32012-08-29 16:22:06 -0700996 break;
997 case HS20_STYPE_OPERATING_CLASS:
998 set_anqp_req(ANQP_REQ_OPERATING_CLASS, "Operating Class",
Dmitry Shmidtb36ed7c2014-03-17 10:57:26 -0700999 hapd->conf->hs20_operating_class != NULL, qi);
Dmitry Shmidt61d9df32012-08-29 16:22:06 -07001000 break;
1001 default:
1002 wpa_printf(MSG_DEBUG, "ANQP: Unsupported HS 2.0 subtype %u",
1003 subtype);
1004 break;
1005 }
1006}
1007
1008
1009static void rx_anqp_hs_nai_home_realm(struct hostapd_data *hapd,
1010 const u8 *pos, const u8 *end,
1011 struct anqp_query_info *qi)
1012{
1013 qi->request |= ANQP_REQ_NAI_HOME_REALM;
1014 qi->home_realm_query = pos;
1015 qi->home_realm_query_len = end - pos;
1016 if (hapd->conf->nai_realm_data != NULL) {
1017 wpa_printf(MSG_DEBUG, "ANQP: HS 2.0 NAI Home Realm Query "
1018 "(local)");
1019 } else {
1020 wpa_printf(MSG_DEBUG, "ANQP: HS 2.0 NAI Home Realm Query not "
1021 "available");
1022 }
1023}
1024
1025
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07001026static void rx_anqp_vendor_specific_hs20(struct hostapd_data *hapd,
1027 const u8 *pos, const u8 *end,
1028 struct anqp_query_info *qi)
Dmitry Shmidt61d9df32012-08-29 16:22:06 -07001029{
Dmitry Shmidt61d9df32012-08-29 16:22:06 -07001030 u8 subtype;
1031
Dmitry Shmidtd80a4012015-11-05 16:35:40 -08001032 if (end - pos <= 1)
Dmitry Shmidt61d9df32012-08-29 16:22:06 -07001033 return;
1034
1035 subtype = *pos++;
1036 pos++; /* Reserved */
1037 switch (subtype) {
1038 case HS20_STYPE_QUERY_LIST:
1039 wpa_printf(MSG_DEBUG, "ANQP: HS 2.0 Query List");
1040 while (pos < end) {
1041 rx_anqp_hs_query_list(hapd, *pos, qi);
1042 pos++;
1043 }
1044 break;
1045 case HS20_STYPE_NAI_HOME_REALM_QUERY:
1046 rx_anqp_hs_nai_home_realm(hapd, pos, end, qi);
1047 break;
1048 default:
1049 wpa_printf(MSG_DEBUG, "ANQP: Unsupported HS 2.0 query subtype "
1050 "%u", subtype);
1051 break;
1052 }
1053}
1054
Dmitry Shmidtaa532512012-09-24 10:35:31 -07001055#endif /* CONFIG_HS20 */
1056
Dmitry Shmidt61d9df32012-08-29 16:22:06 -07001057
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07001058#ifdef CONFIG_P2P
1059static void rx_anqp_vendor_specific_p2p(struct hostapd_data *hapd,
1060 struct anqp_query_info *qi)
1061{
1062 /*
1063 * This is for P2P SD and will be taken care of by the P2P
1064 * implementation. This query needs to be ignored in the generic
1065 * GAS server to avoid duplicated response.
1066 */
1067 wpa_printf(MSG_DEBUG,
1068 "ANQP: Ignore WFA vendor type %u (P2P SD) in generic GAS server",
1069 P2P_OUI_TYPE);
1070 qi->p2p_sd = 1;
1071 return;
1072}
1073#endif /* CONFIG_P2P */
1074
1075
1076#ifdef CONFIG_MBO
1077
1078static void rx_anqp_mbo_query_list(struct hostapd_data *hapd, u8 subtype,
1079 struct anqp_query_info *qi)
1080{
1081 switch (subtype) {
1082 case MBO_ANQP_SUBTYPE_CELL_CONN_PREF:
1083 set_anqp_req(ANQP_REQ_MBO_CELL_DATA_CONN_PREF,
1084 "Cellular Data Connection Preference",
1085 hapd->conf->mbo_cell_data_conn_pref >= 0, qi);
1086 break;
1087 default:
1088 wpa_printf(MSG_DEBUG, "ANQP: Unsupported MBO subtype %u",
1089 subtype);
1090 break;
1091 }
1092}
1093
1094
1095static void rx_anqp_vendor_specific_mbo(struct hostapd_data *hapd,
1096 const u8 *pos, const u8 *end,
1097 struct anqp_query_info *qi)
1098{
1099 u8 subtype;
1100
1101 if (end - pos < 1)
1102 return;
1103
1104 subtype = *pos++;
1105 switch (subtype) {
1106 case MBO_ANQP_SUBTYPE_QUERY_LIST:
1107 wpa_printf(MSG_DEBUG, "ANQP: MBO Query List");
1108 while (pos < end) {
1109 rx_anqp_mbo_query_list(hapd, *pos, qi);
1110 pos++;
1111 }
1112 break;
1113 default:
1114 wpa_printf(MSG_DEBUG, "ANQP: Unsupported MBO query subtype %u",
1115 subtype);
1116 break;
1117 }
1118}
1119
1120#endif /* CONFIG_MBO */
1121
1122
1123static void rx_anqp_vendor_specific(struct hostapd_data *hapd,
1124 const u8 *pos, const u8 *end,
1125 struct anqp_query_info *qi)
1126{
1127 u32 oui;
1128
1129 if (end - pos < 4) {
1130 wpa_printf(MSG_DEBUG, "ANQP: Too short vendor specific ANQP "
1131 "Query element");
1132 return;
1133 }
1134
1135 oui = WPA_GET_BE24(pos);
1136 pos += 3;
1137 if (oui != OUI_WFA) {
1138 wpa_printf(MSG_DEBUG, "ANQP: Unsupported vendor OUI %06x",
1139 oui);
1140 return;
1141 }
1142
1143 switch (*pos) {
1144#ifdef CONFIG_P2P
1145 case P2P_OUI_TYPE:
1146 rx_anqp_vendor_specific_p2p(hapd, qi);
1147 break;
1148#endif /* CONFIG_P2P */
1149#ifdef CONFIG_HS20
1150 case HS20_ANQP_OUI_TYPE:
1151 rx_anqp_vendor_specific_hs20(hapd, pos + 1, end, qi);
1152 break;
1153#endif /* CONFIG_HS20 */
1154#ifdef CONFIG_MBO
1155 case MBO_ANQP_OUI_TYPE:
1156 rx_anqp_vendor_specific_mbo(hapd, pos + 1, end, qi);
1157 break;
1158#endif /* CONFIG_MBO */
1159 default:
1160 wpa_printf(MSG_DEBUG, "ANQP: Unsupported WFA vendor type %u",
1161 *pos);
1162 break;
1163 }
1164}
1165
1166
Dmitry Shmidt04949592012-07-19 12:16:46 -07001167static void gas_serv_req_local_processing(struct hostapd_data *hapd,
1168 const u8 *sa, u8 dialog_token,
Dmitry Shmidtd5ab1b52016-06-21 12:38:41 -07001169 struct anqp_query_info *qi, int prot,
1170 int std_addr3)
Dmitry Shmidt04949592012-07-19 12:16:46 -07001171{
1172 struct wpabuf *buf, *tx_buf;
1173
Dmitry Shmidt09f57ba2014-06-10 16:07:13 -07001174 buf = gas_serv_build_gas_resp_payload(hapd, qi->request,
Dmitry Shmidt61d9df32012-08-29 16:22:06 -07001175 qi->home_realm_query,
Dmitry Shmidtf21452a2014-02-26 10:55:25 -08001176 qi->home_realm_query_len,
Dmitry Shmidtd80a4012015-11-05 16:35:40 -08001177 qi->extra_req, qi->num_extra_req);
Dmitry Shmidt04949592012-07-19 12:16:46 -07001178 wpa_hexdump_buf(MSG_MSGDUMP, "ANQP: Locally generated ANQP responses",
1179 buf);
1180 if (!buf)
1181 return;
Dmitry Shmidtfb45fd52015-01-05 13:08:17 -08001182#ifdef CONFIG_P2P
1183 if (wpabuf_len(buf) == 0 && qi->p2p_sd) {
1184 wpa_printf(MSG_DEBUG,
1185 "ANQP: Do not send response to P2P SD from generic GAS service (P2P SD implementation will process this)");
1186 wpabuf_free(buf);
1187 return;
1188 }
1189#endif /* CONFIG_P2P */
Dmitry Shmidt04949592012-07-19 12:16:46 -07001190
Dmitry Shmidt29333592017-01-09 12:27:11 -08001191 if (wpabuf_len(buf) > hapd->conf->gas_frag_limit ||
Dmitry Shmidt04949592012-07-19 12:16:46 -07001192 hapd->conf->gas_comeback_delay) {
1193 struct gas_dialog_info *di;
1194 u16 comeback_delay = 1;
1195
1196 if (hapd->conf->gas_comeback_delay) {
1197 /* Testing - allow overriding of the delay value */
1198 comeback_delay = hapd->conf->gas_comeback_delay;
1199 }
1200
1201 wpa_printf(MSG_DEBUG, "ANQP: Too long response to fit in "
1202 "initial response - use GAS comeback");
1203 di = gas_dialog_create(hapd, sa, dialog_token);
1204 if (!di) {
1205 wpa_printf(MSG_INFO, "ANQP: Could not create dialog "
1206 "for " MACSTR " (dialog token %u)",
1207 MAC2STR(sa), dialog_token);
1208 wpabuf_free(buf);
Dmitry Shmidt76cd2cc2014-05-27 12:56:04 -07001209 tx_buf = gas_anqp_build_initial_resp_buf(
1210 dialog_token, WLAN_STATUS_UNSPECIFIED_FAILURE,
1211 0, NULL);
1212 } else {
1213 di->prot = prot;
1214 di->sd_resp = buf;
1215 di->sd_resp_pos = 0;
1216 tx_buf = gas_anqp_build_initial_resp_buf(
1217 dialog_token, WLAN_STATUS_SUCCESS,
1218 comeback_delay, NULL);
Dmitry Shmidt04949592012-07-19 12:16:46 -07001219 }
Dmitry Shmidt04949592012-07-19 12:16:46 -07001220 } else {
1221 wpa_printf(MSG_DEBUG, "ANQP: Initial response (no comeback)");
1222 tx_buf = gas_anqp_build_initial_resp_buf(
1223 dialog_token, WLAN_STATUS_SUCCESS, 0, buf);
1224 wpabuf_free(buf);
1225 }
1226 if (!tx_buf)
1227 return;
Dmitry Shmidt18463232014-01-24 12:29:41 -08001228 if (prot)
1229 convert_to_protected_dual(tx_buf);
Dmitry Shmidtd5ab1b52016-06-21 12:38:41 -07001230 if (std_addr3)
1231 hostapd_drv_send_action(hapd, hapd->iface->freq, 0, sa,
1232 wpabuf_head(tx_buf),
1233 wpabuf_len(tx_buf));
1234 else
1235 hostapd_drv_send_action_addr3_ap(hapd, hapd->iface->freq, 0, sa,
1236 wpabuf_head(tx_buf),
1237 wpabuf_len(tx_buf));
Dmitry Shmidt04949592012-07-19 12:16:46 -07001238 wpabuf_free(tx_buf);
1239}
1240
1241
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07001242#ifdef CONFIG_DPP
Hai Shalom81f62d82019-07-22 12:10:00 -07001243void gas_serv_req_dpp_processing(struct hostapd_data *hapd,
1244 const u8 *sa, u8 dialog_token,
Sunil Ravi38ad1ed2023-01-17 23:58:31 +00001245 int prot, struct wpabuf *buf, int freq)
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07001246{
1247 struct wpabuf *tx_buf;
1248
1249 if (wpabuf_len(buf) > hapd->conf->gas_frag_limit ||
1250 hapd->conf->gas_comeback_delay) {
1251 struct gas_dialog_info *di;
1252 u16 comeback_delay = 1;
1253
1254 if (hapd->conf->gas_comeback_delay) {
1255 /* Testing - allow overriding of the delay value */
1256 comeback_delay = hapd->conf->gas_comeback_delay;
1257 }
1258
1259 wpa_printf(MSG_DEBUG,
1260 "DPP: Too long response to fit in initial response - use GAS comeback");
1261 di = gas_dialog_create(hapd, sa, dialog_token);
1262 if (!di) {
1263 wpa_printf(MSG_INFO, "DPP: Could not create dialog for "
1264 MACSTR " (dialog token %u)",
1265 MAC2STR(sa), dialog_token);
1266 wpabuf_free(buf);
1267 tx_buf = gas_build_initial_resp(
1268 dialog_token, WLAN_STATUS_UNSPECIFIED_FAILURE,
1269 0, 10);
1270 if (tx_buf)
1271 gas_serv_write_dpp_adv_proto(tx_buf);
1272 } else {
1273 di->prot = prot;
1274 di->sd_resp = buf;
1275 di->sd_resp_pos = 0;
Hai Shalom899fcc72020-10-19 14:38:18 -07001276 di->dpp = 1;
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07001277 tx_buf = gas_build_initial_resp(
1278 dialog_token, WLAN_STATUS_SUCCESS,
Hai Shalom899fcc72020-10-19 14:38:18 -07001279 comeback_delay, 10 + 2);
1280 if (tx_buf) {
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07001281 gas_serv_write_dpp_adv_proto(tx_buf);
Hai Shalom899fcc72020-10-19 14:38:18 -07001282 wpabuf_put_le16(tx_buf, 0);
1283 }
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07001284 }
1285 } else {
1286 wpa_printf(MSG_DEBUG,
1287 "DPP: GAS Initial response (no comeback)");
1288 tx_buf = gas_build_initial_resp(
1289 dialog_token, WLAN_STATUS_SUCCESS, 0,
1290 10 + 2 + wpabuf_len(buf));
1291 if (tx_buf) {
1292 gas_serv_write_dpp_adv_proto(tx_buf);
1293 wpabuf_put_le16(tx_buf, wpabuf_len(buf));
1294 wpabuf_put_buf(tx_buf, buf);
Roshan Pius3a1667e2018-07-03 15:17:14 -07001295 hostapd_dpp_gas_status_handler(hapd, 1);
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07001296 }
1297 wpabuf_free(buf);
1298 }
1299 if (!tx_buf)
1300 return;
1301 if (prot)
1302 convert_to_protected_dual(tx_buf);
Sunil Ravi38ad1ed2023-01-17 23:58:31 +00001303 hostapd_drv_send_action(hapd, freq ? freq : hapd->iface->freq, 0, sa,
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07001304 wpabuf_head(tx_buf),
1305 wpabuf_len(tx_buf));
1306 wpabuf_free(tx_buf);
1307}
1308#endif /* CONFIG_DPP */
1309
1310
Dmitry Shmidt04949592012-07-19 12:16:46 -07001311static void gas_serv_rx_gas_initial_req(struct hostapd_data *hapd,
1312 const u8 *sa,
Dmitry Shmidtd5ab1b52016-06-21 12:38:41 -07001313 const u8 *data, size_t len, int prot,
Sunil Ravi38ad1ed2023-01-17 23:58:31 +00001314 int std_addr3, int freq)
Dmitry Shmidt04949592012-07-19 12:16:46 -07001315{
1316 const u8 *pos = data;
1317 const u8 *end = data + len;
1318 const u8 *next;
1319 u8 dialog_token;
1320 u16 slen;
1321 struct anqp_query_info qi;
1322 const u8 *adv_proto;
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07001323#ifdef CONFIG_DPP
1324 int dpp = 0;
1325#endif /* CONFIG_DPP */
Dmitry Shmidt04949592012-07-19 12:16:46 -07001326
1327 if (len < 1 + 2)
1328 return;
1329
1330 os_memset(&qi, 0, sizeof(qi));
1331
1332 dialog_token = *pos++;
1333 wpa_msg(hapd->msg_ctx, MSG_DEBUG,
1334 "GAS: GAS Initial Request from " MACSTR " (dialog token %u) ",
1335 MAC2STR(sa), dialog_token);
1336
1337 if (*pos != WLAN_EID_ADV_PROTO) {
1338 wpa_msg(hapd->msg_ctx, MSG_DEBUG,
1339 "GAS: Unexpected IE in GAS Initial Request: %u", *pos);
1340 return;
1341 }
1342 adv_proto = pos++;
1343
1344 slen = *pos++;
Dmitry Shmidtd80a4012015-11-05 16:35:40 -08001345 if (slen > end - pos || slen < 2) {
Dmitry Shmidt04949592012-07-19 12:16:46 -07001346 wpa_msg(hapd->msg_ctx, MSG_DEBUG,
1347 "GAS: Invalid IE in GAS Initial Request");
1348 return;
1349 }
Dmitry Shmidtd80a4012015-11-05 16:35:40 -08001350 next = pos + slen;
Dmitry Shmidt04949592012-07-19 12:16:46 -07001351 pos++; /* skip QueryRespLenLimit and PAME-BI */
1352
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07001353#ifdef CONFIG_DPP
1354 if (slen == 8 && *pos == WLAN_EID_VENDOR_SPECIFIC &&
1355 pos[1] == 5 && WPA_GET_BE24(&pos[2]) == OUI_WFA &&
1356 pos[5] == DPP_OUI_TYPE && pos[6] == 0x01) {
1357 wpa_printf(MSG_DEBUG, "DPP: Configuration Request");
1358 dpp = 1;
1359 } else
1360#endif /* CONFIG_DPP */
1361
Dmitry Shmidt04949592012-07-19 12:16:46 -07001362 if (*pos != ACCESS_NETWORK_QUERY_PROTOCOL) {
1363 struct wpabuf *buf;
1364 wpa_msg(hapd->msg_ctx, MSG_DEBUG,
1365 "GAS: Unsupported GAS advertisement protocol id %u",
1366 *pos);
1367 if (sa[0] & 0x01)
1368 return; /* Invalid source address - drop silently */
1369 buf = gas_build_initial_resp(
1370 dialog_token, WLAN_STATUS_GAS_ADV_PROTO_NOT_SUPPORTED,
1371 0, 2 + slen + 2);
1372 if (buf == NULL)
1373 return;
1374 wpabuf_put_data(buf, adv_proto, 2 + slen);
1375 wpabuf_put_le16(buf, 0); /* Query Response Length */
Dmitry Shmidt18463232014-01-24 12:29:41 -08001376 if (prot)
1377 convert_to_protected_dual(buf);
Dmitry Shmidtd5ab1b52016-06-21 12:38:41 -07001378 if (std_addr3)
1379 hostapd_drv_send_action(hapd, hapd->iface->freq, 0, sa,
1380 wpabuf_head(buf),
1381 wpabuf_len(buf));
1382 else
1383 hostapd_drv_send_action_addr3_ap(hapd,
1384 hapd->iface->freq, 0,
1385 sa, wpabuf_head(buf),
1386 wpabuf_len(buf));
Dmitry Shmidt04949592012-07-19 12:16:46 -07001387 wpabuf_free(buf);
1388 return;
1389 }
1390
1391 pos = next;
1392 /* Query Request */
Dmitry Shmidtd80a4012015-11-05 16:35:40 -08001393 if (end - pos < 2)
Dmitry Shmidt04949592012-07-19 12:16:46 -07001394 return;
1395 slen = WPA_GET_LE16(pos);
1396 pos += 2;
Dmitry Shmidtd80a4012015-11-05 16:35:40 -08001397 if (slen > end - pos)
Dmitry Shmidt04949592012-07-19 12:16:46 -07001398 return;
1399 end = pos + slen;
1400
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07001401#ifdef CONFIG_DPP
1402 if (dpp) {
1403 struct wpabuf *msg;
1404
Hai Shalom81f62d82019-07-22 12:10:00 -07001405 msg = hostapd_dpp_gas_req_handler(hapd, sa, pos, slen,
1406 data, len);
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07001407 if (!msg)
1408 return;
Sunil Ravi38ad1ed2023-01-17 23:58:31 +00001409 gas_serv_req_dpp_processing(hapd, sa, dialog_token, prot, msg,
1410 freq);
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07001411 return;
1412 }
1413#endif /* CONFIG_DPP */
1414
Dmitry Shmidt04949592012-07-19 12:16:46 -07001415 /* ANQP Query Request */
1416 while (pos < end) {
1417 u16 info_id, elen;
1418
Dmitry Shmidtd80a4012015-11-05 16:35:40 -08001419 if (end - pos < 4)
Dmitry Shmidt04949592012-07-19 12:16:46 -07001420 return;
1421
1422 info_id = WPA_GET_LE16(pos);
1423 pos += 2;
1424 elen = WPA_GET_LE16(pos);
1425 pos += 2;
1426
Dmitry Shmidtd80a4012015-11-05 16:35:40 -08001427 if (elen > end - pos) {
Dmitry Shmidt04949592012-07-19 12:16:46 -07001428 wpa_printf(MSG_DEBUG, "ANQP: Invalid Query Request");
1429 return;
1430 }
1431
1432 switch (info_id) {
1433 case ANQP_QUERY_LIST:
1434 rx_anqp_query_list(hapd, pos, pos + elen, &qi);
1435 break;
Dmitry Shmidt61d9df32012-08-29 16:22:06 -07001436 case ANQP_VENDOR_SPECIFIC:
1437 rx_anqp_vendor_specific(hapd, pos, pos + elen, &qi);
1438 break;
Dmitry Shmidt04949592012-07-19 12:16:46 -07001439 default:
1440 wpa_printf(MSG_DEBUG, "ANQP: Unsupported Query "
1441 "Request element %u", info_id);
1442 break;
1443 }
1444
1445 pos += elen;
1446 }
1447
Dmitry Shmidtd5ab1b52016-06-21 12:38:41 -07001448 gas_serv_req_local_processing(hapd, sa, dialog_token, &qi, prot,
1449 std_addr3);
Dmitry Shmidt04949592012-07-19 12:16:46 -07001450}
1451
1452
Dmitry Shmidt04949592012-07-19 12:16:46 -07001453static void gas_serv_rx_gas_comeback_req(struct hostapd_data *hapd,
1454 const u8 *sa,
Dmitry Shmidtd5ab1b52016-06-21 12:38:41 -07001455 const u8 *data, size_t len, int prot,
1456 int std_addr3)
Dmitry Shmidt04949592012-07-19 12:16:46 -07001457{
1458 struct gas_dialog_info *dialog;
1459 struct wpabuf *buf, *tx_buf;
1460 u8 dialog_token;
1461 size_t frag_len;
1462 int more = 0;
1463
1464 wpa_hexdump(MSG_DEBUG, "GAS: RX GAS Comeback Request", data, len);
1465 if (len < 1)
1466 return;
1467 dialog_token = *data;
1468 wpa_msg(hapd->msg_ctx, MSG_DEBUG, "GAS: Dialog Token: %u",
1469 dialog_token);
1470
1471 dialog = gas_serv_dialog_find(hapd, sa, dialog_token);
1472 if (!dialog) {
1473 wpa_msg(hapd->msg_ctx, MSG_DEBUG, "GAS: No pending SD "
1474 "response fragment for " MACSTR " dialog token %u",
1475 MAC2STR(sa), dialog_token);
1476
1477 if (sa[0] & 0x01)
1478 return; /* Invalid source address - drop silently */
1479 tx_buf = gas_anqp_build_comeback_resp_buf(
1480 dialog_token, WLAN_STATUS_NO_OUTSTANDING_GAS_REQ, 0, 0,
1481 0, NULL);
1482 if (tx_buf == NULL)
1483 return;
1484 goto send_resp;
1485 }
1486
Dmitry Shmidt04949592012-07-19 12:16:46 -07001487 frag_len = wpabuf_len(dialog->sd_resp) - dialog->sd_resp_pos;
Dmitry Shmidt29333592017-01-09 12:27:11 -08001488 if (frag_len > hapd->conf->gas_frag_limit) {
1489 frag_len = hapd->conf->gas_frag_limit;
Dmitry Shmidt04949592012-07-19 12:16:46 -07001490 more = 1;
1491 }
1492 wpa_msg(hapd->msg_ctx, MSG_DEBUG, "GAS: resp frag_len %u",
1493 (unsigned int) frag_len);
1494 buf = wpabuf_alloc_copy(wpabuf_head_u8(dialog->sd_resp) +
1495 dialog->sd_resp_pos, frag_len);
1496 if (buf == NULL) {
1497 wpa_msg(hapd->msg_ctx, MSG_DEBUG, "GAS: Failed to allocate "
1498 "buffer");
Dmitry Shmidtb36ed7c2014-03-17 10:57:26 -07001499 gas_serv_dialog_clear(dialog);
1500 return;
Dmitry Shmidt04949592012-07-19 12:16:46 -07001501 }
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07001502#ifdef CONFIG_DPP
1503 if (dialog->dpp) {
1504 tx_buf = gas_build_comeback_resp(dialog_token,
1505 WLAN_STATUS_SUCCESS,
1506 dialog->sd_frag_id, more, 0,
Hai Shalom899fcc72020-10-19 14:38:18 -07001507 10 + 2 + frag_len);
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07001508 if (tx_buf) {
1509 gas_serv_write_dpp_adv_proto(tx_buf);
Hai Shalom899fcc72020-10-19 14:38:18 -07001510 wpabuf_put_le16(tx_buf, frag_len);
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07001511 wpabuf_put_buf(tx_buf, buf);
1512 }
1513 } else
1514#endif /* CONFIG_DPP */
Dmitry Shmidt04949592012-07-19 12:16:46 -07001515 tx_buf = gas_anqp_build_comeback_resp_buf(dialog_token,
1516 WLAN_STATUS_SUCCESS,
1517 dialog->sd_frag_id,
1518 more, 0, buf);
1519 wpabuf_free(buf);
Dmitry Shmidtb36ed7c2014-03-17 10:57:26 -07001520 if (tx_buf == NULL) {
1521 gas_serv_dialog_clear(dialog);
1522 return;
1523 }
Dmitry Shmidt04949592012-07-19 12:16:46 -07001524 wpa_msg(hapd->msg_ctx, MSG_DEBUG, "GAS: Tx GAS Comeback Response "
1525 "(frag_id %d more=%d frag_len=%d)",
1526 dialog->sd_frag_id, more, (int) frag_len);
1527 dialog->sd_frag_id++;
1528 dialog->sd_resp_pos += frag_len;
1529
1530 if (more) {
1531 wpa_msg(hapd->msg_ctx, MSG_DEBUG, "GAS: %d more bytes remain "
1532 "to be sent",
1533 (int) (wpabuf_len(dialog->sd_resp) -
1534 dialog->sd_resp_pos));
1535 } else {
1536 wpa_msg(hapd->msg_ctx, MSG_DEBUG, "GAS: All fragments of "
1537 "SD response sent");
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07001538#ifdef CONFIG_DPP
1539 if (dialog->dpp)
Roshan Pius3a1667e2018-07-03 15:17:14 -07001540 hostapd_dpp_gas_status_handler(hapd, 1);
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07001541#endif /* CONFIG_DPP */
Dmitry Shmidt04949592012-07-19 12:16:46 -07001542 gas_serv_dialog_clear(dialog);
1543 gas_serv_free_dialogs(hapd, sa);
1544 }
1545
1546send_resp:
Dmitry Shmidt18463232014-01-24 12:29:41 -08001547 if (prot)
1548 convert_to_protected_dual(tx_buf);
Dmitry Shmidtd5ab1b52016-06-21 12:38:41 -07001549 if (std_addr3)
1550 hostapd_drv_send_action(hapd, hapd->iface->freq, 0, sa,
1551 wpabuf_head(tx_buf),
1552 wpabuf_len(tx_buf));
1553 else
1554 hostapd_drv_send_action_addr3_ap(hapd, hapd->iface->freq, 0, sa,
1555 wpabuf_head(tx_buf),
1556 wpabuf_len(tx_buf));
Dmitry Shmidt04949592012-07-19 12:16:46 -07001557 wpabuf_free(tx_buf);
Dmitry Shmidt04949592012-07-19 12:16:46 -07001558}
1559
1560
1561static void gas_serv_rx_public_action(void *ctx, const u8 *buf, size_t len,
1562 int freq)
1563{
1564 struct hostapd_data *hapd = ctx;
1565 const struct ieee80211_mgmt *mgmt;
Dmitry Shmidt04949592012-07-19 12:16:46 -07001566 const u8 *sa, *data;
Dmitry Shmidtd5ab1b52016-06-21 12:38:41 -07001567 int prot, std_addr3;
Dmitry Shmidt04949592012-07-19 12:16:46 -07001568
1569 mgmt = (const struct ieee80211_mgmt *) buf;
Dmitry Shmidt623d63a2014-06-13 11:05:14 -07001570 if (len < IEEE80211_HDRLEN + 2)
Dmitry Shmidt04949592012-07-19 12:16:46 -07001571 return;
Dmitry Shmidt18463232014-01-24 12:29:41 -08001572 if (mgmt->u.action.category != WLAN_ACTION_PUBLIC &&
1573 mgmt->u.action.category != WLAN_ACTION_PROTECTED_DUAL)
Dmitry Shmidt04949592012-07-19 12:16:46 -07001574 return;
Dmitry Shmidt18463232014-01-24 12:29:41 -08001575 /*
1576 * Note: Public Action and Protected Dual of Public Action frames share
1577 * the same payload structure, so it is fine to use definitions of
1578 * Public Action frames to process both.
1579 */
1580 prot = mgmt->u.action.category == WLAN_ACTION_PROTECTED_DUAL;
Dmitry Shmidt04949592012-07-19 12:16:46 -07001581 sa = mgmt->sa;
Dmitry Shmidtd5ab1b52016-06-21 12:38:41 -07001582 if (hapd->conf->gas_address3 == 1)
1583 std_addr3 = 1;
1584 else if (hapd->conf->gas_address3 == 2)
1585 std_addr3 = 0;
1586 else
1587 std_addr3 = is_broadcast_ether_addr(mgmt->bssid);
Dmitry Shmidt623d63a2014-06-13 11:05:14 -07001588 len -= IEEE80211_HDRLEN + 1;
1589 data = buf + IEEE80211_HDRLEN + 1;
Dmitry Shmidt04949592012-07-19 12:16:46 -07001590 switch (data[0]) {
1591 case WLAN_PA_GAS_INITIAL_REQ:
Dmitry Shmidtd5ab1b52016-06-21 12:38:41 -07001592 gas_serv_rx_gas_initial_req(hapd, sa, data + 1, len - 1, prot,
Sunil Ravi38ad1ed2023-01-17 23:58:31 +00001593 std_addr3, freq);
Dmitry Shmidt04949592012-07-19 12:16:46 -07001594 break;
1595 case WLAN_PA_GAS_COMEBACK_REQ:
Dmitry Shmidtd5ab1b52016-06-21 12:38:41 -07001596 gas_serv_rx_gas_comeback_req(hapd, sa, data + 1, len - 1, prot,
1597 std_addr3);
Dmitry Shmidt04949592012-07-19 12:16:46 -07001598 break;
1599 }
1600}
1601
1602
1603int gas_serv_init(struct hostapd_data *hapd)
1604{
Dmitry Shmidt4b9d52f2013-02-05 17:44:43 -08001605 hapd->public_action_cb2 = gas_serv_rx_public_action;
1606 hapd->public_action_cb2_ctx = hapd;
Dmitry Shmidt04949592012-07-19 12:16:46 -07001607 return 0;
1608}
1609
1610
1611void gas_serv_deinit(struct hostapd_data *hapd)
1612{
1613}