Branch data Line data Source code
1 : : /*
2 : : * Generic advertisement service (GAS) server
3 : : * Copyright (c) 2011-2012, Qualcomm Atheros, Inc.
4 : : *
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"
14 : : #include "utils/eloop.h"
15 : : #include "hostapd.h"
16 : : #include "ap_config.h"
17 : : #include "ap_drv_ops.h"
18 : : #include "sta_info.h"
19 : : #include "gas_serv.h"
20 : :
21 : :
22 : : static struct gas_dialog_info *
23 : 4 : gas_dialog_create(struct hostapd_data *hapd, const u8 *addr, u8 dialog_token)
24 : : {
25 : : struct sta_info *sta;
26 : 4 : struct gas_dialog_info *dia = NULL;
27 : : int i, j;
28 : :
29 : 4 : sta = ap_get_sta(hapd, addr);
30 [ + + ]: 4 : if (!sta) {
31 : : /*
32 : : * We need a STA entry to be able to maintain state for
33 : : * the GAS query.
34 : : */
35 : 3 : wpa_printf(MSG_DEBUG, "ANQP: Add a temporary STA entry for "
36 : : "GAS query");
37 : 3 : sta = ap_sta_add(hapd, addr);
38 [ - + ]: 3 : if (!sta) {
39 : 0 : wpa_printf(MSG_DEBUG, "Failed to add STA " MACSTR
40 : 0 : " for GAS query", MAC2STR(addr));
41 : 0 : return NULL;
42 : : }
43 : 3 : sta->flags |= WLAN_STA_GAS;
44 : : /*
45 : : * The default inactivity is 300 seconds. We don't need
46 : : * it to be that long.
47 : : */
48 : 3 : ap_sta_session_timeout(hapd, sta, 5);
49 : : } else {
50 : 1 : ap_sta_replenish_timeout(hapd, sta, 5);
51 : : }
52 : :
53 [ + - ]: 4 : if (sta->gas_dialog == NULL) {
54 : 4 : sta->gas_dialog = os_zalloc(GAS_DIALOG_MAX *
55 : : sizeof(struct gas_dialog_info));
56 [ - + ]: 4 : if (sta->gas_dialog == NULL)
57 : 0 : return NULL;
58 : : }
59 : :
60 [ + - ]: 4 : for (i = sta->gas_dialog_next, j = 0; j < GAS_DIALOG_MAX; i++, j++) {
61 [ - + ]: 4 : if (i == GAS_DIALOG_MAX)
62 : 0 : i = 0;
63 [ - + ]: 4 : if (sta->gas_dialog[i].valid)
64 : 0 : continue;
65 : 4 : dia = &sta->gas_dialog[i];
66 : 4 : dia->valid = 1;
67 : 4 : dia->index = i;
68 : 4 : dia->dialog_token = dialog_token;
69 [ + - ]: 4 : sta->gas_dialog_next = (++i == GAS_DIALOG_MAX) ? 0 : i;
70 : 4 : return dia;
71 : : }
72 : :
73 : 0 : wpa_msg(hapd->msg_ctx, MSG_ERROR, "ANQP: Could not create dialog for "
74 : : MACSTR " dialog_token %u. Consider increasing "
75 : 0 : "GAS_DIALOG_MAX.", MAC2STR(addr), dialog_token);
76 : :
77 : 4 : return NULL;
78 : : }
79 : :
80 : :
81 : : struct gas_dialog_info *
82 : 18 : gas_serv_dialog_find(struct hostapd_data *hapd, const u8 *addr,
83 : : u8 dialog_token)
84 : : {
85 : : struct sta_info *sta;
86 : : int i;
87 : :
88 : 18 : sta = ap_get_sta(hapd, addr);
89 [ - + ]: 18 : if (!sta) {
90 : 0 : wpa_printf(MSG_DEBUG, "ANQP: could not find STA " MACSTR,
91 : 0 : MAC2STR(addr));
92 : 0 : return NULL;
93 : : }
94 [ + - ][ + - ]: 24 : for (i = 0; sta->gas_dialog && i < GAS_DIALOG_MAX; i++) {
95 [ + + ][ - + ]: 24 : if (sta->gas_dialog[i].dialog_token != dialog_token ||
96 : 18 : !sta->gas_dialog[i].valid)
97 : 6 : continue;
98 : 18 : return &sta->gas_dialog[i];
99 : : }
100 : 0 : wpa_printf(MSG_DEBUG, "ANQP: Could not find dialog for "
101 : 0 : MACSTR " dialog_token %u", MAC2STR(addr), dialog_token);
102 : 18 : return NULL;
103 : : }
104 : :
105 : :
106 : 4 : void gas_serv_dialog_clear(struct gas_dialog_info *dia)
107 : : {
108 : 4 : wpabuf_free(dia->sd_resp);
109 : 4 : os_memset(dia, 0, sizeof(*dia));
110 : 4 : }
111 : :
112 : :
113 : 4 : static void gas_serv_free_dialogs(struct hostapd_data *hapd,
114 : : const u8 *sta_addr)
115 : : {
116 : : struct sta_info *sta;
117 : : int i;
118 : :
119 : 4 : sta = ap_get_sta(hapd, sta_addr);
120 [ - + ][ + - ]: 4 : if (sta == NULL || sta->gas_dialog == NULL)
121 : 0 : return;
122 : :
123 [ + + ]: 36 : for (i = 0; i < GAS_DIALOG_MAX; i++) {
124 [ - + ]: 32 : if (sta->gas_dialog[i].valid)
125 : 0 : return;
126 : : }
127 : :
128 : 4 : os_free(sta->gas_dialog);
129 : 4 : sta->gas_dialog = NULL;
130 : : }
131 : :
132 : :
133 : : #ifdef CONFIG_HS20
134 : 86 : static void anqp_add_hs_capab_list(struct hostapd_data *hapd,
135 : : struct wpabuf *buf)
136 : : {
137 : : u8 *len;
138 : :
139 : 86 : len = gas_anqp_add_element(buf, ANQP_VENDOR_SPECIFIC);
140 : 86 : wpabuf_put_be24(buf, OUI_WFA);
141 : 86 : wpabuf_put_u8(buf, HS20_ANQP_OUI_TYPE);
142 : 86 : wpabuf_put_u8(buf, HS20_STYPE_CAPABILITY_LIST);
143 : 86 : wpabuf_put_u8(buf, 0); /* Reserved */
144 : 86 : wpabuf_put_u8(buf, HS20_STYPE_CAPABILITY_LIST);
145 [ + + ]: 86 : if (hapd->conf->hs20_oper_friendly_name)
146 : 8 : wpabuf_put_u8(buf, HS20_STYPE_OPERATOR_FRIENDLY_NAME);
147 [ + - ]: 86 : if (hapd->conf->hs20_wan_metrics)
148 : 86 : wpabuf_put_u8(buf, HS20_STYPE_WAN_METRICS);
149 [ + - ]: 86 : if (hapd->conf->hs20_connection_capability)
150 : 86 : wpabuf_put_u8(buf, HS20_STYPE_CONNECTION_CAPABILITY);
151 [ + - ]: 86 : if (hapd->conf->nai_realm_data)
152 : 86 : wpabuf_put_u8(buf, HS20_STYPE_NAI_HOME_REALM_QUERY);
153 [ + - ]: 86 : if (hapd->conf->hs20_operating_class)
154 : 86 : wpabuf_put_u8(buf, HS20_STYPE_OPERATING_CLASS);
155 : 86 : gas_anqp_set_element_len(buf, len);
156 : 86 : }
157 : : #endif /* CONFIG_HS20 */
158 : :
159 : :
160 : 45 : static void anqp_add_capab_list(struct hostapd_data *hapd,
161 : : struct wpabuf *buf)
162 : : {
163 : : u8 *len;
164 : :
165 : 45 : len = gas_anqp_add_element(buf, ANQP_CAPABILITY_LIST);
166 : 45 : wpabuf_put_le16(buf, ANQP_CAPABILITY_LIST);
167 [ + - ]: 45 : if (hapd->conf->venue_name)
168 : 45 : wpabuf_put_le16(buf, ANQP_VENUE_NAME);
169 [ + + ]: 45 : if (hapd->conf->network_auth_type)
170 : 6 : wpabuf_put_le16(buf, ANQP_NETWORK_AUTH_TYPE);
171 [ + - ]: 45 : if (hapd->conf->roaming_consortium)
172 : 45 : wpabuf_put_le16(buf, ANQP_ROAMING_CONSORTIUM);
173 [ + + ]: 45 : if (hapd->conf->ipaddr_type_configured)
174 : 6 : wpabuf_put_le16(buf, ANQP_IP_ADDR_TYPE_AVAILABILITY);
175 [ + - ]: 45 : if (hapd->conf->nai_realm_data)
176 : 45 : wpabuf_put_le16(buf, ANQP_NAI_REALM);
177 [ + - ]: 45 : if (hapd->conf->anqp_3gpp_cell_net)
178 : 45 : wpabuf_put_le16(buf, ANQP_3GPP_CELLULAR_NETWORK);
179 [ + + ]: 45 : if (hapd->conf->domain_name)
180 : 44 : wpabuf_put_le16(buf, ANQP_DOMAIN_NAME);
181 : : #ifdef CONFIG_HS20
182 : 45 : anqp_add_hs_capab_list(hapd, buf);
183 : : #endif /* CONFIG_HS20 */
184 : 45 : gas_anqp_set_element_len(buf, len);
185 : 45 : }
186 : :
187 : :
188 : 5 : static void anqp_add_venue_name(struct hostapd_data *hapd, struct wpabuf *buf)
189 : : {
190 [ + - ]: 5 : if (hapd->conf->venue_name) {
191 : : u8 *len;
192 : : unsigned int i;
193 : 5 : len = gas_anqp_add_element(buf, ANQP_VENUE_NAME);
194 : 5 : wpabuf_put_u8(buf, hapd->conf->venue_group);
195 : 5 : wpabuf_put_u8(buf, hapd->conf->venue_type);
196 [ + + ]: 15 : for (i = 0; i < hapd->conf->venue_name_count; i++) {
197 : : struct hostapd_lang_string *vn;
198 : 10 : vn = &hapd->conf->venue_name[i];
199 : 10 : wpabuf_put_u8(buf, 3 + vn->name_len);
200 : 10 : wpabuf_put_data(buf, vn->lang, 3);
201 : 10 : wpabuf_put_data(buf, vn->name, vn->name_len);
202 : : }
203 : 5 : gas_anqp_set_element_len(buf, len);
204 : : }
205 : 5 : }
206 : :
207 : :
208 : 4 : static void anqp_add_network_auth_type(struct hostapd_data *hapd,
209 : : struct wpabuf *buf)
210 : : {
211 [ + + ]: 4 : if (hapd->conf->network_auth_type) {
212 : 2 : wpabuf_put_le16(buf, ANQP_NETWORK_AUTH_TYPE);
213 : 2 : wpabuf_put_le16(buf, hapd->conf->network_auth_type_len);
214 : 2 : wpabuf_put_data(buf, hapd->conf->network_auth_type,
215 : 2 : hapd->conf->network_auth_type_len);
216 : : }
217 : 4 : }
218 : :
219 : :
220 : 8 : static void anqp_add_roaming_consortium(struct hostapd_data *hapd,
221 : : struct wpabuf *buf)
222 : : {
223 : : unsigned int i;
224 : : u8 *len;
225 : :
226 : 8 : len = gas_anqp_add_element(buf, ANQP_ROAMING_CONSORTIUM);
227 [ + + ]: 40 : for (i = 0; i < hapd->conf->roaming_consortium_count; i++) {
228 : : struct hostapd_roaming_consortium *rc;
229 : 32 : rc = &hapd->conf->roaming_consortium[i];
230 : 32 : wpabuf_put_u8(buf, rc->len);
231 : 32 : wpabuf_put_data(buf, rc->oi, rc->len);
232 : : }
233 : 8 : gas_anqp_set_element_len(buf, len);
234 : 8 : }
235 : :
236 : :
237 : 4 : static void anqp_add_ip_addr_type_availability(struct hostapd_data *hapd,
238 : : struct wpabuf *buf)
239 : : {
240 [ + + ]: 4 : if (hapd->conf->ipaddr_type_configured) {
241 : 2 : wpabuf_put_le16(buf, ANQP_IP_ADDR_TYPE_AVAILABILITY);
242 : 2 : wpabuf_put_le16(buf, 1);
243 : 2 : wpabuf_put_u8(buf, hapd->conf->ipaddr_type_availability);
244 : : }
245 : 4 : }
246 : :
247 : :
248 : 65 : static void anqp_add_nai_realm_eap(struct wpabuf *buf,
249 : : struct hostapd_nai_realm_data *realm)
250 : : {
251 : : unsigned int i, j;
252 : :
253 : 65 : wpabuf_put_u8(buf, realm->eap_method_count);
254 : :
255 [ + + ]: 131 : for (i = 0; i < realm->eap_method_count; i++) {
256 : 66 : struct hostapd_nai_realm_eap *eap = &realm->eap_method[i];
257 : 66 : wpabuf_put_u8(buf, 2 + (3 * eap->num_auths));
258 : 66 : wpabuf_put_u8(buf, eap->eap_method);
259 : 66 : wpabuf_put_u8(buf, eap->num_auths);
260 [ + + ]: 162 : for (j = 0; j < eap->num_auths; j++) {
261 : 96 : wpabuf_put_u8(buf, eap->auth_id[j]);
262 : 96 : wpabuf_put_u8(buf, 1);
263 : 96 : wpabuf_put_u8(buf, eap->auth_val[j]);
264 : : }
265 : : }
266 : 65 : }
267 : :
268 : :
269 : 0 : static void anqp_add_nai_realm_data(struct wpabuf *buf,
270 : : struct hostapd_nai_realm_data *realm,
271 : : unsigned int realm_idx)
272 : : {
273 : : u8 *realm_data_len;
274 : :
275 : 0 : wpa_printf(MSG_DEBUG, "realm=%s, len=%d", realm->realm[realm_idx],
276 : 0 : (int) os_strlen(realm->realm[realm_idx]));
277 : 0 : realm_data_len = wpabuf_put(buf, 2);
278 : 0 : wpabuf_put_u8(buf, realm->encoding);
279 : 0 : wpabuf_put_u8(buf, os_strlen(realm->realm[realm_idx]));
280 : 0 : wpabuf_put_str(buf, realm->realm[realm_idx]);
281 : 0 : anqp_add_nai_realm_eap(buf, realm);
282 : 0 : gas_anqp_set_element_len(buf, realm_data_len);
283 : 0 : }
284 : :
285 : :
286 : 0 : static int hs20_add_nai_home_realm_matches(struct hostapd_data *hapd,
287 : : struct wpabuf *buf,
288 : : const u8 *home_realm,
289 : : size_t home_realm_len)
290 : : {
291 : : unsigned int i, j, k;
292 : 0 : u8 num_realms, num_matching = 0, encoding, realm_len, *realm_list_len;
293 : : struct hostapd_nai_realm_data *realm;
294 : : const u8 *pos, *realm_name, *end;
295 : : struct {
296 : : unsigned int realm_data_idx;
297 : : unsigned int realm_idx;
298 : : } matches[10];
299 : :
300 : 0 : pos = home_realm;
301 : 0 : end = pos + home_realm_len;
302 [ # # ]: 0 : if (pos + 1 > end) {
303 : 0 : wpa_hexdump(MSG_DEBUG, "Too short NAI Home Realm Query",
304 : : home_realm, home_realm_len);
305 : 0 : return -1;
306 : : }
307 : 0 : num_realms = *pos++;
308 : :
309 [ # # ][ # # ]: 0 : for (i = 0; i < num_realms && num_matching < 10; i++) {
310 [ # # ]: 0 : if (pos + 2 > end) {
311 : 0 : wpa_hexdump(MSG_DEBUG,
312 : : "Truncated NAI Home Realm Query",
313 : : home_realm, home_realm_len);
314 : 0 : return -1;
315 : : }
316 : 0 : encoding = *pos++;
317 : 0 : realm_len = *pos++;
318 [ # # ]: 0 : if (pos + realm_len > end) {
319 : 0 : wpa_hexdump(MSG_DEBUG,
320 : : "Truncated NAI Home Realm Query",
321 : : home_realm, home_realm_len);
322 : 0 : return -1;
323 : : }
324 : 0 : realm_name = pos;
325 [ # # ][ # # ]: 0 : for (j = 0; j < hapd->conf->nai_realm_count &&
326 : 0 : num_matching < 10; j++) {
327 : : const u8 *rpos, *rend;
328 : 0 : realm = &hapd->conf->nai_realm_data[j];
329 [ # # ]: 0 : if (encoding != realm->encoding)
330 : 0 : continue;
331 : :
332 : 0 : rpos = realm_name;
333 [ # # ][ # # ]: 0 : while (rpos < realm_name + realm_len &&
334 : : num_matching < 10) {
335 [ # # ]: 0 : for (rend = rpos;
336 : 0 : rend < realm_name + realm_len; rend++) {
337 [ # # ]: 0 : if (*rend == ';')
338 : 0 : break;
339 : : }
340 [ # # ][ # # ]: 0 : for (k = 0; k < MAX_NAI_REALMS &&
341 [ # # ]: 0 : realm->realm[k] &&
342 : 0 : num_matching < 10; k++) {
343 [ # # ]: 0 : if ((int) os_strlen(realm->realm[k]) !=
344 [ # # ]: 0 : rend - rpos ||
345 : 0 : os_strncmp((char *) rpos,
346 : : realm->realm[k],
347 : : rend - rpos) != 0)
348 : 0 : continue;
349 : 0 : matches[num_matching].realm_data_idx =
350 : : j;
351 : 0 : matches[num_matching].realm_idx = k;
352 : 0 : num_matching++;
353 : : }
354 : 0 : rpos = rend + 1;
355 : : }
356 : : }
357 : 0 : pos += realm_len;
358 : : }
359 : :
360 : 0 : realm_list_len = gas_anqp_add_element(buf, ANQP_NAI_REALM);
361 : 0 : wpabuf_put_le16(buf, num_matching);
362 : :
363 : : /*
364 : : * There are two ways to format. 1. each realm in a NAI Realm Data unit
365 : : * 2. all realms that share the same EAP methods in a NAI Realm Data
366 : : * unit. The first format is likely to be bigger in size than the
367 : : * second, but may be easier to parse and process by the receiver.
368 : : */
369 [ # # ]: 0 : for (i = 0; i < num_matching; i++) {
370 : 0 : wpa_printf(MSG_DEBUG, "realm_idx %d, realm_data_idx %d",
371 : : matches[i].realm_data_idx, matches[i].realm_idx);
372 : 0 : realm = &hapd->conf->nai_realm_data[matches[i].realm_data_idx];
373 : 0 : anqp_add_nai_realm_data(buf, realm, matches[i].realm_idx);
374 : : }
375 : 0 : gas_anqp_set_element_len(buf, realm_list_len);
376 : 0 : return 0;
377 : : }
378 : :
379 : :
380 : 37 : static void anqp_add_nai_realm(struct hostapd_data *hapd, struct wpabuf *buf,
381 : : const u8 *home_realm, size_t home_realm_len,
382 : : int nai_realm, int nai_home_realm)
383 : : {
384 [ + - ][ + - ]: 74 : if (nai_realm && hapd->conf->nai_realm_data) {
385 : : u8 *len;
386 : : unsigned int i, j;
387 : 37 : len = gas_anqp_add_element(buf, ANQP_NAI_REALM);
388 : 37 : wpabuf_put_le16(buf, hapd->conf->nai_realm_count);
389 [ + + ]: 102 : for (i = 0; i < hapd->conf->nai_realm_count; i++) {
390 : : u8 *realm_data_len, *realm_len;
391 : : struct hostapd_nai_realm_data *realm;
392 : :
393 : 65 : realm = &hapd->conf->nai_realm_data[i];
394 : 65 : realm_data_len = wpabuf_put(buf, 2);
395 : 65 : wpabuf_put_u8(buf, realm->encoding);
396 : 65 : realm_len = wpabuf_put(buf, 1);
397 [ + + ]: 132 : for (j = 0; realm->realm[j]; j++) {
398 [ + + ]: 67 : if (j > 0)
399 : 2 : wpabuf_put_u8(buf, ';');
400 : 67 : wpabuf_put_str(buf, realm->realm[j]);
401 : : }
402 : 65 : *realm_len = (u8 *) wpabuf_put(buf, 0) - realm_len - 1;
403 : 65 : anqp_add_nai_realm_eap(buf, realm);
404 : 65 : gas_anqp_set_element_len(buf, realm_data_len);
405 : : }
406 : 37 : gas_anqp_set_element_len(buf, len);
407 [ # # ][ # # ]: 0 : } else if (nai_home_realm && hapd->conf->nai_realm_data) {
408 : 0 : hs20_add_nai_home_realm_matches(hapd, buf, home_realm,
409 : : home_realm_len);
410 : : }
411 : 37 : }
412 : :
413 : :
414 : 10 : static void anqp_add_3gpp_cellular_network(struct hostapd_data *hapd,
415 : : struct wpabuf *buf)
416 : : {
417 [ + - ]: 10 : if (hapd->conf->anqp_3gpp_cell_net) {
418 : 10 : wpabuf_put_le16(buf, ANQP_3GPP_CELLULAR_NETWORK);
419 : 10 : wpabuf_put_le16(buf,
420 : 10 : hapd->conf->anqp_3gpp_cell_net_len);
421 : 10 : wpabuf_put_data(buf, hapd->conf->anqp_3gpp_cell_net,
422 : 10 : hapd->conf->anqp_3gpp_cell_net_len);
423 : : }
424 : 10 : }
425 : :
426 : :
427 : 24 : static void anqp_add_domain_name(struct hostapd_data *hapd, struct wpabuf *buf)
428 : : {
429 [ + + ]: 24 : if (hapd->conf->domain_name) {
430 : 23 : wpabuf_put_le16(buf, ANQP_DOMAIN_NAME);
431 : 23 : wpabuf_put_le16(buf, hapd->conf->domain_name_len);
432 : 23 : wpabuf_put_data(buf, hapd->conf->domain_name,
433 : 23 : hapd->conf->domain_name_len);
434 : : }
435 : 24 : }
436 : :
437 : :
438 : : #ifdef CONFIG_HS20
439 : :
440 : 4 : static void anqp_add_operator_friendly_name(struct hostapd_data *hapd,
441 : : struct wpabuf *buf)
442 : : {
443 [ + + ]: 4 : if (hapd->conf->hs20_oper_friendly_name) {
444 : : u8 *len;
445 : : unsigned int i;
446 : 2 : len = gas_anqp_add_element(buf, ANQP_VENDOR_SPECIFIC);
447 : 2 : wpabuf_put_be24(buf, OUI_WFA);
448 : 2 : wpabuf_put_u8(buf, HS20_ANQP_OUI_TYPE);
449 : 2 : wpabuf_put_u8(buf, HS20_STYPE_OPERATOR_FRIENDLY_NAME);
450 : 2 : wpabuf_put_u8(buf, 0); /* Reserved */
451 [ + + ]: 6 : for (i = 0; i < hapd->conf->hs20_oper_friendly_name_count; i++)
452 : : {
453 : : struct hostapd_lang_string *vn;
454 : 4 : vn = &hapd->conf->hs20_oper_friendly_name[i];
455 : 4 : wpabuf_put_u8(buf, 3 + vn->name_len);
456 : 4 : wpabuf_put_data(buf, vn->lang, 3);
457 : 4 : wpabuf_put_data(buf, vn->name, vn->name_len);
458 : : }
459 : 2 : gas_anqp_set_element_len(buf, len);
460 : : }
461 : 4 : }
462 : :
463 : :
464 : 4 : static void anqp_add_wan_metrics(struct hostapd_data *hapd,
465 : : struct wpabuf *buf)
466 : : {
467 [ + - ]: 4 : if (hapd->conf->hs20_wan_metrics) {
468 : 4 : u8 *len = gas_anqp_add_element(buf, ANQP_VENDOR_SPECIFIC);
469 : 4 : wpabuf_put_be24(buf, OUI_WFA);
470 : 4 : wpabuf_put_u8(buf, HS20_ANQP_OUI_TYPE);
471 : 4 : wpabuf_put_u8(buf, HS20_STYPE_WAN_METRICS);
472 : 4 : wpabuf_put_u8(buf, 0); /* Reserved */
473 : 4 : wpabuf_put_data(buf, hapd->conf->hs20_wan_metrics, 13);
474 : 4 : gas_anqp_set_element_len(buf, len);
475 : : }
476 : 4 : }
477 : :
478 : :
479 : 4 : static void anqp_add_connection_capability(struct hostapd_data *hapd,
480 : : struct wpabuf *buf)
481 : : {
482 [ + - ]: 4 : if (hapd->conf->hs20_connection_capability) {
483 : 4 : u8 *len = gas_anqp_add_element(buf, ANQP_VENDOR_SPECIFIC);
484 : 4 : wpabuf_put_be24(buf, OUI_WFA);
485 : 4 : wpabuf_put_u8(buf, HS20_ANQP_OUI_TYPE);
486 : 4 : wpabuf_put_u8(buf, HS20_STYPE_CONNECTION_CAPABILITY);
487 : 4 : wpabuf_put_u8(buf, 0); /* Reserved */
488 : 4 : wpabuf_put_data(buf, hapd->conf->hs20_connection_capability,
489 : 4 : hapd->conf->hs20_connection_capability_len);
490 : 4 : gas_anqp_set_element_len(buf, len);
491 : : }
492 : 4 : }
493 : :
494 : :
495 : 4 : static void anqp_add_operating_class(struct hostapd_data *hapd,
496 : : struct wpabuf *buf)
497 : : {
498 [ + - ]: 4 : if (hapd->conf->hs20_operating_class) {
499 : 4 : u8 *len = gas_anqp_add_element(buf, ANQP_VENDOR_SPECIFIC);
500 : 4 : wpabuf_put_be24(buf, OUI_WFA);
501 : 4 : wpabuf_put_u8(buf, HS20_ANQP_OUI_TYPE);
502 : 4 : wpabuf_put_u8(buf, HS20_STYPE_OPERATING_CLASS);
503 : 4 : wpabuf_put_u8(buf, 0); /* Reserved */
504 : 4 : wpabuf_put_data(buf, hapd->conf->hs20_operating_class,
505 : 4 : hapd->conf->hs20_operating_class_len);
506 : 4 : gas_anqp_set_element_len(buf, len);
507 : : }
508 : 4 : }
509 : :
510 : : #endif /* CONFIG_HS20 */
511 : :
512 : :
513 : : static struct wpabuf *
514 : 50 : gas_serv_build_gas_resp_payload(struct hostapd_data *hapd,
515 : : unsigned int request,
516 : : struct gas_dialog_info *di,
517 : : const u8 *home_realm, size_t home_realm_len)
518 : : {
519 : : struct wpabuf *buf;
520 : :
521 : 50 : buf = wpabuf_alloc(1400);
522 [ - + ]: 50 : if (buf == NULL)
523 : 0 : return NULL;
524 : :
525 [ + + ]: 50 : if (request & ANQP_REQ_CAPABILITY_LIST)
526 : 45 : anqp_add_capab_list(hapd, buf);
527 [ + + ]: 50 : if (request & ANQP_REQ_VENUE_NAME)
528 : 5 : anqp_add_venue_name(hapd, buf);
529 [ + + ]: 50 : if (request & ANQP_REQ_NETWORK_AUTH_TYPE)
530 : 4 : anqp_add_network_auth_type(hapd, buf);
531 [ + + ]: 50 : if (request & ANQP_REQ_ROAMING_CONSORTIUM)
532 : 8 : anqp_add_roaming_consortium(hapd, buf);
533 [ + + ]: 50 : if (request & ANQP_REQ_IP_ADDR_TYPE_AVAILABILITY)
534 : 4 : anqp_add_ip_addr_type_availability(hapd, buf);
535 [ + + ]: 50 : if (request & (ANQP_REQ_NAI_REALM | ANQP_REQ_NAI_HOME_REALM))
536 : 37 : anqp_add_nai_realm(hapd, buf, home_realm, home_realm_len,
537 : : request & ANQP_REQ_NAI_REALM,
538 : : request & ANQP_REQ_NAI_HOME_REALM);
539 [ + + ]: 50 : if (request & ANQP_REQ_3GPP_CELLULAR_NETWORK)
540 : 10 : anqp_add_3gpp_cellular_network(hapd, buf);
541 [ + + ]: 50 : if (request & ANQP_REQ_DOMAIN_NAME)
542 : 24 : anqp_add_domain_name(hapd, buf);
543 : :
544 : : #ifdef CONFIG_HS20
545 [ + + ]: 50 : if (request & ANQP_REQ_HS_CAPABILITY_LIST)
546 : 41 : anqp_add_hs_capab_list(hapd, buf);
547 [ + + ]: 50 : if (request & ANQP_REQ_OPERATOR_FRIENDLY_NAME)
548 : 4 : anqp_add_operator_friendly_name(hapd, buf);
549 [ + + ]: 50 : if (request & ANQP_REQ_WAN_METRICS)
550 : 4 : anqp_add_wan_metrics(hapd, buf);
551 [ + + ]: 50 : if (request & ANQP_REQ_CONNECTION_CAPABILITY)
552 : 4 : anqp_add_connection_capability(hapd, buf);
553 [ + + ]: 50 : if (request & ANQP_REQ_OPERATING_CLASS)
554 : 4 : anqp_add_operating_class(hapd, buf);
555 : : #endif /* CONFIG_HS20 */
556 : :
557 : 50 : return buf;
558 : : }
559 : :
560 : :
561 : 0 : static void gas_serv_clear_cached_ies(void *eloop_data, void *user_ctx)
562 : : {
563 : 0 : struct gas_dialog_info *dia = eloop_data;
564 : :
565 : 0 : wpa_printf(MSG_DEBUG, "GAS: Timeout triggered, clearing dialog for "
566 : 0 : "dialog token %d", dia->dialog_token);
567 : :
568 : 0 : gas_serv_dialog_clear(dia);
569 : 0 : }
570 : :
571 : :
572 : : struct anqp_query_info {
573 : : unsigned int request;
574 : : unsigned int remote_request;
575 : : const u8 *home_realm_query;
576 : : size_t home_realm_query_len;
577 : : u16 remote_delay;
578 : : };
579 : :
580 : :
581 : 194 : static void set_anqp_req(unsigned int bit, const char *name, int local,
582 : : unsigned int remote, u16 remote_delay,
583 : : struct anqp_query_info *qi)
584 : : {
585 : 194 : qi->request |= bit;
586 [ + + ]: 194 : if (local) {
587 : 187 : wpa_printf(MSG_DEBUG, "ANQP: %s (local)", name);
588 [ - + ]: 7 : } else if (bit & remote) {
589 : 0 : wpa_printf(MSG_DEBUG, "ANQP: %s (remote)", name);
590 : 0 : qi->remote_request |= bit;
591 [ # # ]: 0 : if (remote_delay > qi->remote_delay)
592 : 0 : qi->remote_delay = remote_delay;
593 : : } else {
594 : 7 : wpa_printf(MSG_DEBUG, "ANQP: %s not available", name);
595 : : }
596 : 194 : }
597 : :
598 : :
599 : 137 : static void rx_anqp_query_list_id(struct hostapd_data *hapd, u16 info_id,
600 : : struct anqp_query_info *qi)
601 : : {
602 [ + + + + : 137 : switch (info_id) {
+ + + +
- ]
603 : : case ANQP_CAPABILITY_LIST:
604 : 45 : set_anqp_req(ANQP_REQ_CAPABILITY_LIST, "Capability List", 1, 0,
605 : : 0, qi);
606 : 45 : break;
607 : : case ANQP_VENUE_NAME:
608 : 5 : set_anqp_req(ANQP_REQ_VENUE_NAME, "Venue Name",
609 : 5 : hapd->conf->venue_name != NULL, 0, 0, qi);
610 : 5 : break;
611 : : case ANQP_NETWORK_AUTH_TYPE:
612 : 4 : set_anqp_req(ANQP_REQ_NETWORK_AUTH_TYPE, "Network Auth Type",
613 : 4 : hapd->conf->network_auth_type != NULL,
614 : : 0, 0, qi);
615 : 4 : break;
616 : : case ANQP_ROAMING_CONSORTIUM:
617 : 8 : set_anqp_req(ANQP_REQ_ROAMING_CONSORTIUM, "Roaming Consortium",
618 : 8 : hapd->conf->roaming_consortium != NULL, 0, 0, qi);
619 : 8 : break;
620 : : case ANQP_IP_ADDR_TYPE_AVAILABILITY:
621 : 4 : set_anqp_req(ANQP_REQ_IP_ADDR_TYPE_AVAILABILITY,
622 : : "IP Addr Type Availability",
623 : 4 : hapd->conf->ipaddr_type_configured,
624 : : 0, 0, qi);
625 : 4 : break;
626 : : case ANQP_NAI_REALM:
627 : 37 : set_anqp_req(ANQP_REQ_NAI_REALM, "NAI Realm",
628 : 37 : hapd->conf->nai_realm_data != NULL,
629 : : 0, 0, qi);
630 : 37 : break;
631 : : case ANQP_3GPP_CELLULAR_NETWORK:
632 : 10 : set_anqp_req(ANQP_REQ_3GPP_CELLULAR_NETWORK,
633 : : "3GPP Cellular Network",
634 : 10 : hapd->conf->anqp_3gpp_cell_net != NULL,
635 : : 0, 0, qi);
636 : 10 : break;
637 : : case ANQP_DOMAIN_NAME:
638 : 24 : set_anqp_req(ANQP_REQ_DOMAIN_NAME, "Domain Name",
639 : 24 : hapd->conf->domain_name != NULL,
640 : : 0, 0, qi);
641 : 24 : break;
642 : : default:
643 : 0 : wpa_printf(MSG_DEBUG, "ANQP: Unsupported Info Id %u",
644 : : info_id);
645 : 0 : break;
646 : : }
647 : 137 : }
648 : :
649 : :
650 : 50 : static void rx_anqp_query_list(struct hostapd_data *hapd,
651 : : const u8 *pos, const u8 *end,
652 : : struct anqp_query_info *qi)
653 : : {
654 : 50 : wpa_printf(MSG_DEBUG, "ANQP: %u Info IDs requested in Query list",
655 : 50 : (unsigned int) (end - pos) / 2);
656 : :
657 [ + + ]: 187 : while (pos + 2 <= end) {
658 : 137 : rx_anqp_query_list_id(hapd, WPA_GET_LE16(pos), qi);
659 : 137 : pos += 2;
660 : : }
661 : 50 : }
662 : :
663 : :
664 : : #ifdef CONFIG_HS20
665 : :
666 : 57 : static void rx_anqp_hs_query_list(struct hostapd_data *hapd, u8 subtype,
667 : : struct anqp_query_info *qi)
668 : : {
669 [ + + + + : 57 : switch (subtype) {
+ - ]
670 : : case HS20_STYPE_CAPABILITY_LIST:
671 : 41 : set_anqp_req(ANQP_REQ_HS_CAPABILITY_LIST, "HS Capability List",
672 : : 1, 0, 0, qi);
673 : 41 : break;
674 : : case HS20_STYPE_OPERATOR_FRIENDLY_NAME:
675 : 4 : set_anqp_req(ANQP_REQ_OPERATOR_FRIENDLY_NAME,
676 : : "Operator Friendly Name",
677 : 4 : hapd->conf->hs20_oper_friendly_name != NULL,
678 : : 0, 0, qi);
679 : 4 : break;
680 : : case HS20_STYPE_WAN_METRICS:
681 : 4 : set_anqp_req(ANQP_REQ_WAN_METRICS, "WAN Metrics",
682 : 4 : hapd->conf->hs20_wan_metrics != NULL,
683 : : 0, 0, qi);
684 : 4 : break;
685 : : case HS20_STYPE_CONNECTION_CAPABILITY:
686 : 4 : set_anqp_req(ANQP_REQ_CONNECTION_CAPABILITY,
687 : : "Connection Capability",
688 : 4 : hapd->conf->hs20_connection_capability != NULL,
689 : : 0, 0, qi);
690 : 4 : break;
691 : : case HS20_STYPE_OPERATING_CLASS:
692 : 4 : set_anqp_req(ANQP_REQ_OPERATING_CLASS, "Operating Class",
693 : 4 : hapd->conf->hs20_operating_class != NULL,
694 : : 0, 0, qi);
695 : 4 : break;
696 : : default:
697 : 0 : wpa_printf(MSG_DEBUG, "ANQP: Unsupported HS 2.0 subtype %u",
698 : : subtype);
699 : 0 : break;
700 : : }
701 : 57 : }
702 : :
703 : :
704 : 0 : static void rx_anqp_hs_nai_home_realm(struct hostapd_data *hapd,
705 : : const u8 *pos, const u8 *end,
706 : : struct anqp_query_info *qi)
707 : : {
708 : 0 : qi->request |= ANQP_REQ_NAI_HOME_REALM;
709 : 0 : qi->home_realm_query = pos;
710 : 0 : qi->home_realm_query_len = end - pos;
711 [ # # ]: 0 : if (hapd->conf->nai_realm_data != NULL) {
712 : 0 : wpa_printf(MSG_DEBUG, "ANQP: HS 2.0 NAI Home Realm Query "
713 : : "(local)");
714 : : } else {
715 : 0 : wpa_printf(MSG_DEBUG, "ANQP: HS 2.0 NAI Home Realm Query not "
716 : : "available");
717 : : }
718 : 0 : }
719 : :
720 : :
721 : 41 : static void rx_anqp_vendor_specific(struct hostapd_data *hapd,
722 : : const u8 *pos, const u8 *end,
723 : : struct anqp_query_info *qi)
724 : : {
725 : : u32 oui;
726 : : u8 subtype;
727 : :
728 [ - + ]: 41 : if (pos + 4 > end) {
729 : 0 : wpa_printf(MSG_DEBUG, "ANQP: Too short vendor specific ANQP "
730 : : "Query element");
731 : 0 : return;
732 : : }
733 : :
734 : 41 : oui = WPA_GET_BE24(pos);
735 : 41 : pos += 3;
736 [ - + ]: 41 : if (oui != OUI_WFA) {
737 : 0 : wpa_printf(MSG_DEBUG, "ANQP: Unsupported vendor OUI %06x",
738 : : oui);
739 : 0 : return;
740 : : }
741 : :
742 [ - + ]: 41 : if (*pos != HS20_ANQP_OUI_TYPE) {
743 : 0 : wpa_printf(MSG_DEBUG, "ANQP: Unsupported WFA vendor type %u",
744 : 0 : *pos);
745 : 0 : return;
746 : : }
747 : 41 : pos++;
748 : :
749 [ - + ]: 41 : if (pos + 1 >= end)
750 : 0 : return;
751 : :
752 : 41 : subtype = *pos++;
753 : 41 : pos++; /* Reserved */
754 [ + - - ]: 41 : switch (subtype) {
755 : : case HS20_STYPE_QUERY_LIST:
756 : 41 : wpa_printf(MSG_DEBUG, "ANQP: HS 2.0 Query List");
757 [ + + ]: 98 : while (pos < end) {
758 : 57 : rx_anqp_hs_query_list(hapd, *pos, qi);
759 : 57 : pos++;
760 : : }
761 : 41 : break;
762 : : case HS20_STYPE_NAI_HOME_REALM_QUERY:
763 : 0 : rx_anqp_hs_nai_home_realm(hapd, pos, end, qi);
764 : 0 : break;
765 : : default:
766 : 0 : wpa_printf(MSG_DEBUG, "ANQP: Unsupported HS 2.0 query subtype "
767 : : "%u", subtype);
768 : 41 : break;
769 : : }
770 : : }
771 : :
772 : : #endif /* CONFIG_HS20 */
773 : :
774 : :
775 : 50 : static void gas_serv_req_local_processing(struct hostapd_data *hapd,
776 : : const u8 *sa, u8 dialog_token,
777 : : struct anqp_query_info *qi)
778 : : {
779 : : struct wpabuf *buf, *tx_buf;
780 : :
781 : 50 : buf = gas_serv_build_gas_resp_payload(hapd, qi->request, NULL,
782 : : qi->home_realm_query,
783 : : qi->home_realm_query_len);
784 : 50 : wpa_hexdump_buf(MSG_MSGDUMP, "ANQP: Locally generated ANQP responses",
785 : : buf);
786 [ - + ]: 50 : if (!buf)
787 : 0 : return;
788 : :
789 [ + + ][ + + ]: 50 : if (wpabuf_len(buf) > hapd->gas_frag_limit ||
790 : 4 : hapd->conf->gas_comeback_delay) {
791 : : struct gas_dialog_info *di;
792 : 4 : u16 comeback_delay = 1;
793 : :
794 [ + + ]: 4 : if (hapd->conf->gas_comeback_delay) {
795 : : /* Testing - allow overriding of the delay value */
796 : 1 : comeback_delay = hapd->conf->gas_comeback_delay;
797 : : }
798 : :
799 : 4 : wpa_printf(MSG_DEBUG, "ANQP: Too long response to fit in "
800 : : "initial response - use GAS comeback");
801 : 4 : di = gas_dialog_create(hapd, sa, dialog_token);
802 [ - + ]: 4 : if (!di) {
803 : 0 : wpa_printf(MSG_INFO, "ANQP: Could not create dialog "
804 : : "for " MACSTR " (dialog token %u)",
805 : 0 : MAC2STR(sa), dialog_token);
806 : 0 : wpabuf_free(buf);
807 : 0 : return;
808 : : }
809 : 4 : di->sd_resp = buf;
810 : 4 : di->sd_resp_pos = 0;
811 : 4 : tx_buf = gas_anqp_build_initial_resp_buf(
812 : : dialog_token, WLAN_STATUS_SUCCESS, comeback_delay,
813 : : NULL);
814 : : } else {
815 : 46 : wpa_printf(MSG_DEBUG, "ANQP: Initial response (no comeback)");
816 : 46 : tx_buf = gas_anqp_build_initial_resp_buf(
817 : : dialog_token, WLAN_STATUS_SUCCESS, 0, buf);
818 : 46 : wpabuf_free(buf);
819 : : }
820 [ - + ]: 50 : if (!tx_buf)
821 : 0 : return;
822 : :
823 : 100 : hostapd_drv_send_action(hapd, hapd->iface->freq, 0, sa,
824 : 50 : wpabuf_head(tx_buf), wpabuf_len(tx_buf));
825 : 50 : wpabuf_free(tx_buf);
826 : : }
827 : :
828 : :
829 : 50 : static void gas_serv_rx_gas_initial_req(struct hostapd_data *hapd,
830 : : const u8 *sa,
831 : : const u8 *data, size_t len)
832 : : {
833 : 50 : const u8 *pos = data;
834 : 50 : const u8 *end = data + len;
835 : : const u8 *next;
836 : : u8 dialog_token;
837 : : u16 slen;
838 : : struct anqp_query_info qi;
839 : : const u8 *adv_proto;
840 : :
841 [ - + ]: 50 : if (len < 1 + 2)
842 : 0 : return;
843 : :
844 : 50 : os_memset(&qi, 0, sizeof(qi));
845 : :
846 : 50 : dialog_token = *pos++;
847 : 50 : wpa_msg(hapd->msg_ctx, MSG_DEBUG,
848 : : "GAS: GAS Initial Request from " MACSTR " (dialog token %u) ",
849 : 300 : MAC2STR(sa), dialog_token);
850 : :
851 [ - + ]: 50 : if (*pos != WLAN_EID_ADV_PROTO) {
852 : 0 : wpa_msg(hapd->msg_ctx, MSG_DEBUG,
853 : 0 : "GAS: Unexpected IE in GAS Initial Request: %u", *pos);
854 : 0 : return;
855 : : }
856 : 50 : adv_proto = pos++;
857 : :
858 : 50 : slen = *pos++;
859 : 50 : next = pos + slen;
860 [ + - ][ - + ]: 50 : if (next > end || slen < 2) {
861 : 0 : wpa_msg(hapd->msg_ctx, MSG_DEBUG,
862 : : "GAS: Invalid IE in GAS Initial Request");
863 : 0 : return;
864 : : }
865 : 50 : pos++; /* skip QueryRespLenLimit and PAME-BI */
866 : :
867 [ - + ]: 50 : if (*pos != ACCESS_NETWORK_QUERY_PROTOCOL) {
868 : : struct wpabuf *buf;
869 : 0 : wpa_msg(hapd->msg_ctx, MSG_DEBUG,
870 : : "GAS: Unsupported GAS advertisement protocol id %u",
871 : 0 : *pos);
872 [ # # ]: 0 : if (sa[0] & 0x01)
873 : 0 : return; /* Invalid source address - drop silently */
874 : 0 : buf = gas_build_initial_resp(
875 : : dialog_token, WLAN_STATUS_GAS_ADV_PROTO_NOT_SUPPORTED,
876 : 0 : 0, 2 + slen + 2);
877 [ # # ]: 0 : if (buf == NULL)
878 : 0 : return;
879 : 0 : wpabuf_put_data(buf, adv_proto, 2 + slen);
880 : 0 : wpabuf_put_le16(buf, 0); /* Query Response Length */
881 : 0 : hostapd_drv_send_action(hapd, hapd->iface->freq, 0, sa,
882 : 0 : wpabuf_head(buf), wpabuf_len(buf));
883 : 0 : wpabuf_free(buf);
884 : 0 : return;
885 : : }
886 : :
887 : 50 : pos = next;
888 : : /* Query Request */
889 [ - + ]: 50 : if (pos + 2 > end)
890 : 0 : return;
891 : 50 : slen = WPA_GET_LE16(pos);
892 : 50 : pos += 2;
893 [ - + ]: 50 : if (pos + slen > end)
894 : 0 : return;
895 : 50 : end = pos + slen;
896 : :
897 : : /* ANQP Query Request */
898 [ + + ]: 141 : while (pos < end) {
899 : : u16 info_id, elen;
900 : :
901 [ - + ]: 91 : if (pos + 4 > end)
902 : 0 : return;
903 : :
904 : 91 : info_id = WPA_GET_LE16(pos);
905 : 91 : pos += 2;
906 : 91 : elen = WPA_GET_LE16(pos);
907 : 91 : pos += 2;
908 : :
909 [ - + ]: 91 : if (pos + elen > end) {
910 : 0 : wpa_printf(MSG_DEBUG, "ANQP: Invalid Query Request");
911 : 0 : return;
912 : : }
913 : :
914 [ + + - ]: 91 : switch (info_id) {
915 : : case ANQP_QUERY_LIST:
916 : 50 : rx_anqp_query_list(hapd, pos, pos + elen, &qi);
917 : 50 : break;
918 : : #ifdef CONFIG_HS20
919 : : case ANQP_VENDOR_SPECIFIC:
920 : 41 : rx_anqp_vendor_specific(hapd, pos, pos + elen, &qi);
921 : 41 : break;
922 : : #endif /* CONFIG_HS20 */
923 : : default:
924 : 0 : wpa_printf(MSG_DEBUG, "ANQP: Unsupported Query "
925 : : "Request element %u", info_id);
926 : 0 : break;
927 : : }
928 : :
929 : 91 : pos += elen;
930 : : }
931 : :
932 : 50 : gas_serv_req_local_processing(hapd, sa, dialog_token, &qi);
933 : : }
934 : :
935 : :
936 : 0 : void gas_serv_tx_gas_response(struct hostapd_data *hapd, const u8 *dst,
937 : : struct gas_dialog_info *dialog)
938 : : {
939 : : struct wpabuf *buf, *tx_buf;
940 : 0 : u8 dialog_token = dialog->dialog_token;
941 : : size_t frag_len;
942 : :
943 [ # # ]: 0 : if (dialog->sd_resp == NULL) {
944 : 0 : buf = gas_serv_build_gas_resp_payload(hapd,
945 : : dialog->all_requested,
946 : : dialog, NULL, 0);
947 : 0 : wpa_hexdump_buf(MSG_MSGDUMP, "ANQP: Generated ANQP responses",
948 : : buf);
949 [ # # ]: 0 : if (!buf)
950 : 0 : goto tx_gas_response_done;
951 : 0 : dialog->sd_resp = buf;
952 : 0 : dialog->sd_resp_pos = 0;
953 : : }
954 : 0 : frag_len = wpabuf_len(dialog->sd_resp) - dialog->sd_resp_pos;
955 [ # # ][ # # ]: 0 : if (frag_len > hapd->gas_frag_limit || dialog->comeback_delay ||
[ # # ]
956 : 0 : hapd->conf->gas_comeback_delay) {
957 : 0 : u16 comeback_delay_tus = dialog->comeback_delay +
958 : : GAS_SERV_COMEBACK_DELAY_FUDGE;
959 : : u32 comeback_delay_secs, comeback_delay_usecs;
960 : :
961 [ # # ]: 0 : if (hapd->conf->gas_comeback_delay) {
962 : : /* Testing - allow overriding of the delay value */
963 : 0 : comeback_delay_tus = hapd->conf->gas_comeback_delay;
964 : : }
965 : :
966 : 0 : wpa_printf(MSG_DEBUG, "GAS: Response frag_len %u (frag limit "
967 : : "%u) and comeback delay %u, "
968 : : "requesting comebacks", (unsigned int) frag_len,
969 : 0 : (unsigned int) hapd->gas_frag_limit,
970 : 0 : dialog->comeback_delay);
971 : 0 : tx_buf = gas_anqp_build_initial_resp_buf(dialog_token,
972 : : WLAN_STATUS_SUCCESS,
973 : : comeback_delay_tus,
974 : : NULL);
975 [ # # ]: 0 : if (tx_buf) {
976 : 0 : wpa_msg(hapd->msg_ctx, MSG_DEBUG,
977 : : "GAS: Tx GAS Initial Resp (comeback = 10TU)");
978 : 0 : hostapd_drv_send_action(hapd, hapd->iface->freq, 0,
979 : : dst,
980 : 0 : wpabuf_head(tx_buf),
981 : : wpabuf_len(tx_buf));
982 : : }
983 : 0 : wpabuf_free(tx_buf);
984 : :
985 : : /* start a timer of 1.5 * comeback-delay */
986 : 0 : comeback_delay_tus = comeback_delay_tus +
987 : : (comeback_delay_tus / 2);
988 : 0 : comeback_delay_secs = (comeback_delay_tus * 1024) / 1000000;
989 : 0 : comeback_delay_usecs = (comeback_delay_tus * 1024) -
990 : 0 : (comeback_delay_secs * 1000000);
991 : 0 : eloop_register_timeout(comeback_delay_secs,
992 : : comeback_delay_usecs,
993 : : gas_serv_clear_cached_ies, dialog,
994 : : NULL);
995 : 0 : goto tx_gas_response_done;
996 : : }
997 : :
998 : 0 : buf = wpabuf_alloc_copy(wpabuf_head_u8(dialog->sd_resp) +
999 : : dialog->sd_resp_pos, frag_len);
1000 [ # # ]: 0 : if (buf == NULL) {
1001 : 0 : wpa_msg(hapd->msg_ctx, MSG_DEBUG, "GAS: Buffer allocation "
1002 : : "failed");
1003 : 0 : goto tx_gas_response_done;
1004 : : }
1005 : 0 : tx_buf = gas_anqp_build_initial_resp_buf(dialog_token,
1006 : : WLAN_STATUS_SUCCESS, 0, buf);
1007 : 0 : wpabuf_free(buf);
1008 [ # # ]: 0 : if (tx_buf == NULL)
1009 : 0 : goto tx_gas_response_done;
1010 : 0 : wpa_msg(hapd->msg_ctx, MSG_DEBUG, "GAS: Tx GAS Initial "
1011 : : "Response (frag_id %d frag_len %d)",
1012 : 0 : dialog->sd_frag_id, (int) frag_len);
1013 : 0 : dialog->sd_frag_id++;
1014 : :
1015 : 0 : hostapd_drv_send_action(hapd, hapd->iface->freq, 0, dst,
1016 : 0 : wpabuf_head(tx_buf), wpabuf_len(tx_buf));
1017 : 0 : wpabuf_free(tx_buf);
1018 : : tx_gas_response_done:
1019 : 0 : gas_serv_clear_cached_ies(dialog, NULL);
1020 : 0 : }
1021 : :
1022 : :
1023 : 18 : static void gas_serv_rx_gas_comeback_req(struct hostapd_data *hapd,
1024 : : const u8 *sa,
1025 : : const u8 *data, size_t len)
1026 : : {
1027 : : struct gas_dialog_info *dialog;
1028 : : struct wpabuf *buf, *tx_buf;
1029 : : u8 dialog_token;
1030 : : size_t frag_len;
1031 : 18 : int more = 0;
1032 : :
1033 : 18 : wpa_hexdump(MSG_DEBUG, "GAS: RX GAS Comeback Request", data, len);
1034 [ - + ]: 18 : if (len < 1)
1035 : 0 : return;
1036 : 18 : dialog_token = *data;
1037 : 18 : wpa_msg(hapd->msg_ctx, MSG_DEBUG, "GAS: Dialog Token: %u",
1038 : : dialog_token);
1039 : :
1040 : 18 : dialog = gas_serv_dialog_find(hapd, sa, dialog_token);
1041 [ - + ]: 18 : if (!dialog) {
1042 : 0 : wpa_msg(hapd->msg_ctx, MSG_DEBUG, "GAS: No pending SD "
1043 : : "response fragment for " MACSTR " dialog token %u",
1044 : 0 : MAC2STR(sa), dialog_token);
1045 : :
1046 [ # # ]: 0 : if (sa[0] & 0x01)
1047 : 0 : return; /* Invalid source address - drop silently */
1048 : 0 : tx_buf = gas_anqp_build_comeback_resp_buf(
1049 : : dialog_token, WLAN_STATUS_NO_OUTSTANDING_GAS_REQ, 0, 0,
1050 : : 0, NULL);
1051 [ # # ]: 0 : if (tx_buf == NULL)
1052 : 0 : return;
1053 : 0 : goto send_resp;
1054 : : }
1055 : :
1056 [ - + ]: 18 : if (dialog->sd_resp == NULL) {
1057 : 0 : wpa_printf(MSG_DEBUG, "GAS: Remote request 0x%x received 0x%x",
1058 : : dialog->requested, dialog->received);
1059 [ # # ]: 0 : if ((dialog->requested & dialog->received) !=
1060 : 0 : dialog->requested) {
1061 : 0 : wpa_printf(MSG_DEBUG, "GAS: Did not receive response "
1062 : : "from remote processing");
1063 : 0 : gas_serv_dialog_clear(dialog);
1064 : 0 : tx_buf = gas_anqp_build_comeback_resp_buf(
1065 : : dialog_token,
1066 : : WLAN_STATUS_GAS_RESP_NOT_RECEIVED, 0, 0, 0,
1067 : : NULL);
1068 [ # # ]: 0 : if (tx_buf == NULL)
1069 : 0 : return;
1070 : 0 : goto send_resp;
1071 : : }
1072 : :
1073 : 0 : buf = gas_serv_build_gas_resp_payload(hapd,
1074 : : dialog->all_requested,
1075 : : dialog, NULL, 0);
1076 : 0 : wpa_hexdump_buf(MSG_MSGDUMP, "ANQP: Generated ANQP responses",
1077 : : buf);
1078 [ # # ]: 0 : if (!buf)
1079 : 0 : goto rx_gas_comeback_req_done;
1080 : 0 : dialog->sd_resp = buf;
1081 : 0 : dialog->sd_resp_pos = 0;
1082 : : }
1083 : 18 : frag_len = wpabuf_len(dialog->sd_resp) - dialog->sd_resp_pos;
1084 [ + + ]: 18 : if (frag_len > hapd->gas_frag_limit) {
1085 : 14 : frag_len = hapd->gas_frag_limit;
1086 : 14 : more = 1;
1087 : : }
1088 : 18 : wpa_msg(hapd->msg_ctx, MSG_DEBUG, "GAS: resp frag_len %u",
1089 : : (unsigned int) frag_len);
1090 : 18 : buf = wpabuf_alloc_copy(wpabuf_head_u8(dialog->sd_resp) +
1091 : : dialog->sd_resp_pos, frag_len);
1092 [ - + ]: 18 : if (buf == NULL) {
1093 : 0 : wpa_msg(hapd->msg_ctx, MSG_DEBUG, "GAS: Failed to allocate "
1094 : : "buffer");
1095 : 0 : goto rx_gas_comeback_req_done;
1096 : : }
1097 : 18 : tx_buf = gas_anqp_build_comeback_resp_buf(dialog_token,
1098 : : WLAN_STATUS_SUCCESS,
1099 : 18 : dialog->sd_frag_id,
1100 : : more, 0, buf);
1101 : 18 : wpabuf_free(buf);
1102 [ - + ]: 18 : if (tx_buf == NULL)
1103 : 0 : goto rx_gas_comeback_req_done;
1104 : 18 : wpa_msg(hapd->msg_ctx, MSG_DEBUG, "GAS: Tx GAS Comeback Response "
1105 : : "(frag_id %d more=%d frag_len=%d)",
1106 : 18 : dialog->sd_frag_id, more, (int) frag_len);
1107 : 18 : dialog->sd_frag_id++;
1108 : 18 : dialog->sd_resp_pos += frag_len;
1109 : :
1110 [ + + ]: 18 : if (more) {
1111 : 14 : wpa_msg(hapd->msg_ctx, MSG_DEBUG, "GAS: %d more bytes remain "
1112 : : "to be sent",
1113 : 14 : (int) (wpabuf_len(dialog->sd_resp) -
1114 : 14 : dialog->sd_resp_pos));
1115 : : } else {
1116 : 4 : wpa_msg(hapd->msg_ctx, MSG_DEBUG, "GAS: All fragments of "
1117 : : "SD response sent");
1118 : 4 : gas_serv_dialog_clear(dialog);
1119 : 4 : gas_serv_free_dialogs(hapd, sa);
1120 : : }
1121 : :
1122 : : send_resp:
1123 : 36 : hostapd_drv_send_action(hapd, hapd->iface->freq, 0, sa,
1124 : 18 : wpabuf_head(tx_buf), wpabuf_len(tx_buf));
1125 : 18 : wpabuf_free(tx_buf);
1126 : 18 : return;
1127 : :
1128 : : rx_gas_comeback_req_done:
1129 : 18 : gas_serv_clear_cached_ies(dialog, NULL);
1130 : : }
1131 : :
1132 : :
1133 : 68 : static void gas_serv_rx_public_action(void *ctx, const u8 *buf, size_t len,
1134 : : int freq)
1135 : : {
1136 : 68 : struct hostapd_data *hapd = ctx;
1137 : : const struct ieee80211_mgmt *mgmt;
1138 : : size_t hdr_len;
1139 : : const u8 *sa, *data;
1140 : :
1141 : 68 : mgmt = (const struct ieee80211_mgmt *) buf;
1142 : 68 : hdr_len = (const u8 *) &mgmt->u.action.u.vs_public_action.action - buf;
1143 [ - + ]: 68 : if (hdr_len > len)
1144 : 0 : return;
1145 [ - + ]: 68 : if (mgmt->u.action.category != WLAN_ACTION_PUBLIC)
1146 : 0 : return;
1147 : 68 : sa = mgmt->sa;
1148 : 68 : len -= hdr_len;
1149 : 68 : data = &mgmt->u.action.u.public_action.action;
1150 [ + + - ]: 68 : switch (data[0]) {
1151 : : case WLAN_PA_GAS_INITIAL_REQ:
1152 : 50 : gas_serv_rx_gas_initial_req(hapd, sa, data + 1, len - 1);
1153 : 50 : break;
1154 : : case WLAN_PA_GAS_COMEBACK_REQ:
1155 : 18 : gas_serv_rx_gas_comeback_req(hapd, sa, data + 1, len - 1);
1156 : 68 : break;
1157 : : }
1158 : : }
1159 : :
1160 : :
1161 : 234 : int gas_serv_init(struct hostapd_data *hapd)
1162 : : {
1163 : 234 : hapd->public_action_cb2 = gas_serv_rx_public_action;
1164 : 234 : hapd->public_action_cb2_ctx = hapd;
1165 : 234 : hapd->gas_frag_limit = 1400;
1166 [ - + ]: 234 : if (hapd->conf->gas_frag_limit > 0)
1167 : 0 : hapd->gas_frag_limit = hapd->conf->gas_frag_limit;
1168 : 234 : return 0;
1169 : : }
1170 : :
1171 : :
1172 : 234 : void gas_serv_deinit(struct hostapd_data *hapd)
1173 : : {
1174 : 234 : }
|