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