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