1/* 2 * Generic advertisement service (GAS) server 3 * Copyright (c) 2011-2014, 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 "common/wpa_ctrl.h" 15#include "utils/eloop.h" 16#include "hostapd.h" 17#include "ap_config.h" 18#include "ap_drv_ops.h" 19#include "dpp_hostapd.h" 20#include "sta_info.h" 21#include "gas_serv.h" 22 23 24#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 39static 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 46static 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 70 * it to be that long. Use five second timeout and increase this 71 * with the comeback_delay for testing cases. 72 */ 73 ap_sta_session_timeout(hapd, sta, 74 hapd->conf->gas_comeback_delay / 1024 + 75 5); 76 } else { 77 ap_sta_replenish_timeout(hapd, sta, 5); 78 } 79 80 if (sta->gas_dialog == NULL) { 81 sta->gas_dialog = os_calloc(GAS_DIALOG_MAX, 82 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; 94 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; 124 ap_sta_replenish_timeout(hapd, sta, 5); 125 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 160#ifdef CONFIG_HS20 161static 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 if (hapd->conf->hs20_osu_providers_count) 183 wpabuf_put_u8(buf, HS20_STYPE_OSU_PROVIDERS_LIST); 184 if (hapd->conf->hs20_icons_count) 185 wpabuf_put_u8(buf, HS20_STYPE_ICON_REQUEST); 186 gas_anqp_set_element_len(buf, len); 187} 188#endif /* CONFIG_HS20 */ 189 190 191static struct anqp_element * get_anqp_elem(struct hostapd_data *hapd, 192 u16 infoid) 193{ 194 struct anqp_element *elem; 195 196 dl_list_for_each(elem, &hapd->conf->anqp_elem, struct anqp_element, 197 list) { 198 if (elem->infoid == infoid) 199 return elem; 200 } 201 202 return NULL; 203} 204 205 206static void anqp_add_elem(struct hostapd_data *hapd, struct wpabuf *buf, 207 u16 infoid) 208{ 209 struct anqp_element *elem; 210 211 elem = get_anqp_elem(hapd, infoid); 212 if (!elem) 213 return; 214 if (wpabuf_tailroom(buf) < 2 + 2 + wpabuf_len(elem->payload)) { 215 wpa_printf(MSG_DEBUG, "ANQP: No room for InfoID %u payload", 216 infoid); 217 return; 218 } 219 220 wpabuf_put_le16(buf, infoid); 221 wpabuf_put_le16(buf, wpabuf_len(elem->payload)); 222 wpabuf_put_buf(buf, elem->payload); 223} 224 225 226static int anqp_add_override(struct hostapd_data *hapd, struct wpabuf *buf, 227 u16 infoid) 228{ 229 if (get_anqp_elem(hapd, infoid)) { 230 anqp_add_elem(hapd, buf, infoid); 231 return 1; 232 } 233 234 return 0; 235} 236 237 238static void anqp_add_capab_list(struct hostapd_data *hapd, 239 struct wpabuf *buf) 240{ 241 u8 *len; 242 u16 id; 243 244 if (anqp_add_override(hapd, buf, ANQP_CAPABILITY_LIST)) 245 return; 246 247 len = gas_anqp_add_element(buf, ANQP_CAPABILITY_LIST); 248 wpabuf_put_le16(buf, ANQP_CAPABILITY_LIST); 249 if (hapd->conf->venue_name || get_anqp_elem(hapd, ANQP_VENUE_NAME)) 250 wpabuf_put_le16(buf, ANQP_VENUE_NAME); 251 if (get_anqp_elem(hapd, ANQP_EMERGENCY_CALL_NUMBER)) 252 wpabuf_put_le16(buf, ANQP_EMERGENCY_CALL_NUMBER); 253 if (hapd->conf->network_auth_type || 254 get_anqp_elem(hapd, ANQP_NETWORK_AUTH_TYPE)) 255 wpabuf_put_le16(buf, ANQP_NETWORK_AUTH_TYPE); 256 if (hapd->conf->roaming_consortium || 257 get_anqp_elem(hapd, ANQP_ROAMING_CONSORTIUM)) 258 wpabuf_put_le16(buf, ANQP_ROAMING_CONSORTIUM); 259 if (hapd->conf->ipaddr_type_configured || 260 get_anqp_elem(hapd, ANQP_IP_ADDR_TYPE_AVAILABILITY)) 261 wpabuf_put_le16(buf, ANQP_IP_ADDR_TYPE_AVAILABILITY); 262 if (hapd->conf->nai_realm_data || 263 get_anqp_elem(hapd, ANQP_NAI_REALM)) 264 wpabuf_put_le16(buf, ANQP_NAI_REALM); 265 if (hapd->conf->anqp_3gpp_cell_net || 266 get_anqp_elem(hapd, ANQP_3GPP_CELLULAR_NETWORK)) 267 wpabuf_put_le16(buf, ANQP_3GPP_CELLULAR_NETWORK); 268 if (get_anqp_elem(hapd, ANQP_AP_GEOSPATIAL_LOCATION)) 269 wpabuf_put_le16(buf, ANQP_AP_GEOSPATIAL_LOCATION); 270 if (get_anqp_elem(hapd, ANQP_AP_CIVIC_LOCATION)) 271 wpabuf_put_le16(buf, ANQP_AP_CIVIC_LOCATION); 272 if (get_anqp_elem(hapd, ANQP_AP_LOCATION_PUBLIC_URI)) 273 wpabuf_put_le16(buf, ANQP_AP_LOCATION_PUBLIC_URI); 274 if (hapd->conf->domain_name || get_anqp_elem(hapd, ANQP_DOMAIN_NAME)) 275 wpabuf_put_le16(buf, ANQP_DOMAIN_NAME); 276 if (get_anqp_elem(hapd, ANQP_EMERGENCY_ALERT_URI)) 277 wpabuf_put_le16(buf, ANQP_EMERGENCY_ALERT_URI); 278 if (get_anqp_elem(hapd, ANQP_TDLS_CAPABILITY)) 279 wpabuf_put_le16(buf, ANQP_TDLS_CAPABILITY); 280 if (get_anqp_elem(hapd, ANQP_EMERGENCY_NAI)) 281 wpabuf_put_le16(buf, ANQP_EMERGENCY_NAI); 282 if (get_anqp_elem(hapd, ANQP_NEIGHBOR_REPORT)) 283 wpabuf_put_le16(buf, ANQP_NEIGHBOR_REPORT); 284#ifdef CONFIG_FILS 285 if (!dl_list_empty(&hapd->conf->fils_realms) || 286 get_anqp_elem(hapd, ANQP_FILS_REALM_INFO)) 287 wpabuf_put_le16(buf, ANQP_FILS_REALM_INFO); 288#endif /* CONFIG_FILS */ 289 if (get_anqp_elem(hapd, ANQP_CAG)) 290 wpabuf_put_le16(buf, ANQP_CAG); 291 if (get_anqp_elem(hapd, ANQP_VENUE_URL)) 292 wpabuf_put_le16(buf, ANQP_VENUE_URL); 293 if (get_anqp_elem(hapd, ANQP_ADVICE_OF_CHARGE)) 294 wpabuf_put_le16(buf, ANQP_ADVICE_OF_CHARGE); 295 if (get_anqp_elem(hapd, ANQP_LOCAL_CONTENT)) 296 wpabuf_put_le16(buf, ANQP_LOCAL_CONTENT); 297 for (id = 280; id < 300; id++) { 298 if (get_anqp_elem(hapd, id)) 299 wpabuf_put_le16(buf, id); 300 } 301#ifdef CONFIG_HS20 302 anqp_add_hs_capab_list(hapd, buf); 303#endif /* CONFIG_HS20 */ 304 gas_anqp_set_element_len(buf, len); 305} 306 307 308static void anqp_add_venue_name(struct hostapd_data *hapd, struct wpabuf *buf) 309{ 310 if (anqp_add_override(hapd, buf, ANQP_VENUE_NAME)) 311 return; 312 313 if (hapd->conf->venue_name) { 314 u8 *len; 315 unsigned int i; 316 len = gas_anqp_add_element(buf, ANQP_VENUE_NAME); 317 wpabuf_put_u8(buf, hapd->conf->venue_group); 318 wpabuf_put_u8(buf, hapd->conf->venue_type); 319 for (i = 0; i < hapd->conf->venue_name_count; i++) { 320 struct hostapd_lang_string *vn; 321 vn = &hapd->conf->venue_name[i]; 322 wpabuf_put_u8(buf, 3 + vn->name_len); 323 wpabuf_put_data(buf, vn->lang, 3); 324 wpabuf_put_data(buf, vn->name, vn->name_len); 325 } 326 gas_anqp_set_element_len(buf, len); 327 } 328} 329 330 331static void anqp_add_network_auth_type(struct hostapd_data *hapd, 332 struct wpabuf *buf) 333{ 334 if (anqp_add_override(hapd, buf, ANQP_NETWORK_AUTH_TYPE)) 335 return; 336 337 if (hapd->conf->network_auth_type) { 338 wpabuf_put_le16(buf, ANQP_NETWORK_AUTH_TYPE); 339 wpabuf_put_le16(buf, hapd->conf->network_auth_type_len); 340 wpabuf_put_data(buf, hapd->conf->network_auth_type, 341 hapd->conf->network_auth_type_len); 342 } 343} 344 345 346static void anqp_add_roaming_consortium(struct hostapd_data *hapd, 347 struct wpabuf *buf) 348{ 349 unsigned int i; 350 u8 *len; 351 352 if (anqp_add_override(hapd, buf, ANQP_ROAMING_CONSORTIUM)) 353 return; 354 355 len = gas_anqp_add_element(buf, ANQP_ROAMING_CONSORTIUM); 356 for (i = 0; i < hapd->conf->roaming_consortium_count; i++) { 357 struct hostapd_roaming_consortium *rc; 358 rc = &hapd->conf->roaming_consortium[i]; 359 wpabuf_put_u8(buf, rc->len); 360 wpabuf_put_data(buf, rc->oi, rc->len); 361 } 362 gas_anqp_set_element_len(buf, len); 363} 364 365 366static void anqp_add_ip_addr_type_availability(struct hostapd_data *hapd, 367 struct wpabuf *buf) 368{ 369 if (anqp_add_override(hapd, buf, ANQP_IP_ADDR_TYPE_AVAILABILITY)) 370 return; 371 372 if (hapd->conf->ipaddr_type_configured) { 373 wpabuf_put_le16(buf, ANQP_IP_ADDR_TYPE_AVAILABILITY); 374 wpabuf_put_le16(buf, 1); 375 wpabuf_put_u8(buf, hapd->conf->ipaddr_type_availability); 376 } 377} 378 379 380static void anqp_add_nai_realm_eap(struct wpabuf *buf, 381 struct hostapd_nai_realm_data *realm) 382{ 383 unsigned int i, j; 384 385 wpabuf_put_u8(buf, realm->eap_method_count); 386 387 for (i = 0; i < realm->eap_method_count; i++) { 388 struct hostapd_nai_realm_eap *eap = &realm->eap_method[i]; 389 wpabuf_put_u8(buf, 2 + (3 * eap->num_auths)); 390 wpabuf_put_u8(buf, eap->eap_method); 391 wpabuf_put_u8(buf, eap->num_auths); 392 for (j = 0; j < eap->num_auths; j++) { 393 wpabuf_put_u8(buf, eap->auth_id[j]); 394 wpabuf_put_u8(buf, 1); 395 wpabuf_put_u8(buf, eap->auth_val[j]); 396 } 397 } 398} 399 400 401static void anqp_add_nai_realm_data(struct wpabuf *buf, 402 struct hostapd_nai_realm_data *realm, 403 unsigned int realm_idx) 404{ 405 u8 *realm_data_len; 406 407 wpa_printf(MSG_DEBUG, "realm=%s, len=%d", realm->realm[realm_idx], 408 (int) os_strlen(realm->realm[realm_idx])); 409 realm_data_len = wpabuf_put(buf, 2); 410 wpabuf_put_u8(buf, realm->encoding); 411 wpabuf_put_u8(buf, os_strlen(realm->realm[realm_idx])); 412 wpabuf_put_str(buf, realm->realm[realm_idx]); 413 anqp_add_nai_realm_eap(buf, realm); 414 gas_anqp_set_element_len(buf, realm_data_len); 415} 416 417 418static int hs20_add_nai_home_realm_matches(struct hostapd_data *hapd, 419 struct wpabuf *buf, 420 const u8 *home_realm, 421 size_t home_realm_len) 422{ 423 unsigned int i, j, k; 424 u8 num_realms, num_matching = 0, encoding, realm_len, *realm_list_len; 425 struct hostapd_nai_realm_data *realm; 426 const u8 *pos, *realm_name, *end; 427 struct { 428 unsigned int realm_data_idx; 429 unsigned int realm_idx; 430 } matches[10]; 431 432 pos = home_realm; 433 end = pos + home_realm_len; 434 if (end - pos < 1) { 435 wpa_hexdump(MSG_DEBUG, "Too short NAI Home Realm Query", 436 home_realm, home_realm_len); 437 return -1; 438 } 439 num_realms = *pos++; 440 441 for (i = 0; i < num_realms && num_matching < 10; i++) { 442 if (end - pos < 2) { 443 wpa_hexdump(MSG_DEBUG, 444 "Truncated NAI Home Realm Query", 445 home_realm, home_realm_len); 446 return -1; 447 } 448 encoding = *pos++; 449 realm_len = *pos++; 450 if (realm_len > end - pos) { 451 wpa_hexdump(MSG_DEBUG, 452 "Truncated NAI Home Realm Query", 453 home_realm, home_realm_len); 454 return -1; 455 } 456 realm_name = pos; 457 for (j = 0; j < hapd->conf->nai_realm_count && 458 num_matching < 10; j++) { 459 const u8 *rpos, *rend; 460 realm = &hapd->conf->nai_realm_data[j]; 461 if (encoding != realm->encoding) 462 continue; 463 464 rpos = realm_name; 465 while (rpos < realm_name + realm_len && 466 num_matching < 10) { 467 for (rend = rpos; 468 rend < realm_name + realm_len; rend++) { 469 if (*rend == ';') 470 break; 471 } 472 for (k = 0; k < MAX_NAI_REALMS && 473 realm->realm[k] && 474 num_matching < 10; k++) { 475 if ((int) os_strlen(realm->realm[k]) != 476 rend - rpos || 477 os_strncmp((char *) rpos, 478 realm->realm[k], 479 rend - rpos) != 0) 480 continue; 481 matches[num_matching].realm_data_idx = 482 j; 483 matches[num_matching].realm_idx = k; 484 num_matching++; 485 } 486 rpos = rend + 1; 487 } 488 } 489 pos += realm_len; 490 } 491 492 realm_list_len = gas_anqp_add_element(buf, ANQP_NAI_REALM); 493 wpabuf_put_le16(buf, num_matching); 494 495 /* 496 * There are two ways to format. 1. each realm in a NAI Realm Data unit 497 * 2. all realms that share the same EAP methods in a NAI Realm Data 498 * unit. The first format is likely to be bigger in size than the 499 * second, but may be easier to parse and process by the receiver. 500 */ 501 for (i = 0; i < num_matching; i++) { 502 wpa_printf(MSG_DEBUG, "realm_idx %d, realm_data_idx %d", 503 matches[i].realm_data_idx, matches[i].realm_idx); 504 realm = &hapd->conf->nai_realm_data[matches[i].realm_data_idx]; 505 anqp_add_nai_realm_data(buf, realm, matches[i].realm_idx); 506 } 507 gas_anqp_set_element_len(buf, realm_list_len); 508 return 0; 509} 510 511 512static void anqp_add_nai_realm(struct hostapd_data *hapd, struct wpabuf *buf, 513 const u8 *home_realm, size_t home_realm_len, 514 int nai_realm, int nai_home_realm) 515{ 516 if (nai_realm && !nai_home_realm && 517 anqp_add_override(hapd, buf, ANQP_NAI_REALM)) 518 return; 519 520 if (nai_realm && hapd->conf->nai_realm_data) { 521 u8 *len; 522 unsigned int i, j; 523 len = gas_anqp_add_element(buf, ANQP_NAI_REALM); 524 wpabuf_put_le16(buf, hapd->conf->nai_realm_count); 525 for (i = 0; i < hapd->conf->nai_realm_count; i++) { 526 u8 *realm_data_len, *realm_len; 527 struct hostapd_nai_realm_data *realm; 528 529 realm = &hapd->conf->nai_realm_data[i]; 530 realm_data_len = wpabuf_put(buf, 2); 531 wpabuf_put_u8(buf, realm->encoding); 532 realm_len = wpabuf_put(buf, 1); 533 for (j = 0; realm->realm[j]; j++) { 534 if (j > 0) 535 wpabuf_put_u8(buf, ';'); 536 wpabuf_put_str(buf, realm->realm[j]); 537 } 538 *realm_len = (u8 *) wpabuf_put(buf, 0) - realm_len - 1; 539 anqp_add_nai_realm_eap(buf, realm); 540 gas_anqp_set_element_len(buf, realm_data_len); 541 } 542 gas_anqp_set_element_len(buf, len); 543 } else if (nai_home_realm && hapd->conf->nai_realm_data && home_realm) { 544 hs20_add_nai_home_realm_matches(hapd, buf, home_realm, 545 home_realm_len); 546 } 547} 548 549 550static void anqp_add_3gpp_cellular_network(struct hostapd_data *hapd, 551 struct wpabuf *buf) 552{ 553 if (anqp_add_override(hapd, buf, ANQP_3GPP_CELLULAR_NETWORK)) 554 return; 555 556 if (hapd->conf->anqp_3gpp_cell_net) { 557 wpabuf_put_le16(buf, ANQP_3GPP_CELLULAR_NETWORK); 558 wpabuf_put_le16(buf, 559 hapd->conf->anqp_3gpp_cell_net_len); 560 wpabuf_put_data(buf, hapd->conf->anqp_3gpp_cell_net, 561 hapd->conf->anqp_3gpp_cell_net_len); 562 } 563} 564 565 566static void anqp_add_domain_name(struct hostapd_data *hapd, struct wpabuf *buf) 567{ 568 if (anqp_add_override(hapd, buf, ANQP_DOMAIN_NAME)) 569 return; 570 571 if (hapd->conf->domain_name) { 572 wpabuf_put_le16(buf, ANQP_DOMAIN_NAME); 573 wpabuf_put_le16(buf, hapd->conf->domain_name_len); 574 wpabuf_put_data(buf, hapd->conf->domain_name, 575 hapd->conf->domain_name_len); 576 } 577} 578 579 580#ifdef CONFIG_FILS 581static void anqp_add_fils_realm_info(struct hostapd_data *hapd, 582 struct wpabuf *buf) 583{ 584 size_t count; 585 586 if (anqp_add_override(hapd, buf, ANQP_FILS_REALM_INFO)) 587 return; 588 589 count = dl_list_len(&hapd->conf->fils_realms); 590 if (count > 10000) 591 count = 10000; 592 if (count) { 593 struct fils_realm *realm; 594 595 wpabuf_put_le16(buf, ANQP_FILS_REALM_INFO); 596 wpabuf_put_le16(buf, 2 * count); 597 598 dl_list_for_each(realm, &hapd->conf->fils_realms, 599 struct fils_realm, list) { 600 if (count == 0) 601 break; 602 wpabuf_put_data(buf, realm->hash, 2); 603 count--; 604 } 605 } 606} 607#endif /* CONFIG_FILS */ 608 609 610#ifdef CONFIG_HS20 611 612static void anqp_add_operator_friendly_name(struct hostapd_data *hapd, 613 struct wpabuf *buf) 614{ 615 if (hapd->conf->hs20_oper_friendly_name) { 616 u8 *len; 617 unsigned int i; 618 len = gas_anqp_add_element(buf, ANQP_VENDOR_SPECIFIC); 619 wpabuf_put_be24(buf, OUI_WFA); 620 wpabuf_put_u8(buf, HS20_ANQP_OUI_TYPE); 621 wpabuf_put_u8(buf, HS20_STYPE_OPERATOR_FRIENDLY_NAME); 622 wpabuf_put_u8(buf, 0); /* Reserved */ 623 for (i = 0; i < hapd->conf->hs20_oper_friendly_name_count; i++) 624 { 625 struct hostapd_lang_string *vn; 626 vn = &hapd->conf->hs20_oper_friendly_name[i]; 627 wpabuf_put_u8(buf, 3 + vn->name_len); 628 wpabuf_put_data(buf, vn->lang, 3); 629 wpabuf_put_data(buf, vn->name, vn->name_len); 630 } 631 gas_anqp_set_element_len(buf, len); 632 } 633} 634 635 636static void anqp_add_wan_metrics(struct hostapd_data *hapd, 637 struct wpabuf *buf) 638{ 639 if (hapd->conf->hs20_wan_metrics) { 640 u8 *len = gas_anqp_add_element(buf, ANQP_VENDOR_SPECIFIC); 641 wpabuf_put_be24(buf, OUI_WFA); 642 wpabuf_put_u8(buf, HS20_ANQP_OUI_TYPE); 643 wpabuf_put_u8(buf, HS20_STYPE_WAN_METRICS); 644 wpabuf_put_u8(buf, 0); /* Reserved */ 645 wpabuf_put_data(buf, hapd->conf->hs20_wan_metrics, 13); 646 gas_anqp_set_element_len(buf, len); 647 } 648} 649 650 651static void anqp_add_connection_capability(struct hostapd_data *hapd, 652 struct wpabuf *buf) 653{ 654 if (hapd->conf->hs20_connection_capability) { 655 u8 *len = gas_anqp_add_element(buf, ANQP_VENDOR_SPECIFIC); 656 wpabuf_put_be24(buf, OUI_WFA); 657 wpabuf_put_u8(buf, HS20_ANQP_OUI_TYPE); 658 wpabuf_put_u8(buf, HS20_STYPE_CONNECTION_CAPABILITY); 659 wpabuf_put_u8(buf, 0); /* Reserved */ 660 wpabuf_put_data(buf, hapd->conf->hs20_connection_capability, 661 hapd->conf->hs20_connection_capability_len); 662 gas_anqp_set_element_len(buf, len); 663 } 664} 665 666 667static void anqp_add_operating_class(struct hostapd_data *hapd, 668 struct wpabuf *buf) 669{ 670 if (hapd->conf->hs20_operating_class) { 671 u8 *len = gas_anqp_add_element(buf, ANQP_VENDOR_SPECIFIC); 672 wpabuf_put_be24(buf, OUI_WFA); 673 wpabuf_put_u8(buf, HS20_ANQP_OUI_TYPE); 674 wpabuf_put_u8(buf, HS20_STYPE_OPERATING_CLASS); 675 wpabuf_put_u8(buf, 0); /* Reserved */ 676 wpabuf_put_data(buf, hapd->conf->hs20_operating_class, 677 hapd->conf->hs20_operating_class_len); 678 gas_anqp_set_element_len(buf, len); 679 } 680} 681 682 683static void anqp_add_osu_provider(struct wpabuf *buf, 684 struct hostapd_bss_config *bss, 685 struct hs20_osu_provider *p) 686{ 687 u8 *len, *len2, *count; 688 unsigned int i; 689 690 len = wpabuf_put(buf, 2); /* OSU Provider Length to be filled */ 691 692 /* OSU Friendly Name Duples */ 693 len2 = wpabuf_put(buf, 2); 694 for (i = 0; i < p->friendly_name_count; i++) { 695 struct hostapd_lang_string *s = &p->friendly_name[i]; 696 wpabuf_put_u8(buf, 3 + s->name_len); 697 wpabuf_put_data(buf, s->lang, 3); 698 wpabuf_put_data(buf, s->name, s->name_len); 699 } 700 WPA_PUT_LE16(len2, (u8 *) wpabuf_put(buf, 0) - len2 - 2); 701 702 /* OSU Server URI */ 703 if (p->server_uri) { 704 wpabuf_put_u8(buf, os_strlen(p->server_uri)); 705 wpabuf_put_str(buf, p->server_uri); 706 } else 707 wpabuf_put_u8(buf, 0); 708 709 /* OSU Method List */ 710 count = wpabuf_put(buf, 1); 711 for (i = 0; p->method_list && p->method_list[i] >= 0; i++) 712 wpabuf_put_u8(buf, p->method_list[i]); 713 *count = i; 714 715 /* Icons Available */ 716 len2 = wpabuf_put(buf, 2); 717 for (i = 0; i < p->icons_count; i++) { 718 size_t j; 719 struct hs20_icon *icon = NULL; 720 721 for (j = 0; j < bss->hs20_icons_count && !icon; j++) { 722 if (os_strcmp(p->icons[i], bss->hs20_icons[j].name) == 723 0) 724 icon = &bss->hs20_icons[j]; 725 } 726 if (!icon) 727 continue; /* icon info not found */ 728 729 wpabuf_put_le16(buf, icon->width); 730 wpabuf_put_le16(buf, icon->height); 731 wpabuf_put_data(buf, icon->language, 3); 732 wpabuf_put_u8(buf, os_strlen(icon->type)); 733 wpabuf_put_str(buf, icon->type); 734 wpabuf_put_u8(buf, os_strlen(icon->name)); 735 wpabuf_put_str(buf, icon->name); 736 } 737 WPA_PUT_LE16(len2, (u8 *) wpabuf_put(buf, 0) - len2 - 2); 738 739 /* OSU_NAI */ 740 if (p->osu_nai) { 741 wpabuf_put_u8(buf, os_strlen(p->osu_nai)); 742 wpabuf_put_str(buf, p->osu_nai); 743 } else 744 wpabuf_put_u8(buf, 0); 745 746 /* OSU Service Description Duples */ 747 len2 = wpabuf_put(buf, 2); 748 for (i = 0; i < p->service_desc_count; i++) { 749 struct hostapd_lang_string *s = &p->service_desc[i]; 750 wpabuf_put_u8(buf, 3 + s->name_len); 751 wpabuf_put_data(buf, s->lang, 3); 752 wpabuf_put_data(buf, s->name, s->name_len); 753 } 754 WPA_PUT_LE16(len2, (u8 *) wpabuf_put(buf, 0) - len2 - 2); 755 756 WPA_PUT_LE16(len, (u8 *) wpabuf_put(buf, 0) - len - 2); 757} 758 759 760static void anqp_add_osu_providers_list(struct hostapd_data *hapd, 761 struct wpabuf *buf) 762{ 763 if (hapd->conf->hs20_osu_providers_count) { 764 size_t i; 765 u8 *len = gas_anqp_add_element(buf, ANQP_VENDOR_SPECIFIC); 766 wpabuf_put_be24(buf, OUI_WFA); 767 wpabuf_put_u8(buf, HS20_ANQP_OUI_TYPE); 768 wpabuf_put_u8(buf, HS20_STYPE_OSU_PROVIDERS_LIST); 769 wpabuf_put_u8(buf, 0); /* Reserved */ 770 771 /* OSU SSID */ 772 wpabuf_put_u8(buf, hapd->conf->osu_ssid_len); 773 wpabuf_put_data(buf, hapd->conf->osu_ssid, 774 hapd->conf->osu_ssid_len); 775 776 /* Number of OSU Providers */ 777 wpabuf_put_u8(buf, hapd->conf->hs20_osu_providers_count); 778 779 for (i = 0; i < hapd->conf->hs20_osu_providers_count; i++) { 780 anqp_add_osu_provider( 781 buf, hapd->conf, 782 &hapd->conf->hs20_osu_providers[i]); 783 } 784 785 gas_anqp_set_element_len(buf, len); 786 } 787} 788 789 790static void anqp_add_icon_binary_file(struct hostapd_data *hapd, 791 struct wpabuf *buf, 792 const u8 *name, size_t name_len) 793{ 794 struct hs20_icon *icon; 795 size_t i; 796 u8 *len; 797 798 wpa_hexdump_ascii(MSG_DEBUG, "HS 2.0: Requested Icon Filename", 799 name, name_len); 800 for (i = 0; i < hapd->conf->hs20_icons_count; i++) { 801 icon = &hapd->conf->hs20_icons[i]; 802 if (name_len == os_strlen(icon->name) && 803 os_memcmp(name, icon->name, name_len) == 0) 804 break; 805 } 806 807 if (i < hapd->conf->hs20_icons_count) 808 icon = &hapd->conf->hs20_icons[i]; 809 else 810 icon = NULL; 811 812 len = gas_anqp_add_element(buf, ANQP_VENDOR_SPECIFIC); 813 wpabuf_put_be24(buf, OUI_WFA); 814 wpabuf_put_u8(buf, HS20_ANQP_OUI_TYPE); 815 wpabuf_put_u8(buf, HS20_STYPE_ICON_BINARY_FILE); 816 wpabuf_put_u8(buf, 0); /* Reserved */ 817 818 if (icon) { 819 char *data; 820 size_t data_len; 821 822 data = os_readfile(icon->file, &data_len); 823 if (data == NULL || data_len > 65535) { 824 wpabuf_put_u8(buf, 2); /* Download Status: 825 * Unspecified file error */ 826 wpabuf_put_u8(buf, 0); 827 wpabuf_put_le16(buf, 0); 828 } else { 829 wpabuf_put_u8(buf, 0); /* Download Status: Success */ 830 wpabuf_put_u8(buf, os_strlen(icon->type)); 831 wpabuf_put_str(buf, icon->type); 832 wpabuf_put_le16(buf, data_len); 833 wpabuf_put_data(buf, data, data_len); 834 } 835 os_free(data); 836 } else { 837 wpabuf_put_u8(buf, 1); /* Download Status: File not found */ 838 wpabuf_put_u8(buf, 0); 839 wpabuf_put_le16(buf, 0); 840 } 841 842 gas_anqp_set_element_len(buf, len); 843} 844 845#endif /* CONFIG_HS20 */ 846 847 848#ifdef CONFIG_MBO 849static void anqp_add_mbo_cell_data_conn_pref(struct hostapd_data *hapd, 850 struct wpabuf *buf) 851{ 852 if (hapd->conf->mbo_cell_data_conn_pref >= 0) { 853 u8 *len = gas_anqp_add_element(buf, ANQP_VENDOR_SPECIFIC); 854 wpabuf_put_be24(buf, OUI_WFA); 855 wpabuf_put_u8(buf, MBO_ANQP_OUI_TYPE); 856 wpabuf_put_u8(buf, MBO_ANQP_SUBTYPE_CELL_CONN_PREF); 857 wpabuf_put_u8(buf, hapd->conf->mbo_cell_data_conn_pref); 858 gas_anqp_set_element_len(buf, len); 859 } 860} 861#endif /* CONFIG_MBO */ 862 863 864static size_t anqp_get_required_len(struct hostapd_data *hapd, 865 const u16 *infoid, 866 unsigned int num_infoid) 867{ 868 size_t len = 0; 869 unsigned int i; 870 871 for (i = 0; i < num_infoid; i++) { 872 struct anqp_element *elem = get_anqp_elem(hapd, infoid[i]); 873 874 if (elem) 875 len += 2 + 2 + wpabuf_len(elem->payload); 876 } 877 878 return len; 879} 880 881 882static struct wpabuf * 883gas_serv_build_gas_resp_payload(struct hostapd_data *hapd, 884 unsigned int request, 885 const u8 *home_realm, size_t home_realm_len, 886 const u8 *icon_name, size_t icon_name_len, 887 const u16 *extra_req, 888 unsigned int num_extra_req) 889{ 890 struct wpabuf *buf; 891 size_t len; 892 unsigned int i; 893 894 len = 1400; 895 if (request & (ANQP_REQ_NAI_REALM | ANQP_REQ_NAI_HOME_REALM)) 896 len += 1000; 897 if (request & ANQP_REQ_ICON_REQUEST) 898 len += 65536; 899#ifdef CONFIG_FILS 900 if (request & ANQP_FILS_REALM_INFO) 901 len += 2 * dl_list_len(&hapd->conf->fils_realms); 902#endif /* CONFIG_FILS */ 903 len += anqp_get_required_len(hapd, extra_req, num_extra_req); 904 905 buf = wpabuf_alloc(len); 906 if (buf == NULL) 907 return NULL; 908 909 if (request & ANQP_REQ_CAPABILITY_LIST) 910 anqp_add_capab_list(hapd, buf); 911 if (request & ANQP_REQ_VENUE_NAME) 912 anqp_add_venue_name(hapd, buf); 913 if (request & ANQP_REQ_EMERGENCY_CALL_NUMBER) 914 anqp_add_elem(hapd, buf, ANQP_EMERGENCY_CALL_NUMBER); 915 if (request & ANQP_REQ_NETWORK_AUTH_TYPE) 916 anqp_add_network_auth_type(hapd, buf); 917 if (request & ANQP_REQ_ROAMING_CONSORTIUM) 918 anqp_add_roaming_consortium(hapd, buf); 919 if (request & ANQP_REQ_IP_ADDR_TYPE_AVAILABILITY) 920 anqp_add_ip_addr_type_availability(hapd, buf); 921 if (request & (ANQP_REQ_NAI_REALM | ANQP_REQ_NAI_HOME_REALM)) 922 anqp_add_nai_realm(hapd, buf, home_realm, home_realm_len, 923 request & ANQP_REQ_NAI_REALM, 924 request & ANQP_REQ_NAI_HOME_REALM); 925 if (request & ANQP_REQ_3GPP_CELLULAR_NETWORK) 926 anqp_add_3gpp_cellular_network(hapd, buf); 927 if (request & ANQP_REQ_AP_GEOSPATIAL_LOCATION) 928 anqp_add_elem(hapd, buf, ANQP_AP_GEOSPATIAL_LOCATION); 929 if (request & ANQP_REQ_AP_CIVIC_LOCATION) 930 anqp_add_elem(hapd, buf, ANQP_AP_CIVIC_LOCATION); 931 if (request & ANQP_REQ_AP_LOCATION_PUBLIC_URI) 932 anqp_add_elem(hapd, buf, ANQP_AP_LOCATION_PUBLIC_URI); 933 if (request & ANQP_REQ_DOMAIN_NAME) 934 anqp_add_domain_name(hapd, buf); 935 if (request & ANQP_REQ_EMERGENCY_ALERT_URI) 936 anqp_add_elem(hapd, buf, ANQP_EMERGENCY_ALERT_URI); 937 if (request & ANQP_REQ_TDLS_CAPABILITY) 938 anqp_add_elem(hapd, buf, ANQP_TDLS_CAPABILITY); 939 if (request & ANQP_REQ_EMERGENCY_NAI) 940 anqp_add_elem(hapd, buf, ANQP_EMERGENCY_NAI); 941 942 for (i = 0; i < num_extra_req; i++) { 943#ifdef CONFIG_FILS 944 if (extra_req[i] == ANQP_FILS_REALM_INFO) { 945 anqp_add_fils_realm_info(hapd, buf); 946 continue; 947 } 948#endif /* CONFIG_FILS */ 949 anqp_add_elem(hapd, buf, extra_req[i]); 950 } 951 952#ifdef CONFIG_HS20 953 if (request & ANQP_REQ_HS_CAPABILITY_LIST) 954 anqp_add_hs_capab_list(hapd, buf); 955 if (request & ANQP_REQ_OPERATOR_FRIENDLY_NAME) 956 anqp_add_operator_friendly_name(hapd, buf); 957 if (request & ANQP_REQ_WAN_METRICS) 958 anqp_add_wan_metrics(hapd, buf); 959 if (request & ANQP_REQ_CONNECTION_CAPABILITY) 960 anqp_add_connection_capability(hapd, buf); 961 if (request & ANQP_REQ_OPERATING_CLASS) 962 anqp_add_operating_class(hapd, buf); 963 if (request & ANQP_REQ_OSU_PROVIDERS_LIST) 964 anqp_add_osu_providers_list(hapd, buf); 965 if (request & ANQP_REQ_ICON_REQUEST) 966 anqp_add_icon_binary_file(hapd, buf, icon_name, icon_name_len); 967#endif /* CONFIG_HS20 */ 968 969#ifdef CONFIG_MBO 970 if (request & ANQP_REQ_MBO_CELL_DATA_CONN_PREF) 971 anqp_add_mbo_cell_data_conn_pref(hapd, buf); 972#endif /* CONFIG_MBO */ 973 974 return buf; 975} 976 977 978#define ANQP_MAX_EXTRA_REQ 20 979 980struct anqp_query_info { 981 unsigned int request; 982 const u8 *home_realm_query; 983 size_t home_realm_query_len; 984 const u8 *icon_name; 985 size_t icon_name_len; 986 int p2p_sd; 987 u16 extra_req[ANQP_MAX_EXTRA_REQ]; 988 unsigned int num_extra_req; 989}; 990 991 992static void set_anqp_req(unsigned int bit, const char *name, int local, 993 struct anqp_query_info *qi) 994{ 995 qi->request |= bit; 996 if (local) { 997 wpa_printf(MSG_DEBUG, "ANQP: %s (local)", name); 998 } else { 999 wpa_printf(MSG_DEBUG, "ANQP: %s not available", name); 1000 } 1001} 1002 1003 1004static void rx_anqp_query_list_id(struct hostapd_data *hapd, u16 info_id, 1005 struct anqp_query_info *qi) 1006{ 1007 switch (info_id) { 1008 case ANQP_CAPABILITY_LIST: 1009 set_anqp_req(ANQP_REQ_CAPABILITY_LIST, "Capability List", 1, 1010 qi); 1011 break; 1012 case ANQP_VENUE_NAME: 1013 set_anqp_req(ANQP_REQ_VENUE_NAME, "Venue Name", 1014 hapd->conf->venue_name != NULL, qi); 1015 break; 1016 case ANQP_EMERGENCY_CALL_NUMBER: 1017 set_anqp_req(ANQP_REQ_EMERGENCY_CALL_NUMBER, 1018 "Emergency Call Number", 1019 get_anqp_elem(hapd, info_id) != NULL, qi); 1020 break; 1021 case ANQP_NETWORK_AUTH_TYPE: 1022 set_anqp_req(ANQP_REQ_NETWORK_AUTH_TYPE, "Network Auth Type", 1023 hapd->conf->network_auth_type != NULL, qi); 1024 break; 1025 case ANQP_ROAMING_CONSORTIUM: 1026 set_anqp_req(ANQP_REQ_ROAMING_CONSORTIUM, "Roaming Consortium", 1027 hapd->conf->roaming_consortium != NULL, qi); 1028 break; 1029 case ANQP_IP_ADDR_TYPE_AVAILABILITY: 1030 set_anqp_req(ANQP_REQ_IP_ADDR_TYPE_AVAILABILITY, 1031 "IP Addr Type Availability", 1032 hapd->conf->ipaddr_type_configured, qi); 1033 break; 1034 case ANQP_NAI_REALM: 1035 set_anqp_req(ANQP_REQ_NAI_REALM, "NAI Realm", 1036 hapd->conf->nai_realm_data != NULL, qi); 1037 break; 1038 case ANQP_3GPP_CELLULAR_NETWORK: 1039 set_anqp_req(ANQP_REQ_3GPP_CELLULAR_NETWORK, 1040 "3GPP Cellular Network", 1041 hapd->conf->anqp_3gpp_cell_net != NULL, qi); 1042 break; 1043 case ANQP_AP_GEOSPATIAL_LOCATION: 1044 set_anqp_req(ANQP_REQ_AP_GEOSPATIAL_LOCATION, 1045 "AP Geospatial Location", 1046 get_anqp_elem(hapd, info_id) != NULL, qi); 1047 break; 1048 case ANQP_AP_CIVIC_LOCATION: 1049 set_anqp_req(ANQP_REQ_AP_CIVIC_LOCATION, 1050 "AP Civic Location", 1051 get_anqp_elem(hapd, info_id) != NULL, qi); 1052 break; 1053 case ANQP_AP_LOCATION_PUBLIC_URI: 1054 set_anqp_req(ANQP_REQ_AP_LOCATION_PUBLIC_URI, 1055 "AP Location Public URI", 1056 get_anqp_elem(hapd, info_id) != NULL, qi); 1057 break; 1058 case ANQP_DOMAIN_NAME: 1059 set_anqp_req(ANQP_REQ_DOMAIN_NAME, "Domain Name", 1060 hapd->conf->domain_name != NULL, qi); 1061 break; 1062 case ANQP_EMERGENCY_ALERT_URI: 1063 set_anqp_req(ANQP_REQ_EMERGENCY_ALERT_URI, 1064 "Emergency Alert URI", 1065 get_anqp_elem(hapd, info_id) != NULL, qi); 1066 break; 1067 case ANQP_TDLS_CAPABILITY: 1068 set_anqp_req(ANQP_REQ_TDLS_CAPABILITY, 1069 "TDLS Capability", 1070 get_anqp_elem(hapd, info_id) != NULL, qi); 1071 break; 1072 case ANQP_EMERGENCY_NAI: 1073 set_anqp_req(ANQP_REQ_EMERGENCY_NAI, 1074 "Emergency NAI", 1075 get_anqp_elem(hapd, info_id) != NULL, qi); 1076 break; 1077 default: 1078#ifdef CONFIG_FILS 1079 if (info_id == ANQP_FILS_REALM_INFO && 1080 !dl_list_empty(&hapd->conf->fils_realms)) { 1081 wpa_printf(MSG_DEBUG, 1082 "ANQP: FILS Realm Information (local)"); 1083 } else 1084#endif /* CONFIG_FILS */ 1085 if (!get_anqp_elem(hapd, info_id)) { 1086 wpa_printf(MSG_DEBUG, "ANQP: Unsupported Info Id %u", 1087 info_id); 1088 break; 1089 } 1090 if (qi->num_extra_req == ANQP_MAX_EXTRA_REQ) { 1091 wpa_printf(MSG_DEBUG, 1092 "ANQP: No more room for extra requests - ignore Info Id %u", 1093 info_id); 1094 break; 1095 } 1096 wpa_printf(MSG_DEBUG, "ANQP: Info Id %u (local)", info_id); 1097 qi->extra_req[qi->num_extra_req] = info_id; 1098 qi->num_extra_req++; 1099 break; 1100 } 1101} 1102 1103 1104static void rx_anqp_query_list(struct hostapd_data *hapd, 1105 const u8 *pos, const u8 *end, 1106 struct anqp_query_info *qi) 1107{ 1108 wpa_printf(MSG_DEBUG, "ANQP: %u Info IDs requested in Query list", 1109 (unsigned int) (end - pos) / 2); 1110 1111 while (end - pos >= 2) { 1112 rx_anqp_query_list_id(hapd, WPA_GET_LE16(pos), qi); 1113 pos += 2; 1114 } 1115} 1116 1117 1118#ifdef CONFIG_HS20 1119 1120static void rx_anqp_hs_query_list(struct hostapd_data *hapd, u8 subtype, 1121 struct anqp_query_info *qi) 1122{ 1123 switch (subtype) { 1124 case HS20_STYPE_CAPABILITY_LIST: 1125 set_anqp_req(ANQP_REQ_HS_CAPABILITY_LIST, "HS Capability List", 1126 1, qi); 1127 break; 1128 case HS20_STYPE_OPERATOR_FRIENDLY_NAME: 1129 set_anqp_req(ANQP_REQ_OPERATOR_FRIENDLY_NAME, 1130 "Operator Friendly Name", 1131 hapd->conf->hs20_oper_friendly_name != NULL, qi); 1132 break; 1133 case HS20_STYPE_WAN_METRICS: 1134 set_anqp_req(ANQP_REQ_WAN_METRICS, "WAN Metrics", 1135 hapd->conf->hs20_wan_metrics != NULL, qi); 1136 break; 1137 case HS20_STYPE_CONNECTION_CAPABILITY: 1138 set_anqp_req(ANQP_REQ_CONNECTION_CAPABILITY, 1139 "Connection Capability", 1140 hapd->conf->hs20_connection_capability != NULL, 1141 qi); 1142 break; 1143 case HS20_STYPE_OPERATING_CLASS: 1144 set_anqp_req(ANQP_REQ_OPERATING_CLASS, "Operating Class", 1145 hapd->conf->hs20_operating_class != NULL, qi); 1146 break; 1147 case HS20_STYPE_OSU_PROVIDERS_LIST: 1148 set_anqp_req(ANQP_REQ_OSU_PROVIDERS_LIST, "OSU Providers list", 1149 hapd->conf->hs20_osu_providers_count, qi); 1150 break; 1151 default: 1152 wpa_printf(MSG_DEBUG, "ANQP: Unsupported HS 2.0 subtype %u", 1153 subtype); 1154 break; 1155 } 1156} 1157 1158 1159static void rx_anqp_hs_nai_home_realm(struct hostapd_data *hapd, 1160 const u8 *pos, const u8 *end, 1161 struct anqp_query_info *qi) 1162{ 1163 qi->request |= ANQP_REQ_NAI_HOME_REALM; 1164 qi->home_realm_query = pos; 1165 qi->home_realm_query_len = end - pos; 1166 if (hapd->conf->nai_realm_data != NULL) { 1167 wpa_printf(MSG_DEBUG, "ANQP: HS 2.0 NAI Home Realm Query " 1168 "(local)"); 1169 } else { 1170 wpa_printf(MSG_DEBUG, "ANQP: HS 2.0 NAI Home Realm Query not " 1171 "available"); 1172 } 1173} 1174 1175 1176static void rx_anqp_hs_icon_request(struct hostapd_data *hapd, 1177 const u8 *pos, const u8 *end, 1178 struct anqp_query_info *qi) 1179{ 1180 qi->request |= ANQP_REQ_ICON_REQUEST; 1181 qi->icon_name = pos; 1182 qi->icon_name_len = end - pos; 1183 if (hapd->conf->hs20_icons_count) { 1184 wpa_printf(MSG_DEBUG, "ANQP: HS 2.0 Icon Request Query " 1185 "(local)"); 1186 } else { 1187 wpa_printf(MSG_DEBUG, "ANQP: HS 2.0 Icon Request Query not " 1188 "available"); 1189 } 1190} 1191 1192 1193static void rx_anqp_vendor_specific_hs20(struct hostapd_data *hapd, 1194 const u8 *pos, const u8 *end, 1195 struct anqp_query_info *qi) 1196{ 1197 u8 subtype; 1198 1199 if (end - pos <= 1) 1200 return; 1201 1202 subtype = *pos++; 1203 pos++; /* Reserved */ 1204 switch (subtype) { 1205 case HS20_STYPE_QUERY_LIST: 1206 wpa_printf(MSG_DEBUG, "ANQP: HS 2.0 Query List"); 1207 while (pos < end) { 1208 rx_anqp_hs_query_list(hapd, *pos, qi); 1209 pos++; 1210 } 1211 break; 1212 case HS20_STYPE_NAI_HOME_REALM_QUERY: 1213 rx_anqp_hs_nai_home_realm(hapd, pos, end, qi); 1214 break; 1215 case HS20_STYPE_ICON_REQUEST: 1216 rx_anqp_hs_icon_request(hapd, pos, end, qi); 1217 break; 1218 default: 1219 wpa_printf(MSG_DEBUG, "ANQP: Unsupported HS 2.0 query subtype " 1220 "%u", subtype); 1221 break; 1222 } 1223} 1224 1225#endif /* CONFIG_HS20 */ 1226 1227 1228#ifdef CONFIG_P2P 1229static void rx_anqp_vendor_specific_p2p(struct hostapd_data *hapd, 1230 struct anqp_query_info *qi) 1231{ 1232 /* 1233 * This is for P2P SD and will be taken care of by the P2P 1234 * implementation. This query needs to be ignored in the generic 1235 * GAS server to avoid duplicated response. 1236 */ 1237 wpa_printf(MSG_DEBUG, 1238 "ANQP: Ignore WFA vendor type %u (P2P SD) in generic GAS server", 1239 P2P_OUI_TYPE); 1240 qi->p2p_sd = 1; 1241 return; 1242} 1243#endif /* CONFIG_P2P */ 1244 1245 1246#ifdef CONFIG_MBO 1247 1248static void rx_anqp_mbo_query_list(struct hostapd_data *hapd, u8 subtype, 1249 struct anqp_query_info *qi) 1250{ 1251 switch (subtype) { 1252 case MBO_ANQP_SUBTYPE_CELL_CONN_PREF: 1253 set_anqp_req(ANQP_REQ_MBO_CELL_DATA_CONN_PREF, 1254 "Cellular Data Connection Preference", 1255 hapd->conf->mbo_cell_data_conn_pref >= 0, qi); 1256 break; 1257 default: 1258 wpa_printf(MSG_DEBUG, "ANQP: Unsupported MBO subtype %u", 1259 subtype); 1260 break; 1261 } 1262} 1263 1264 1265static void rx_anqp_vendor_specific_mbo(struct hostapd_data *hapd, 1266 const u8 *pos, const u8 *end, 1267 struct anqp_query_info *qi) 1268{ 1269 u8 subtype; 1270 1271 if (end - pos < 1) 1272 return; 1273 1274 subtype = *pos++; 1275 switch (subtype) { 1276 case MBO_ANQP_SUBTYPE_QUERY_LIST: 1277 wpa_printf(MSG_DEBUG, "ANQP: MBO Query List"); 1278 while (pos < end) { 1279 rx_anqp_mbo_query_list(hapd, *pos, qi); 1280 pos++; 1281 } 1282 break; 1283 default: 1284 wpa_printf(MSG_DEBUG, "ANQP: Unsupported MBO query subtype %u", 1285 subtype); 1286 break; 1287 } 1288} 1289 1290#endif /* CONFIG_MBO */ 1291 1292 1293static void rx_anqp_vendor_specific(struct hostapd_data *hapd, 1294 const u8 *pos, const u8 *end, 1295 struct anqp_query_info *qi) 1296{ 1297 u32 oui; 1298 1299 if (end - pos < 4) { 1300 wpa_printf(MSG_DEBUG, "ANQP: Too short vendor specific ANQP " 1301 "Query element"); 1302 return; 1303 } 1304 1305 oui = WPA_GET_BE24(pos); 1306 pos += 3; 1307 if (oui != OUI_WFA) { 1308 wpa_printf(MSG_DEBUG, "ANQP: Unsupported vendor OUI %06x", 1309 oui); 1310 return; 1311 } 1312 1313 switch (*pos) { 1314#ifdef CONFIG_P2P 1315 case P2P_OUI_TYPE: 1316 rx_anqp_vendor_specific_p2p(hapd, qi); 1317 break; 1318#endif /* CONFIG_P2P */ 1319#ifdef CONFIG_HS20 1320 case HS20_ANQP_OUI_TYPE: 1321 rx_anqp_vendor_specific_hs20(hapd, pos + 1, end, qi); 1322 break; 1323#endif /* CONFIG_HS20 */ 1324#ifdef CONFIG_MBO 1325 case MBO_ANQP_OUI_TYPE: 1326 rx_anqp_vendor_specific_mbo(hapd, pos + 1, end, qi); 1327 break; 1328#endif /* CONFIG_MBO */ 1329 default: 1330 wpa_printf(MSG_DEBUG, "ANQP: Unsupported WFA vendor type %u", 1331 *pos); 1332 break; 1333 } 1334} 1335 1336 1337static void gas_serv_req_local_processing(struct hostapd_data *hapd, 1338 const u8 *sa, u8 dialog_token, 1339 struct anqp_query_info *qi, int prot, 1340 int std_addr3) 1341{ 1342 struct wpabuf *buf, *tx_buf; 1343 1344 buf = gas_serv_build_gas_resp_payload(hapd, qi->request, 1345 qi->home_realm_query, 1346 qi->home_realm_query_len, 1347 qi->icon_name, qi->icon_name_len, 1348 qi->extra_req, qi->num_extra_req); 1349 wpa_hexdump_buf(MSG_MSGDUMP, "ANQP: Locally generated ANQP responses", 1350 buf); 1351 if (!buf) 1352 return; 1353#ifdef CONFIG_P2P 1354 if (wpabuf_len(buf) == 0 && qi->p2p_sd) { 1355 wpa_printf(MSG_DEBUG, 1356 "ANQP: Do not send response to P2P SD from generic GAS service (P2P SD implementation will process this)"); 1357 wpabuf_free(buf); 1358 return; 1359 } 1360#endif /* CONFIG_P2P */ 1361 1362 if (wpabuf_len(buf) > hapd->conf->gas_frag_limit || 1363 hapd->conf->gas_comeback_delay) { 1364 struct gas_dialog_info *di; 1365 u16 comeback_delay = 1; 1366 1367 if (hapd->conf->gas_comeback_delay) { 1368 /* Testing - allow overriding of the delay value */ 1369 comeback_delay = hapd->conf->gas_comeback_delay; 1370 } 1371 1372 wpa_printf(MSG_DEBUG, "ANQP: Too long response to fit in " 1373 "initial response - use GAS comeback"); 1374 di = gas_dialog_create(hapd, sa, dialog_token); 1375 if (!di) { 1376 wpa_printf(MSG_INFO, "ANQP: Could not create dialog " 1377 "for " MACSTR " (dialog token %u)", 1378 MAC2STR(sa), dialog_token); 1379 wpabuf_free(buf); 1380 tx_buf = gas_anqp_build_initial_resp_buf( 1381 dialog_token, WLAN_STATUS_UNSPECIFIED_FAILURE, 1382 0, NULL); 1383 } else { 1384 di->prot = prot; 1385 di->sd_resp = buf; 1386 di->sd_resp_pos = 0; 1387 tx_buf = gas_anqp_build_initial_resp_buf( 1388 dialog_token, WLAN_STATUS_SUCCESS, 1389 comeback_delay, NULL); 1390 } 1391 } else { 1392 wpa_printf(MSG_DEBUG, "ANQP: Initial response (no comeback)"); 1393 tx_buf = gas_anqp_build_initial_resp_buf( 1394 dialog_token, WLAN_STATUS_SUCCESS, 0, buf); 1395 wpabuf_free(buf); 1396 } 1397 if (!tx_buf) 1398 return; 1399 if (prot) 1400 convert_to_protected_dual(tx_buf); 1401 if (std_addr3) 1402 hostapd_drv_send_action(hapd, hapd->iface->freq, 0, sa, 1403 wpabuf_head(tx_buf), 1404 wpabuf_len(tx_buf)); 1405 else 1406 hostapd_drv_send_action_addr3_ap(hapd, hapd->iface->freq, 0, sa, 1407 wpabuf_head(tx_buf), 1408 wpabuf_len(tx_buf)); 1409 wpabuf_free(tx_buf); 1410} 1411 1412 1413#ifdef CONFIG_DPP 1414static void gas_serv_req_dpp_processing(struct hostapd_data *hapd, 1415 const u8 *sa, u8 dialog_token, 1416 int prot, struct wpabuf *buf) 1417{ 1418 struct wpabuf *tx_buf; 1419 1420 if (wpabuf_len(buf) > hapd->conf->gas_frag_limit || 1421 hapd->conf->gas_comeback_delay) { 1422 struct gas_dialog_info *di; 1423 u16 comeback_delay = 1; 1424 1425 if (hapd->conf->gas_comeback_delay) { 1426 /* Testing - allow overriding of the delay value */ 1427 comeback_delay = hapd->conf->gas_comeback_delay; 1428 } 1429 1430 wpa_printf(MSG_DEBUG, 1431 "DPP: Too long response to fit in initial response - use GAS comeback"); 1432 di = gas_dialog_create(hapd, sa, dialog_token); 1433 if (!di) { 1434 wpa_printf(MSG_INFO, "DPP: Could not create dialog for " 1435 MACSTR " (dialog token %u)", 1436 MAC2STR(sa), dialog_token); 1437 wpabuf_free(buf); 1438 tx_buf = gas_build_initial_resp( 1439 dialog_token, WLAN_STATUS_UNSPECIFIED_FAILURE, 1440 0, 10); 1441 if (tx_buf) 1442 gas_serv_write_dpp_adv_proto(tx_buf); 1443 } else { 1444 di->prot = prot; 1445 di->sd_resp = buf; 1446 di->sd_resp_pos = 0; 1447 tx_buf = gas_build_initial_resp( 1448 dialog_token, WLAN_STATUS_SUCCESS, 1449 comeback_delay, 10); 1450 if (tx_buf) 1451 gas_serv_write_dpp_adv_proto(tx_buf); 1452 } 1453 } else { 1454 wpa_printf(MSG_DEBUG, 1455 "DPP: GAS Initial response (no comeback)"); 1456 tx_buf = gas_build_initial_resp( 1457 dialog_token, WLAN_STATUS_SUCCESS, 0, 1458 10 + 2 + wpabuf_len(buf)); 1459 if (tx_buf) { 1460 gas_serv_write_dpp_adv_proto(tx_buf); 1461 wpabuf_put_le16(tx_buf, wpabuf_len(buf)); 1462 wpabuf_put_buf(tx_buf, buf); 1463 wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_CONF_SENT); 1464 } 1465 wpabuf_free(buf); 1466 } 1467 if (!tx_buf) 1468 return; 1469 if (prot) 1470 convert_to_protected_dual(tx_buf); 1471 hostapd_drv_send_action(hapd, hapd->iface->freq, 0, sa, 1472 wpabuf_head(tx_buf), 1473 wpabuf_len(tx_buf)); 1474 wpabuf_free(tx_buf); 1475} 1476#endif /* CONFIG_DPP */ 1477 1478 1479static void gas_serv_rx_gas_initial_req(struct hostapd_data *hapd, 1480 const u8 *sa, 1481 const u8 *data, size_t len, int prot, 1482 int std_addr3) 1483{ 1484 const u8 *pos = data; 1485 const u8 *end = data + len; 1486 const u8 *next; 1487 u8 dialog_token; 1488 u16 slen; 1489 struct anqp_query_info qi; 1490 const u8 *adv_proto; 1491#ifdef CONFIG_DPP 1492 int dpp = 0; 1493#endif /* CONFIG_DPP */ 1494 1495 if (len < 1 + 2) 1496 return; 1497 1498 os_memset(&qi, 0, sizeof(qi)); 1499 1500 dialog_token = *pos++; 1501 wpa_msg(hapd->msg_ctx, MSG_DEBUG, 1502 "GAS: GAS Initial Request from " MACSTR " (dialog token %u) ", 1503 MAC2STR(sa), dialog_token); 1504 1505 if (*pos != WLAN_EID_ADV_PROTO) { 1506 wpa_msg(hapd->msg_ctx, MSG_DEBUG, 1507 "GAS: Unexpected IE in GAS Initial Request: %u", *pos); 1508 return; 1509 } 1510 adv_proto = pos++; 1511 1512 slen = *pos++; 1513 if (slen > end - pos || slen < 2) { 1514 wpa_msg(hapd->msg_ctx, MSG_DEBUG, 1515 "GAS: Invalid IE in GAS Initial Request"); 1516 return; 1517 } 1518 next = pos + slen; 1519 pos++; /* skip QueryRespLenLimit and PAME-BI */ 1520 1521#ifdef CONFIG_DPP 1522 if (slen == 8 && *pos == WLAN_EID_VENDOR_SPECIFIC && 1523 pos[1] == 5 && WPA_GET_BE24(&pos[2]) == OUI_WFA && 1524 pos[5] == DPP_OUI_TYPE && pos[6] == 0x01) { 1525 wpa_printf(MSG_DEBUG, "DPP: Configuration Request"); 1526 dpp = 1; 1527 } else 1528#endif /* CONFIG_DPP */ 1529 1530 if (*pos != ACCESS_NETWORK_QUERY_PROTOCOL) { 1531 struct wpabuf *buf; 1532 wpa_msg(hapd->msg_ctx, MSG_DEBUG, 1533 "GAS: Unsupported GAS advertisement protocol id %u", 1534 *pos); 1535 if (sa[0] & 0x01) 1536 return; /* Invalid source address - drop silently */ 1537 buf = gas_build_initial_resp( 1538 dialog_token, WLAN_STATUS_GAS_ADV_PROTO_NOT_SUPPORTED, 1539 0, 2 + slen + 2); 1540 if (buf == NULL) 1541 return; 1542 wpabuf_put_data(buf, adv_proto, 2 + slen); 1543 wpabuf_put_le16(buf, 0); /* Query Response Length */ 1544 if (prot) 1545 convert_to_protected_dual(buf); 1546 if (std_addr3) 1547 hostapd_drv_send_action(hapd, hapd->iface->freq, 0, sa, 1548 wpabuf_head(buf), 1549 wpabuf_len(buf)); 1550 else 1551 hostapd_drv_send_action_addr3_ap(hapd, 1552 hapd->iface->freq, 0, 1553 sa, wpabuf_head(buf), 1554 wpabuf_len(buf)); 1555 wpabuf_free(buf); 1556 return; 1557 } 1558 1559 pos = next; 1560 /* Query Request */ 1561 if (end - pos < 2) 1562 return; 1563 slen = WPA_GET_LE16(pos); 1564 pos += 2; 1565 if (slen > end - pos) 1566 return; 1567 end = pos + slen; 1568 1569#ifdef CONFIG_DPP 1570 if (dpp) { 1571 struct wpabuf *msg; 1572 1573 msg = hostapd_dpp_gas_req_handler(hapd, sa, pos, slen); 1574 if (!msg) 1575 return; 1576 gas_serv_req_dpp_processing(hapd, sa, dialog_token, prot, msg); 1577 return; 1578 } 1579#endif /* CONFIG_DPP */ 1580 1581 /* ANQP Query Request */ 1582 while (pos < end) { 1583 u16 info_id, elen; 1584 1585 if (end - pos < 4) 1586 return; 1587 1588 info_id = WPA_GET_LE16(pos); 1589 pos += 2; 1590 elen = WPA_GET_LE16(pos); 1591 pos += 2; 1592 1593 if (elen > end - pos) { 1594 wpa_printf(MSG_DEBUG, "ANQP: Invalid Query Request"); 1595 return; 1596 } 1597 1598 switch (info_id) { 1599 case ANQP_QUERY_LIST: 1600 rx_anqp_query_list(hapd, pos, pos + elen, &qi); 1601 break; 1602 case ANQP_VENDOR_SPECIFIC: 1603 rx_anqp_vendor_specific(hapd, pos, pos + elen, &qi); 1604 break; 1605 default: 1606 wpa_printf(MSG_DEBUG, "ANQP: Unsupported Query " 1607 "Request element %u", info_id); 1608 break; 1609 } 1610 1611 pos += elen; 1612 } 1613 1614 gas_serv_req_local_processing(hapd, sa, dialog_token, &qi, prot, 1615 std_addr3); 1616} 1617 1618 1619static void gas_serv_rx_gas_comeback_req(struct hostapd_data *hapd, 1620 const u8 *sa, 1621 const u8 *data, size_t len, int prot, 1622 int std_addr3) 1623{ 1624 struct gas_dialog_info *dialog; 1625 struct wpabuf *buf, *tx_buf; 1626 u8 dialog_token; 1627 size_t frag_len; 1628 int more = 0; 1629 1630 wpa_hexdump(MSG_DEBUG, "GAS: RX GAS Comeback Request", data, len); 1631 if (len < 1) 1632 return; 1633 dialog_token = *data; 1634 wpa_msg(hapd->msg_ctx, MSG_DEBUG, "GAS: Dialog Token: %u", 1635 dialog_token); 1636 1637 dialog = gas_serv_dialog_find(hapd, sa, dialog_token); 1638 if (!dialog) { 1639 wpa_msg(hapd->msg_ctx, MSG_DEBUG, "GAS: No pending SD " 1640 "response fragment for " MACSTR " dialog token %u", 1641 MAC2STR(sa), dialog_token); 1642 1643 if (sa[0] & 0x01) 1644 return; /* Invalid source address - drop silently */ 1645 tx_buf = gas_anqp_build_comeback_resp_buf( 1646 dialog_token, WLAN_STATUS_NO_OUTSTANDING_GAS_REQ, 0, 0, 1647 0, NULL); 1648 if (tx_buf == NULL) 1649 return; 1650 goto send_resp; 1651 } 1652 1653 frag_len = wpabuf_len(dialog->sd_resp) - dialog->sd_resp_pos; 1654 if (frag_len > hapd->conf->gas_frag_limit) { 1655 frag_len = hapd->conf->gas_frag_limit; 1656 more = 1; 1657 } 1658 wpa_msg(hapd->msg_ctx, MSG_DEBUG, "GAS: resp frag_len %u", 1659 (unsigned int) frag_len); 1660 buf = wpabuf_alloc_copy(wpabuf_head_u8(dialog->sd_resp) + 1661 dialog->sd_resp_pos, frag_len); 1662 if (buf == NULL) { 1663 wpa_msg(hapd->msg_ctx, MSG_DEBUG, "GAS: Failed to allocate " 1664 "buffer"); 1665 gas_serv_dialog_clear(dialog); 1666 return; 1667 } 1668#ifdef CONFIG_DPP 1669 if (dialog->dpp) { 1670 tx_buf = gas_build_comeback_resp(dialog_token, 1671 WLAN_STATUS_SUCCESS, 1672 dialog->sd_frag_id, more, 0, 1673 10 + frag_len); 1674 if (tx_buf) { 1675 gas_serv_write_dpp_adv_proto(tx_buf); 1676 wpabuf_put_buf(tx_buf, buf); 1677 } 1678 } else 1679#endif /* CONFIG_DPP */ 1680 tx_buf = gas_anqp_build_comeback_resp_buf(dialog_token, 1681 WLAN_STATUS_SUCCESS, 1682 dialog->sd_frag_id, 1683 more, 0, buf); 1684 wpabuf_free(buf); 1685 if (tx_buf == NULL) { 1686 gas_serv_dialog_clear(dialog); 1687 return; 1688 } 1689 wpa_msg(hapd->msg_ctx, MSG_DEBUG, "GAS: Tx GAS Comeback Response " 1690 "(frag_id %d more=%d frag_len=%d)", 1691 dialog->sd_frag_id, more, (int) frag_len); 1692 dialog->sd_frag_id++; 1693 dialog->sd_resp_pos += frag_len; 1694 1695 if (more) { 1696 wpa_msg(hapd->msg_ctx, MSG_DEBUG, "GAS: %d more bytes remain " 1697 "to be sent", 1698 (int) (wpabuf_len(dialog->sd_resp) - 1699 dialog->sd_resp_pos)); 1700 } else { 1701 wpa_msg(hapd->msg_ctx, MSG_DEBUG, "GAS: All fragments of " 1702 "SD response sent"); 1703#ifdef CONFIG_DPP 1704 if (dialog->dpp) 1705 wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_CONF_SENT); 1706#endif /* CONFIG_DPP */ 1707 gas_serv_dialog_clear(dialog); 1708 gas_serv_free_dialogs(hapd, sa); 1709 } 1710 1711send_resp: 1712 if (prot) 1713 convert_to_protected_dual(tx_buf); 1714 if (std_addr3) 1715 hostapd_drv_send_action(hapd, hapd->iface->freq, 0, sa, 1716 wpabuf_head(tx_buf), 1717 wpabuf_len(tx_buf)); 1718 else 1719 hostapd_drv_send_action_addr3_ap(hapd, hapd->iface->freq, 0, sa, 1720 wpabuf_head(tx_buf), 1721 wpabuf_len(tx_buf)); 1722 wpabuf_free(tx_buf); 1723} 1724 1725 1726static void gas_serv_rx_public_action(void *ctx, const u8 *buf, size_t len, 1727 int freq) 1728{ 1729 struct hostapd_data *hapd = ctx; 1730 const struct ieee80211_mgmt *mgmt; 1731 const u8 *sa, *data; 1732 int prot, std_addr3; 1733 1734 mgmt = (const struct ieee80211_mgmt *) buf; 1735 if (len < IEEE80211_HDRLEN + 2) 1736 return; 1737 if (mgmt->u.action.category != WLAN_ACTION_PUBLIC && 1738 mgmt->u.action.category != WLAN_ACTION_PROTECTED_DUAL) 1739 return; 1740 /* 1741 * Note: Public Action and Protected Dual of Public Action frames share 1742 * the same payload structure, so it is fine to use definitions of 1743 * Public Action frames to process both. 1744 */ 1745 prot = mgmt->u.action.category == WLAN_ACTION_PROTECTED_DUAL; 1746 sa = mgmt->sa; 1747 if (hapd->conf->gas_address3 == 1) 1748 std_addr3 = 1; 1749 else if (hapd->conf->gas_address3 == 2) 1750 std_addr3 = 0; 1751 else 1752 std_addr3 = is_broadcast_ether_addr(mgmt->bssid); 1753 len -= IEEE80211_HDRLEN + 1; 1754 data = buf + IEEE80211_HDRLEN + 1; 1755 switch (data[0]) { 1756 case WLAN_PA_GAS_INITIAL_REQ: 1757 gas_serv_rx_gas_initial_req(hapd, sa, data + 1, len - 1, prot, 1758 std_addr3); 1759 break; 1760 case WLAN_PA_GAS_COMEBACK_REQ: 1761 gas_serv_rx_gas_comeback_req(hapd, sa, data + 1, len - 1, prot, 1762 std_addr3); 1763 break; 1764 } 1765} 1766 1767 1768int gas_serv_init(struct hostapd_data *hapd) 1769{ 1770 hapd->public_action_cb2 = gas_serv_rx_public_action; 1771 hapd->public_action_cb2_ctx = hapd; 1772 return 0; 1773} 1774 1775 1776void gas_serv_deinit(struct hostapd_data *hapd) 1777{ 1778} 1779