1252190Srpaulo/* 2252190Srpaulo * Generic advertisement service (GAS) server 3252190Srpaulo * Copyright (c) 2011-2012, Qualcomm Atheros, Inc. 4252190Srpaulo * 5252190Srpaulo * This software may be distributed under the terms of the BSD license. 6252190Srpaulo * See README for more details. 7252190Srpaulo */ 8252190Srpaulo 9252190Srpaulo#include "includes.h" 10252190Srpaulo 11252190Srpaulo#include "common.h" 12252190Srpaulo#include "common/ieee802_11_defs.h" 13252190Srpaulo#include "common/gas.h" 14252190Srpaulo#include "utils/eloop.h" 15252190Srpaulo#include "hostapd.h" 16252190Srpaulo#include "ap_config.h" 17252190Srpaulo#include "ap_drv_ops.h" 18252190Srpaulo#include "sta_info.h" 19252190Srpaulo#include "gas_serv.h" 20252190Srpaulo 21252190Srpaulo 22252190Srpaulostatic struct gas_dialog_info * 23252190Srpaulogas_dialog_create(struct hostapd_data *hapd, const u8 *addr, u8 dialog_token) 24252190Srpaulo{ 25252190Srpaulo struct sta_info *sta; 26252190Srpaulo struct gas_dialog_info *dia = NULL; 27252190Srpaulo int i, j; 28252190Srpaulo 29252190Srpaulo sta = ap_get_sta(hapd, addr); 30252190Srpaulo if (!sta) { 31252190Srpaulo /* 32252190Srpaulo * We need a STA entry to be able to maintain state for 33252190Srpaulo * the GAS query. 34252190Srpaulo */ 35252190Srpaulo wpa_printf(MSG_DEBUG, "ANQP: Add a temporary STA entry for " 36252190Srpaulo "GAS query"); 37252190Srpaulo sta = ap_sta_add(hapd, addr); 38252190Srpaulo if (!sta) { 39252190Srpaulo wpa_printf(MSG_DEBUG, "Failed to add STA " MACSTR 40252190Srpaulo " for GAS query", MAC2STR(addr)); 41252190Srpaulo return NULL; 42252190Srpaulo } 43252190Srpaulo sta->flags |= WLAN_STA_GAS; 44252190Srpaulo /* 45252190Srpaulo * The default inactivity is 300 seconds. We don't need 46252190Srpaulo * it to be that long. 47252190Srpaulo */ 48252190Srpaulo ap_sta_session_timeout(hapd, sta, 5); 49252190Srpaulo } 50252190Srpaulo 51252190Srpaulo if (sta->gas_dialog == NULL) { 52252190Srpaulo sta->gas_dialog = os_zalloc(GAS_DIALOG_MAX * 53252190Srpaulo sizeof(struct gas_dialog_info)); 54252190Srpaulo if (sta->gas_dialog == NULL) 55252190Srpaulo return NULL; 56252190Srpaulo } 57252190Srpaulo 58252190Srpaulo for (i = sta->gas_dialog_next, j = 0; j < GAS_DIALOG_MAX; i++, j++) { 59252190Srpaulo if (i == GAS_DIALOG_MAX) 60252190Srpaulo i = 0; 61252190Srpaulo if (sta->gas_dialog[i].valid) 62252190Srpaulo continue; 63252190Srpaulo dia = &sta->gas_dialog[i]; 64252190Srpaulo dia->valid = 1; 65252190Srpaulo dia->index = i; 66252190Srpaulo dia->dialog_token = dialog_token; 67252190Srpaulo sta->gas_dialog_next = (++i == GAS_DIALOG_MAX) ? 0 : i; 68252190Srpaulo return dia; 69252190Srpaulo } 70252190Srpaulo 71252190Srpaulo wpa_msg(hapd->msg_ctx, MSG_ERROR, "ANQP: Could not create dialog for " 72252190Srpaulo MACSTR " dialog_token %u. Consider increasing " 73252190Srpaulo "GAS_DIALOG_MAX.", MAC2STR(addr), dialog_token); 74252190Srpaulo 75252190Srpaulo return NULL; 76252190Srpaulo} 77252190Srpaulo 78252190Srpaulo 79252190Srpaulostruct gas_dialog_info * 80252190Srpaulogas_serv_dialog_find(struct hostapd_data *hapd, const u8 *addr, 81252190Srpaulo u8 dialog_token) 82252190Srpaulo{ 83252190Srpaulo struct sta_info *sta; 84252190Srpaulo int i; 85252190Srpaulo 86252190Srpaulo sta = ap_get_sta(hapd, addr); 87252190Srpaulo if (!sta) { 88252190Srpaulo wpa_printf(MSG_DEBUG, "ANQP: could not find STA " MACSTR, 89252190Srpaulo MAC2STR(addr)); 90252190Srpaulo return NULL; 91252190Srpaulo } 92252190Srpaulo for (i = 0; sta->gas_dialog && i < GAS_DIALOG_MAX; i++) { 93252190Srpaulo if (sta->gas_dialog[i].dialog_token != dialog_token || 94252190Srpaulo !sta->gas_dialog[i].valid) 95252190Srpaulo continue; 96252190Srpaulo return &sta->gas_dialog[i]; 97252190Srpaulo } 98252190Srpaulo wpa_printf(MSG_DEBUG, "ANQP: Could not find dialog for " 99252190Srpaulo MACSTR " dialog_token %u", MAC2STR(addr), dialog_token); 100252190Srpaulo return NULL; 101252190Srpaulo} 102252190Srpaulo 103252190Srpaulo 104252190Srpaulovoid gas_serv_dialog_clear(struct gas_dialog_info *dia) 105252190Srpaulo{ 106252190Srpaulo wpabuf_free(dia->sd_resp); 107252190Srpaulo os_memset(dia, 0, sizeof(*dia)); 108252190Srpaulo} 109252190Srpaulo 110252190Srpaulo 111252190Srpaulostatic void gas_serv_free_dialogs(struct hostapd_data *hapd, 112252190Srpaulo const u8 *sta_addr) 113252190Srpaulo{ 114252190Srpaulo struct sta_info *sta; 115252190Srpaulo int i; 116252190Srpaulo 117252190Srpaulo sta = ap_get_sta(hapd, sta_addr); 118252190Srpaulo if (sta == NULL || sta->gas_dialog == NULL) 119252190Srpaulo return; 120252190Srpaulo 121252190Srpaulo for (i = 0; i < GAS_DIALOG_MAX; i++) { 122252190Srpaulo if (sta->gas_dialog[i].valid) 123252190Srpaulo return; 124252190Srpaulo } 125252190Srpaulo 126252190Srpaulo os_free(sta->gas_dialog); 127252190Srpaulo sta->gas_dialog = NULL; 128252190Srpaulo} 129252190Srpaulo 130252190Srpaulo 131252190Srpaulo#ifdef CONFIG_HS20 132252190Srpaulostatic void anqp_add_hs_capab_list(struct hostapd_data *hapd, 133252190Srpaulo struct wpabuf *buf) 134252190Srpaulo{ 135252190Srpaulo u8 *len; 136252190Srpaulo 137252190Srpaulo len = gas_anqp_add_element(buf, ANQP_VENDOR_SPECIFIC); 138252190Srpaulo wpabuf_put_be24(buf, OUI_WFA); 139252190Srpaulo wpabuf_put_u8(buf, HS20_ANQP_OUI_TYPE); 140252190Srpaulo wpabuf_put_u8(buf, HS20_STYPE_CAPABILITY_LIST); 141252190Srpaulo wpabuf_put_u8(buf, 0); /* Reserved */ 142252190Srpaulo wpabuf_put_u8(buf, HS20_STYPE_CAPABILITY_LIST); 143252190Srpaulo if (hapd->conf->hs20_oper_friendly_name) 144252190Srpaulo wpabuf_put_u8(buf, HS20_STYPE_OPERATOR_FRIENDLY_NAME); 145252190Srpaulo if (hapd->conf->hs20_wan_metrics) 146252190Srpaulo wpabuf_put_u8(buf, HS20_STYPE_WAN_METRICS); 147252190Srpaulo if (hapd->conf->hs20_connection_capability) 148252190Srpaulo wpabuf_put_u8(buf, HS20_STYPE_CONNECTION_CAPABILITY); 149252190Srpaulo if (hapd->conf->nai_realm_data) 150252190Srpaulo wpabuf_put_u8(buf, HS20_STYPE_NAI_HOME_REALM_QUERY); 151252190Srpaulo if (hapd->conf->hs20_operating_class) 152252190Srpaulo wpabuf_put_u8(buf, HS20_STYPE_OPERATING_CLASS); 153252190Srpaulo gas_anqp_set_element_len(buf, len); 154252190Srpaulo} 155252190Srpaulo#endif /* CONFIG_HS20 */ 156252190Srpaulo 157252190Srpaulo 158252190Srpaulostatic void anqp_add_capab_list(struct hostapd_data *hapd, 159252190Srpaulo struct wpabuf *buf) 160252190Srpaulo{ 161252190Srpaulo u8 *len; 162252190Srpaulo 163252190Srpaulo len = gas_anqp_add_element(buf, ANQP_CAPABILITY_LIST); 164252190Srpaulo wpabuf_put_le16(buf, ANQP_CAPABILITY_LIST); 165252190Srpaulo if (hapd->conf->venue_name) 166252190Srpaulo wpabuf_put_le16(buf, ANQP_VENUE_NAME); 167252190Srpaulo if (hapd->conf->network_auth_type) 168252190Srpaulo wpabuf_put_le16(buf, ANQP_NETWORK_AUTH_TYPE); 169252190Srpaulo if (hapd->conf->roaming_consortium) 170252190Srpaulo wpabuf_put_le16(buf, ANQP_ROAMING_CONSORTIUM); 171252190Srpaulo if (hapd->conf->ipaddr_type_configured) 172252190Srpaulo wpabuf_put_le16(buf, ANQP_IP_ADDR_TYPE_AVAILABILITY); 173252190Srpaulo if (hapd->conf->nai_realm_data) 174252190Srpaulo wpabuf_put_le16(buf, ANQP_NAI_REALM); 175252190Srpaulo if (hapd->conf->anqp_3gpp_cell_net) 176252190Srpaulo wpabuf_put_le16(buf, ANQP_3GPP_CELLULAR_NETWORK); 177252190Srpaulo if (hapd->conf->domain_name) 178252190Srpaulo wpabuf_put_le16(buf, ANQP_DOMAIN_NAME); 179252190Srpaulo#ifdef CONFIG_HS20 180252190Srpaulo anqp_add_hs_capab_list(hapd, buf); 181252190Srpaulo#endif /* CONFIG_HS20 */ 182252190Srpaulo gas_anqp_set_element_len(buf, len); 183252190Srpaulo} 184252190Srpaulo 185252190Srpaulo 186252190Srpaulostatic void anqp_add_venue_name(struct hostapd_data *hapd, struct wpabuf *buf) 187252190Srpaulo{ 188252190Srpaulo if (hapd->conf->venue_name) { 189252190Srpaulo u8 *len; 190252190Srpaulo unsigned int i; 191252190Srpaulo len = gas_anqp_add_element(buf, ANQP_VENUE_NAME); 192252190Srpaulo wpabuf_put_u8(buf, hapd->conf->venue_group); 193252190Srpaulo wpabuf_put_u8(buf, hapd->conf->venue_type); 194252190Srpaulo for (i = 0; i < hapd->conf->venue_name_count; i++) { 195252190Srpaulo struct hostapd_lang_string *vn; 196252190Srpaulo vn = &hapd->conf->venue_name[i]; 197252190Srpaulo wpabuf_put_u8(buf, 3 + vn->name_len); 198252190Srpaulo wpabuf_put_data(buf, vn->lang, 3); 199252190Srpaulo wpabuf_put_data(buf, vn->name, vn->name_len); 200252190Srpaulo } 201252190Srpaulo gas_anqp_set_element_len(buf, len); 202252190Srpaulo } 203252190Srpaulo} 204252190Srpaulo 205252190Srpaulo 206252190Srpaulostatic void anqp_add_network_auth_type(struct hostapd_data *hapd, 207252190Srpaulo struct wpabuf *buf) 208252190Srpaulo{ 209252190Srpaulo if (hapd->conf->network_auth_type) { 210252190Srpaulo wpabuf_put_le16(buf, ANQP_NETWORK_AUTH_TYPE); 211252190Srpaulo wpabuf_put_le16(buf, hapd->conf->network_auth_type_len); 212252190Srpaulo wpabuf_put_data(buf, hapd->conf->network_auth_type, 213252190Srpaulo hapd->conf->network_auth_type_len); 214252190Srpaulo } 215252190Srpaulo} 216252190Srpaulo 217252190Srpaulo 218252190Srpaulostatic void anqp_add_roaming_consortium(struct hostapd_data *hapd, 219252190Srpaulo struct wpabuf *buf) 220252190Srpaulo{ 221252190Srpaulo unsigned int i; 222252190Srpaulo u8 *len; 223252190Srpaulo 224252190Srpaulo len = gas_anqp_add_element(buf, ANQP_ROAMING_CONSORTIUM); 225252190Srpaulo for (i = 0; i < hapd->conf->roaming_consortium_count; i++) { 226252190Srpaulo struct hostapd_roaming_consortium *rc; 227252190Srpaulo rc = &hapd->conf->roaming_consortium[i]; 228252190Srpaulo wpabuf_put_u8(buf, rc->len); 229252190Srpaulo wpabuf_put_data(buf, rc->oi, rc->len); 230252190Srpaulo } 231252190Srpaulo gas_anqp_set_element_len(buf, len); 232252190Srpaulo} 233252190Srpaulo 234252190Srpaulo 235252190Srpaulostatic void anqp_add_ip_addr_type_availability(struct hostapd_data *hapd, 236252190Srpaulo struct wpabuf *buf) 237252190Srpaulo{ 238252190Srpaulo if (hapd->conf->ipaddr_type_configured) { 239252190Srpaulo wpabuf_put_le16(buf, ANQP_IP_ADDR_TYPE_AVAILABILITY); 240252190Srpaulo wpabuf_put_le16(buf, 1); 241252190Srpaulo wpabuf_put_u8(buf, hapd->conf->ipaddr_type_availability); 242252190Srpaulo } 243252190Srpaulo} 244252190Srpaulo 245252190Srpaulo 246252190Srpaulostatic void anqp_add_nai_realm_eap(struct wpabuf *buf, 247252190Srpaulo struct hostapd_nai_realm_data *realm) 248252190Srpaulo{ 249252190Srpaulo unsigned int i, j; 250252190Srpaulo 251252190Srpaulo wpabuf_put_u8(buf, realm->eap_method_count); 252252190Srpaulo 253252190Srpaulo for (i = 0; i < realm->eap_method_count; i++) { 254252190Srpaulo struct hostapd_nai_realm_eap *eap = &realm->eap_method[i]; 255252190Srpaulo wpabuf_put_u8(buf, 2 + (3 * eap->num_auths)); 256252190Srpaulo wpabuf_put_u8(buf, eap->eap_method); 257252190Srpaulo wpabuf_put_u8(buf, eap->num_auths); 258252190Srpaulo for (j = 0; j < eap->num_auths; j++) { 259252190Srpaulo wpabuf_put_u8(buf, eap->auth_id[j]); 260252190Srpaulo wpabuf_put_u8(buf, 1); 261252190Srpaulo wpabuf_put_u8(buf, eap->auth_val[j]); 262252190Srpaulo } 263252190Srpaulo } 264252190Srpaulo} 265252190Srpaulo 266252190Srpaulo 267252190Srpaulostatic void anqp_add_nai_realm_data(struct wpabuf *buf, 268252190Srpaulo struct hostapd_nai_realm_data *realm, 269252190Srpaulo unsigned int realm_idx) 270252190Srpaulo{ 271252190Srpaulo u8 *realm_data_len; 272252190Srpaulo 273252190Srpaulo wpa_printf(MSG_DEBUG, "realm=%s, len=%d", realm->realm[realm_idx], 274252190Srpaulo (int) os_strlen(realm->realm[realm_idx])); 275252190Srpaulo realm_data_len = wpabuf_put(buf, 2); 276252190Srpaulo wpabuf_put_u8(buf, realm->encoding); 277252190Srpaulo wpabuf_put_u8(buf, os_strlen(realm->realm[realm_idx])); 278252190Srpaulo wpabuf_put_str(buf, realm->realm[realm_idx]); 279252190Srpaulo anqp_add_nai_realm_eap(buf, realm); 280252190Srpaulo gas_anqp_set_element_len(buf, realm_data_len); 281252190Srpaulo} 282252190Srpaulo 283252190Srpaulo 284252190Srpaulostatic int hs20_add_nai_home_realm_matches(struct hostapd_data *hapd, 285252190Srpaulo struct wpabuf *buf, 286252190Srpaulo const u8 *home_realm, 287252190Srpaulo size_t home_realm_len) 288252190Srpaulo{ 289252190Srpaulo unsigned int i, j, k; 290252190Srpaulo u8 num_realms, num_matching = 0, encoding, realm_len, *realm_list_len; 291252190Srpaulo struct hostapd_nai_realm_data *realm; 292252190Srpaulo const u8 *pos, *realm_name, *end; 293252190Srpaulo struct { 294252190Srpaulo unsigned int realm_data_idx; 295252190Srpaulo unsigned int realm_idx; 296252190Srpaulo } matches[10]; 297252190Srpaulo 298252190Srpaulo pos = home_realm; 299252190Srpaulo end = pos + home_realm_len; 300252190Srpaulo if (pos + 1 > end) { 301252190Srpaulo wpa_hexdump(MSG_DEBUG, "Too short NAI Home Realm Query", 302252190Srpaulo home_realm, home_realm_len); 303252190Srpaulo return -1; 304252190Srpaulo } 305252190Srpaulo num_realms = *pos++; 306252190Srpaulo 307252190Srpaulo for (i = 0; i < num_realms && num_matching < 10; i++) { 308252190Srpaulo if (pos + 2 > end) { 309252190Srpaulo wpa_hexdump(MSG_DEBUG, 310252190Srpaulo "Truncated NAI Home Realm Query", 311252190Srpaulo home_realm, home_realm_len); 312252190Srpaulo return -1; 313252190Srpaulo } 314252190Srpaulo encoding = *pos++; 315252190Srpaulo realm_len = *pos++; 316252190Srpaulo if (pos + realm_len > end) { 317252190Srpaulo wpa_hexdump(MSG_DEBUG, 318252190Srpaulo "Truncated NAI Home Realm Query", 319252190Srpaulo home_realm, home_realm_len); 320252190Srpaulo return -1; 321252190Srpaulo } 322252190Srpaulo realm_name = pos; 323252190Srpaulo for (j = 0; j < hapd->conf->nai_realm_count && 324252190Srpaulo num_matching < 10; j++) { 325252190Srpaulo const u8 *rpos, *rend; 326252190Srpaulo realm = &hapd->conf->nai_realm_data[j]; 327252190Srpaulo if (encoding != realm->encoding) 328252190Srpaulo continue; 329252190Srpaulo 330252190Srpaulo rpos = realm_name; 331252190Srpaulo while (rpos < realm_name + realm_len && 332252190Srpaulo num_matching < 10) { 333252190Srpaulo for (rend = rpos; 334252190Srpaulo rend < realm_name + realm_len; rend++) { 335252190Srpaulo if (*rend == ';') 336252190Srpaulo break; 337252190Srpaulo } 338252190Srpaulo for (k = 0; k < MAX_NAI_REALMS && 339252190Srpaulo realm->realm[k] && 340252190Srpaulo num_matching < 10; k++) { 341252190Srpaulo if ((int) os_strlen(realm->realm[k]) != 342252190Srpaulo rend - rpos || 343252190Srpaulo os_strncmp((char *) rpos, 344252190Srpaulo realm->realm[k], 345252190Srpaulo rend - rpos) != 0) 346252190Srpaulo continue; 347252190Srpaulo matches[num_matching].realm_data_idx = 348252190Srpaulo j; 349252190Srpaulo matches[num_matching].realm_idx = k; 350252190Srpaulo num_matching++; 351252190Srpaulo } 352252190Srpaulo rpos = rend + 1; 353252190Srpaulo } 354252190Srpaulo } 355252190Srpaulo pos += realm_len; 356252190Srpaulo } 357252190Srpaulo 358252190Srpaulo realm_list_len = gas_anqp_add_element(buf, ANQP_NAI_REALM); 359252190Srpaulo wpabuf_put_le16(buf, num_matching); 360252190Srpaulo 361252190Srpaulo /* 362252190Srpaulo * There are two ways to format. 1. each realm in a NAI Realm Data unit 363252190Srpaulo * 2. all realms that share the same EAP methods in a NAI Realm Data 364252190Srpaulo * unit. The first format is likely to be bigger in size than the 365252190Srpaulo * second, but may be easier to parse and process by the receiver. 366252190Srpaulo */ 367252190Srpaulo for (i = 0; i < num_matching; i++) { 368252190Srpaulo wpa_printf(MSG_DEBUG, "realm_idx %d, realm_data_idx %d", 369252190Srpaulo matches[i].realm_data_idx, matches[i].realm_idx); 370252190Srpaulo realm = &hapd->conf->nai_realm_data[matches[i].realm_data_idx]; 371252190Srpaulo anqp_add_nai_realm_data(buf, realm, matches[i].realm_idx); 372252190Srpaulo } 373252190Srpaulo gas_anqp_set_element_len(buf, realm_list_len); 374252190Srpaulo return 0; 375252190Srpaulo} 376252190Srpaulo 377252190Srpaulo 378252190Srpaulostatic void anqp_add_nai_realm(struct hostapd_data *hapd, struct wpabuf *buf, 379252190Srpaulo const u8 *home_realm, size_t home_realm_len, 380252190Srpaulo int nai_realm, int nai_home_realm) 381252190Srpaulo{ 382252190Srpaulo if (nai_realm && hapd->conf->nai_realm_data) { 383252190Srpaulo u8 *len; 384252190Srpaulo unsigned int i, j; 385252190Srpaulo len = gas_anqp_add_element(buf, ANQP_NAI_REALM); 386252190Srpaulo wpabuf_put_le16(buf, hapd->conf->nai_realm_count); 387252190Srpaulo for (i = 0; i < hapd->conf->nai_realm_count; i++) { 388252190Srpaulo u8 *realm_data_len, *realm_len; 389252190Srpaulo struct hostapd_nai_realm_data *realm; 390252190Srpaulo 391252190Srpaulo realm = &hapd->conf->nai_realm_data[i]; 392252190Srpaulo realm_data_len = wpabuf_put(buf, 2); 393252190Srpaulo wpabuf_put_u8(buf, realm->encoding); 394252190Srpaulo realm_len = wpabuf_put(buf, 1); 395252190Srpaulo for (j = 0; realm->realm[j]; j++) { 396252190Srpaulo if (j > 0) 397252190Srpaulo wpabuf_put_u8(buf, ';'); 398252190Srpaulo wpabuf_put_str(buf, realm->realm[j]); 399252190Srpaulo } 400252190Srpaulo *realm_len = (u8 *) wpabuf_put(buf, 0) - realm_len - 1; 401252190Srpaulo anqp_add_nai_realm_eap(buf, realm); 402252190Srpaulo gas_anqp_set_element_len(buf, realm_data_len); 403252190Srpaulo } 404252190Srpaulo gas_anqp_set_element_len(buf, len); 405252190Srpaulo } else if (nai_home_realm && hapd->conf->nai_realm_data) { 406252190Srpaulo hs20_add_nai_home_realm_matches(hapd, buf, home_realm, 407252190Srpaulo home_realm_len); 408252190Srpaulo } 409252190Srpaulo} 410252190Srpaulo 411252190Srpaulo 412252190Srpaulostatic void anqp_add_3gpp_cellular_network(struct hostapd_data *hapd, 413252190Srpaulo struct wpabuf *buf) 414252190Srpaulo{ 415252190Srpaulo if (hapd->conf->anqp_3gpp_cell_net) { 416252190Srpaulo wpabuf_put_le16(buf, ANQP_3GPP_CELLULAR_NETWORK); 417252190Srpaulo wpabuf_put_le16(buf, 418252190Srpaulo hapd->conf->anqp_3gpp_cell_net_len); 419252190Srpaulo wpabuf_put_data(buf, hapd->conf->anqp_3gpp_cell_net, 420252190Srpaulo hapd->conf->anqp_3gpp_cell_net_len); 421252190Srpaulo } 422252190Srpaulo} 423252190Srpaulo 424252190Srpaulo 425252190Srpaulostatic void anqp_add_domain_name(struct hostapd_data *hapd, struct wpabuf *buf) 426252190Srpaulo{ 427252190Srpaulo if (hapd->conf->domain_name) { 428252190Srpaulo wpabuf_put_le16(buf, ANQP_DOMAIN_NAME); 429252190Srpaulo wpabuf_put_le16(buf, hapd->conf->domain_name_len); 430252190Srpaulo wpabuf_put_data(buf, hapd->conf->domain_name, 431252190Srpaulo hapd->conf->domain_name_len); 432252190Srpaulo } 433252190Srpaulo} 434252190Srpaulo 435252190Srpaulo 436252190Srpaulo#ifdef CONFIG_HS20 437252190Srpaulo 438252190Srpaulostatic void anqp_add_operator_friendly_name(struct hostapd_data *hapd, 439252190Srpaulo struct wpabuf *buf) 440252190Srpaulo{ 441252190Srpaulo if (hapd->conf->hs20_oper_friendly_name) { 442252190Srpaulo u8 *len; 443252190Srpaulo unsigned int i; 444252190Srpaulo len = gas_anqp_add_element(buf, ANQP_VENDOR_SPECIFIC); 445252190Srpaulo wpabuf_put_be24(buf, OUI_WFA); 446252190Srpaulo wpabuf_put_u8(buf, HS20_ANQP_OUI_TYPE); 447252190Srpaulo wpabuf_put_u8(buf, HS20_STYPE_OPERATOR_FRIENDLY_NAME); 448252190Srpaulo wpabuf_put_u8(buf, 0); /* Reserved */ 449252190Srpaulo for (i = 0; i < hapd->conf->hs20_oper_friendly_name_count; i++) 450252190Srpaulo { 451252190Srpaulo struct hostapd_lang_string *vn; 452252190Srpaulo vn = &hapd->conf->hs20_oper_friendly_name[i]; 453252190Srpaulo wpabuf_put_u8(buf, 3 + vn->name_len); 454252190Srpaulo wpabuf_put_data(buf, vn->lang, 3); 455252190Srpaulo wpabuf_put_data(buf, vn->name, vn->name_len); 456252190Srpaulo } 457252190Srpaulo gas_anqp_set_element_len(buf, len); 458252190Srpaulo } 459252190Srpaulo} 460252190Srpaulo 461252190Srpaulo 462252190Srpaulostatic void anqp_add_wan_metrics(struct hostapd_data *hapd, 463252190Srpaulo struct wpabuf *buf) 464252190Srpaulo{ 465252190Srpaulo if (hapd->conf->hs20_wan_metrics) { 466252190Srpaulo u8 *len = gas_anqp_add_element(buf, ANQP_VENDOR_SPECIFIC); 467252190Srpaulo wpabuf_put_be24(buf, OUI_WFA); 468252190Srpaulo wpabuf_put_u8(buf, HS20_ANQP_OUI_TYPE); 469252190Srpaulo wpabuf_put_u8(buf, HS20_STYPE_WAN_METRICS); 470252190Srpaulo wpabuf_put_u8(buf, 0); /* Reserved */ 471252190Srpaulo wpabuf_put_data(buf, hapd->conf->hs20_wan_metrics, 13); 472252190Srpaulo gas_anqp_set_element_len(buf, len); 473252190Srpaulo } 474252190Srpaulo} 475252190Srpaulo 476252190Srpaulo 477252190Srpaulostatic void anqp_add_connection_capability(struct hostapd_data *hapd, 478252190Srpaulo struct wpabuf *buf) 479252190Srpaulo{ 480252190Srpaulo if (hapd->conf->hs20_connection_capability) { 481252190Srpaulo u8 *len = gas_anqp_add_element(buf, ANQP_VENDOR_SPECIFIC); 482252190Srpaulo wpabuf_put_be24(buf, OUI_WFA); 483252190Srpaulo wpabuf_put_u8(buf, HS20_ANQP_OUI_TYPE); 484252190Srpaulo wpabuf_put_u8(buf, HS20_STYPE_CONNECTION_CAPABILITY); 485252190Srpaulo wpabuf_put_u8(buf, 0); /* Reserved */ 486252190Srpaulo wpabuf_put_data(buf, hapd->conf->hs20_connection_capability, 487252190Srpaulo hapd->conf->hs20_connection_capability_len); 488252190Srpaulo gas_anqp_set_element_len(buf, len); 489252190Srpaulo } 490252190Srpaulo} 491252190Srpaulo 492252190Srpaulo 493252190Srpaulostatic void anqp_add_operating_class(struct hostapd_data *hapd, 494252190Srpaulo struct wpabuf *buf) 495252190Srpaulo{ 496252190Srpaulo if (hapd->conf->hs20_operating_class) { 497252190Srpaulo u8 *len = gas_anqp_add_element(buf, ANQP_VENDOR_SPECIFIC); 498252190Srpaulo wpabuf_put_be24(buf, OUI_WFA); 499252190Srpaulo wpabuf_put_u8(buf, HS20_ANQP_OUI_TYPE); 500252190Srpaulo wpabuf_put_u8(buf, HS20_STYPE_OPERATING_CLASS); 501252190Srpaulo wpabuf_put_u8(buf, 0); /* Reserved */ 502252190Srpaulo wpabuf_put_data(buf, hapd->conf->hs20_operating_class, 503252190Srpaulo hapd->conf->hs20_operating_class_len); 504252190Srpaulo gas_anqp_set_element_len(buf, len); 505252190Srpaulo } 506252190Srpaulo} 507252190Srpaulo 508252190Srpaulo#endif /* CONFIG_HS20 */ 509252190Srpaulo 510252190Srpaulo 511252190Srpaulostatic struct wpabuf * 512252190Srpaulogas_serv_build_gas_resp_payload(struct hostapd_data *hapd, 513252190Srpaulo unsigned int request, 514252190Srpaulo struct gas_dialog_info *di, 515252190Srpaulo const u8 *home_realm, size_t home_realm_len) 516252190Srpaulo{ 517252190Srpaulo struct wpabuf *buf; 518252190Srpaulo 519252190Srpaulo buf = wpabuf_alloc(1400); 520252190Srpaulo if (buf == NULL) 521252190Srpaulo return NULL; 522252190Srpaulo 523252190Srpaulo if (request & ANQP_REQ_CAPABILITY_LIST) 524252190Srpaulo anqp_add_capab_list(hapd, buf); 525252190Srpaulo if (request & ANQP_REQ_VENUE_NAME) 526252190Srpaulo anqp_add_venue_name(hapd, buf); 527252190Srpaulo if (request & ANQP_REQ_NETWORK_AUTH_TYPE) 528252190Srpaulo anqp_add_network_auth_type(hapd, buf); 529252190Srpaulo if (request & ANQP_REQ_ROAMING_CONSORTIUM) 530252190Srpaulo anqp_add_roaming_consortium(hapd, buf); 531252190Srpaulo if (request & ANQP_REQ_IP_ADDR_TYPE_AVAILABILITY) 532252190Srpaulo anqp_add_ip_addr_type_availability(hapd, buf); 533252190Srpaulo if (request & (ANQP_REQ_NAI_REALM | ANQP_REQ_NAI_HOME_REALM)) 534252190Srpaulo anqp_add_nai_realm(hapd, buf, home_realm, home_realm_len, 535252190Srpaulo request & ANQP_REQ_NAI_REALM, 536252190Srpaulo request & ANQP_REQ_NAI_HOME_REALM); 537252190Srpaulo if (request & ANQP_REQ_3GPP_CELLULAR_NETWORK) 538252190Srpaulo anqp_add_3gpp_cellular_network(hapd, buf); 539252190Srpaulo if (request & ANQP_REQ_DOMAIN_NAME) 540252190Srpaulo anqp_add_domain_name(hapd, buf); 541252190Srpaulo 542252190Srpaulo#ifdef CONFIG_HS20 543252190Srpaulo if (request & ANQP_REQ_HS_CAPABILITY_LIST) 544252190Srpaulo anqp_add_hs_capab_list(hapd, buf); 545252190Srpaulo if (request & ANQP_REQ_OPERATOR_FRIENDLY_NAME) 546252190Srpaulo anqp_add_operator_friendly_name(hapd, buf); 547252190Srpaulo if (request & ANQP_REQ_WAN_METRICS) 548252190Srpaulo anqp_add_wan_metrics(hapd, buf); 549252190Srpaulo if (request & ANQP_REQ_CONNECTION_CAPABILITY) 550252190Srpaulo anqp_add_connection_capability(hapd, buf); 551252190Srpaulo if (request & ANQP_REQ_OPERATING_CLASS) 552252190Srpaulo anqp_add_operating_class(hapd, buf); 553252190Srpaulo#endif /* CONFIG_HS20 */ 554252190Srpaulo 555252190Srpaulo return buf; 556252190Srpaulo} 557252190Srpaulo 558252190Srpaulo 559252190Srpaulostatic void gas_serv_clear_cached_ies(void *eloop_data, void *user_ctx) 560252190Srpaulo{ 561252190Srpaulo struct gas_dialog_info *dia = eloop_data; 562252190Srpaulo 563252190Srpaulo wpa_printf(MSG_DEBUG, "GAS: Timeout triggered, clearing dialog for " 564252190Srpaulo "dialog token %d", dia->dialog_token); 565252190Srpaulo 566252190Srpaulo gas_serv_dialog_clear(dia); 567252190Srpaulo} 568252190Srpaulo 569252190Srpaulo 570252190Srpaulostruct anqp_query_info { 571252190Srpaulo unsigned int request; 572252190Srpaulo unsigned int remote_request; 573252190Srpaulo const u8 *home_realm_query; 574252190Srpaulo size_t home_realm_query_len; 575252190Srpaulo u16 remote_delay; 576252190Srpaulo}; 577252190Srpaulo 578252190Srpaulo 579252190Srpaulostatic void set_anqp_req(unsigned int bit, const char *name, int local, 580252190Srpaulo unsigned int remote, u16 remote_delay, 581252190Srpaulo struct anqp_query_info *qi) 582252190Srpaulo{ 583252190Srpaulo qi->request |= bit; 584252190Srpaulo if (local) { 585252190Srpaulo wpa_printf(MSG_DEBUG, "ANQP: %s (local)", name); 586252190Srpaulo } else if (bit & remote) { 587252190Srpaulo wpa_printf(MSG_DEBUG, "ANQP: %s (remote)", name); 588252190Srpaulo qi->remote_request |= bit; 589252190Srpaulo if (remote_delay > qi->remote_delay) 590252190Srpaulo qi->remote_delay = remote_delay; 591252190Srpaulo } else { 592252190Srpaulo wpa_printf(MSG_DEBUG, "ANQP: %s not available", name); 593252190Srpaulo } 594252190Srpaulo} 595252190Srpaulo 596252190Srpaulo 597252190Srpaulostatic void rx_anqp_query_list_id(struct hostapd_data *hapd, u16 info_id, 598252190Srpaulo struct anqp_query_info *qi) 599252190Srpaulo{ 600252190Srpaulo switch (info_id) { 601252190Srpaulo case ANQP_CAPABILITY_LIST: 602252190Srpaulo set_anqp_req(ANQP_REQ_CAPABILITY_LIST, "Capability List", 1, 0, 603252190Srpaulo 0, qi); 604252190Srpaulo break; 605252190Srpaulo case ANQP_VENUE_NAME: 606252190Srpaulo set_anqp_req(ANQP_REQ_VENUE_NAME, "Venue Name", 607252190Srpaulo hapd->conf->venue_name != NULL, 0, 0, qi); 608252190Srpaulo break; 609252190Srpaulo case ANQP_NETWORK_AUTH_TYPE: 610252190Srpaulo set_anqp_req(ANQP_REQ_NETWORK_AUTH_TYPE, "Network Auth Type", 611252190Srpaulo hapd->conf->network_auth_type != NULL, 612252190Srpaulo 0, 0, qi); 613252190Srpaulo break; 614252190Srpaulo case ANQP_ROAMING_CONSORTIUM: 615252190Srpaulo set_anqp_req(ANQP_REQ_ROAMING_CONSORTIUM, "Roaming Consortium", 616252190Srpaulo hapd->conf->roaming_consortium != NULL, 0, 0, qi); 617252190Srpaulo break; 618252190Srpaulo case ANQP_IP_ADDR_TYPE_AVAILABILITY: 619252190Srpaulo set_anqp_req(ANQP_REQ_IP_ADDR_TYPE_AVAILABILITY, 620252190Srpaulo "IP Addr Type Availability", 621252190Srpaulo hapd->conf->ipaddr_type_configured, 622252190Srpaulo 0, 0, qi); 623252190Srpaulo break; 624252190Srpaulo case ANQP_NAI_REALM: 625252190Srpaulo set_anqp_req(ANQP_REQ_NAI_REALM, "NAI Realm", 626252190Srpaulo hapd->conf->nai_realm_data != NULL, 627252190Srpaulo 0, 0, qi); 628252190Srpaulo break; 629252190Srpaulo case ANQP_3GPP_CELLULAR_NETWORK: 630252190Srpaulo set_anqp_req(ANQP_REQ_3GPP_CELLULAR_NETWORK, 631252190Srpaulo "3GPP Cellular Network", 632252190Srpaulo hapd->conf->anqp_3gpp_cell_net != NULL, 633252190Srpaulo 0, 0, qi); 634252190Srpaulo break; 635252190Srpaulo case ANQP_DOMAIN_NAME: 636252190Srpaulo set_anqp_req(ANQP_REQ_DOMAIN_NAME, "Domain Name", 637252190Srpaulo hapd->conf->domain_name != NULL, 638252190Srpaulo 0, 0, qi); 639252190Srpaulo break; 640252190Srpaulo default: 641252190Srpaulo wpa_printf(MSG_DEBUG, "ANQP: Unsupported Info Id %u", 642252190Srpaulo info_id); 643252190Srpaulo break; 644252190Srpaulo } 645252190Srpaulo} 646252190Srpaulo 647252190Srpaulo 648252190Srpaulostatic void rx_anqp_query_list(struct hostapd_data *hapd, 649252190Srpaulo const u8 *pos, const u8 *end, 650252190Srpaulo struct anqp_query_info *qi) 651252190Srpaulo{ 652252190Srpaulo wpa_printf(MSG_DEBUG, "ANQP: %u Info IDs requested in Query list", 653252190Srpaulo (unsigned int) (end - pos) / 2); 654252190Srpaulo 655252190Srpaulo while (pos + 2 <= end) { 656252190Srpaulo rx_anqp_query_list_id(hapd, WPA_GET_LE16(pos), qi); 657252190Srpaulo pos += 2; 658252190Srpaulo } 659252190Srpaulo} 660252190Srpaulo 661252190Srpaulo 662252190Srpaulo#ifdef CONFIG_HS20 663252190Srpaulo 664252190Srpaulostatic void rx_anqp_hs_query_list(struct hostapd_data *hapd, u8 subtype, 665252190Srpaulo struct anqp_query_info *qi) 666252190Srpaulo{ 667252190Srpaulo switch (subtype) { 668252190Srpaulo case HS20_STYPE_CAPABILITY_LIST: 669252190Srpaulo set_anqp_req(ANQP_REQ_HS_CAPABILITY_LIST, "HS Capability List", 670252190Srpaulo 1, 0, 0, qi); 671252190Srpaulo break; 672252190Srpaulo case HS20_STYPE_OPERATOR_FRIENDLY_NAME: 673252190Srpaulo set_anqp_req(ANQP_REQ_OPERATOR_FRIENDLY_NAME, 674252190Srpaulo "Operator Friendly Name", 675252190Srpaulo hapd->conf->hs20_oper_friendly_name != NULL, 676252190Srpaulo 0, 0, qi); 677252190Srpaulo break; 678252190Srpaulo case HS20_STYPE_WAN_METRICS: 679252190Srpaulo set_anqp_req(ANQP_REQ_WAN_METRICS, "WAN Metrics", 680252190Srpaulo hapd->conf->hs20_wan_metrics != NULL, 681252190Srpaulo 0, 0, qi); 682252190Srpaulo break; 683252190Srpaulo case HS20_STYPE_CONNECTION_CAPABILITY: 684252190Srpaulo set_anqp_req(ANQP_REQ_CONNECTION_CAPABILITY, 685252190Srpaulo "Connection Capability", 686252190Srpaulo hapd->conf->hs20_connection_capability != NULL, 687252190Srpaulo 0, 0, qi); 688252190Srpaulo break; 689252190Srpaulo case HS20_STYPE_OPERATING_CLASS: 690252190Srpaulo set_anqp_req(ANQP_REQ_OPERATING_CLASS, "Operating Class", 691252190Srpaulo hapd->conf->hs20_operating_class != NULL, 692252190Srpaulo 0, 0, qi); 693252190Srpaulo break; 694252190Srpaulo default: 695252190Srpaulo wpa_printf(MSG_DEBUG, "ANQP: Unsupported HS 2.0 subtype %u", 696252190Srpaulo subtype); 697252190Srpaulo break; 698252190Srpaulo } 699252190Srpaulo} 700252190Srpaulo 701252190Srpaulo 702252190Srpaulostatic void rx_anqp_hs_nai_home_realm(struct hostapd_data *hapd, 703252190Srpaulo const u8 *pos, const u8 *end, 704252190Srpaulo struct anqp_query_info *qi) 705252190Srpaulo{ 706252190Srpaulo qi->request |= ANQP_REQ_NAI_HOME_REALM; 707252190Srpaulo qi->home_realm_query = pos; 708252190Srpaulo qi->home_realm_query_len = end - pos; 709252190Srpaulo if (hapd->conf->nai_realm_data != NULL) { 710252190Srpaulo wpa_printf(MSG_DEBUG, "ANQP: HS 2.0 NAI Home Realm Query " 711252190Srpaulo "(local)"); 712252190Srpaulo } else { 713252190Srpaulo wpa_printf(MSG_DEBUG, "ANQP: HS 2.0 NAI Home Realm Query not " 714252190Srpaulo "available"); 715252190Srpaulo } 716252190Srpaulo} 717252190Srpaulo 718252190Srpaulo 719252190Srpaulostatic void rx_anqp_vendor_specific(struct hostapd_data *hapd, 720252190Srpaulo const u8 *pos, const u8 *end, 721252190Srpaulo struct anqp_query_info *qi) 722252190Srpaulo{ 723252190Srpaulo u32 oui; 724252190Srpaulo u8 subtype; 725252190Srpaulo 726252190Srpaulo if (pos + 4 > end) { 727252190Srpaulo wpa_printf(MSG_DEBUG, "ANQP: Too short vendor specific ANQP " 728252190Srpaulo "Query element"); 729252190Srpaulo return; 730252190Srpaulo } 731252190Srpaulo 732252190Srpaulo oui = WPA_GET_BE24(pos); 733252190Srpaulo pos += 3; 734252190Srpaulo if (oui != OUI_WFA) { 735252190Srpaulo wpa_printf(MSG_DEBUG, "ANQP: Unsupported vendor OUI %06x", 736252190Srpaulo oui); 737252190Srpaulo return; 738252190Srpaulo } 739252190Srpaulo 740252190Srpaulo if (*pos != HS20_ANQP_OUI_TYPE) { 741252190Srpaulo wpa_printf(MSG_DEBUG, "ANQP: Unsupported WFA vendor type %u", 742252190Srpaulo *pos); 743252190Srpaulo return; 744252190Srpaulo } 745252190Srpaulo pos++; 746252190Srpaulo 747252190Srpaulo if (pos + 1 >= end) 748252190Srpaulo return; 749252190Srpaulo 750252190Srpaulo subtype = *pos++; 751252190Srpaulo pos++; /* Reserved */ 752252190Srpaulo switch (subtype) { 753252190Srpaulo case HS20_STYPE_QUERY_LIST: 754252190Srpaulo wpa_printf(MSG_DEBUG, "ANQP: HS 2.0 Query List"); 755252190Srpaulo while (pos < end) { 756252190Srpaulo rx_anqp_hs_query_list(hapd, *pos, qi); 757252190Srpaulo pos++; 758252190Srpaulo } 759252190Srpaulo break; 760252190Srpaulo case HS20_STYPE_NAI_HOME_REALM_QUERY: 761252190Srpaulo rx_anqp_hs_nai_home_realm(hapd, pos, end, qi); 762252190Srpaulo break; 763252190Srpaulo default: 764252190Srpaulo wpa_printf(MSG_DEBUG, "ANQP: Unsupported HS 2.0 query subtype " 765252190Srpaulo "%u", subtype); 766252190Srpaulo break; 767252190Srpaulo } 768252190Srpaulo} 769252190Srpaulo 770252190Srpaulo#endif /* CONFIG_HS20 */ 771252190Srpaulo 772252190Srpaulo 773252190Srpaulostatic void gas_serv_req_local_processing(struct hostapd_data *hapd, 774252190Srpaulo const u8 *sa, u8 dialog_token, 775252190Srpaulo struct anqp_query_info *qi) 776252190Srpaulo{ 777252190Srpaulo struct wpabuf *buf, *tx_buf; 778252190Srpaulo 779252190Srpaulo buf = gas_serv_build_gas_resp_payload(hapd, qi->request, NULL, 780252190Srpaulo qi->home_realm_query, 781252190Srpaulo qi->home_realm_query_len); 782252190Srpaulo wpa_hexdump_buf(MSG_MSGDUMP, "ANQP: Locally generated ANQP responses", 783252190Srpaulo buf); 784252190Srpaulo if (!buf) 785252190Srpaulo return; 786252190Srpaulo 787252190Srpaulo if (wpabuf_len(buf) > hapd->gas_frag_limit || 788252190Srpaulo hapd->conf->gas_comeback_delay) { 789252190Srpaulo struct gas_dialog_info *di; 790252190Srpaulo u16 comeback_delay = 1; 791252190Srpaulo 792252190Srpaulo if (hapd->conf->gas_comeback_delay) { 793252190Srpaulo /* Testing - allow overriding of the delay value */ 794252190Srpaulo comeback_delay = hapd->conf->gas_comeback_delay; 795252190Srpaulo } 796252190Srpaulo 797252190Srpaulo wpa_printf(MSG_DEBUG, "ANQP: Too long response to fit in " 798252190Srpaulo "initial response - use GAS comeback"); 799252190Srpaulo di = gas_dialog_create(hapd, sa, dialog_token); 800252190Srpaulo if (!di) { 801252190Srpaulo wpa_printf(MSG_INFO, "ANQP: Could not create dialog " 802252190Srpaulo "for " MACSTR " (dialog token %u)", 803252190Srpaulo MAC2STR(sa), dialog_token); 804252190Srpaulo wpabuf_free(buf); 805252190Srpaulo return; 806252190Srpaulo } 807252190Srpaulo di->sd_resp = buf; 808252190Srpaulo di->sd_resp_pos = 0; 809252190Srpaulo tx_buf = gas_anqp_build_initial_resp_buf( 810252190Srpaulo dialog_token, WLAN_STATUS_SUCCESS, comeback_delay, 811252190Srpaulo NULL); 812252190Srpaulo } else { 813252190Srpaulo wpa_printf(MSG_DEBUG, "ANQP: Initial response (no comeback)"); 814252190Srpaulo tx_buf = gas_anqp_build_initial_resp_buf( 815252190Srpaulo dialog_token, WLAN_STATUS_SUCCESS, 0, buf); 816252190Srpaulo wpabuf_free(buf); 817252190Srpaulo } 818252190Srpaulo if (!tx_buf) 819252190Srpaulo return; 820252190Srpaulo 821252190Srpaulo hostapd_drv_send_action(hapd, hapd->iface->freq, 0, sa, 822252190Srpaulo wpabuf_head(tx_buf), wpabuf_len(tx_buf)); 823252190Srpaulo wpabuf_free(tx_buf); 824252190Srpaulo} 825252190Srpaulo 826252190Srpaulo 827252190Srpaulostatic void gas_serv_rx_gas_initial_req(struct hostapd_data *hapd, 828252190Srpaulo const u8 *sa, 829252190Srpaulo const u8 *data, size_t len) 830252190Srpaulo{ 831252190Srpaulo const u8 *pos = data; 832252190Srpaulo const u8 *end = data + len; 833252190Srpaulo const u8 *next; 834252190Srpaulo u8 dialog_token; 835252190Srpaulo u16 slen; 836252190Srpaulo struct anqp_query_info qi; 837252190Srpaulo const u8 *adv_proto; 838252190Srpaulo 839252190Srpaulo if (len < 1 + 2) 840252190Srpaulo return; 841252190Srpaulo 842252190Srpaulo os_memset(&qi, 0, sizeof(qi)); 843252190Srpaulo 844252190Srpaulo dialog_token = *pos++; 845252190Srpaulo wpa_msg(hapd->msg_ctx, MSG_DEBUG, 846252190Srpaulo "GAS: GAS Initial Request from " MACSTR " (dialog token %u) ", 847252190Srpaulo MAC2STR(sa), dialog_token); 848252190Srpaulo 849252190Srpaulo if (*pos != WLAN_EID_ADV_PROTO) { 850252190Srpaulo wpa_msg(hapd->msg_ctx, MSG_DEBUG, 851252190Srpaulo "GAS: Unexpected IE in GAS Initial Request: %u", *pos); 852252190Srpaulo return; 853252190Srpaulo } 854252190Srpaulo adv_proto = pos++; 855252190Srpaulo 856252190Srpaulo slen = *pos++; 857252190Srpaulo next = pos + slen; 858252190Srpaulo if (next > end || slen < 2) { 859252190Srpaulo wpa_msg(hapd->msg_ctx, MSG_DEBUG, 860252190Srpaulo "GAS: Invalid IE in GAS Initial Request"); 861252190Srpaulo return; 862252190Srpaulo } 863252190Srpaulo pos++; /* skip QueryRespLenLimit and PAME-BI */ 864252190Srpaulo 865252190Srpaulo if (*pos != ACCESS_NETWORK_QUERY_PROTOCOL) { 866252190Srpaulo struct wpabuf *buf; 867252190Srpaulo wpa_msg(hapd->msg_ctx, MSG_DEBUG, 868252190Srpaulo "GAS: Unsupported GAS advertisement protocol id %u", 869252190Srpaulo *pos); 870252190Srpaulo if (sa[0] & 0x01) 871252190Srpaulo return; /* Invalid source address - drop silently */ 872252190Srpaulo buf = gas_build_initial_resp( 873252190Srpaulo dialog_token, WLAN_STATUS_GAS_ADV_PROTO_NOT_SUPPORTED, 874252190Srpaulo 0, 2 + slen + 2); 875252190Srpaulo if (buf == NULL) 876252190Srpaulo return; 877252190Srpaulo wpabuf_put_data(buf, adv_proto, 2 + slen); 878252190Srpaulo wpabuf_put_le16(buf, 0); /* Query Response Length */ 879252190Srpaulo hostapd_drv_send_action(hapd, hapd->iface->freq, 0, sa, 880252190Srpaulo wpabuf_head(buf), wpabuf_len(buf)); 881252190Srpaulo wpabuf_free(buf); 882252190Srpaulo return; 883252190Srpaulo } 884252190Srpaulo 885252190Srpaulo pos = next; 886252190Srpaulo /* Query Request */ 887252190Srpaulo if (pos + 2 > end) 888252190Srpaulo return; 889252190Srpaulo slen = WPA_GET_LE16(pos); 890252190Srpaulo pos += 2; 891252190Srpaulo if (pos + slen > end) 892252190Srpaulo return; 893252190Srpaulo end = pos + slen; 894252190Srpaulo 895252190Srpaulo /* ANQP Query Request */ 896252190Srpaulo while (pos < end) { 897252190Srpaulo u16 info_id, elen; 898252190Srpaulo 899252190Srpaulo if (pos + 4 > end) 900252190Srpaulo return; 901252190Srpaulo 902252190Srpaulo info_id = WPA_GET_LE16(pos); 903252190Srpaulo pos += 2; 904252190Srpaulo elen = WPA_GET_LE16(pos); 905252190Srpaulo pos += 2; 906252190Srpaulo 907252190Srpaulo if (pos + elen > end) { 908252190Srpaulo wpa_printf(MSG_DEBUG, "ANQP: Invalid Query Request"); 909252190Srpaulo return; 910252190Srpaulo } 911252190Srpaulo 912252190Srpaulo switch (info_id) { 913252190Srpaulo case ANQP_QUERY_LIST: 914252190Srpaulo rx_anqp_query_list(hapd, pos, pos + elen, &qi); 915252190Srpaulo break; 916252190Srpaulo#ifdef CONFIG_HS20 917252190Srpaulo case ANQP_VENDOR_SPECIFIC: 918252190Srpaulo rx_anqp_vendor_specific(hapd, pos, pos + elen, &qi); 919252190Srpaulo break; 920252190Srpaulo#endif /* CONFIG_HS20 */ 921252190Srpaulo default: 922252190Srpaulo wpa_printf(MSG_DEBUG, "ANQP: Unsupported Query " 923252190Srpaulo "Request element %u", info_id); 924252190Srpaulo break; 925252190Srpaulo } 926252190Srpaulo 927252190Srpaulo pos += elen; 928252190Srpaulo } 929252190Srpaulo 930252190Srpaulo gas_serv_req_local_processing(hapd, sa, dialog_token, &qi); 931252190Srpaulo} 932252190Srpaulo 933252190Srpaulo 934252190Srpaulovoid gas_serv_tx_gas_response(struct hostapd_data *hapd, const u8 *dst, 935252190Srpaulo struct gas_dialog_info *dialog) 936252190Srpaulo{ 937252190Srpaulo struct wpabuf *buf, *tx_buf; 938252190Srpaulo u8 dialog_token = dialog->dialog_token; 939252190Srpaulo size_t frag_len; 940252190Srpaulo 941252190Srpaulo if (dialog->sd_resp == NULL) { 942252190Srpaulo buf = gas_serv_build_gas_resp_payload(hapd, 943252190Srpaulo dialog->all_requested, 944252190Srpaulo dialog, NULL, 0); 945252190Srpaulo wpa_hexdump_buf(MSG_MSGDUMP, "ANQP: Generated ANQP responses", 946252190Srpaulo buf); 947252190Srpaulo if (!buf) 948252190Srpaulo goto tx_gas_response_done; 949252190Srpaulo dialog->sd_resp = buf; 950252190Srpaulo dialog->sd_resp_pos = 0; 951252190Srpaulo } 952252190Srpaulo frag_len = wpabuf_len(dialog->sd_resp) - dialog->sd_resp_pos; 953252190Srpaulo if (frag_len > hapd->gas_frag_limit || dialog->comeback_delay || 954252190Srpaulo hapd->conf->gas_comeback_delay) { 955252190Srpaulo u16 comeback_delay_tus = dialog->comeback_delay + 956252190Srpaulo GAS_SERV_COMEBACK_DELAY_FUDGE; 957252190Srpaulo u32 comeback_delay_secs, comeback_delay_usecs; 958252190Srpaulo 959252190Srpaulo if (hapd->conf->gas_comeback_delay) { 960252190Srpaulo /* Testing - allow overriding of the delay value */ 961252190Srpaulo comeback_delay_tus = hapd->conf->gas_comeback_delay; 962252190Srpaulo } 963252190Srpaulo 964252190Srpaulo wpa_printf(MSG_DEBUG, "GAS: Response frag_len %u (frag limit " 965252190Srpaulo "%u) and comeback delay %u, " 966252190Srpaulo "requesting comebacks", (unsigned int) frag_len, 967252190Srpaulo (unsigned int) hapd->gas_frag_limit, 968252190Srpaulo dialog->comeback_delay); 969252190Srpaulo tx_buf = gas_anqp_build_initial_resp_buf(dialog_token, 970252190Srpaulo WLAN_STATUS_SUCCESS, 971252190Srpaulo comeback_delay_tus, 972252190Srpaulo NULL); 973252190Srpaulo if (tx_buf) { 974252190Srpaulo wpa_msg(hapd->msg_ctx, MSG_DEBUG, 975252190Srpaulo "GAS: Tx GAS Initial Resp (comeback = 10TU)"); 976252190Srpaulo hostapd_drv_send_action(hapd, hapd->iface->freq, 0, 977252190Srpaulo dst, 978252190Srpaulo wpabuf_head(tx_buf), 979252190Srpaulo wpabuf_len(tx_buf)); 980252190Srpaulo } 981252190Srpaulo wpabuf_free(tx_buf); 982252190Srpaulo 983252190Srpaulo /* start a timer of 1.5 * comeback-delay */ 984252190Srpaulo comeback_delay_tus = comeback_delay_tus + 985252190Srpaulo (comeback_delay_tus / 2); 986252190Srpaulo comeback_delay_secs = (comeback_delay_tus * 1024) / 1000000; 987252190Srpaulo comeback_delay_usecs = (comeback_delay_tus * 1024) - 988252190Srpaulo (comeback_delay_secs * 1000000); 989252190Srpaulo eloop_register_timeout(comeback_delay_secs, 990252190Srpaulo comeback_delay_usecs, 991252190Srpaulo gas_serv_clear_cached_ies, dialog, 992252190Srpaulo NULL); 993252190Srpaulo goto tx_gas_response_done; 994252190Srpaulo } 995252190Srpaulo 996252190Srpaulo buf = wpabuf_alloc_copy(wpabuf_head_u8(dialog->sd_resp) + 997252190Srpaulo dialog->sd_resp_pos, frag_len); 998252190Srpaulo if (buf == NULL) { 999252190Srpaulo wpa_msg(hapd->msg_ctx, MSG_DEBUG, "GAS: Buffer allocation " 1000252190Srpaulo "failed"); 1001252190Srpaulo goto tx_gas_response_done; 1002252190Srpaulo } 1003252190Srpaulo tx_buf = gas_anqp_build_initial_resp_buf(dialog_token, 1004252190Srpaulo WLAN_STATUS_SUCCESS, 0, buf); 1005252190Srpaulo wpabuf_free(buf); 1006252190Srpaulo if (tx_buf == NULL) 1007252190Srpaulo goto tx_gas_response_done; 1008252190Srpaulo wpa_msg(hapd->msg_ctx, MSG_DEBUG, "GAS: Tx GAS Initial " 1009252190Srpaulo "Response (frag_id %d frag_len %d)", 1010252190Srpaulo dialog->sd_frag_id, (int) frag_len); 1011252190Srpaulo dialog->sd_frag_id++; 1012252190Srpaulo 1013252190Srpaulo hostapd_drv_send_action(hapd, hapd->iface->freq, 0, dst, 1014252190Srpaulo wpabuf_head(tx_buf), wpabuf_len(tx_buf)); 1015252190Srpaulo wpabuf_free(tx_buf); 1016252190Srpaulotx_gas_response_done: 1017252190Srpaulo gas_serv_clear_cached_ies(dialog, NULL); 1018252190Srpaulo} 1019252190Srpaulo 1020252190Srpaulo 1021252190Srpaulostatic void gas_serv_rx_gas_comeback_req(struct hostapd_data *hapd, 1022252190Srpaulo const u8 *sa, 1023252190Srpaulo const u8 *data, size_t len) 1024252190Srpaulo{ 1025252190Srpaulo struct gas_dialog_info *dialog; 1026252190Srpaulo struct wpabuf *buf, *tx_buf; 1027252190Srpaulo u8 dialog_token; 1028252190Srpaulo size_t frag_len; 1029252190Srpaulo int more = 0; 1030252190Srpaulo 1031252190Srpaulo wpa_hexdump(MSG_DEBUG, "GAS: RX GAS Comeback Request", data, len); 1032252190Srpaulo if (len < 1) 1033252190Srpaulo return; 1034252190Srpaulo dialog_token = *data; 1035252190Srpaulo wpa_msg(hapd->msg_ctx, MSG_DEBUG, "GAS: Dialog Token: %u", 1036252190Srpaulo dialog_token); 1037252190Srpaulo 1038252190Srpaulo dialog = gas_serv_dialog_find(hapd, sa, dialog_token); 1039252190Srpaulo if (!dialog) { 1040252190Srpaulo wpa_msg(hapd->msg_ctx, MSG_DEBUG, "GAS: No pending SD " 1041252190Srpaulo "response fragment for " MACSTR " dialog token %u", 1042252190Srpaulo MAC2STR(sa), dialog_token); 1043252190Srpaulo 1044252190Srpaulo if (sa[0] & 0x01) 1045252190Srpaulo return; /* Invalid source address - drop silently */ 1046252190Srpaulo tx_buf = gas_anqp_build_comeback_resp_buf( 1047252190Srpaulo dialog_token, WLAN_STATUS_NO_OUTSTANDING_GAS_REQ, 0, 0, 1048252190Srpaulo 0, NULL); 1049252190Srpaulo if (tx_buf == NULL) 1050252190Srpaulo return; 1051252190Srpaulo goto send_resp; 1052252190Srpaulo } 1053252190Srpaulo 1054252190Srpaulo if (dialog->sd_resp == NULL) { 1055252190Srpaulo wpa_printf(MSG_DEBUG, "GAS: Remote request 0x%x received 0x%x", 1056252190Srpaulo dialog->requested, dialog->received); 1057252190Srpaulo if ((dialog->requested & dialog->received) != 1058252190Srpaulo dialog->requested) { 1059252190Srpaulo wpa_printf(MSG_DEBUG, "GAS: Did not receive response " 1060252190Srpaulo "from remote processing"); 1061252190Srpaulo gas_serv_dialog_clear(dialog); 1062252190Srpaulo tx_buf = gas_anqp_build_comeback_resp_buf( 1063252190Srpaulo dialog_token, 1064252190Srpaulo WLAN_STATUS_GAS_RESP_NOT_RECEIVED, 0, 0, 0, 1065252190Srpaulo NULL); 1066252190Srpaulo if (tx_buf == NULL) 1067252190Srpaulo return; 1068252190Srpaulo goto send_resp; 1069252190Srpaulo } 1070252190Srpaulo 1071252190Srpaulo buf = gas_serv_build_gas_resp_payload(hapd, 1072252190Srpaulo dialog->all_requested, 1073252190Srpaulo dialog, NULL, 0); 1074252190Srpaulo wpa_hexdump_buf(MSG_MSGDUMP, "ANQP: Generated ANQP responses", 1075252190Srpaulo buf); 1076252190Srpaulo if (!buf) 1077252190Srpaulo goto rx_gas_comeback_req_done; 1078252190Srpaulo dialog->sd_resp = buf; 1079252190Srpaulo dialog->sd_resp_pos = 0; 1080252190Srpaulo } 1081252190Srpaulo frag_len = wpabuf_len(dialog->sd_resp) - dialog->sd_resp_pos; 1082252190Srpaulo if (frag_len > hapd->gas_frag_limit) { 1083252190Srpaulo frag_len = hapd->gas_frag_limit; 1084252190Srpaulo more = 1; 1085252190Srpaulo } 1086252190Srpaulo wpa_msg(hapd->msg_ctx, MSG_DEBUG, "GAS: resp frag_len %u", 1087252190Srpaulo (unsigned int) frag_len); 1088252190Srpaulo buf = wpabuf_alloc_copy(wpabuf_head_u8(dialog->sd_resp) + 1089252190Srpaulo dialog->sd_resp_pos, frag_len); 1090252190Srpaulo if (buf == NULL) { 1091252190Srpaulo wpa_msg(hapd->msg_ctx, MSG_DEBUG, "GAS: Failed to allocate " 1092252190Srpaulo "buffer"); 1093252190Srpaulo goto rx_gas_comeback_req_done; 1094252190Srpaulo } 1095252190Srpaulo tx_buf = gas_anqp_build_comeback_resp_buf(dialog_token, 1096252190Srpaulo WLAN_STATUS_SUCCESS, 1097252190Srpaulo dialog->sd_frag_id, 1098252190Srpaulo more, 0, buf); 1099252190Srpaulo wpabuf_free(buf); 1100252190Srpaulo if (tx_buf == NULL) 1101252190Srpaulo goto rx_gas_comeback_req_done; 1102252190Srpaulo wpa_msg(hapd->msg_ctx, MSG_DEBUG, "GAS: Tx GAS Comeback Response " 1103252190Srpaulo "(frag_id %d more=%d frag_len=%d)", 1104252190Srpaulo dialog->sd_frag_id, more, (int) frag_len); 1105252190Srpaulo dialog->sd_frag_id++; 1106252190Srpaulo dialog->sd_resp_pos += frag_len; 1107252190Srpaulo 1108252190Srpaulo if (more) { 1109252190Srpaulo wpa_msg(hapd->msg_ctx, MSG_DEBUG, "GAS: %d more bytes remain " 1110252190Srpaulo "to be sent", 1111252190Srpaulo (int) (wpabuf_len(dialog->sd_resp) - 1112252190Srpaulo dialog->sd_resp_pos)); 1113252190Srpaulo } else { 1114252190Srpaulo wpa_msg(hapd->msg_ctx, MSG_DEBUG, "GAS: All fragments of " 1115252190Srpaulo "SD response sent"); 1116252190Srpaulo gas_serv_dialog_clear(dialog); 1117252190Srpaulo gas_serv_free_dialogs(hapd, sa); 1118252190Srpaulo } 1119252190Srpaulo 1120252190Srpaulosend_resp: 1121252190Srpaulo hostapd_drv_send_action(hapd, hapd->iface->freq, 0, sa, 1122252190Srpaulo wpabuf_head(tx_buf), wpabuf_len(tx_buf)); 1123252190Srpaulo wpabuf_free(tx_buf); 1124252190Srpaulo return; 1125252190Srpaulo 1126252190Srpaulorx_gas_comeback_req_done: 1127252190Srpaulo gas_serv_clear_cached_ies(dialog, NULL); 1128252190Srpaulo} 1129252190Srpaulo 1130252190Srpaulo 1131252190Srpaulostatic void gas_serv_rx_public_action(void *ctx, const u8 *buf, size_t len, 1132252190Srpaulo int freq) 1133252190Srpaulo{ 1134252190Srpaulo struct hostapd_data *hapd = ctx; 1135252190Srpaulo const struct ieee80211_mgmt *mgmt; 1136252190Srpaulo size_t hdr_len; 1137252190Srpaulo const u8 *sa, *data; 1138252190Srpaulo 1139252190Srpaulo mgmt = (const struct ieee80211_mgmt *) buf; 1140252190Srpaulo hdr_len = (const u8 *) &mgmt->u.action.u.vs_public_action.action - buf; 1141252190Srpaulo if (hdr_len > len) 1142252190Srpaulo return; 1143252190Srpaulo if (mgmt->u.action.category != WLAN_ACTION_PUBLIC) 1144252190Srpaulo return; 1145252190Srpaulo sa = mgmt->sa; 1146252190Srpaulo len -= hdr_len; 1147252190Srpaulo data = &mgmt->u.action.u.public_action.action; 1148252190Srpaulo switch (data[0]) { 1149252190Srpaulo case WLAN_PA_GAS_INITIAL_REQ: 1150252190Srpaulo gas_serv_rx_gas_initial_req(hapd, sa, data + 1, len - 1); 1151252190Srpaulo break; 1152252190Srpaulo case WLAN_PA_GAS_COMEBACK_REQ: 1153252190Srpaulo gas_serv_rx_gas_comeback_req(hapd, sa, data + 1, len - 1); 1154252190Srpaulo break; 1155252190Srpaulo } 1156252190Srpaulo} 1157252190Srpaulo 1158252190Srpaulo 1159252190Srpauloint gas_serv_init(struct hostapd_data *hapd) 1160252190Srpaulo{ 1161252190Srpaulo hapd->public_action_cb = gas_serv_rx_public_action; 1162252190Srpaulo hapd->public_action_cb_ctx = hapd; 1163252190Srpaulo hapd->gas_frag_limit = 1400; 1164252190Srpaulo if (hapd->conf->gas_frag_limit > 0) 1165252190Srpaulo hapd->gas_frag_limit = hapd->conf->gas_frag_limit; 1166252190Srpaulo return 0; 1167252190Srpaulo} 1168252190Srpaulo 1169252190Srpaulo 1170252190Srpaulovoid gas_serv_deinit(struct hostapd_data *hapd) 1171252190Srpaulo{ 1172252190Srpaulo} 1173