1252190Srpaulo/* 2252190Srpaulo * Generic advertisement service (GAS) server 3281806Srpaulo * Copyright (c) 2011-2014, Qualcomm Atheros, Inc. 4252190Srpaulo * 5252190Srpaulo * This software may be distributed under the terms of the BSD license. 6252190Srpaulo * See README for more details. 7252190Srpaulo */ 8252190Srpaulo 9252190Srpaulo#include "includes.h" 10252190Srpaulo 11252190Srpaulo#include "common.h" 12252190Srpaulo#include "common/ieee802_11_defs.h" 13252190Srpaulo#include "common/gas.h" 14346981Scy#include "common/wpa_ctrl.h" 15252190Srpaulo#include "utils/eloop.h" 16252190Srpaulo#include "hostapd.h" 17252190Srpaulo#include "ap_config.h" 18252190Srpaulo#include "ap_drv_ops.h" 19346981Scy#include "dpp_hostapd.h" 20252190Srpaulo#include "sta_info.h" 21252190Srpaulo#include "gas_serv.h" 22252190Srpaulo 23252190Srpaulo 24346981Scy#ifdef CONFIG_DPP 25346981Scystatic void gas_serv_write_dpp_adv_proto(struct wpabuf *buf) 26346981Scy{ 27346981Scy wpabuf_put_u8(buf, WLAN_EID_ADV_PROTO); 28346981Scy wpabuf_put_u8(buf, 8); /* Length */ 29346981Scy wpabuf_put_u8(buf, 0x7f); 30346981Scy wpabuf_put_u8(buf, WLAN_EID_VENDOR_SPECIFIC); 31346981Scy wpabuf_put_u8(buf, 5); 32346981Scy wpabuf_put_be24(buf, OUI_WFA); 33346981Scy wpabuf_put_u8(buf, DPP_OUI_TYPE); 34346981Scy wpabuf_put_u8(buf, 0x01); 35346981Scy} 36346981Scy#endif /* CONFIG_DPP */ 37346981Scy 38346981Scy 39281806Srpaulostatic void convert_to_protected_dual(struct wpabuf *msg) 40281806Srpaulo{ 41281806Srpaulo u8 *categ = wpabuf_mhead_u8(msg); 42281806Srpaulo *categ = WLAN_ACTION_PROTECTED_DUAL; 43281806Srpaulo} 44281806Srpaulo 45281806Srpaulo 46252190Srpaulostatic struct gas_dialog_info * 47252190Srpaulogas_dialog_create(struct hostapd_data *hapd, const u8 *addr, u8 dialog_token) 48252190Srpaulo{ 49252190Srpaulo struct sta_info *sta; 50252190Srpaulo struct gas_dialog_info *dia = NULL; 51252190Srpaulo int i, j; 52252190Srpaulo 53252190Srpaulo sta = ap_get_sta(hapd, addr); 54252190Srpaulo if (!sta) { 55252190Srpaulo /* 56252190Srpaulo * We need a STA entry to be able to maintain state for 57252190Srpaulo * the GAS query. 58252190Srpaulo */ 59252190Srpaulo wpa_printf(MSG_DEBUG, "ANQP: Add a temporary STA entry for " 60252190Srpaulo "GAS query"); 61252190Srpaulo sta = ap_sta_add(hapd, addr); 62252190Srpaulo if (!sta) { 63252190Srpaulo wpa_printf(MSG_DEBUG, "Failed to add STA " MACSTR 64252190Srpaulo " for GAS query", MAC2STR(addr)); 65252190Srpaulo return NULL; 66252190Srpaulo } 67252190Srpaulo sta->flags |= WLAN_STA_GAS; 68252190Srpaulo /* 69252190Srpaulo * The default inactivity is 300 seconds. We don't need 70346981Scy * it to be that long. Use five second timeout and increase this 71346981Scy * with the comeback_delay for testing cases. 72252190Srpaulo */ 73346981Scy ap_sta_session_timeout(hapd, sta, 74346981Scy hapd->conf->gas_comeback_delay / 1024 + 75346981Scy 5); 76281806Srpaulo } else { 77281806Srpaulo ap_sta_replenish_timeout(hapd, sta, 5); 78252190Srpaulo } 79252190Srpaulo 80252190Srpaulo if (sta->gas_dialog == NULL) { 81281806Srpaulo sta->gas_dialog = os_calloc(GAS_DIALOG_MAX, 82252190Srpaulo sizeof(struct gas_dialog_info)); 83252190Srpaulo if (sta->gas_dialog == NULL) 84252190Srpaulo return NULL; 85252190Srpaulo } 86252190Srpaulo 87252190Srpaulo for (i = sta->gas_dialog_next, j = 0; j < GAS_DIALOG_MAX; i++, j++) { 88252190Srpaulo if (i == GAS_DIALOG_MAX) 89252190Srpaulo i = 0; 90252190Srpaulo if (sta->gas_dialog[i].valid) 91252190Srpaulo continue; 92252190Srpaulo dia = &sta->gas_dialog[i]; 93252190Srpaulo dia->valid = 1; 94252190Srpaulo dia->dialog_token = dialog_token; 95252190Srpaulo sta->gas_dialog_next = (++i == GAS_DIALOG_MAX) ? 0 : i; 96252190Srpaulo return dia; 97252190Srpaulo } 98252190Srpaulo 99252190Srpaulo wpa_msg(hapd->msg_ctx, MSG_ERROR, "ANQP: Could not create dialog for " 100252190Srpaulo MACSTR " dialog_token %u. Consider increasing " 101252190Srpaulo "GAS_DIALOG_MAX.", MAC2STR(addr), dialog_token); 102252190Srpaulo 103252190Srpaulo return NULL; 104252190Srpaulo} 105252190Srpaulo 106252190Srpaulo 107252190Srpaulostruct gas_dialog_info * 108252190Srpaulogas_serv_dialog_find(struct hostapd_data *hapd, const u8 *addr, 109252190Srpaulo u8 dialog_token) 110252190Srpaulo{ 111252190Srpaulo struct sta_info *sta; 112252190Srpaulo int i; 113252190Srpaulo 114252190Srpaulo sta = ap_get_sta(hapd, addr); 115252190Srpaulo if (!sta) { 116252190Srpaulo wpa_printf(MSG_DEBUG, "ANQP: could not find STA " MACSTR, 117252190Srpaulo MAC2STR(addr)); 118252190Srpaulo return NULL; 119252190Srpaulo } 120252190Srpaulo for (i = 0; sta->gas_dialog && i < GAS_DIALOG_MAX; i++) { 121252190Srpaulo if (sta->gas_dialog[i].dialog_token != dialog_token || 122252190Srpaulo !sta->gas_dialog[i].valid) 123252190Srpaulo continue; 124337817Scy ap_sta_replenish_timeout(hapd, sta, 5); 125252190Srpaulo return &sta->gas_dialog[i]; 126252190Srpaulo } 127252190Srpaulo wpa_printf(MSG_DEBUG, "ANQP: Could not find dialog for " 128252190Srpaulo MACSTR " dialog_token %u", MAC2STR(addr), dialog_token); 129252190Srpaulo return NULL; 130252190Srpaulo} 131252190Srpaulo 132252190Srpaulo 133252190Srpaulovoid gas_serv_dialog_clear(struct gas_dialog_info *dia) 134252190Srpaulo{ 135252190Srpaulo wpabuf_free(dia->sd_resp); 136252190Srpaulo os_memset(dia, 0, sizeof(*dia)); 137252190Srpaulo} 138252190Srpaulo 139252190Srpaulo 140252190Srpaulostatic void gas_serv_free_dialogs(struct hostapd_data *hapd, 141252190Srpaulo const u8 *sta_addr) 142252190Srpaulo{ 143252190Srpaulo struct sta_info *sta; 144252190Srpaulo int i; 145252190Srpaulo 146252190Srpaulo sta = ap_get_sta(hapd, sta_addr); 147252190Srpaulo if (sta == NULL || sta->gas_dialog == NULL) 148252190Srpaulo return; 149252190Srpaulo 150252190Srpaulo for (i = 0; i < GAS_DIALOG_MAX; i++) { 151252190Srpaulo if (sta->gas_dialog[i].valid) 152252190Srpaulo return; 153252190Srpaulo } 154252190Srpaulo 155252190Srpaulo os_free(sta->gas_dialog); 156252190Srpaulo sta->gas_dialog = NULL; 157252190Srpaulo} 158252190Srpaulo 159252190Srpaulo 160252190Srpaulo#ifdef CONFIG_HS20 161252190Srpaulostatic void anqp_add_hs_capab_list(struct hostapd_data *hapd, 162252190Srpaulo struct wpabuf *buf) 163252190Srpaulo{ 164252190Srpaulo u8 *len; 165252190Srpaulo 166252190Srpaulo len = gas_anqp_add_element(buf, ANQP_VENDOR_SPECIFIC); 167252190Srpaulo wpabuf_put_be24(buf, OUI_WFA); 168252190Srpaulo wpabuf_put_u8(buf, HS20_ANQP_OUI_TYPE); 169252190Srpaulo wpabuf_put_u8(buf, HS20_STYPE_CAPABILITY_LIST); 170252190Srpaulo wpabuf_put_u8(buf, 0); /* Reserved */ 171252190Srpaulo wpabuf_put_u8(buf, HS20_STYPE_CAPABILITY_LIST); 172252190Srpaulo if (hapd->conf->hs20_oper_friendly_name) 173252190Srpaulo wpabuf_put_u8(buf, HS20_STYPE_OPERATOR_FRIENDLY_NAME); 174252190Srpaulo if (hapd->conf->hs20_wan_metrics) 175252190Srpaulo wpabuf_put_u8(buf, HS20_STYPE_WAN_METRICS); 176252190Srpaulo if (hapd->conf->hs20_connection_capability) 177252190Srpaulo wpabuf_put_u8(buf, HS20_STYPE_CONNECTION_CAPABILITY); 178252190Srpaulo if (hapd->conf->nai_realm_data) 179252190Srpaulo wpabuf_put_u8(buf, HS20_STYPE_NAI_HOME_REALM_QUERY); 180252190Srpaulo if (hapd->conf->hs20_operating_class) 181252190Srpaulo wpabuf_put_u8(buf, HS20_STYPE_OPERATING_CLASS); 182281806Srpaulo if (hapd->conf->hs20_osu_providers_count) 183281806Srpaulo wpabuf_put_u8(buf, HS20_STYPE_OSU_PROVIDERS_LIST); 184346981Scy if (hapd->conf->hs20_osu_providers_nai_count) 185346981Scy wpabuf_put_u8(buf, HS20_STYPE_OSU_PROVIDERS_NAI_LIST); 186281806Srpaulo if (hapd->conf->hs20_icons_count) 187281806Srpaulo wpabuf_put_u8(buf, HS20_STYPE_ICON_REQUEST); 188346981Scy if (hapd->conf->hs20_operator_icon_count) 189346981Scy wpabuf_put_u8(buf, HS20_STYPE_OPERATOR_ICON_METADATA); 190252190Srpaulo gas_anqp_set_element_len(buf, len); 191252190Srpaulo} 192252190Srpaulo#endif /* CONFIG_HS20 */ 193252190Srpaulo 194252190Srpaulo 195337817Scystatic struct anqp_element * get_anqp_elem(struct hostapd_data *hapd, 196337817Scy u16 infoid) 197337817Scy{ 198337817Scy struct anqp_element *elem; 199337817Scy 200337817Scy dl_list_for_each(elem, &hapd->conf->anqp_elem, struct anqp_element, 201337817Scy list) { 202337817Scy if (elem->infoid == infoid) 203337817Scy return elem; 204337817Scy } 205337817Scy 206337817Scy return NULL; 207337817Scy} 208337817Scy 209337817Scy 210337817Scystatic void anqp_add_elem(struct hostapd_data *hapd, struct wpabuf *buf, 211337817Scy u16 infoid) 212337817Scy{ 213337817Scy struct anqp_element *elem; 214337817Scy 215337817Scy elem = get_anqp_elem(hapd, infoid); 216337817Scy if (!elem) 217337817Scy return; 218337817Scy if (wpabuf_tailroom(buf) < 2 + 2 + wpabuf_len(elem->payload)) { 219337817Scy wpa_printf(MSG_DEBUG, "ANQP: No room for InfoID %u payload", 220337817Scy infoid); 221337817Scy return; 222337817Scy } 223337817Scy 224337817Scy wpabuf_put_le16(buf, infoid); 225337817Scy wpabuf_put_le16(buf, wpabuf_len(elem->payload)); 226337817Scy wpabuf_put_buf(buf, elem->payload); 227337817Scy} 228337817Scy 229337817Scy 230337817Scystatic int anqp_add_override(struct hostapd_data *hapd, struct wpabuf *buf, 231337817Scy u16 infoid) 232337817Scy{ 233337817Scy if (get_anqp_elem(hapd, infoid)) { 234337817Scy anqp_add_elem(hapd, buf, infoid); 235337817Scy return 1; 236337817Scy } 237337817Scy 238337817Scy return 0; 239337817Scy} 240337817Scy 241337817Scy 242252190Srpaulostatic void anqp_add_capab_list(struct hostapd_data *hapd, 243252190Srpaulo struct wpabuf *buf) 244252190Srpaulo{ 245252190Srpaulo u8 *len; 246337817Scy u16 id; 247252190Srpaulo 248337817Scy if (anqp_add_override(hapd, buf, ANQP_CAPABILITY_LIST)) 249337817Scy return; 250337817Scy 251252190Srpaulo len = gas_anqp_add_element(buf, ANQP_CAPABILITY_LIST); 252252190Srpaulo wpabuf_put_le16(buf, ANQP_CAPABILITY_LIST); 253337817Scy if (hapd->conf->venue_name || get_anqp_elem(hapd, ANQP_VENUE_NAME)) 254252190Srpaulo wpabuf_put_le16(buf, ANQP_VENUE_NAME); 255337817Scy if (get_anqp_elem(hapd, ANQP_EMERGENCY_CALL_NUMBER)) 256337817Scy wpabuf_put_le16(buf, ANQP_EMERGENCY_CALL_NUMBER); 257337817Scy if (hapd->conf->network_auth_type || 258337817Scy get_anqp_elem(hapd, ANQP_NETWORK_AUTH_TYPE)) 259252190Srpaulo wpabuf_put_le16(buf, ANQP_NETWORK_AUTH_TYPE); 260337817Scy if (hapd->conf->roaming_consortium || 261337817Scy get_anqp_elem(hapd, ANQP_ROAMING_CONSORTIUM)) 262252190Srpaulo wpabuf_put_le16(buf, ANQP_ROAMING_CONSORTIUM); 263337817Scy if (hapd->conf->ipaddr_type_configured || 264337817Scy get_anqp_elem(hapd, ANQP_IP_ADDR_TYPE_AVAILABILITY)) 265252190Srpaulo wpabuf_put_le16(buf, ANQP_IP_ADDR_TYPE_AVAILABILITY); 266337817Scy if (hapd->conf->nai_realm_data || 267337817Scy get_anqp_elem(hapd, ANQP_NAI_REALM)) 268252190Srpaulo wpabuf_put_le16(buf, ANQP_NAI_REALM); 269337817Scy if (hapd->conf->anqp_3gpp_cell_net || 270337817Scy get_anqp_elem(hapd, ANQP_3GPP_CELLULAR_NETWORK)) 271252190Srpaulo wpabuf_put_le16(buf, ANQP_3GPP_CELLULAR_NETWORK); 272337817Scy if (get_anqp_elem(hapd, ANQP_AP_GEOSPATIAL_LOCATION)) 273337817Scy wpabuf_put_le16(buf, ANQP_AP_GEOSPATIAL_LOCATION); 274337817Scy if (get_anqp_elem(hapd, ANQP_AP_CIVIC_LOCATION)) 275337817Scy wpabuf_put_le16(buf, ANQP_AP_CIVIC_LOCATION); 276337817Scy if (get_anqp_elem(hapd, ANQP_AP_LOCATION_PUBLIC_URI)) 277337817Scy wpabuf_put_le16(buf, ANQP_AP_LOCATION_PUBLIC_URI); 278337817Scy if (hapd->conf->domain_name || get_anqp_elem(hapd, ANQP_DOMAIN_NAME)) 279252190Srpaulo wpabuf_put_le16(buf, ANQP_DOMAIN_NAME); 280337817Scy if (get_anqp_elem(hapd, ANQP_EMERGENCY_ALERT_URI)) 281337817Scy wpabuf_put_le16(buf, ANQP_EMERGENCY_ALERT_URI); 282346981Scy if (get_anqp_elem(hapd, ANQP_TDLS_CAPABILITY)) 283346981Scy wpabuf_put_le16(buf, ANQP_TDLS_CAPABILITY); 284337817Scy if (get_anqp_elem(hapd, ANQP_EMERGENCY_NAI)) 285337817Scy wpabuf_put_le16(buf, ANQP_EMERGENCY_NAI); 286337817Scy if (get_anqp_elem(hapd, ANQP_NEIGHBOR_REPORT)) 287337817Scy wpabuf_put_le16(buf, ANQP_NEIGHBOR_REPORT); 288346981Scy#ifdef CONFIG_FILS 289346981Scy if (!dl_list_empty(&hapd->conf->fils_realms) || 290346981Scy get_anqp_elem(hapd, ANQP_FILS_REALM_INFO)) 291346981Scy wpabuf_put_le16(buf, ANQP_FILS_REALM_INFO); 292346981Scy#endif /* CONFIG_FILS */ 293346981Scy if (get_anqp_elem(hapd, ANQP_CAG)) 294346981Scy wpabuf_put_le16(buf, ANQP_CAG); 295346981Scy if (hapd->conf->venue_url || get_anqp_elem(hapd, ANQP_VENUE_URL)) 296337817Scy wpabuf_put_le16(buf, ANQP_VENUE_URL); 297337817Scy if (get_anqp_elem(hapd, ANQP_ADVICE_OF_CHARGE)) 298337817Scy wpabuf_put_le16(buf, ANQP_ADVICE_OF_CHARGE); 299337817Scy if (get_anqp_elem(hapd, ANQP_LOCAL_CONTENT)) 300337817Scy wpabuf_put_le16(buf, ANQP_LOCAL_CONTENT); 301346981Scy for (id = 280; id < 300; id++) { 302346981Scy if (get_anqp_elem(hapd, id)) 303346981Scy wpabuf_put_le16(buf, id); 304346981Scy } 305252190Srpaulo#ifdef CONFIG_HS20 306252190Srpaulo anqp_add_hs_capab_list(hapd, buf); 307252190Srpaulo#endif /* CONFIG_HS20 */ 308252190Srpaulo gas_anqp_set_element_len(buf, len); 309252190Srpaulo} 310252190Srpaulo 311252190Srpaulo 312252190Srpaulostatic void anqp_add_venue_name(struct hostapd_data *hapd, struct wpabuf *buf) 313252190Srpaulo{ 314337817Scy if (anqp_add_override(hapd, buf, ANQP_VENUE_NAME)) 315337817Scy return; 316337817Scy 317252190Srpaulo if (hapd->conf->venue_name) { 318252190Srpaulo u8 *len; 319252190Srpaulo unsigned int i; 320252190Srpaulo len = gas_anqp_add_element(buf, ANQP_VENUE_NAME); 321252190Srpaulo wpabuf_put_u8(buf, hapd->conf->venue_group); 322252190Srpaulo wpabuf_put_u8(buf, hapd->conf->venue_type); 323252190Srpaulo for (i = 0; i < hapd->conf->venue_name_count; i++) { 324252190Srpaulo struct hostapd_lang_string *vn; 325252190Srpaulo vn = &hapd->conf->venue_name[i]; 326252190Srpaulo wpabuf_put_u8(buf, 3 + vn->name_len); 327252190Srpaulo wpabuf_put_data(buf, vn->lang, 3); 328252190Srpaulo wpabuf_put_data(buf, vn->name, vn->name_len); 329252190Srpaulo } 330252190Srpaulo gas_anqp_set_element_len(buf, len); 331252190Srpaulo } 332252190Srpaulo} 333252190Srpaulo 334252190Srpaulo 335346981Scystatic void anqp_add_venue_url(struct hostapd_data *hapd, struct wpabuf *buf) 336346981Scy{ 337346981Scy if (anqp_add_override(hapd, buf, ANQP_VENUE_URL)) 338346981Scy return; 339346981Scy 340346981Scy if (hapd->conf->venue_url) { 341346981Scy u8 *len; 342346981Scy unsigned int i; 343346981Scy 344346981Scy len = gas_anqp_add_element(buf, ANQP_VENUE_URL); 345346981Scy for (i = 0; i < hapd->conf->venue_url_count; i++) { 346346981Scy struct hostapd_venue_url *url; 347346981Scy 348346981Scy url = &hapd->conf->venue_url[i]; 349346981Scy wpabuf_put_u8(buf, 1 + url->url_len); 350346981Scy wpabuf_put_u8(buf, url->venue_number); 351346981Scy wpabuf_put_data(buf, url->url, url->url_len); 352346981Scy } 353346981Scy gas_anqp_set_element_len(buf, len); 354346981Scy } 355346981Scy} 356346981Scy 357346981Scy 358252190Srpaulostatic void anqp_add_network_auth_type(struct hostapd_data *hapd, 359252190Srpaulo struct wpabuf *buf) 360252190Srpaulo{ 361337817Scy if (anqp_add_override(hapd, buf, ANQP_NETWORK_AUTH_TYPE)) 362337817Scy return; 363337817Scy 364252190Srpaulo if (hapd->conf->network_auth_type) { 365252190Srpaulo wpabuf_put_le16(buf, ANQP_NETWORK_AUTH_TYPE); 366252190Srpaulo wpabuf_put_le16(buf, hapd->conf->network_auth_type_len); 367252190Srpaulo wpabuf_put_data(buf, hapd->conf->network_auth_type, 368252190Srpaulo hapd->conf->network_auth_type_len); 369252190Srpaulo } 370252190Srpaulo} 371252190Srpaulo 372252190Srpaulo 373252190Srpaulostatic void anqp_add_roaming_consortium(struct hostapd_data *hapd, 374252190Srpaulo struct wpabuf *buf) 375252190Srpaulo{ 376252190Srpaulo unsigned int i; 377252190Srpaulo u8 *len; 378252190Srpaulo 379337817Scy if (anqp_add_override(hapd, buf, ANQP_ROAMING_CONSORTIUM)) 380337817Scy return; 381337817Scy 382252190Srpaulo len = gas_anqp_add_element(buf, ANQP_ROAMING_CONSORTIUM); 383252190Srpaulo for (i = 0; i < hapd->conf->roaming_consortium_count; i++) { 384252190Srpaulo struct hostapd_roaming_consortium *rc; 385252190Srpaulo rc = &hapd->conf->roaming_consortium[i]; 386252190Srpaulo wpabuf_put_u8(buf, rc->len); 387252190Srpaulo wpabuf_put_data(buf, rc->oi, rc->len); 388252190Srpaulo } 389252190Srpaulo gas_anqp_set_element_len(buf, len); 390252190Srpaulo} 391252190Srpaulo 392252190Srpaulo 393252190Srpaulostatic void anqp_add_ip_addr_type_availability(struct hostapd_data *hapd, 394252190Srpaulo struct wpabuf *buf) 395252190Srpaulo{ 396337817Scy if (anqp_add_override(hapd, buf, ANQP_IP_ADDR_TYPE_AVAILABILITY)) 397337817Scy return; 398337817Scy 399252190Srpaulo if (hapd->conf->ipaddr_type_configured) { 400252190Srpaulo wpabuf_put_le16(buf, ANQP_IP_ADDR_TYPE_AVAILABILITY); 401252190Srpaulo wpabuf_put_le16(buf, 1); 402252190Srpaulo wpabuf_put_u8(buf, hapd->conf->ipaddr_type_availability); 403252190Srpaulo } 404252190Srpaulo} 405252190Srpaulo 406252190Srpaulo 407252190Srpaulostatic void anqp_add_nai_realm_eap(struct wpabuf *buf, 408252190Srpaulo struct hostapd_nai_realm_data *realm) 409252190Srpaulo{ 410252190Srpaulo unsigned int i, j; 411252190Srpaulo 412252190Srpaulo wpabuf_put_u8(buf, realm->eap_method_count); 413252190Srpaulo 414252190Srpaulo for (i = 0; i < realm->eap_method_count; i++) { 415252190Srpaulo struct hostapd_nai_realm_eap *eap = &realm->eap_method[i]; 416252190Srpaulo wpabuf_put_u8(buf, 2 + (3 * eap->num_auths)); 417252190Srpaulo wpabuf_put_u8(buf, eap->eap_method); 418252190Srpaulo wpabuf_put_u8(buf, eap->num_auths); 419252190Srpaulo for (j = 0; j < eap->num_auths; j++) { 420252190Srpaulo wpabuf_put_u8(buf, eap->auth_id[j]); 421252190Srpaulo wpabuf_put_u8(buf, 1); 422252190Srpaulo wpabuf_put_u8(buf, eap->auth_val[j]); 423252190Srpaulo } 424252190Srpaulo } 425252190Srpaulo} 426252190Srpaulo 427252190Srpaulo 428252190Srpaulostatic void anqp_add_nai_realm_data(struct wpabuf *buf, 429252190Srpaulo struct hostapd_nai_realm_data *realm, 430252190Srpaulo unsigned int realm_idx) 431252190Srpaulo{ 432252190Srpaulo u8 *realm_data_len; 433252190Srpaulo 434252190Srpaulo wpa_printf(MSG_DEBUG, "realm=%s, len=%d", realm->realm[realm_idx], 435252190Srpaulo (int) os_strlen(realm->realm[realm_idx])); 436252190Srpaulo realm_data_len = wpabuf_put(buf, 2); 437252190Srpaulo wpabuf_put_u8(buf, realm->encoding); 438252190Srpaulo wpabuf_put_u8(buf, os_strlen(realm->realm[realm_idx])); 439252190Srpaulo wpabuf_put_str(buf, realm->realm[realm_idx]); 440252190Srpaulo anqp_add_nai_realm_eap(buf, realm); 441252190Srpaulo gas_anqp_set_element_len(buf, realm_data_len); 442252190Srpaulo} 443252190Srpaulo 444252190Srpaulo 445252190Srpaulostatic int hs20_add_nai_home_realm_matches(struct hostapd_data *hapd, 446252190Srpaulo struct wpabuf *buf, 447252190Srpaulo const u8 *home_realm, 448252190Srpaulo size_t home_realm_len) 449252190Srpaulo{ 450252190Srpaulo unsigned int i, j, k; 451252190Srpaulo u8 num_realms, num_matching = 0, encoding, realm_len, *realm_list_len; 452252190Srpaulo struct hostapd_nai_realm_data *realm; 453252190Srpaulo const u8 *pos, *realm_name, *end; 454252190Srpaulo struct { 455252190Srpaulo unsigned int realm_data_idx; 456252190Srpaulo unsigned int realm_idx; 457252190Srpaulo } matches[10]; 458252190Srpaulo 459252190Srpaulo pos = home_realm; 460252190Srpaulo end = pos + home_realm_len; 461337817Scy if (end - pos < 1) { 462252190Srpaulo wpa_hexdump(MSG_DEBUG, "Too short NAI Home Realm Query", 463252190Srpaulo home_realm, home_realm_len); 464252190Srpaulo return -1; 465252190Srpaulo } 466252190Srpaulo num_realms = *pos++; 467252190Srpaulo 468252190Srpaulo for (i = 0; i < num_realms && num_matching < 10; i++) { 469337817Scy if (end - pos < 2) { 470252190Srpaulo wpa_hexdump(MSG_DEBUG, 471252190Srpaulo "Truncated NAI Home Realm Query", 472252190Srpaulo home_realm, home_realm_len); 473252190Srpaulo return -1; 474252190Srpaulo } 475252190Srpaulo encoding = *pos++; 476252190Srpaulo realm_len = *pos++; 477337817Scy if (realm_len > end - pos) { 478252190Srpaulo wpa_hexdump(MSG_DEBUG, 479252190Srpaulo "Truncated NAI Home Realm Query", 480252190Srpaulo home_realm, home_realm_len); 481252190Srpaulo return -1; 482252190Srpaulo } 483252190Srpaulo realm_name = pos; 484252190Srpaulo for (j = 0; j < hapd->conf->nai_realm_count && 485252190Srpaulo num_matching < 10; j++) { 486252190Srpaulo const u8 *rpos, *rend; 487252190Srpaulo realm = &hapd->conf->nai_realm_data[j]; 488252190Srpaulo if (encoding != realm->encoding) 489252190Srpaulo continue; 490252190Srpaulo 491252190Srpaulo rpos = realm_name; 492252190Srpaulo while (rpos < realm_name + realm_len && 493252190Srpaulo num_matching < 10) { 494252190Srpaulo for (rend = rpos; 495252190Srpaulo rend < realm_name + realm_len; rend++) { 496252190Srpaulo if (*rend == ';') 497252190Srpaulo break; 498252190Srpaulo } 499252190Srpaulo for (k = 0; k < MAX_NAI_REALMS && 500252190Srpaulo realm->realm[k] && 501252190Srpaulo num_matching < 10; k++) { 502252190Srpaulo if ((int) os_strlen(realm->realm[k]) != 503252190Srpaulo rend - rpos || 504252190Srpaulo os_strncmp((char *) rpos, 505252190Srpaulo realm->realm[k], 506252190Srpaulo rend - rpos) != 0) 507252190Srpaulo continue; 508252190Srpaulo matches[num_matching].realm_data_idx = 509252190Srpaulo j; 510252190Srpaulo matches[num_matching].realm_idx = k; 511252190Srpaulo num_matching++; 512252190Srpaulo } 513252190Srpaulo rpos = rend + 1; 514252190Srpaulo } 515252190Srpaulo } 516252190Srpaulo pos += realm_len; 517252190Srpaulo } 518252190Srpaulo 519252190Srpaulo realm_list_len = gas_anqp_add_element(buf, ANQP_NAI_REALM); 520252190Srpaulo wpabuf_put_le16(buf, num_matching); 521252190Srpaulo 522252190Srpaulo /* 523252190Srpaulo * There are two ways to format. 1. each realm in a NAI Realm Data unit 524252190Srpaulo * 2. all realms that share the same EAP methods in a NAI Realm Data 525252190Srpaulo * unit. The first format is likely to be bigger in size than the 526252190Srpaulo * second, but may be easier to parse and process by the receiver. 527252190Srpaulo */ 528252190Srpaulo for (i = 0; i < num_matching; i++) { 529252190Srpaulo wpa_printf(MSG_DEBUG, "realm_idx %d, realm_data_idx %d", 530252190Srpaulo matches[i].realm_data_idx, matches[i].realm_idx); 531252190Srpaulo realm = &hapd->conf->nai_realm_data[matches[i].realm_data_idx]; 532252190Srpaulo anqp_add_nai_realm_data(buf, realm, matches[i].realm_idx); 533252190Srpaulo } 534252190Srpaulo gas_anqp_set_element_len(buf, realm_list_len); 535252190Srpaulo return 0; 536252190Srpaulo} 537252190Srpaulo 538252190Srpaulo 539252190Srpaulostatic void anqp_add_nai_realm(struct hostapd_data *hapd, struct wpabuf *buf, 540252190Srpaulo const u8 *home_realm, size_t home_realm_len, 541252190Srpaulo int nai_realm, int nai_home_realm) 542252190Srpaulo{ 543337817Scy if (nai_realm && !nai_home_realm && 544337817Scy anqp_add_override(hapd, buf, ANQP_NAI_REALM)) 545337817Scy return; 546337817Scy 547252190Srpaulo if (nai_realm && hapd->conf->nai_realm_data) { 548252190Srpaulo u8 *len; 549252190Srpaulo unsigned int i, j; 550252190Srpaulo len = gas_anqp_add_element(buf, ANQP_NAI_REALM); 551252190Srpaulo wpabuf_put_le16(buf, hapd->conf->nai_realm_count); 552252190Srpaulo for (i = 0; i < hapd->conf->nai_realm_count; i++) { 553252190Srpaulo u8 *realm_data_len, *realm_len; 554252190Srpaulo struct hostapd_nai_realm_data *realm; 555252190Srpaulo 556252190Srpaulo realm = &hapd->conf->nai_realm_data[i]; 557252190Srpaulo realm_data_len = wpabuf_put(buf, 2); 558252190Srpaulo wpabuf_put_u8(buf, realm->encoding); 559252190Srpaulo realm_len = wpabuf_put(buf, 1); 560252190Srpaulo for (j = 0; realm->realm[j]; j++) { 561252190Srpaulo if (j > 0) 562252190Srpaulo wpabuf_put_u8(buf, ';'); 563252190Srpaulo wpabuf_put_str(buf, realm->realm[j]); 564252190Srpaulo } 565252190Srpaulo *realm_len = (u8 *) wpabuf_put(buf, 0) - realm_len - 1; 566252190Srpaulo anqp_add_nai_realm_eap(buf, realm); 567252190Srpaulo gas_anqp_set_element_len(buf, realm_data_len); 568252190Srpaulo } 569252190Srpaulo gas_anqp_set_element_len(buf, len); 570281806Srpaulo } else if (nai_home_realm && hapd->conf->nai_realm_data && home_realm) { 571252190Srpaulo hs20_add_nai_home_realm_matches(hapd, buf, home_realm, 572252190Srpaulo home_realm_len); 573252190Srpaulo } 574252190Srpaulo} 575252190Srpaulo 576252190Srpaulo 577252190Srpaulostatic void anqp_add_3gpp_cellular_network(struct hostapd_data *hapd, 578252190Srpaulo struct wpabuf *buf) 579252190Srpaulo{ 580337817Scy if (anqp_add_override(hapd, buf, ANQP_3GPP_CELLULAR_NETWORK)) 581337817Scy return; 582337817Scy 583252190Srpaulo if (hapd->conf->anqp_3gpp_cell_net) { 584252190Srpaulo wpabuf_put_le16(buf, ANQP_3GPP_CELLULAR_NETWORK); 585252190Srpaulo wpabuf_put_le16(buf, 586252190Srpaulo hapd->conf->anqp_3gpp_cell_net_len); 587252190Srpaulo wpabuf_put_data(buf, hapd->conf->anqp_3gpp_cell_net, 588252190Srpaulo hapd->conf->anqp_3gpp_cell_net_len); 589252190Srpaulo } 590252190Srpaulo} 591252190Srpaulo 592252190Srpaulo 593252190Srpaulostatic void anqp_add_domain_name(struct hostapd_data *hapd, struct wpabuf *buf) 594252190Srpaulo{ 595337817Scy if (anqp_add_override(hapd, buf, ANQP_DOMAIN_NAME)) 596337817Scy return; 597337817Scy 598252190Srpaulo if (hapd->conf->domain_name) { 599252190Srpaulo wpabuf_put_le16(buf, ANQP_DOMAIN_NAME); 600252190Srpaulo wpabuf_put_le16(buf, hapd->conf->domain_name_len); 601252190Srpaulo wpabuf_put_data(buf, hapd->conf->domain_name, 602252190Srpaulo hapd->conf->domain_name_len); 603252190Srpaulo } 604252190Srpaulo} 605252190Srpaulo 606252190Srpaulo 607346981Scy#ifdef CONFIG_FILS 608346981Scystatic void anqp_add_fils_realm_info(struct hostapd_data *hapd, 609346981Scy struct wpabuf *buf) 610346981Scy{ 611346981Scy size_t count; 612346981Scy 613346981Scy if (anqp_add_override(hapd, buf, ANQP_FILS_REALM_INFO)) 614346981Scy return; 615346981Scy 616346981Scy count = dl_list_len(&hapd->conf->fils_realms); 617346981Scy if (count > 10000) 618346981Scy count = 10000; 619346981Scy if (count) { 620346981Scy struct fils_realm *realm; 621346981Scy 622346981Scy wpabuf_put_le16(buf, ANQP_FILS_REALM_INFO); 623346981Scy wpabuf_put_le16(buf, 2 * count); 624346981Scy 625346981Scy dl_list_for_each(realm, &hapd->conf->fils_realms, 626346981Scy struct fils_realm, list) { 627346981Scy if (count == 0) 628346981Scy break; 629346981Scy wpabuf_put_data(buf, realm->hash, 2); 630346981Scy count--; 631346981Scy } 632346981Scy } 633346981Scy} 634346981Scy#endif /* CONFIG_FILS */ 635346981Scy 636346981Scy 637252190Srpaulo#ifdef CONFIG_HS20 638252190Srpaulo 639252190Srpaulostatic void anqp_add_operator_friendly_name(struct hostapd_data *hapd, 640252190Srpaulo struct wpabuf *buf) 641252190Srpaulo{ 642252190Srpaulo if (hapd->conf->hs20_oper_friendly_name) { 643252190Srpaulo u8 *len; 644252190Srpaulo unsigned int i; 645252190Srpaulo len = gas_anqp_add_element(buf, ANQP_VENDOR_SPECIFIC); 646252190Srpaulo wpabuf_put_be24(buf, OUI_WFA); 647252190Srpaulo wpabuf_put_u8(buf, HS20_ANQP_OUI_TYPE); 648252190Srpaulo wpabuf_put_u8(buf, HS20_STYPE_OPERATOR_FRIENDLY_NAME); 649252190Srpaulo wpabuf_put_u8(buf, 0); /* Reserved */ 650252190Srpaulo for (i = 0; i < hapd->conf->hs20_oper_friendly_name_count; i++) 651252190Srpaulo { 652252190Srpaulo struct hostapd_lang_string *vn; 653252190Srpaulo vn = &hapd->conf->hs20_oper_friendly_name[i]; 654252190Srpaulo wpabuf_put_u8(buf, 3 + vn->name_len); 655252190Srpaulo wpabuf_put_data(buf, vn->lang, 3); 656252190Srpaulo wpabuf_put_data(buf, vn->name, vn->name_len); 657252190Srpaulo } 658252190Srpaulo gas_anqp_set_element_len(buf, len); 659252190Srpaulo } 660252190Srpaulo} 661252190Srpaulo 662252190Srpaulo 663252190Srpaulostatic void anqp_add_wan_metrics(struct hostapd_data *hapd, 664252190Srpaulo struct wpabuf *buf) 665252190Srpaulo{ 666252190Srpaulo if (hapd->conf->hs20_wan_metrics) { 667252190Srpaulo u8 *len = gas_anqp_add_element(buf, ANQP_VENDOR_SPECIFIC); 668252190Srpaulo wpabuf_put_be24(buf, OUI_WFA); 669252190Srpaulo wpabuf_put_u8(buf, HS20_ANQP_OUI_TYPE); 670252190Srpaulo wpabuf_put_u8(buf, HS20_STYPE_WAN_METRICS); 671252190Srpaulo wpabuf_put_u8(buf, 0); /* Reserved */ 672252190Srpaulo wpabuf_put_data(buf, hapd->conf->hs20_wan_metrics, 13); 673252190Srpaulo gas_anqp_set_element_len(buf, len); 674252190Srpaulo } 675252190Srpaulo} 676252190Srpaulo 677252190Srpaulo 678252190Srpaulostatic void anqp_add_connection_capability(struct hostapd_data *hapd, 679252190Srpaulo struct wpabuf *buf) 680252190Srpaulo{ 681252190Srpaulo if (hapd->conf->hs20_connection_capability) { 682252190Srpaulo u8 *len = gas_anqp_add_element(buf, ANQP_VENDOR_SPECIFIC); 683252190Srpaulo wpabuf_put_be24(buf, OUI_WFA); 684252190Srpaulo wpabuf_put_u8(buf, HS20_ANQP_OUI_TYPE); 685252190Srpaulo wpabuf_put_u8(buf, HS20_STYPE_CONNECTION_CAPABILITY); 686252190Srpaulo wpabuf_put_u8(buf, 0); /* Reserved */ 687252190Srpaulo wpabuf_put_data(buf, hapd->conf->hs20_connection_capability, 688252190Srpaulo hapd->conf->hs20_connection_capability_len); 689252190Srpaulo gas_anqp_set_element_len(buf, len); 690252190Srpaulo } 691252190Srpaulo} 692252190Srpaulo 693252190Srpaulo 694252190Srpaulostatic void anqp_add_operating_class(struct hostapd_data *hapd, 695252190Srpaulo struct wpabuf *buf) 696252190Srpaulo{ 697252190Srpaulo if (hapd->conf->hs20_operating_class) { 698252190Srpaulo u8 *len = gas_anqp_add_element(buf, ANQP_VENDOR_SPECIFIC); 699252190Srpaulo wpabuf_put_be24(buf, OUI_WFA); 700252190Srpaulo wpabuf_put_u8(buf, HS20_ANQP_OUI_TYPE); 701252190Srpaulo wpabuf_put_u8(buf, HS20_STYPE_OPERATING_CLASS); 702252190Srpaulo wpabuf_put_u8(buf, 0); /* Reserved */ 703252190Srpaulo wpabuf_put_data(buf, hapd->conf->hs20_operating_class, 704252190Srpaulo hapd->conf->hs20_operating_class_len); 705252190Srpaulo gas_anqp_set_element_len(buf, len); 706252190Srpaulo } 707252190Srpaulo} 708252190Srpaulo 709281806Srpaulo 710346981Scystatic void anqp_add_icon(struct wpabuf *buf, struct hostapd_bss_config *bss, 711346981Scy const char *name) 712346981Scy{ 713346981Scy size_t j; 714346981Scy struct hs20_icon *icon = NULL; 715346981Scy 716346981Scy for (j = 0; j < bss->hs20_icons_count && !icon; j++) { 717346981Scy if (os_strcmp(name, bss->hs20_icons[j].name) == 0) 718346981Scy icon = &bss->hs20_icons[j]; 719346981Scy } 720346981Scy if (!icon) 721346981Scy return; /* icon info not found */ 722346981Scy 723346981Scy wpabuf_put_le16(buf, icon->width); 724346981Scy wpabuf_put_le16(buf, icon->height); 725346981Scy wpabuf_put_data(buf, icon->language, 3); 726346981Scy wpabuf_put_u8(buf, os_strlen(icon->type)); 727346981Scy wpabuf_put_str(buf, icon->type); 728346981Scy wpabuf_put_u8(buf, os_strlen(icon->name)); 729346981Scy wpabuf_put_str(buf, icon->name); 730346981Scy} 731346981Scy 732346981Scy 733281806Srpaulostatic void anqp_add_osu_provider(struct wpabuf *buf, 734281806Srpaulo struct hostapd_bss_config *bss, 735281806Srpaulo struct hs20_osu_provider *p) 736281806Srpaulo{ 737281806Srpaulo u8 *len, *len2, *count; 738281806Srpaulo unsigned int i; 739281806Srpaulo 740281806Srpaulo len = wpabuf_put(buf, 2); /* OSU Provider Length to be filled */ 741281806Srpaulo 742281806Srpaulo /* OSU Friendly Name Duples */ 743281806Srpaulo len2 = wpabuf_put(buf, 2); 744281806Srpaulo for (i = 0; i < p->friendly_name_count; i++) { 745281806Srpaulo struct hostapd_lang_string *s = &p->friendly_name[i]; 746281806Srpaulo wpabuf_put_u8(buf, 3 + s->name_len); 747281806Srpaulo wpabuf_put_data(buf, s->lang, 3); 748281806Srpaulo wpabuf_put_data(buf, s->name, s->name_len); 749281806Srpaulo } 750281806Srpaulo WPA_PUT_LE16(len2, (u8 *) wpabuf_put(buf, 0) - len2 - 2); 751281806Srpaulo 752281806Srpaulo /* OSU Server URI */ 753281806Srpaulo if (p->server_uri) { 754281806Srpaulo wpabuf_put_u8(buf, os_strlen(p->server_uri)); 755281806Srpaulo wpabuf_put_str(buf, p->server_uri); 756281806Srpaulo } else 757281806Srpaulo wpabuf_put_u8(buf, 0); 758281806Srpaulo 759281806Srpaulo /* OSU Method List */ 760281806Srpaulo count = wpabuf_put(buf, 1); 761346981Scy for (i = 0; p->method_list && p->method_list[i] >= 0; i++) 762281806Srpaulo wpabuf_put_u8(buf, p->method_list[i]); 763281806Srpaulo *count = i; 764281806Srpaulo 765281806Srpaulo /* Icons Available */ 766281806Srpaulo len2 = wpabuf_put(buf, 2); 767346981Scy for (i = 0; i < p->icons_count; i++) 768346981Scy anqp_add_icon(buf, bss, p->icons[i]); 769281806Srpaulo WPA_PUT_LE16(len2, (u8 *) wpabuf_put(buf, 0) - len2 - 2); 770281806Srpaulo 771281806Srpaulo /* OSU_NAI */ 772281806Srpaulo if (p->osu_nai) { 773281806Srpaulo wpabuf_put_u8(buf, os_strlen(p->osu_nai)); 774281806Srpaulo wpabuf_put_str(buf, p->osu_nai); 775281806Srpaulo } else 776281806Srpaulo wpabuf_put_u8(buf, 0); 777281806Srpaulo 778281806Srpaulo /* OSU Service Description Duples */ 779281806Srpaulo len2 = wpabuf_put(buf, 2); 780281806Srpaulo for (i = 0; i < p->service_desc_count; i++) { 781281806Srpaulo struct hostapd_lang_string *s = &p->service_desc[i]; 782281806Srpaulo wpabuf_put_u8(buf, 3 + s->name_len); 783281806Srpaulo wpabuf_put_data(buf, s->lang, 3); 784281806Srpaulo wpabuf_put_data(buf, s->name, s->name_len); 785281806Srpaulo } 786281806Srpaulo WPA_PUT_LE16(len2, (u8 *) wpabuf_put(buf, 0) - len2 - 2); 787281806Srpaulo 788281806Srpaulo WPA_PUT_LE16(len, (u8 *) wpabuf_put(buf, 0) - len - 2); 789281806Srpaulo} 790281806Srpaulo 791281806Srpaulo 792281806Srpaulostatic void anqp_add_osu_providers_list(struct hostapd_data *hapd, 793281806Srpaulo struct wpabuf *buf) 794281806Srpaulo{ 795281806Srpaulo if (hapd->conf->hs20_osu_providers_count) { 796281806Srpaulo size_t i; 797281806Srpaulo u8 *len = gas_anqp_add_element(buf, ANQP_VENDOR_SPECIFIC); 798281806Srpaulo wpabuf_put_be24(buf, OUI_WFA); 799281806Srpaulo wpabuf_put_u8(buf, HS20_ANQP_OUI_TYPE); 800281806Srpaulo wpabuf_put_u8(buf, HS20_STYPE_OSU_PROVIDERS_LIST); 801281806Srpaulo wpabuf_put_u8(buf, 0); /* Reserved */ 802281806Srpaulo 803281806Srpaulo /* OSU SSID */ 804281806Srpaulo wpabuf_put_u8(buf, hapd->conf->osu_ssid_len); 805281806Srpaulo wpabuf_put_data(buf, hapd->conf->osu_ssid, 806281806Srpaulo hapd->conf->osu_ssid_len); 807281806Srpaulo 808281806Srpaulo /* Number of OSU Providers */ 809281806Srpaulo wpabuf_put_u8(buf, hapd->conf->hs20_osu_providers_count); 810281806Srpaulo 811281806Srpaulo for (i = 0; i < hapd->conf->hs20_osu_providers_count; i++) { 812281806Srpaulo anqp_add_osu_provider( 813281806Srpaulo buf, hapd->conf, 814281806Srpaulo &hapd->conf->hs20_osu_providers[i]); 815281806Srpaulo } 816281806Srpaulo 817281806Srpaulo gas_anqp_set_element_len(buf, len); 818281806Srpaulo } 819281806Srpaulo} 820281806Srpaulo 821281806Srpaulo 822346981Scystatic void anqp_add_osu_provider_nai(struct wpabuf *buf, 823346981Scy struct hs20_osu_provider *p) 824346981Scy{ 825346981Scy /* OSU_NAI for shared BSS (Single SSID) */ 826346981Scy if (p->osu_nai2) { 827346981Scy wpabuf_put_u8(buf, os_strlen(p->osu_nai2)); 828346981Scy wpabuf_put_str(buf, p->osu_nai2); 829346981Scy } else { 830346981Scy wpabuf_put_u8(buf, 0); 831346981Scy } 832346981Scy} 833346981Scy 834346981Scy 835346981Scystatic void anqp_add_osu_providers_nai_list(struct hostapd_data *hapd, 836346981Scy struct wpabuf *buf) 837346981Scy{ 838346981Scy if (hapd->conf->hs20_osu_providers_nai_count) { 839346981Scy size_t i; 840346981Scy u8 *len = gas_anqp_add_element(buf, ANQP_VENDOR_SPECIFIC); 841346981Scy wpabuf_put_be24(buf, OUI_WFA); 842346981Scy wpabuf_put_u8(buf, HS20_ANQP_OUI_TYPE); 843346981Scy wpabuf_put_u8(buf, HS20_STYPE_OSU_PROVIDERS_NAI_LIST); 844346981Scy wpabuf_put_u8(buf, 0); /* Reserved */ 845346981Scy 846346981Scy for (i = 0; i < hapd->conf->hs20_osu_providers_count; i++) { 847346981Scy anqp_add_osu_provider_nai( 848346981Scy buf, &hapd->conf->hs20_osu_providers[i]); 849346981Scy } 850346981Scy 851346981Scy gas_anqp_set_element_len(buf, len); 852346981Scy } 853346981Scy} 854346981Scy 855346981Scy 856281806Srpaulostatic void anqp_add_icon_binary_file(struct hostapd_data *hapd, 857281806Srpaulo struct wpabuf *buf, 858281806Srpaulo const u8 *name, size_t name_len) 859281806Srpaulo{ 860281806Srpaulo struct hs20_icon *icon; 861281806Srpaulo size_t i; 862281806Srpaulo u8 *len; 863281806Srpaulo 864281806Srpaulo wpa_hexdump_ascii(MSG_DEBUG, "HS 2.0: Requested Icon Filename", 865281806Srpaulo name, name_len); 866281806Srpaulo for (i = 0; i < hapd->conf->hs20_icons_count; i++) { 867281806Srpaulo icon = &hapd->conf->hs20_icons[i]; 868281806Srpaulo if (name_len == os_strlen(icon->name) && 869281806Srpaulo os_memcmp(name, icon->name, name_len) == 0) 870281806Srpaulo break; 871281806Srpaulo } 872281806Srpaulo 873281806Srpaulo if (i < hapd->conf->hs20_icons_count) 874281806Srpaulo icon = &hapd->conf->hs20_icons[i]; 875281806Srpaulo else 876281806Srpaulo icon = NULL; 877281806Srpaulo 878281806Srpaulo len = gas_anqp_add_element(buf, ANQP_VENDOR_SPECIFIC); 879281806Srpaulo wpabuf_put_be24(buf, OUI_WFA); 880281806Srpaulo wpabuf_put_u8(buf, HS20_ANQP_OUI_TYPE); 881281806Srpaulo wpabuf_put_u8(buf, HS20_STYPE_ICON_BINARY_FILE); 882281806Srpaulo wpabuf_put_u8(buf, 0); /* Reserved */ 883281806Srpaulo 884281806Srpaulo if (icon) { 885281806Srpaulo char *data; 886281806Srpaulo size_t data_len; 887281806Srpaulo 888281806Srpaulo data = os_readfile(icon->file, &data_len); 889281806Srpaulo if (data == NULL || data_len > 65535) { 890281806Srpaulo wpabuf_put_u8(buf, 2); /* Download Status: 891281806Srpaulo * Unspecified file error */ 892281806Srpaulo wpabuf_put_u8(buf, 0); 893281806Srpaulo wpabuf_put_le16(buf, 0); 894281806Srpaulo } else { 895281806Srpaulo wpabuf_put_u8(buf, 0); /* Download Status: Success */ 896281806Srpaulo wpabuf_put_u8(buf, os_strlen(icon->type)); 897281806Srpaulo wpabuf_put_str(buf, icon->type); 898281806Srpaulo wpabuf_put_le16(buf, data_len); 899281806Srpaulo wpabuf_put_data(buf, data, data_len); 900281806Srpaulo } 901281806Srpaulo os_free(data); 902281806Srpaulo } else { 903281806Srpaulo wpabuf_put_u8(buf, 1); /* Download Status: File not found */ 904281806Srpaulo wpabuf_put_u8(buf, 0); 905281806Srpaulo wpabuf_put_le16(buf, 0); 906281806Srpaulo } 907281806Srpaulo 908281806Srpaulo gas_anqp_set_element_len(buf, len); 909281806Srpaulo} 910281806Srpaulo 911346981Scy 912346981Scystatic void anqp_add_operator_icon_metadata(struct hostapd_data *hapd, 913346981Scy struct wpabuf *buf) 914346981Scy{ 915346981Scy struct hostapd_bss_config *bss = hapd->conf; 916346981Scy size_t i; 917346981Scy u8 *len; 918346981Scy 919346981Scy if (!bss->hs20_operator_icon_count) 920346981Scy return; 921346981Scy 922346981Scy len = gas_anqp_add_element(buf, ANQP_VENDOR_SPECIFIC); 923346981Scy 924346981Scy wpabuf_put_be24(buf, OUI_WFA); 925346981Scy wpabuf_put_u8(buf, HS20_ANQP_OUI_TYPE); 926346981Scy wpabuf_put_u8(buf, HS20_STYPE_OPERATOR_ICON_METADATA); 927346981Scy wpabuf_put_u8(buf, 0); /* Reserved */ 928346981Scy 929346981Scy for (i = 0; i < bss->hs20_operator_icon_count; i++) 930346981Scy anqp_add_icon(buf, bss, bss->hs20_operator_icon[i]); 931346981Scy 932346981Scy gas_anqp_set_element_len(buf, len); 933346981Scy} 934346981Scy 935252190Srpaulo#endif /* CONFIG_HS20 */ 936252190Srpaulo 937252190Srpaulo 938346981Scy#ifdef CONFIG_MBO 939346981Scystatic void anqp_add_mbo_cell_data_conn_pref(struct hostapd_data *hapd, 940346981Scy struct wpabuf *buf) 941346981Scy{ 942346981Scy if (hapd->conf->mbo_cell_data_conn_pref >= 0) { 943346981Scy u8 *len = gas_anqp_add_element(buf, ANQP_VENDOR_SPECIFIC); 944346981Scy wpabuf_put_be24(buf, OUI_WFA); 945346981Scy wpabuf_put_u8(buf, MBO_ANQP_OUI_TYPE); 946346981Scy wpabuf_put_u8(buf, MBO_ANQP_SUBTYPE_CELL_CONN_PREF); 947346981Scy wpabuf_put_u8(buf, hapd->conf->mbo_cell_data_conn_pref); 948346981Scy gas_anqp_set_element_len(buf, len); 949346981Scy } 950346981Scy} 951346981Scy#endif /* CONFIG_MBO */ 952346981Scy 953346981Scy 954337817Scystatic size_t anqp_get_required_len(struct hostapd_data *hapd, 955337817Scy const u16 *infoid, 956337817Scy unsigned int num_infoid) 957337817Scy{ 958337817Scy size_t len = 0; 959337817Scy unsigned int i; 960337817Scy 961337817Scy for (i = 0; i < num_infoid; i++) { 962337817Scy struct anqp_element *elem = get_anqp_elem(hapd, infoid[i]); 963337817Scy 964337817Scy if (elem) 965337817Scy len += 2 + 2 + wpabuf_len(elem->payload); 966337817Scy } 967337817Scy 968337817Scy return len; 969337817Scy} 970337817Scy 971337817Scy 972252190Srpaulostatic struct wpabuf * 973252190Srpaulogas_serv_build_gas_resp_payload(struct hostapd_data *hapd, 974252190Srpaulo unsigned int request, 975281806Srpaulo const u8 *home_realm, size_t home_realm_len, 976337817Scy const u8 *icon_name, size_t icon_name_len, 977337817Scy const u16 *extra_req, 978337817Scy unsigned int num_extra_req) 979252190Srpaulo{ 980252190Srpaulo struct wpabuf *buf; 981281806Srpaulo size_t len; 982337817Scy unsigned int i; 983252190Srpaulo 984281806Srpaulo len = 1400; 985281806Srpaulo if (request & (ANQP_REQ_NAI_REALM | ANQP_REQ_NAI_HOME_REALM)) 986281806Srpaulo len += 1000; 987281806Srpaulo if (request & ANQP_REQ_ICON_REQUEST) 988281806Srpaulo len += 65536; 989346981Scy#ifdef CONFIG_FILS 990346981Scy if (request & ANQP_FILS_REALM_INFO) 991346981Scy len += 2 * dl_list_len(&hapd->conf->fils_realms); 992346981Scy#endif /* CONFIG_FILS */ 993337817Scy len += anqp_get_required_len(hapd, extra_req, num_extra_req); 994281806Srpaulo 995281806Srpaulo buf = wpabuf_alloc(len); 996252190Srpaulo if (buf == NULL) 997252190Srpaulo return NULL; 998252190Srpaulo 999252190Srpaulo if (request & ANQP_REQ_CAPABILITY_LIST) 1000252190Srpaulo anqp_add_capab_list(hapd, buf); 1001252190Srpaulo if (request & ANQP_REQ_VENUE_NAME) 1002252190Srpaulo anqp_add_venue_name(hapd, buf); 1003337817Scy if (request & ANQP_REQ_EMERGENCY_CALL_NUMBER) 1004337817Scy anqp_add_elem(hapd, buf, ANQP_EMERGENCY_CALL_NUMBER); 1005252190Srpaulo if (request & ANQP_REQ_NETWORK_AUTH_TYPE) 1006252190Srpaulo anqp_add_network_auth_type(hapd, buf); 1007252190Srpaulo if (request & ANQP_REQ_ROAMING_CONSORTIUM) 1008252190Srpaulo anqp_add_roaming_consortium(hapd, buf); 1009252190Srpaulo if (request & ANQP_REQ_IP_ADDR_TYPE_AVAILABILITY) 1010252190Srpaulo anqp_add_ip_addr_type_availability(hapd, buf); 1011252190Srpaulo if (request & (ANQP_REQ_NAI_REALM | ANQP_REQ_NAI_HOME_REALM)) 1012252190Srpaulo anqp_add_nai_realm(hapd, buf, home_realm, home_realm_len, 1013252190Srpaulo request & ANQP_REQ_NAI_REALM, 1014252190Srpaulo request & ANQP_REQ_NAI_HOME_REALM); 1015252190Srpaulo if (request & ANQP_REQ_3GPP_CELLULAR_NETWORK) 1016252190Srpaulo anqp_add_3gpp_cellular_network(hapd, buf); 1017337817Scy if (request & ANQP_REQ_AP_GEOSPATIAL_LOCATION) 1018337817Scy anqp_add_elem(hapd, buf, ANQP_AP_GEOSPATIAL_LOCATION); 1019337817Scy if (request & ANQP_REQ_AP_CIVIC_LOCATION) 1020337817Scy anqp_add_elem(hapd, buf, ANQP_AP_CIVIC_LOCATION); 1021337817Scy if (request & ANQP_REQ_AP_LOCATION_PUBLIC_URI) 1022337817Scy anqp_add_elem(hapd, buf, ANQP_AP_LOCATION_PUBLIC_URI); 1023252190Srpaulo if (request & ANQP_REQ_DOMAIN_NAME) 1024252190Srpaulo anqp_add_domain_name(hapd, buf); 1025337817Scy if (request & ANQP_REQ_EMERGENCY_ALERT_URI) 1026337817Scy anqp_add_elem(hapd, buf, ANQP_EMERGENCY_ALERT_URI); 1027337817Scy if (request & ANQP_REQ_TDLS_CAPABILITY) 1028337817Scy anqp_add_elem(hapd, buf, ANQP_TDLS_CAPABILITY); 1029337817Scy if (request & ANQP_REQ_EMERGENCY_NAI) 1030337817Scy anqp_add_elem(hapd, buf, ANQP_EMERGENCY_NAI); 1031252190Srpaulo 1032346981Scy for (i = 0; i < num_extra_req; i++) { 1033346981Scy#ifdef CONFIG_FILS 1034346981Scy if (extra_req[i] == ANQP_FILS_REALM_INFO) { 1035346981Scy anqp_add_fils_realm_info(hapd, buf); 1036346981Scy continue; 1037346981Scy } 1038346981Scy#endif /* CONFIG_FILS */ 1039346981Scy if (extra_req[i] == ANQP_VENUE_URL) { 1040346981Scy anqp_add_venue_url(hapd, buf); 1041346981Scy continue; 1042346981Scy } 1043337817Scy anqp_add_elem(hapd, buf, extra_req[i]); 1044346981Scy } 1045337817Scy 1046252190Srpaulo#ifdef CONFIG_HS20 1047252190Srpaulo if (request & ANQP_REQ_HS_CAPABILITY_LIST) 1048252190Srpaulo anqp_add_hs_capab_list(hapd, buf); 1049252190Srpaulo if (request & ANQP_REQ_OPERATOR_FRIENDLY_NAME) 1050252190Srpaulo anqp_add_operator_friendly_name(hapd, buf); 1051252190Srpaulo if (request & ANQP_REQ_WAN_METRICS) 1052252190Srpaulo anqp_add_wan_metrics(hapd, buf); 1053252190Srpaulo if (request & ANQP_REQ_CONNECTION_CAPABILITY) 1054252190Srpaulo anqp_add_connection_capability(hapd, buf); 1055252190Srpaulo if (request & ANQP_REQ_OPERATING_CLASS) 1056252190Srpaulo anqp_add_operating_class(hapd, buf); 1057281806Srpaulo if (request & ANQP_REQ_OSU_PROVIDERS_LIST) 1058281806Srpaulo anqp_add_osu_providers_list(hapd, buf); 1059281806Srpaulo if (request & ANQP_REQ_ICON_REQUEST) 1060281806Srpaulo anqp_add_icon_binary_file(hapd, buf, icon_name, icon_name_len); 1061346981Scy if (request & ANQP_REQ_OPERATOR_ICON_METADATA) 1062346981Scy anqp_add_operator_icon_metadata(hapd, buf); 1063346981Scy if (request & ANQP_REQ_OSU_PROVIDERS_NAI_LIST) 1064346981Scy anqp_add_osu_providers_nai_list(hapd, buf); 1065252190Srpaulo#endif /* CONFIG_HS20 */ 1066252190Srpaulo 1067346981Scy#ifdef CONFIG_MBO 1068346981Scy if (request & ANQP_REQ_MBO_CELL_DATA_CONN_PREF) 1069346981Scy anqp_add_mbo_cell_data_conn_pref(hapd, buf); 1070346981Scy#endif /* CONFIG_MBO */ 1071346981Scy 1072252190Srpaulo return buf; 1073252190Srpaulo} 1074252190Srpaulo 1075252190Srpaulo 1076337817Scy#define ANQP_MAX_EXTRA_REQ 20 1077337817Scy 1078252190Srpaulostruct anqp_query_info { 1079252190Srpaulo unsigned int request; 1080252190Srpaulo const u8 *home_realm_query; 1081252190Srpaulo size_t home_realm_query_len; 1082281806Srpaulo const u8 *icon_name; 1083281806Srpaulo size_t icon_name_len; 1084281806Srpaulo int p2p_sd; 1085337817Scy u16 extra_req[ANQP_MAX_EXTRA_REQ]; 1086337817Scy unsigned int num_extra_req; 1087252190Srpaulo}; 1088252190Srpaulo 1089252190Srpaulo 1090252190Srpaulostatic void set_anqp_req(unsigned int bit, const char *name, int local, 1091252190Srpaulo struct anqp_query_info *qi) 1092252190Srpaulo{ 1093252190Srpaulo qi->request |= bit; 1094252190Srpaulo if (local) { 1095252190Srpaulo wpa_printf(MSG_DEBUG, "ANQP: %s (local)", name); 1096252190Srpaulo } else { 1097252190Srpaulo wpa_printf(MSG_DEBUG, "ANQP: %s not available", name); 1098252190Srpaulo } 1099252190Srpaulo} 1100252190Srpaulo 1101252190Srpaulo 1102252190Srpaulostatic void rx_anqp_query_list_id(struct hostapd_data *hapd, u16 info_id, 1103252190Srpaulo struct anqp_query_info *qi) 1104252190Srpaulo{ 1105252190Srpaulo switch (info_id) { 1106252190Srpaulo case ANQP_CAPABILITY_LIST: 1107281806Srpaulo set_anqp_req(ANQP_REQ_CAPABILITY_LIST, "Capability List", 1, 1108281806Srpaulo qi); 1109252190Srpaulo break; 1110252190Srpaulo case ANQP_VENUE_NAME: 1111252190Srpaulo set_anqp_req(ANQP_REQ_VENUE_NAME, "Venue Name", 1112281806Srpaulo hapd->conf->venue_name != NULL, qi); 1113252190Srpaulo break; 1114337817Scy case ANQP_EMERGENCY_CALL_NUMBER: 1115337817Scy set_anqp_req(ANQP_REQ_EMERGENCY_CALL_NUMBER, 1116337817Scy "Emergency Call Number", 1117337817Scy get_anqp_elem(hapd, info_id) != NULL, qi); 1118337817Scy break; 1119252190Srpaulo case ANQP_NETWORK_AUTH_TYPE: 1120252190Srpaulo set_anqp_req(ANQP_REQ_NETWORK_AUTH_TYPE, "Network Auth Type", 1121281806Srpaulo hapd->conf->network_auth_type != NULL, qi); 1122252190Srpaulo break; 1123252190Srpaulo case ANQP_ROAMING_CONSORTIUM: 1124252190Srpaulo set_anqp_req(ANQP_REQ_ROAMING_CONSORTIUM, "Roaming Consortium", 1125281806Srpaulo hapd->conf->roaming_consortium != NULL, qi); 1126252190Srpaulo break; 1127252190Srpaulo case ANQP_IP_ADDR_TYPE_AVAILABILITY: 1128252190Srpaulo set_anqp_req(ANQP_REQ_IP_ADDR_TYPE_AVAILABILITY, 1129252190Srpaulo "IP Addr Type Availability", 1130281806Srpaulo hapd->conf->ipaddr_type_configured, qi); 1131252190Srpaulo break; 1132252190Srpaulo case ANQP_NAI_REALM: 1133252190Srpaulo set_anqp_req(ANQP_REQ_NAI_REALM, "NAI Realm", 1134281806Srpaulo hapd->conf->nai_realm_data != NULL, qi); 1135252190Srpaulo break; 1136252190Srpaulo case ANQP_3GPP_CELLULAR_NETWORK: 1137252190Srpaulo set_anqp_req(ANQP_REQ_3GPP_CELLULAR_NETWORK, 1138252190Srpaulo "3GPP Cellular Network", 1139281806Srpaulo hapd->conf->anqp_3gpp_cell_net != NULL, qi); 1140252190Srpaulo break; 1141337817Scy case ANQP_AP_GEOSPATIAL_LOCATION: 1142337817Scy set_anqp_req(ANQP_REQ_AP_GEOSPATIAL_LOCATION, 1143337817Scy "AP Geospatial Location", 1144337817Scy get_anqp_elem(hapd, info_id) != NULL, qi); 1145337817Scy break; 1146337817Scy case ANQP_AP_CIVIC_LOCATION: 1147337817Scy set_anqp_req(ANQP_REQ_AP_CIVIC_LOCATION, 1148337817Scy "AP Civic Location", 1149337817Scy get_anqp_elem(hapd, info_id) != NULL, qi); 1150337817Scy break; 1151337817Scy case ANQP_AP_LOCATION_PUBLIC_URI: 1152337817Scy set_anqp_req(ANQP_REQ_AP_LOCATION_PUBLIC_URI, 1153337817Scy "AP Location Public URI", 1154337817Scy get_anqp_elem(hapd, info_id) != NULL, qi); 1155337817Scy break; 1156252190Srpaulo case ANQP_DOMAIN_NAME: 1157252190Srpaulo set_anqp_req(ANQP_REQ_DOMAIN_NAME, "Domain Name", 1158281806Srpaulo hapd->conf->domain_name != NULL, qi); 1159252190Srpaulo break; 1160337817Scy case ANQP_EMERGENCY_ALERT_URI: 1161337817Scy set_anqp_req(ANQP_REQ_EMERGENCY_ALERT_URI, 1162337817Scy "Emergency Alert URI", 1163337817Scy get_anqp_elem(hapd, info_id) != NULL, qi); 1164337817Scy break; 1165337817Scy case ANQP_TDLS_CAPABILITY: 1166337817Scy set_anqp_req(ANQP_REQ_TDLS_CAPABILITY, 1167337817Scy "TDLS Capability", 1168337817Scy get_anqp_elem(hapd, info_id) != NULL, qi); 1169337817Scy break; 1170337817Scy case ANQP_EMERGENCY_NAI: 1171337817Scy set_anqp_req(ANQP_REQ_EMERGENCY_NAI, 1172337817Scy "Emergency NAI", 1173337817Scy get_anqp_elem(hapd, info_id) != NULL, qi); 1174337817Scy break; 1175252190Srpaulo default: 1176346981Scy#ifdef CONFIG_FILS 1177346981Scy if (info_id == ANQP_FILS_REALM_INFO && 1178346981Scy !dl_list_empty(&hapd->conf->fils_realms)) { 1179346981Scy wpa_printf(MSG_DEBUG, 1180346981Scy "ANQP: FILS Realm Information (local)"); 1181346981Scy } else 1182346981Scy#endif /* CONFIG_FILS */ 1183346981Scy if (info_id == ANQP_VENUE_URL && hapd->conf->venue_url) { 1184346981Scy wpa_printf(MSG_DEBUG, 1185346981Scy "ANQP: Venue URL (local)"); 1186346981Scy } else if (!get_anqp_elem(hapd, info_id)) { 1187337817Scy wpa_printf(MSG_DEBUG, "ANQP: Unsupported Info Id %u", 1188337817Scy info_id); 1189337817Scy break; 1190337817Scy } 1191337817Scy if (qi->num_extra_req == ANQP_MAX_EXTRA_REQ) { 1192337817Scy wpa_printf(MSG_DEBUG, 1193337817Scy "ANQP: No more room for extra requests - ignore Info Id %u", 1194337817Scy info_id); 1195337817Scy break; 1196337817Scy } 1197337817Scy wpa_printf(MSG_DEBUG, "ANQP: Info Id %u (local)", info_id); 1198337817Scy qi->extra_req[qi->num_extra_req] = info_id; 1199337817Scy qi->num_extra_req++; 1200252190Srpaulo break; 1201252190Srpaulo } 1202252190Srpaulo} 1203252190Srpaulo 1204252190Srpaulo 1205252190Srpaulostatic void rx_anqp_query_list(struct hostapd_data *hapd, 1206252190Srpaulo const u8 *pos, const u8 *end, 1207252190Srpaulo struct anqp_query_info *qi) 1208252190Srpaulo{ 1209252190Srpaulo wpa_printf(MSG_DEBUG, "ANQP: %u Info IDs requested in Query list", 1210252190Srpaulo (unsigned int) (end - pos) / 2); 1211252190Srpaulo 1212337817Scy while (end - pos >= 2) { 1213252190Srpaulo rx_anqp_query_list_id(hapd, WPA_GET_LE16(pos), qi); 1214252190Srpaulo pos += 2; 1215252190Srpaulo } 1216252190Srpaulo} 1217252190Srpaulo 1218252190Srpaulo 1219252190Srpaulo#ifdef CONFIG_HS20 1220252190Srpaulo 1221252190Srpaulostatic void rx_anqp_hs_query_list(struct hostapd_data *hapd, u8 subtype, 1222252190Srpaulo struct anqp_query_info *qi) 1223252190Srpaulo{ 1224252190Srpaulo switch (subtype) { 1225252190Srpaulo case HS20_STYPE_CAPABILITY_LIST: 1226252190Srpaulo set_anqp_req(ANQP_REQ_HS_CAPABILITY_LIST, "HS Capability List", 1227281806Srpaulo 1, qi); 1228252190Srpaulo break; 1229252190Srpaulo case HS20_STYPE_OPERATOR_FRIENDLY_NAME: 1230252190Srpaulo set_anqp_req(ANQP_REQ_OPERATOR_FRIENDLY_NAME, 1231252190Srpaulo "Operator Friendly Name", 1232281806Srpaulo hapd->conf->hs20_oper_friendly_name != NULL, qi); 1233252190Srpaulo break; 1234252190Srpaulo case HS20_STYPE_WAN_METRICS: 1235252190Srpaulo set_anqp_req(ANQP_REQ_WAN_METRICS, "WAN Metrics", 1236281806Srpaulo hapd->conf->hs20_wan_metrics != NULL, qi); 1237252190Srpaulo break; 1238252190Srpaulo case HS20_STYPE_CONNECTION_CAPABILITY: 1239252190Srpaulo set_anqp_req(ANQP_REQ_CONNECTION_CAPABILITY, 1240252190Srpaulo "Connection Capability", 1241252190Srpaulo hapd->conf->hs20_connection_capability != NULL, 1242281806Srpaulo qi); 1243252190Srpaulo break; 1244252190Srpaulo case HS20_STYPE_OPERATING_CLASS: 1245252190Srpaulo set_anqp_req(ANQP_REQ_OPERATING_CLASS, "Operating Class", 1246281806Srpaulo hapd->conf->hs20_operating_class != NULL, qi); 1247252190Srpaulo break; 1248281806Srpaulo case HS20_STYPE_OSU_PROVIDERS_LIST: 1249281806Srpaulo set_anqp_req(ANQP_REQ_OSU_PROVIDERS_LIST, "OSU Providers list", 1250281806Srpaulo hapd->conf->hs20_osu_providers_count, qi); 1251281806Srpaulo break; 1252346981Scy case HS20_STYPE_OPERATOR_ICON_METADATA: 1253346981Scy set_anqp_req(ANQP_REQ_OPERATOR_ICON_METADATA, 1254346981Scy "Operator Icon Metadata", 1255346981Scy hapd->conf->hs20_operator_icon_count, qi); 1256346981Scy break; 1257346981Scy case HS20_STYPE_OSU_PROVIDERS_NAI_LIST: 1258346981Scy set_anqp_req(ANQP_REQ_OSU_PROVIDERS_NAI_LIST, 1259346981Scy "OSU Providers NAI List", 1260346981Scy hapd->conf->hs20_osu_providers_nai_count, qi); 1261346981Scy break; 1262252190Srpaulo default: 1263252190Srpaulo wpa_printf(MSG_DEBUG, "ANQP: Unsupported HS 2.0 subtype %u", 1264252190Srpaulo subtype); 1265252190Srpaulo break; 1266252190Srpaulo } 1267252190Srpaulo} 1268252190Srpaulo 1269252190Srpaulo 1270252190Srpaulostatic void rx_anqp_hs_nai_home_realm(struct hostapd_data *hapd, 1271252190Srpaulo const u8 *pos, const u8 *end, 1272252190Srpaulo struct anqp_query_info *qi) 1273252190Srpaulo{ 1274252190Srpaulo qi->request |= ANQP_REQ_NAI_HOME_REALM; 1275252190Srpaulo qi->home_realm_query = pos; 1276252190Srpaulo qi->home_realm_query_len = end - pos; 1277252190Srpaulo if (hapd->conf->nai_realm_data != NULL) { 1278252190Srpaulo wpa_printf(MSG_DEBUG, "ANQP: HS 2.0 NAI Home Realm Query " 1279252190Srpaulo "(local)"); 1280252190Srpaulo } else { 1281252190Srpaulo wpa_printf(MSG_DEBUG, "ANQP: HS 2.0 NAI Home Realm Query not " 1282252190Srpaulo "available"); 1283252190Srpaulo } 1284252190Srpaulo} 1285252190Srpaulo 1286252190Srpaulo 1287281806Srpaulostatic void rx_anqp_hs_icon_request(struct hostapd_data *hapd, 1288281806Srpaulo const u8 *pos, const u8 *end, 1289281806Srpaulo struct anqp_query_info *qi) 1290281806Srpaulo{ 1291281806Srpaulo qi->request |= ANQP_REQ_ICON_REQUEST; 1292281806Srpaulo qi->icon_name = pos; 1293281806Srpaulo qi->icon_name_len = end - pos; 1294281806Srpaulo if (hapd->conf->hs20_icons_count) { 1295281806Srpaulo wpa_printf(MSG_DEBUG, "ANQP: HS 2.0 Icon Request Query " 1296281806Srpaulo "(local)"); 1297281806Srpaulo } else { 1298281806Srpaulo wpa_printf(MSG_DEBUG, "ANQP: HS 2.0 Icon Request Query not " 1299281806Srpaulo "available"); 1300281806Srpaulo } 1301281806Srpaulo} 1302281806Srpaulo 1303281806Srpaulo 1304346981Scystatic void rx_anqp_vendor_specific_hs20(struct hostapd_data *hapd, 1305346981Scy const u8 *pos, const u8 *end, 1306346981Scy struct anqp_query_info *qi) 1307252190Srpaulo{ 1308252190Srpaulo u8 subtype; 1309252190Srpaulo 1310337817Scy if (end - pos <= 1) 1311252190Srpaulo return; 1312252190Srpaulo 1313252190Srpaulo subtype = *pos++; 1314252190Srpaulo pos++; /* Reserved */ 1315252190Srpaulo switch (subtype) { 1316252190Srpaulo case HS20_STYPE_QUERY_LIST: 1317252190Srpaulo wpa_printf(MSG_DEBUG, "ANQP: HS 2.0 Query List"); 1318252190Srpaulo while (pos < end) { 1319252190Srpaulo rx_anqp_hs_query_list(hapd, *pos, qi); 1320252190Srpaulo pos++; 1321252190Srpaulo } 1322252190Srpaulo break; 1323252190Srpaulo case HS20_STYPE_NAI_HOME_REALM_QUERY: 1324252190Srpaulo rx_anqp_hs_nai_home_realm(hapd, pos, end, qi); 1325252190Srpaulo break; 1326281806Srpaulo case HS20_STYPE_ICON_REQUEST: 1327281806Srpaulo rx_anqp_hs_icon_request(hapd, pos, end, qi); 1328281806Srpaulo break; 1329252190Srpaulo default: 1330252190Srpaulo wpa_printf(MSG_DEBUG, "ANQP: Unsupported HS 2.0 query subtype " 1331252190Srpaulo "%u", subtype); 1332252190Srpaulo break; 1333252190Srpaulo } 1334252190Srpaulo} 1335252190Srpaulo 1336252190Srpaulo#endif /* CONFIG_HS20 */ 1337252190Srpaulo 1338252190Srpaulo 1339346981Scy#ifdef CONFIG_P2P 1340346981Scystatic void rx_anqp_vendor_specific_p2p(struct hostapd_data *hapd, 1341346981Scy struct anqp_query_info *qi) 1342346981Scy{ 1343346981Scy /* 1344346981Scy * This is for P2P SD and will be taken care of by the P2P 1345346981Scy * implementation. This query needs to be ignored in the generic 1346346981Scy * GAS server to avoid duplicated response. 1347346981Scy */ 1348346981Scy wpa_printf(MSG_DEBUG, 1349346981Scy "ANQP: Ignore WFA vendor type %u (P2P SD) in generic GAS server", 1350346981Scy P2P_OUI_TYPE); 1351346981Scy qi->p2p_sd = 1; 1352346981Scy return; 1353346981Scy} 1354346981Scy#endif /* CONFIG_P2P */ 1355346981Scy 1356346981Scy 1357346981Scy#ifdef CONFIG_MBO 1358346981Scy 1359346981Scystatic void rx_anqp_mbo_query_list(struct hostapd_data *hapd, u8 subtype, 1360346981Scy struct anqp_query_info *qi) 1361346981Scy{ 1362346981Scy switch (subtype) { 1363346981Scy case MBO_ANQP_SUBTYPE_CELL_CONN_PREF: 1364346981Scy set_anqp_req(ANQP_REQ_MBO_CELL_DATA_CONN_PREF, 1365346981Scy "Cellular Data Connection Preference", 1366346981Scy hapd->conf->mbo_cell_data_conn_pref >= 0, qi); 1367346981Scy break; 1368346981Scy default: 1369346981Scy wpa_printf(MSG_DEBUG, "ANQP: Unsupported MBO subtype %u", 1370346981Scy subtype); 1371346981Scy break; 1372346981Scy } 1373346981Scy} 1374346981Scy 1375346981Scy 1376346981Scystatic void rx_anqp_vendor_specific_mbo(struct hostapd_data *hapd, 1377346981Scy const u8 *pos, const u8 *end, 1378346981Scy struct anqp_query_info *qi) 1379346981Scy{ 1380346981Scy u8 subtype; 1381346981Scy 1382346981Scy if (end - pos < 1) 1383346981Scy return; 1384346981Scy 1385346981Scy subtype = *pos++; 1386346981Scy switch (subtype) { 1387346981Scy case MBO_ANQP_SUBTYPE_QUERY_LIST: 1388346981Scy wpa_printf(MSG_DEBUG, "ANQP: MBO Query List"); 1389346981Scy while (pos < end) { 1390346981Scy rx_anqp_mbo_query_list(hapd, *pos, qi); 1391346981Scy pos++; 1392346981Scy } 1393346981Scy break; 1394346981Scy default: 1395346981Scy wpa_printf(MSG_DEBUG, "ANQP: Unsupported MBO query subtype %u", 1396346981Scy subtype); 1397346981Scy break; 1398346981Scy } 1399346981Scy} 1400346981Scy 1401346981Scy#endif /* CONFIG_MBO */ 1402346981Scy 1403346981Scy 1404346981Scystatic void rx_anqp_vendor_specific(struct hostapd_data *hapd, 1405346981Scy const u8 *pos, const u8 *end, 1406346981Scy struct anqp_query_info *qi) 1407346981Scy{ 1408346981Scy u32 oui; 1409346981Scy 1410346981Scy if (end - pos < 4) { 1411346981Scy wpa_printf(MSG_DEBUG, "ANQP: Too short vendor specific ANQP " 1412346981Scy "Query element"); 1413346981Scy return; 1414346981Scy } 1415346981Scy 1416346981Scy oui = WPA_GET_BE24(pos); 1417346981Scy pos += 3; 1418346981Scy if (oui != OUI_WFA) { 1419346981Scy wpa_printf(MSG_DEBUG, "ANQP: Unsupported vendor OUI %06x", 1420346981Scy oui); 1421346981Scy return; 1422346981Scy } 1423346981Scy 1424346981Scy switch (*pos) { 1425346981Scy#ifdef CONFIG_P2P 1426346981Scy case P2P_OUI_TYPE: 1427346981Scy rx_anqp_vendor_specific_p2p(hapd, qi); 1428346981Scy break; 1429346981Scy#endif /* CONFIG_P2P */ 1430346981Scy#ifdef CONFIG_HS20 1431346981Scy case HS20_ANQP_OUI_TYPE: 1432346981Scy rx_anqp_vendor_specific_hs20(hapd, pos + 1, end, qi); 1433346981Scy break; 1434346981Scy#endif /* CONFIG_HS20 */ 1435346981Scy#ifdef CONFIG_MBO 1436346981Scy case MBO_ANQP_OUI_TYPE: 1437346981Scy rx_anqp_vendor_specific_mbo(hapd, pos + 1, end, qi); 1438346981Scy break; 1439346981Scy#endif /* CONFIG_MBO */ 1440346981Scy default: 1441346981Scy wpa_printf(MSG_DEBUG, "ANQP: Unsupported WFA vendor type %u", 1442346981Scy *pos); 1443346981Scy break; 1444346981Scy } 1445346981Scy} 1446346981Scy 1447346981Scy 1448252190Srpaulostatic void gas_serv_req_local_processing(struct hostapd_data *hapd, 1449252190Srpaulo const u8 *sa, u8 dialog_token, 1450337817Scy struct anqp_query_info *qi, int prot, 1451337817Scy int std_addr3) 1452252190Srpaulo{ 1453252190Srpaulo struct wpabuf *buf, *tx_buf; 1454252190Srpaulo 1455281806Srpaulo buf = gas_serv_build_gas_resp_payload(hapd, qi->request, 1456252190Srpaulo qi->home_realm_query, 1457281806Srpaulo qi->home_realm_query_len, 1458337817Scy qi->icon_name, qi->icon_name_len, 1459337817Scy qi->extra_req, qi->num_extra_req); 1460252190Srpaulo wpa_hexdump_buf(MSG_MSGDUMP, "ANQP: Locally generated ANQP responses", 1461252190Srpaulo buf); 1462252190Srpaulo if (!buf) 1463252190Srpaulo return; 1464281806Srpaulo#ifdef CONFIG_P2P 1465281806Srpaulo if (wpabuf_len(buf) == 0 && qi->p2p_sd) { 1466281806Srpaulo wpa_printf(MSG_DEBUG, 1467281806Srpaulo "ANQP: Do not send response to P2P SD from generic GAS service (P2P SD implementation will process this)"); 1468281806Srpaulo wpabuf_free(buf); 1469281806Srpaulo return; 1470281806Srpaulo } 1471281806Srpaulo#endif /* CONFIG_P2P */ 1472252190Srpaulo 1473346981Scy if (wpabuf_len(buf) > hapd->conf->gas_frag_limit || 1474252190Srpaulo hapd->conf->gas_comeback_delay) { 1475252190Srpaulo struct gas_dialog_info *di; 1476252190Srpaulo u16 comeback_delay = 1; 1477252190Srpaulo 1478252190Srpaulo if (hapd->conf->gas_comeback_delay) { 1479252190Srpaulo /* Testing - allow overriding of the delay value */ 1480252190Srpaulo comeback_delay = hapd->conf->gas_comeback_delay; 1481252190Srpaulo } 1482252190Srpaulo 1483252190Srpaulo wpa_printf(MSG_DEBUG, "ANQP: Too long response to fit in " 1484252190Srpaulo "initial response - use GAS comeback"); 1485252190Srpaulo di = gas_dialog_create(hapd, sa, dialog_token); 1486252190Srpaulo if (!di) { 1487252190Srpaulo wpa_printf(MSG_INFO, "ANQP: Could not create dialog " 1488252190Srpaulo "for " MACSTR " (dialog token %u)", 1489252190Srpaulo MAC2STR(sa), dialog_token); 1490252190Srpaulo wpabuf_free(buf); 1491281806Srpaulo tx_buf = gas_anqp_build_initial_resp_buf( 1492281806Srpaulo dialog_token, WLAN_STATUS_UNSPECIFIED_FAILURE, 1493281806Srpaulo 0, NULL); 1494281806Srpaulo } else { 1495281806Srpaulo di->prot = prot; 1496281806Srpaulo di->sd_resp = buf; 1497281806Srpaulo di->sd_resp_pos = 0; 1498281806Srpaulo tx_buf = gas_anqp_build_initial_resp_buf( 1499281806Srpaulo dialog_token, WLAN_STATUS_SUCCESS, 1500281806Srpaulo comeback_delay, NULL); 1501252190Srpaulo } 1502252190Srpaulo } else { 1503252190Srpaulo wpa_printf(MSG_DEBUG, "ANQP: Initial response (no comeback)"); 1504252190Srpaulo tx_buf = gas_anqp_build_initial_resp_buf( 1505252190Srpaulo dialog_token, WLAN_STATUS_SUCCESS, 0, buf); 1506252190Srpaulo wpabuf_free(buf); 1507252190Srpaulo } 1508252190Srpaulo if (!tx_buf) 1509252190Srpaulo return; 1510281806Srpaulo if (prot) 1511281806Srpaulo convert_to_protected_dual(tx_buf); 1512337817Scy if (std_addr3) 1513337817Scy hostapd_drv_send_action(hapd, hapd->iface->freq, 0, sa, 1514337817Scy wpabuf_head(tx_buf), 1515337817Scy wpabuf_len(tx_buf)); 1516337817Scy else 1517337817Scy hostapd_drv_send_action_addr3_ap(hapd, hapd->iface->freq, 0, sa, 1518337817Scy wpabuf_head(tx_buf), 1519337817Scy wpabuf_len(tx_buf)); 1520252190Srpaulo wpabuf_free(tx_buf); 1521252190Srpaulo} 1522252190Srpaulo 1523252190Srpaulo 1524346981Scy#ifdef CONFIG_DPP 1525351611Scyvoid gas_serv_req_dpp_processing(struct hostapd_data *hapd, 1526351611Scy const u8 *sa, u8 dialog_token, 1527351611Scy int prot, struct wpabuf *buf) 1528346981Scy{ 1529346981Scy struct wpabuf *tx_buf; 1530346981Scy 1531346981Scy if (wpabuf_len(buf) > hapd->conf->gas_frag_limit || 1532346981Scy hapd->conf->gas_comeback_delay) { 1533346981Scy struct gas_dialog_info *di; 1534346981Scy u16 comeback_delay = 1; 1535346981Scy 1536346981Scy if (hapd->conf->gas_comeback_delay) { 1537346981Scy /* Testing - allow overriding of the delay value */ 1538346981Scy comeback_delay = hapd->conf->gas_comeback_delay; 1539346981Scy } 1540346981Scy 1541346981Scy wpa_printf(MSG_DEBUG, 1542346981Scy "DPP: Too long response to fit in initial response - use GAS comeback"); 1543346981Scy di = gas_dialog_create(hapd, sa, dialog_token); 1544346981Scy if (!di) { 1545346981Scy wpa_printf(MSG_INFO, "DPP: Could not create dialog for " 1546346981Scy MACSTR " (dialog token %u)", 1547346981Scy MAC2STR(sa), dialog_token); 1548346981Scy wpabuf_free(buf); 1549346981Scy tx_buf = gas_build_initial_resp( 1550346981Scy dialog_token, WLAN_STATUS_UNSPECIFIED_FAILURE, 1551346981Scy 0, 10); 1552346981Scy if (tx_buf) 1553346981Scy gas_serv_write_dpp_adv_proto(tx_buf); 1554346981Scy } else { 1555346981Scy di->prot = prot; 1556346981Scy di->sd_resp = buf; 1557346981Scy di->sd_resp_pos = 0; 1558346981Scy tx_buf = gas_build_initial_resp( 1559346981Scy dialog_token, WLAN_STATUS_SUCCESS, 1560346981Scy comeback_delay, 10); 1561346981Scy if (tx_buf) 1562346981Scy gas_serv_write_dpp_adv_proto(tx_buf); 1563346981Scy } 1564346981Scy } else { 1565346981Scy wpa_printf(MSG_DEBUG, 1566346981Scy "DPP: GAS Initial response (no comeback)"); 1567346981Scy tx_buf = gas_build_initial_resp( 1568346981Scy dialog_token, WLAN_STATUS_SUCCESS, 0, 1569346981Scy 10 + 2 + wpabuf_len(buf)); 1570346981Scy if (tx_buf) { 1571346981Scy gas_serv_write_dpp_adv_proto(tx_buf); 1572346981Scy wpabuf_put_le16(tx_buf, wpabuf_len(buf)); 1573346981Scy wpabuf_put_buf(tx_buf, buf); 1574346981Scy hostapd_dpp_gas_status_handler(hapd, 1); 1575346981Scy } 1576346981Scy wpabuf_free(buf); 1577346981Scy } 1578346981Scy if (!tx_buf) 1579346981Scy return; 1580346981Scy if (prot) 1581346981Scy convert_to_protected_dual(tx_buf); 1582346981Scy hostapd_drv_send_action(hapd, hapd->iface->freq, 0, sa, 1583346981Scy wpabuf_head(tx_buf), 1584346981Scy wpabuf_len(tx_buf)); 1585346981Scy wpabuf_free(tx_buf); 1586346981Scy} 1587346981Scy#endif /* CONFIG_DPP */ 1588346981Scy 1589346981Scy 1590252190Srpaulostatic void gas_serv_rx_gas_initial_req(struct hostapd_data *hapd, 1591252190Srpaulo const u8 *sa, 1592337817Scy const u8 *data, size_t len, int prot, 1593337817Scy int std_addr3) 1594252190Srpaulo{ 1595252190Srpaulo const u8 *pos = data; 1596252190Srpaulo const u8 *end = data + len; 1597252190Srpaulo const u8 *next; 1598252190Srpaulo u8 dialog_token; 1599252190Srpaulo u16 slen; 1600252190Srpaulo struct anqp_query_info qi; 1601252190Srpaulo const u8 *adv_proto; 1602346981Scy#ifdef CONFIG_DPP 1603346981Scy int dpp = 0; 1604346981Scy#endif /* CONFIG_DPP */ 1605252190Srpaulo 1606252190Srpaulo if (len < 1 + 2) 1607252190Srpaulo return; 1608252190Srpaulo 1609252190Srpaulo os_memset(&qi, 0, sizeof(qi)); 1610252190Srpaulo 1611252190Srpaulo dialog_token = *pos++; 1612252190Srpaulo wpa_msg(hapd->msg_ctx, MSG_DEBUG, 1613252190Srpaulo "GAS: GAS Initial Request from " MACSTR " (dialog token %u) ", 1614252190Srpaulo MAC2STR(sa), dialog_token); 1615252190Srpaulo 1616252190Srpaulo if (*pos != WLAN_EID_ADV_PROTO) { 1617252190Srpaulo wpa_msg(hapd->msg_ctx, MSG_DEBUG, 1618252190Srpaulo "GAS: Unexpected IE in GAS Initial Request: %u", *pos); 1619252190Srpaulo return; 1620252190Srpaulo } 1621252190Srpaulo adv_proto = pos++; 1622252190Srpaulo 1623252190Srpaulo slen = *pos++; 1624337817Scy if (slen > end - pos || slen < 2) { 1625252190Srpaulo wpa_msg(hapd->msg_ctx, MSG_DEBUG, 1626252190Srpaulo "GAS: Invalid IE in GAS Initial Request"); 1627252190Srpaulo return; 1628252190Srpaulo } 1629337817Scy next = pos + slen; 1630252190Srpaulo pos++; /* skip QueryRespLenLimit and PAME-BI */ 1631252190Srpaulo 1632346981Scy#ifdef CONFIG_DPP 1633346981Scy if (slen == 8 && *pos == WLAN_EID_VENDOR_SPECIFIC && 1634346981Scy pos[1] == 5 && WPA_GET_BE24(&pos[2]) == OUI_WFA && 1635346981Scy pos[5] == DPP_OUI_TYPE && pos[6] == 0x01) { 1636346981Scy wpa_printf(MSG_DEBUG, "DPP: Configuration Request"); 1637346981Scy dpp = 1; 1638346981Scy } else 1639346981Scy#endif /* CONFIG_DPP */ 1640346981Scy 1641252190Srpaulo if (*pos != ACCESS_NETWORK_QUERY_PROTOCOL) { 1642252190Srpaulo struct wpabuf *buf; 1643252190Srpaulo wpa_msg(hapd->msg_ctx, MSG_DEBUG, 1644252190Srpaulo "GAS: Unsupported GAS advertisement protocol id %u", 1645252190Srpaulo *pos); 1646252190Srpaulo if (sa[0] & 0x01) 1647252190Srpaulo return; /* Invalid source address - drop silently */ 1648252190Srpaulo buf = gas_build_initial_resp( 1649252190Srpaulo dialog_token, WLAN_STATUS_GAS_ADV_PROTO_NOT_SUPPORTED, 1650252190Srpaulo 0, 2 + slen + 2); 1651252190Srpaulo if (buf == NULL) 1652252190Srpaulo return; 1653252190Srpaulo wpabuf_put_data(buf, adv_proto, 2 + slen); 1654252190Srpaulo wpabuf_put_le16(buf, 0); /* Query Response Length */ 1655281806Srpaulo if (prot) 1656281806Srpaulo convert_to_protected_dual(buf); 1657337817Scy if (std_addr3) 1658337817Scy hostapd_drv_send_action(hapd, hapd->iface->freq, 0, sa, 1659337817Scy wpabuf_head(buf), 1660337817Scy wpabuf_len(buf)); 1661337817Scy else 1662337817Scy hostapd_drv_send_action_addr3_ap(hapd, 1663337817Scy hapd->iface->freq, 0, 1664337817Scy sa, wpabuf_head(buf), 1665337817Scy wpabuf_len(buf)); 1666252190Srpaulo wpabuf_free(buf); 1667252190Srpaulo return; 1668252190Srpaulo } 1669252190Srpaulo 1670252190Srpaulo pos = next; 1671252190Srpaulo /* Query Request */ 1672337817Scy if (end - pos < 2) 1673252190Srpaulo return; 1674252190Srpaulo slen = WPA_GET_LE16(pos); 1675252190Srpaulo pos += 2; 1676337817Scy if (slen > end - pos) 1677252190Srpaulo return; 1678252190Srpaulo end = pos + slen; 1679252190Srpaulo 1680346981Scy#ifdef CONFIG_DPP 1681346981Scy if (dpp) { 1682346981Scy struct wpabuf *msg; 1683346981Scy 1684351611Scy msg = hostapd_dpp_gas_req_handler(hapd, sa, pos, slen, 1685351611Scy data, len); 1686346981Scy if (!msg) 1687346981Scy return; 1688346981Scy gas_serv_req_dpp_processing(hapd, sa, dialog_token, prot, msg); 1689346981Scy return; 1690346981Scy } 1691346981Scy#endif /* CONFIG_DPP */ 1692346981Scy 1693252190Srpaulo /* ANQP Query Request */ 1694252190Srpaulo while (pos < end) { 1695252190Srpaulo u16 info_id, elen; 1696252190Srpaulo 1697337817Scy if (end - pos < 4) 1698252190Srpaulo return; 1699252190Srpaulo 1700252190Srpaulo info_id = WPA_GET_LE16(pos); 1701252190Srpaulo pos += 2; 1702252190Srpaulo elen = WPA_GET_LE16(pos); 1703252190Srpaulo pos += 2; 1704252190Srpaulo 1705337817Scy if (elen > end - pos) { 1706252190Srpaulo wpa_printf(MSG_DEBUG, "ANQP: Invalid Query Request"); 1707252190Srpaulo return; 1708252190Srpaulo } 1709252190Srpaulo 1710252190Srpaulo switch (info_id) { 1711252190Srpaulo case ANQP_QUERY_LIST: 1712252190Srpaulo rx_anqp_query_list(hapd, pos, pos + elen, &qi); 1713252190Srpaulo break; 1714252190Srpaulo case ANQP_VENDOR_SPECIFIC: 1715252190Srpaulo rx_anqp_vendor_specific(hapd, pos, pos + elen, &qi); 1716252190Srpaulo break; 1717252190Srpaulo default: 1718252190Srpaulo wpa_printf(MSG_DEBUG, "ANQP: Unsupported Query " 1719252190Srpaulo "Request element %u", info_id); 1720252190Srpaulo break; 1721252190Srpaulo } 1722252190Srpaulo 1723252190Srpaulo pos += elen; 1724252190Srpaulo } 1725252190Srpaulo 1726337817Scy gas_serv_req_local_processing(hapd, sa, dialog_token, &qi, prot, 1727337817Scy std_addr3); 1728252190Srpaulo} 1729252190Srpaulo 1730252190Srpaulo 1731252190Srpaulostatic void gas_serv_rx_gas_comeback_req(struct hostapd_data *hapd, 1732252190Srpaulo const u8 *sa, 1733337817Scy const u8 *data, size_t len, int prot, 1734337817Scy int std_addr3) 1735252190Srpaulo{ 1736252190Srpaulo struct gas_dialog_info *dialog; 1737252190Srpaulo struct wpabuf *buf, *tx_buf; 1738252190Srpaulo u8 dialog_token; 1739252190Srpaulo size_t frag_len; 1740252190Srpaulo int more = 0; 1741252190Srpaulo 1742252190Srpaulo wpa_hexdump(MSG_DEBUG, "GAS: RX GAS Comeback Request", data, len); 1743252190Srpaulo if (len < 1) 1744252190Srpaulo return; 1745252190Srpaulo dialog_token = *data; 1746252190Srpaulo wpa_msg(hapd->msg_ctx, MSG_DEBUG, "GAS: Dialog Token: %u", 1747252190Srpaulo dialog_token); 1748252190Srpaulo 1749252190Srpaulo dialog = gas_serv_dialog_find(hapd, sa, dialog_token); 1750252190Srpaulo if (!dialog) { 1751252190Srpaulo wpa_msg(hapd->msg_ctx, MSG_DEBUG, "GAS: No pending SD " 1752252190Srpaulo "response fragment for " MACSTR " dialog token %u", 1753252190Srpaulo MAC2STR(sa), dialog_token); 1754252190Srpaulo 1755252190Srpaulo if (sa[0] & 0x01) 1756252190Srpaulo return; /* Invalid source address - drop silently */ 1757252190Srpaulo tx_buf = gas_anqp_build_comeback_resp_buf( 1758252190Srpaulo dialog_token, WLAN_STATUS_NO_OUTSTANDING_GAS_REQ, 0, 0, 1759252190Srpaulo 0, NULL); 1760252190Srpaulo if (tx_buf == NULL) 1761252190Srpaulo return; 1762252190Srpaulo goto send_resp; 1763252190Srpaulo } 1764252190Srpaulo 1765252190Srpaulo frag_len = wpabuf_len(dialog->sd_resp) - dialog->sd_resp_pos; 1766346981Scy if (frag_len > hapd->conf->gas_frag_limit) { 1767346981Scy frag_len = hapd->conf->gas_frag_limit; 1768252190Srpaulo more = 1; 1769252190Srpaulo } 1770252190Srpaulo wpa_msg(hapd->msg_ctx, MSG_DEBUG, "GAS: resp frag_len %u", 1771252190Srpaulo (unsigned int) frag_len); 1772252190Srpaulo buf = wpabuf_alloc_copy(wpabuf_head_u8(dialog->sd_resp) + 1773252190Srpaulo dialog->sd_resp_pos, frag_len); 1774252190Srpaulo if (buf == NULL) { 1775252190Srpaulo wpa_msg(hapd->msg_ctx, MSG_DEBUG, "GAS: Failed to allocate " 1776252190Srpaulo "buffer"); 1777281806Srpaulo gas_serv_dialog_clear(dialog); 1778281806Srpaulo return; 1779252190Srpaulo } 1780346981Scy#ifdef CONFIG_DPP 1781346981Scy if (dialog->dpp) { 1782346981Scy tx_buf = gas_build_comeback_resp(dialog_token, 1783346981Scy WLAN_STATUS_SUCCESS, 1784346981Scy dialog->sd_frag_id, more, 0, 1785346981Scy 10 + frag_len); 1786346981Scy if (tx_buf) { 1787346981Scy gas_serv_write_dpp_adv_proto(tx_buf); 1788346981Scy wpabuf_put_buf(tx_buf, buf); 1789346981Scy } 1790346981Scy } else 1791346981Scy#endif /* CONFIG_DPP */ 1792252190Srpaulo tx_buf = gas_anqp_build_comeback_resp_buf(dialog_token, 1793252190Srpaulo WLAN_STATUS_SUCCESS, 1794252190Srpaulo dialog->sd_frag_id, 1795252190Srpaulo more, 0, buf); 1796252190Srpaulo wpabuf_free(buf); 1797281806Srpaulo if (tx_buf == NULL) { 1798281806Srpaulo gas_serv_dialog_clear(dialog); 1799281806Srpaulo return; 1800281806Srpaulo } 1801252190Srpaulo wpa_msg(hapd->msg_ctx, MSG_DEBUG, "GAS: Tx GAS Comeback Response " 1802252190Srpaulo "(frag_id %d more=%d frag_len=%d)", 1803252190Srpaulo dialog->sd_frag_id, more, (int) frag_len); 1804252190Srpaulo dialog->sd_frag_id++; 1805252190Srpaulo dialog->sd_resp_pos += frag_len; 1806252190Srpaulo 1807252190Srpaulo if (more) { 1808252190Srpaulo wpa_msg(hapd->msg_ctx, MSG_DEBUG, "GAS: %d more bytes remain " 1809252190Srpaulo "to be sent", 1810252190Srpaulo (int) (wpabuf_len(dialog->sd_resp) - 1811252190Srpaulo dialog->sd_resp_pos)); 1812252190Srpaulo } else { 1813252190Srpaulo wpa_msg(hapd->msg_ctx, MSG_DEBUG, "GAS: All fragments of " 1814252190Srpaulo "SD response sent"); 1815346981Scy#ifdef CONFIG_DPP 1816346981Scy if (dialog->dpp) 1817346981Scy hostapd_dpp_gas_status_handler(hapd, 1); 1818346981Scy#endif /* CONFIG_DPP */ 1819252190Srpaulo gas_serv_dialog_clear(dialog); 1820252190Srpaulo gas_serv_free_dialogs(hapd, sa); 1821252190Srpaulo } 1822252190Srpaulo 1823252190Srpaulosend_resp: 1824281806Srpaulo if (prot) 1825281806Srpaulo convert_to_protected_dual(tx_buf); 1826337817Scy if (std_addr3) 1827337817Scy hostapd_drv_send_action(hapd, hapd->iface->freq, 0, sa, 1828337817Scy wpabuf_head(tx_buf), 1829337817Scy wpabuf_len(tx_buf)); 1830337817Scy else 1831337817Scy hostapd_drv_send_action_addr3_ap(hapd, hapd->iface->freq, 0, sa, 1832337817Scy wpabuf_head(tx_buf), 1833337817Scy wpabuf_len(tx_buf)); 1834252190Srpaulo wpabuf_free(tx_buf); 1835252190Srpaulo} 1836252190Srpaulo 1837252190Srpaulo 1838252190Srpaulostatic void gas_serv_rx_public_action(void *ctx, const u8 *buf, size_t len, 1839252190Srpaulo int freq) 1840252190Srpaulo{ 1841252190Srpaulo struct hostapd_data *hapd = ctx; 1842252190Srpaulo const struct ieee80211_mgmt *mgmt; 1843252190Srpaulo const u8 *sa, *data; 1844337817Scy int prot, std_addr3; 1845252190Srpaulo 1846252190Srpaulo mgmt = (const struct ieee80211_mgmt *) buf; 1847281806Srpaulo if (len < IEEE80211_HDRLEN + 2) 1848252190Srpaulo return; 1849281806Srpaulo if (mgmt->u.action.category != WLAN_ACTION_PUBLIC && 1850281806Srpaulo mgmt->u.action.category != WLAN_ACTION_PROTECTED_DUAL) 1851252190Srpaulo return; 1852281806Srpaulo /* 1853281806Srpaulo * Note: Public Action and Protected Dual of Public Action frames share 1854281806Srpaulo * the same payload structure, so it is fine to use definitions of 1855281806Srpaulo * Public Action frames to process both. 1856281806Srpaulo */ 1857281806Srpaulo prot = mgmt->u.action.category == WLAN_ACTION_PROTECTED_DUAL; 1858252190Srpaulo sa = mgmt->sa; 1859337817Scy if (hapd->conf->gas_address3 == 1) 1860337817Scy std_addr3 = 1; 1861337817Scy else if (hapd->conf->gas_address3 == 2) 1862337817Scy std_addr3 = 0; 1863337817Scy else 1864337817Scy std_addr3 = is_broadcast_ether_addr(mgmt->bssid); 1865281806Srpaulo len -= IEEE80211_HDRLEN + 1; 1866281806Srpaulo data = buf + IEEE80211_HDRLEN + 1; 1867252190Srpaulo switch (data[0]) { 1868252190Srpaulo case WLAN_PA_GAS_INITIAL_REQ: 1869337817Scy gas_serv_rx_gas_initial_req(hapd, sa, data + 1, len - 1, prot, 1870337817Scy std_addr3); 1871252190Srpaulo break; 1872252190Srpaulo case WLAN_PA_GAS_COMEBACK_REQ: 1873337817Scy gas_serv_rx_gas_comeback_req(hapd, sa, data + 1, len - 1, prot, 1874337817Scy std_addr3); 1875252190Srpaulo break; 1876252190Srpaulo } 1877252190Srpaulo} 1878252190Srpaulo 1879252190Srpaulo 1880252190Srpauloint gas_serv_init(struct hostapd_data *hapd) 1881252190Srpaulo{ 1882281806Srpaulo hapd->public_action_cb2 = gas_serv_rx_public_action; 1883281806Srpaulo hapd->public_action_cb2_ctx = hapd; 1884252190Srpaulo return 0; 1885252190Srpaulo} 1886252190Srpaulo 1887252190Srpaulo 1888252190Srpaulovoid gas_serv_deinit(struct hostapd_data *hapd) 1889252190Srpaulo{ 1890252190Srpaulo} 1891