1281681Srpaulo/* 2281681Srpaulo * WPA Supplicant - Basic mesh mode routines 3281681Srpaulo * Copyright (c) 2013-2014, cozybit, Inc. All rights reserved. 4281681Srpaulo * 5281681Srpaulo * This software may be distributed under the terms of the BSD license. 6281681Srpaulo * See README for more details. 7281681Srpaulo */ 8281681Srpaulo 9281681Srpaulo#include "utils/includes.h" 10281681Srpaulo 11281681Srpaulo#include "utils/common.h" 12281681Srpaulo#include "utils/eloop.h" 13281681Srpaulo#include "utils/uuid.h" 14281681Srpaulo#include "common/ieee802_11_defs.h" 15281681Srpaulo#include "common/wpa_ctrl.h" 16281681Srpaulo#include "ap/sta_info.h" 17281681Srpaulo#include "ap/hostapd.h" 18281681Srpaulo#include "ap/ieee802_11.h" 19281681Srpaulo#include "config_ssid.h" 20281681Srpaulo#include "config.h" 21281681Srpaulo#include "wpa_supplicant_i.h" 22281681Srpaulo#include "driver_i.h" 23281681Srpaulo#include "notify.h" 24281681Srpaulo#include "ap.h" 25281681Srpaulo#include "mesh_mpm.h" 26281681Srpaulo#include "mesh_rsn.h" 27281681Srpaulo#include "mesh.h" 28281681Srpaulo 29281681Srpaulo 30281681Srpaulostatic void wpa_supplicant_mesh_deinit(struct wpa_supplicant *wpa_s) 31281681Srpaulo{ 32281681Srpaulo wpa_supplicant_mesh_iface_deinit(wpa_s, wpa_s->ifmsh); 33281681Srpaulo wpa_s->ifmsh = NULL; 34281681Srpaulo wpa_s->current_ssid = NULL; 35281681Srpaulo os_free(wpa_s->mesh_rsn); 36281681Srpaulo wpa_s->mesh_rsn = NULL; 37346981Scy os_free(wpa_s->mesh_params); 38346981Scy wpa_s->mesh_params = NULL; 39281681Srpaulo /* TODO: leave mesh (stop beacon). This will happen on link down 40281681Srpaulo * anyway, so it's not urgent */ 41281681Srpaulo} 42281681Srpaulo 43281681Srpaulo 44281681Srpaulovoid wpa_supplicant_mesh_iface_deinit(struct wpa_supplicant *wpa_s, 45281681Srpaulo struct hostapd_iface *ifmsh) 46281681Srpaulo{ 47281681Srpaulo if (!ifmsh) 48281681Srpaulo return; 49281681Srpaulo 50281681Srpaulo if (ifmsh->mconf) { 51281681Srpaulo mesh_mpm_deinit(wpa_s, ifmsh); 52289549Srpaulo if (ifmsh->mconf->rsn_ie) { 53289549Srpaulo ifmsh->mconf->rsn_ie = NULL; 54281681Srpaulo /* We cannot free this struct 55281681Srpaulo * because wpa_authenticator on 56281681Srpaulo * hostapd side is also using it 57281681Srpaulo * for now just set to NULL and 58281681Srpaulo * let hostapd code free it. 59281681Srpaulo */ 60281681Srpaulo } 61281681Srpaulo os_free(ifmsh->mconf); 62281681Srpaulo ifmsh->mconf = NULL; 63281681Srpaulo } 64281681Srpaulo 65281681Srpaulo /* take care of shared data */ 66281681Srpaulo hostapd_interface_deinit(ifmsh); 67281681Srpaulo hostapd_interface_free(ifmsh); 68281681Srpaulo} 69281681Srpaulo 70281681Srpaulo 71337817Scystatic struct mesh_conf * mesh_config_create(struct wpa_supplicant *wpa_s, 72337817Scy struct wpa_ssid *ssid) 73281681Srpaulo{ 74281681Srpaulo struct mesh_conf *conf; 75337817Scy int cipher; 76281681Srpaulo 77281681Srpaulo conf = os_zalloc(sizeof(struct mesh_conf)); 78281681Srpaulo if (!conf) 79281681Srpaulo return NULL; 80281681Srpaulo 81281681Srpaulo os_memcpy(conf->meshid, ssid->ssid, ssid->ssid_len); 82281681Srpaulo conf->meshid_len = ssid->ssid_len; 83281681Srpaulo 84281681Srpaulo if (ssid->key_mgmt & WPA_KEY_MGMT_SAE) 85281681Srpaulo conf->security |= MESH_CONF_SEC_AUTH | 86281681Srpaulo MESH_CONF_SEC_AMPE; 87281681Srpaulo else 88281681Srpaulo conf->security |= MESH_CONF_SEC_NONE; 89346981Scy#ifdef CONFIG_IEEE80211W 90337817Scy conf->ieee80211w = ssid->ieee80211w; 91337817Scy if (conf->ieee80211w == MGMT_FRAME_PROTECTION_DEFAULT) { 92337817Scy if (wpa_s->drv_enc & WPA_DRIVER_CAPA_ENC_BIP) 93337817Scy conf->ieee80211w = wpa_s->conf->pmf; 94337817Scy else 95337817Scy conf->ieee80211w = NO_MGMT_FRAME_PROTECTION; 96337817Scy } 97346981Scy#endif /* CONFIG_IEEE80211W */ 98346981Scy#ifdef CONFIG_OCV 99346981Scy conf->ocv = ssid->ocv; 100346981Scy#endif /* CONFIG_OCV */ 101281681Srpaulo 102337817Scy cipher = wpa_pick_pairwise_cipher(ssid->pairwise_cipher, 0); 103337817Scy if (cipher < 0 || cipher == WPA_CIPHER_TKIP) { 104337817Scy wpa_msg(wpa_s, MSG_INFO, "mesh: Invalid pairwise cipher"); 105337817Scy os_free(conf); 106337817Scy return NULL; 107337817Scy } 108337817Scy conf->pairwise_cipher = cipher; 109337817Scy 110337817Scy cipher = wpa_pick_group_cipher(ssid->group_cipher); 111337817Scy if (cipher < 0 || cipher == WPA_CIPHER_TKIP || 112337817Scy cipher == WPA_CIPHER_GTK_NOT_USED) { 113337817Scy wpa_msg(wpa_s, MSG_INFO, "mesh: Invalid group cipher"); 114337817Scy os_free(conf); 115337817Scy return NULL; 116337817Scy } 117337817Scy 118337817Scy conf->group_cipher = cipher; 119337817Scy if (conf->ieee80211w != NO_MGMT_FRAME_PROTECTION) 120337817Scy conf->mgmt_group_cipher = WPA_CIPHER_AES_128_CMAC; 121337817Scy 122281681Srpaulo /* defaults */ 123281681Srpaulo conf->mesh_pp_id = MESH_PATH_PROTOCOL_HWMP; 124281681Srpaulo conf->mesh_pm_id = MESH_PATH_METRIC_AIRTIME; 125281681Srpaulo conf->mesh_cc_id = 0; 126281681Srpaulo conf->mesh_sp_id = MESH_SYNC_METHOD_NEIGHBOR_OFFSET; 127281681Srpaulo conf->mesh_auth_id = (conf->security & MESH_CONF_SEC_AUTH) ? 1 : 0; 128281681Srpaulo conf->dot11MeshMaxRetries = ssid->dot11MeshMaxRetries; 129281681Srpaulo conf->dot11MeshRetryTimeout = ssid->dot11MeshRetryTimeout; 130281681Srpaulo conf->dot11MeshConfirmTimeout = ssid->dot11MeshConfirmTimeout; 131281681Srpaulo conf->dot11MeshHoldingTimeout = ssid->dot11MeshHoldingTimeout; 132281681Srpaulo 133281681Srpaulo return conf; 134281681Srpaulo} 135281681Srpaulo 136281681Srpaulo 137281681Srpaulostatic void wpas_mesh_copy_groups(struct hostapd_data *bss, 138281681Srpaulo struct wpa_supplicant *wpa_s) 139281681Srpaulo{ 140281681Srpaulo int num_groups; 141281681Srpaulo size_t groups_size; 142281681Srpaulo 143281681Srpaulo for (num_groups = 0; wpa_s->conf->sae_groups[num_groups] > 0; 144281681Srpaulo num_groups++) 145281681Srpaulo ; 146281681Srpaulo 147281681Srpaulo groups_size = (num_groups + 1) * sizeof(wpa_s->conf->sae_groups[0]); 148281681Srpaulo bss->conf->sae_groups = os_malloc(groups_size); 149281681Srpaulo if (bss->conf->sae_groups) 150281681Srpaulo os_memcpy(bss->conf->sae_groups, wpa_s->conf->sae_groups, 151281681Srpaulo groups_size); 152281681Srpaulo} 153281681Srpaulo 154281681Srpaulo 155346981Scystatic int wpas_mesh_init_rsn(struct wpa_supplicant *wpa_s) 156346981Scy{ 157346981Scy struct hostapd_iface *ifmsh = wpa_s->ifmsh; 158346981Scy struct wpa_ssid *ssid = wpa_s->current_ssid; 159346981Scy struct hostapd_data *bss = ifmsh->bss[0]; 160346981Scy static int default_groups[] = { 19, 20, 21, 25, 26, -1 }; 161346981Scy const char *password; 162346981Scy size_t len; 163346981Scy 164346981Scy password = ssid->sae_password; 165346981Scy if (!password) 166346981Scy password = ssid->passphrase; 167346981Scy if (!password) { 168346981Scy wpa_printf(MSG_ERROR, 169346981Scy "mesh: Passphrase for SAE not configured"); 170346981Scy return -1; 171346981Scy } 172346981Scy 173346981Scy bss->conf->wpa = ssid->proto; 174346981Scy bss->conf->wpa_key_mgmt = ssid->key_mgmt; 175346981Scy 176346981Scy if (wpa_s->conf->sae_groups && wpa_s->conf->sae_groups[0] > 0) { 177346981Scy wpas_mesh_copy_groups(bss, wpa_s); 178346981Scy } else { 179346981Scy bss->conf->sae_groups = os_memdup(default_groups, 180346981Scy sizeof(default_groups)); 181346981Scy if (!bss->conf->sae_groups) 182346981Scy return -1; 183346981Scy } 184346981Scy 185346981Scy len = os_strlen(password); 186346981Scy bss->conf->ssid.wpa_passphrase = dup_binstr(password, len); 187346981Scy 188346981Scy wpa_s->mesh_rsn = mesh_rsn_auth_init(wpa_s, ifmsh->mconf); 189346981Scy return !wpa_s->mesh_rsn ? -1 : 0; 190346981Scy} 191346981Scy 192346981Scy 193346981Scystatic int wpas_mesh_complete(struct wpa_supplicant *wpa_s) 194346981Scy{ 195346981Scy struct hostapd_iface *ifmsh = wpa_s->ifmsh; 196346981Scy struct wpa_driver_mesh_join_params *params = wpa_s->mesh_params; 197346981Scy struct wpa_ssid *ssid = wpa_s->current_ssid; 198346981Scy int ret; 199346981Scy 200346981Scy if (!params || !ssid || !ifmsh) { 201346981Scy wpa_printf(MSG_ERROR, "mesh: %s called without active mesh", 202346981Scy __func__); 203346981Scy return -1; 204346981Scy } 205346981Scy 206346981Scy if (ifmsh->mconf->security != MESH_CONF_SEC_NONE && 207346981Scy wpas_mesh_init_rsn(wpa_s)) { 208346981Scy wpa_printf(MSG_ERROR, 209346981Scy "mesh: RSN initialization failed - deinit mesh"); 210346981Scy wpa_supplicant_mesh_deinit(wpa_s); 211351611Scy wpa_drv_leave_mesh(wpa_s); 212346981Scy return -1; 213346981Scy } 214346981Scy 215346981Scy if (ssid->key_mgmt & WPA_KEY_MGMT_SAE) { 216346981Scy wpa_s->pairwise_cipher = wpa_s->mesh_rsn->pairwise_cipher; 217346981Scy wpa_s->group_cipher = wpa_s->mesh_rsn->group_cipher; 218346981Scy wpa_s->mgmt_group_cipher = wpa_s->mesh_rsn->mgmt_group_cipher; 219346981Scy } 220346981Scy 221346981Scy params->ies = ifmsh->mconf->rsn_ie; 222346981Scy params->ie_len = ifmsh->mconf->rsn_ie_len; 223346981Scy params->basic_rates = ifmsh->basic_rates; 224346981Scy params->conf.flags |= WPA_DRIVER_MESH_CONF_FLAG_HT_OP_MODE; 225346981Scy params->conf.ht_opmode = ifmsh->bss[0]->iface->ht_op_mode; 226346981Scy 227346981Scy wpa_msg(wpa_s, MSG_INFO, "joining mesh %s", 228346981Scy wpa_ssid_txt(ssid->ssid, ssid->ssid_len)); 229346981Scy ret = wpa_drv_join_mesh(wpa_s, params); 230346981Scy if (ret) 231346981Scy wpa_msg(wpa_s, MSG_ERROR, "mesh join error=%d", ret); 232346981Scy 233346981Scy /* hostapd sets the interface down until we associate */ 234346981Scy wpa_drv_set_operstate(wpa_s, 1); 235346981Scy 236346981Scy if (!ret) 237346981Scy wpa_supplicant_set_state(wpa_s, WPA_COMPLETED); 238346981Scy 239346981Scy return ret; 240346981Scy} 241346981Scy 242346981Scy 243281681Srpaulostatic int wpa_supplicant_mesh_init(struct wpa_supplicant *wpa_s, 244346981Scy struct wpa_ssid *ssid, 245346981Scy struct hostapd_freq_params *freq) 246281681Srpaulo{ 247281681Srpaulo struct hostapd_iface *ifmsh; 248281681Srpaulo struct hostapd_data *bss; 249281681Srpaulo struct hostapd_config *conf; 250281681Srpaulo struct mesh_conf *mconf; 251281681Srpaulo int basic_rates_erp[] = { 10, 20, 55, 60, 110, 120, 240, -1 }; 252281681Srpaulo int rate_len; 253346981Scy int frequency; 254281681Srpaulo 255281681Srpaulo if (!wpa_s->conf->user_mpm) { 256281681Srpaulo /* not much for us to do here */ 257281681Srpaulo wpa_msg(wpa_s, MSG_WARNING, 258281681Srpaulo "user_mpm is not enabled in configuration"); 259281681Srpaulo return 0; 260281681Srpaulo } 261281681Srpaulo 262346981Scy wpa_s->ifmsh = ifmsh = hostapd_alloc_iface(); 263281681Srpaulo if (!ifmsh) 264281681Srpaulo return -ENOMEM; 265281681Srpaulo 266281681Srpaulo ifmsh->drv_flags = wpa_s->drv_flags; 267281681Srpaulo ifmsh->num_bss = 1; 268281681Srpaulo ifmsh->bss = os_calloc(wpa_s->ifmsh->num_bss, 269281681Srpaulo sizeof(struct hostapd_data *)); 270281681Srpaulo if (!ifmsh->bss) 271281681Srpaulo goto out_free; 272281681Srpaulo 273346981Scy ifmsh->bss[0] = bss = hostapd_alloc_bss_data(NULL, NULL, NULL); 274281681Srpaulo if (!bss) 275281681Srpaulo goto out_free; 276281681Srpaulo 277346981Scy ifmsh->bss[0]->msg_ctx = wpa_s; 278281681Srpaulo os_memcpy(bss->own_addr, wpa_s->own_addr, ETH_ALEN); 279281681Srpaulo bss->driver = wpa_s->driver; 280281681Srpaulo bss->drv_priv = wpa_s->drv_priv; 281281681Srpaulo bss->iface = ifmsh; 282281681Srpaulo bss->mesh_sta_free_cb = mesh_mpm_free_sta; 283346981Scy frequency = ssid->frequency; 284346981Scy if (frequency != freq->freq && 285346981Scy frequency == freq->freq + freq->sec_channel_offset * 20) { 286346981Scy wpa_printf(MSG_DEBUG, "mesh: pri/sec channels switched"); 287346981Scy frequency = freq->freq; 288346981Scy } 289346981Scy wpa_s->assoc_freq = frequency; 290281681Srpaulo wpa_s->current_ssid = ssid; 291281681Srpaulo 292281681Srpaulo /* setup an AP config for auth processing */ 293281681Srpaulo conf = hostapd_config_defaults(); 294281681Srpaulo if (!conf) 295281681Srpaulo goto out_free; 296281681Srpaulo 297281681Srpaulo bss->conf = *conf->bss; 298281681Srpaulo bss->conf->start_disabled = 1; 299281681Srpaulo bss->conf->mesh = MESH_ENABLED; 300281681Srpaulo bss->conf->ap_max_inactivity = wpa_s->conf->mesh_max_inactivity; 301346981Scy 302346981Scy if (ieee80211_is_dfs(ssid->frequency, wpa_s->hw.modes, 303346981Scy wpa_s->hw.num_modes) && wpa_s->conf->country[0]) { 304346981Scy conf->ieee80211h = 1; 305346981Scy conf->ieee80211d = 1; 306346981Scy conf->country[0] = wpa_s->conf->country[0]; 307346981Scy conf->country[1] = wpa_s->conf->country[1]; 308346981Scy conf->country[2] = ' '; 309346981Scy } 310346981Scy 311281681Srpaulo bss->iconf = conf; 312281681Srpaulo ifmsh->conf = conf; 313281681Srpaulo 314281681Srpaulo ifmsh->bss[0]->max_plinks = wpa_s->conf->max_peer_links; 315289549Srpaulo ifmsh->bss[0]->dot11RSNASAERetransPeriod = 316289549Srpaulo wpa_s->conf->dot11RSNASAERetransPeriod; 317281681Srpaulo os_strlcpy(bss->conf->iface, wpa_s->ifname, sizeof(bss->conf->iface)); 318281681Srpaulo 319337817Scy mconf = mesh_config_create(wpa_s, ssid); 320281681Srpaulo if (!mconf) 321281681Srpaulo goto out_free; 322281681Srpaulo ifmsh->mconf = mconf; 323281681Srpaulo 324281681Srpaulo /* need conf->hw_mode for supported rates. */ 325346981Scy conf->hw_mode = ieee80211_freq_to_chan(frequency, &conf->channel); 326281681Srpaulo if (conf->hw_mode == NUM_HOSTAPD_MODES) { 327281681Srpaulo wpa_printf(MSG_ERROR, "Unsupported mesh mode frequency: %d MHz", 328346981Scy frequency); 329281681Srpaulo goto out_free; 330281681Srpaulo } 331337817Scy if (ssid->ht40) 332337817Scy conf->secondary_channel = ssid->ht40; 333337817Scy if (conf->hw_mode == HOSTAPD_MODE_IEEE80211A && ssid->vht) { 334346981Scy if (ssid->max_oper_chwidth != DEFAULT_MAX_OPER_CHWIDTH) 335346981Scy conf->vht_oper_chwidth = ssid->max_oper_chwidth; 336337817Scy switch (conf->vht_oper_chwidth) { 337351611Scy case CHANWIDTH_80MHZ: 338351611Scy case CHANWIDTH_80P80MHZ: 339337817Scy ieee80211_freq_to_chan( 340346981Scy frequency, 341337817Scy &conf->vht_oper_centr_freq_seg0_idx); 342337817Scy conf->vht_oper_centr_freq_seg0_idx += ssid->ht40 * 2; 343337817Scy break; 344351611Scy case CHANWIDTH_160MHZ: 345337817Scy ieee80211_freq_to_chan( 346346981Scy frequency, 347337817Scy &conf->vht_oper_centr_freq_seg0_idx); 348337817Scy conf->vht_oper_centr_freq_seg0_idx += ssid->ht40 * 2; 349337817Scy conf->vht_oper_centr_freq_seg0_idx += 40 / 5; 350337817Scy break; 351337817Scy } 352337817Scy ieee80211_freq_to_chan(ssid->vht_center_freq2, 353337817Scy &conf->vht_oper_centr_freq_seg1_idx); 354337817Scy } 355281681Srpaulo 356281681Srpaulo if (ssid->mesh_basic_rates == NULL) { 357281681Srpaulo /* 358281681Srpaulo * XXX: Hack! This is so an MPM which correctly sets the ERP 359281681Srpaulo * mandatory rates as BSSBasicRateSet doesn't reject us. We 360281681Srpaulo * could add a new hw_mode HOSTAPD_MODE_IEEE80211G_ERP, but 361281681Srpaulo * this is way easier. This also makes our BSSBasicRateSet 362281681Srpaulo * advertised in beacons match the one in peering frames, sigh. 363281681Srpaulo */ 364281681Srpaulo if (conf->hw_mode == HOSTAPD_MODE_IEEE80211G) { 365346981Scy conf->basic_rates = os_memdup(basic_rates_erp, 366346981Scy sizeof(basic_rates_erp)); 367281681Srpaulo if (!conf->basic_rates) 368281681Srpaulo goto out_free; 369281681Srpaulo } 370281681Srpaulo } else { 371281681Srpaulo rate_len = 0; 372281681Srpaulo while (1) { 373281681Srpaulo if (ssid->mesh_basic_rates[rate_len] < 1) 374281681Srpaulo break; 375281681Srpaulo rate_len++; 376281681Srpaulo } 377281681Srpaulo conf->basic_rates = os_calloc(rate_len + 1, sizeof(int)); 378281681Srpaulo if (conf->basic_rates == NULL) 379281681Srpaulo goto out_free; 380281681Srpaulo os_memcpy(conf->basic_rates, ssid->mesh_basic_rates, 381281681Srpaulo rate_len * sizeof(int)); 382281681Srpaulo conf->basic_rates[rate_len] = -1; 383281681Srpaulo } 384281681Srpaulo 385281681Srpaulo if (wpa_drv_init_mesh(wpa_s)) { 386281681Srpaulo wpa_msg(wpa_s, MSG_ERROR, "Failed to init mesh in driver"); 387281681Srpaulo return -1; 388281681Srpaulo } 389281681Srpaulo 390346981Scy if (hostapd_setup_interface(ifmsh)) { 391346981Scy wpa_printf(MSG_ERROR, 392346981Scy "Failed to initialize hostapd interface for mesh"); 393346981Scy return -1; 394281681Srpaulo } 395281681Srpaulo 396281681Srpaulo wpa_supplicant_conf_ap_ht(wpa_s, ssid, conf); 397281681Srpaulo 398281681Srpaulo return 0; 399281681Srpauloout_free: 400281681Srpaulo wpa_supplicant_mesh_deinit(wpa_s); 401281681Srpaulo return -ENOMEM; 402281681Srpaulo} 403281681Srpaulo 404281681Srpaulo 405281681Srpaulovoid wpa_mesh_notify_peer(struct wpa_supplicant *wpa_s, const u8 *addr, 406281681Srpaulo const u8 *ies, size_t ie_len) 407281681Srpaulo{ 408281681Srpaulo struct ieee802_11_elems elems; 409281681Srpaulo 410281681Srpaulo wpa_msg(wpa_s, MSG_INFO, 411281681Srpaulo "new peer notification for " MACSTR, MAC2STR(addr)); 412281681Srpaulo 413281681Srpaulo if (ieee802_11_parse_elems(ies, ie_len, &elems, 0) == ParseFailed) { 414281681Srpaulo wpa_msg(wpa_s, MSG_INFO, "Could not parse beacon from " MACSTR, 415281681Srpaulo MAC2STR(addr)); 416281681Srpaulo return; 417281681Srpaulo } 418281681Srpaulo wpa_mesh_new_mesh_peer(wpa_s, addr, &elems); 419281681Srpaulo} 420281681Srpaulo 421281681Srpaulo 422281681Srpaulovoid wpa_supplicant_mesh_add_scan_ie(struct wpa_supplicant *wpa_s, 423281681Srpaulo struct wpabuf **extra_ie) 424281681Srpaulo{ 425281681Srpaulo /* EID + 0-length (wildcard) mesh-id */ 426281681Srpaulo size_t ielen = 2; 427281681Srpaulo 428281681Srpaulo if (wpabuf_resize(extra_ie, ielen) == 0) { 429281681Srpaulo wpabuf_put_u8(*extra_ie, WLAN_EID_MESH_ID); 430281681Srpaulo wpabuf_put_u8(*extra_ie, 0); 431281681Srpaulo } 432281681Srpaulo} 433281681Srpaulo 434281681Srpaulo 435281681Srpauloint wpa_supplicant_join_mesh(struct wpa_supplicant *wpa_s, 436281681Srpaulo struct wpa_ssid *ssid) 437281681Srpaulo{ 438346981Scy struct wpa_driver_mesh_join_params *params = os_zalloc(sizeof(*params)); 439281681Srpaulo int ret = 0; 440281681Srpaulo 441346981Scy if (!ssid || !ssid->ssid || !ssid->ssid_len || !ssid->frequency || 442346981Scy !params) { 443281681Srpaulo ret = -ENOENT; 444346981Scy os_free(params); 445281681Srpaulo goto out; 446281681Srpaulo } 447281681Srpaulo 448281681Srpaulo wpa_supplicant_mesh_deinit(wpa_s); 449281681Srpaulo 450337817Scy wpa_s->pairwise_cipher = WPA_CIPHER_NONE; 451337817Scy wpa_s->group_cipher = WPA_CIPHER_NONE; 452337817Scy wpa_s->mgmt_group_cipher = 0; 453337817Scy 454346981Scy params->meshid = ssid->ssid; 455346981Scy params->meshid_len = ssid->ssid_len; 456346981Scy ibss_mesh_setup_freq(wpa_s, ssid, ¶ms->freq); 457346981Scy wpa_s->mesh_ht_enabled = !!params->freq.ht_enabled; 458346981Scy wpa_s->mesh_vht_enabled = !!params->freq.vht_enabled; 459351611Scy wpa_s->mesh_he_enabled = !!params->freq.he_enabled; 460346981Scy if (params->freq.ht_enabled && params->freq.sec_channel_offset) 461346981Scy ssid->ht40 = params->freq.sec_channel_offset; 462346981Scy 463337817Scy if (wpa_s->mesh_vht_enabled) { 464337817Scy ssid->vht = 1; 465346981Scy ssid->vht_center_freq1 = params->freq.center_freq1; 466346981Scy switch (params->freq.bandwidth) { 467337817Scy case 80: 468346981Scy if (params->freq.center_freq2) { 469351611Scy ssid->max_oper_chwidth = CHANWIDTH_80P80MHZ; 470337817Scy ssid->vht_center_freq2 = 471346981Scy params->freq.center_freq2; 472337817Scy } else { 473351611Scy ssid->max_oper_chwidth = CHANWIDTH_80MHZ; 474337817Scy } 475337817Scy break; 476337817Scy case 160: 477351611Scy ssid->max_oper_chwidth = CHANWIDTH_160MHZ; 478337817Scy break; 479337817Scy default: 480351611Scy ssid->max_oper_chwidth = CHANWIDTH_USE_HT; 481337817Scy break; 482337817Scy } 483337817Scy } 484351611Scy if (wpa_s->mesh_he_enabled) 485351611Scy ssid->he = 1; 486281681Srpaulo if (ssid->beacon_int > 0) 487346981Scy params->beacon_int = ssid->beacon_int; 488281681Srpaulo else if (wpa_s->conf->beacon_int > 0) 489346981Scy params->beacon_int = wpa_s->conf->beacon_int; 490337817Scy if (ssid->dtim_period > 0) 491346981Scy params->dtim_period = ssid->dtim_period; 492337817Scy else if (wpa_s->conf->dtim_period > 0) 493346981Scy params->dtim_period = wpa_s->conf->dtim_period; 494346981Scy params->conf.max_peer_links = wpa_s->conf->max_peer_links; 495346981Scy if (ssid->mesh_rssi_threshold < DEFAULT_MESH_RSSI_THRESHOLD) { 496346981Scy params->conf.rssi_threshold = ssid->mesh_rssi_threshold; 497346981Scy params->conf.flags |= WPA_DRIVER_MESH_CONF_FLAG_RSSI_THRESHOLD; 498346981Scy } 499281681Srpaulo 500281681Srpaulo if (ssid->key_mgmt & WPA_KEY_MGMT_SAE) { 501346981Scy params->flags |= WPA_DRIVER_MESH_FLAG_SAE_AUTH; 502346981Scy params->flags |= WPA_DRIVER_MESH_FLAG_AMPE; 503281681Srpaulo wpa_s->conf->user_mpm = 1; 504281681Srpaulo } 505281681Srpaulo 506281681Srpaulo if (wpa_s->conf->user_mpm) { 507346981Scy params->flags |= WPA_DRIVER_MESH_FLAG_USER_MPM; 508346981Scy params->conf.auto_plinks = 0; 509281681Srpaulo } else { 510346981Scy params->flags |= WPA_DRIVER_MESH_FLAG_DRIVER_MPM; 511346981Scy params->conf.auto_plinks = 1; 512281681Srpaulo } 513346981Scy params->conf.peer_link_timeout = wpa_s->conf->mesh_max_inactivity; 514281681Srpaulo 515346981Scy os_free(wpa_s->mesh_params); 516346981Scy wpa_s->mesh_params = params; 517346981Scy if (wpa_supplicant_mesh_init(wpa_s, ssid, ¶ms->freq)) { 518281681Srpaulo wpa_msg(wpa_s, MSG_ERROR, "Failed to init mesh"); 519281681Srpaulo wpa_drv_leave_mesh(wpa_s); 520281681Srpaulo ret = -1; 521281681Srpaulo goto out; 522281681Srpaulo } 523281681Srpaulo 524346981Scy ret = wpas_mesh_complete(wpa_s); 525281681Srpauloout: 526281681Srpaulo return ret; 527281681Srpaulo} 528281681Srpaulo 529281681Srpaulo 530281681Srpauloint wpa_supplicant_leave_mesh(struct wpa_supplicant *wpa_s) 531281681Srpaulo{ 532281681Srpaulo int ret = 0; 533281681Srpaulo 534281681Srpaulo wpa_msg(wpa_s, MSG_INFO, "leaving mesh"); 535281681Srpaulo 536281681Srpaulo /* Need to send peering close messages first */ 537281681Srpaulo wpa_supplicant_mesh_deinit(wpa_s); 538281681Srpaulo 539281681Srpaulo ret = wpa_drv_leave_mesh(wpa_s); 540281681Srpaulo if (ret) 541281681Srpaulo wpa_msg(wpa_s, MSG_ERROR, "mesh leave error=%d", ret); 542281681Srpaulo 543281681Srpaulo wpa_drv_set_operstate(wpa_s, 1); 544281681Srpaulo 545281681Srpaulo return ret; 546281681Srpaulo} 547281681Srpaulo 548281681Srpaulo 549281681Srpaulostatic int mesh_attr_text(const u8 *ies, size_t ies_len, char *buf, char *end) 550281681Srpaulo{ 551281681Srpaulo struct ieee802_11_elems elems; 552281681Srpaulo char *mesh_id, *pos = buf; 553281681Srpaulo u8 *bss_basic_rate_set; 554281681Srpaulo int bss_basic_rate_set_len, ret, i; 555281681Srpaulo 556281681Srpaulo if (ieee802_11_parse_elems(ies, ies_len, &elems, 0) == ParseFailed) 557281681Srpaulo return -1; 558281681Srpaulo 559281681Srpaulo if (elems.mesh_id_len < 1) 560281681Srpaulo return 0; 561281681Srpaulo 562281681Srpaulo mesh_id = os_malloc(elems.mesh_id_len + 1); 563281681Srpaulo if (mesh_id == NULL) 564281681Srpaulo return -1; 565281681Srpaulo 566281681Srpaulo os_memcpy(mesh_id, elems.mesh_id, elems.mesh_id_len); 567281681Srpaulo mesh_id[elems.mesh_id_len] = '\0'; 568281681Srpaulo ret = os_snprintf(pos, end - pos, "mesh_id=%s\n", mesh_id); 569281681Srpaulo os_free(mesh_id); 570281681Srpaulo if (os_snprintf_error(end - pos, ret)) 571281681Srpaulo return pos - buf; 572281681Srpaulo pos += ret; 573281681Srpaulo 574281681Srpaulo if (elems.mesh_config_len > 6) { 575281681Srpaulo ret = os_snprintf(pos, end - pos, 576281681Srpaulo "active_path_selection_protocol_id=0x%02x\n" 577281681Srpaulo "active_path_selection_metric_id=0x%02x\n" 578281681Srpaulo "congestion_control_mode_id=0x%02x\n" 579281681Srpaulo "synchronization_method_id=0x%02x\n" 580281681Srpaulo "authentication_protocol_id=0x%02x\n" 581281681Srpaulo "mesh_formation_info=0x%02x\n" 582281681Srpaulo "mesh_capability=0x%02x\n", 583281681Srpaulo elems.mesh_config[0], elems.mesh_config[1], 584281681Srpaulo elems.mesh_config[2], elems.mesh_config[3], 585281681Srpaulo elems.mesh_config[4], elems.mesh_config[5], 586281681Srpaulo elems.mesh_config[6]); 587281681Srpaulo if (os_snprintf_error(end - pos, ret)) 588281681Srpaulo return pos - buf; 589281681Srpaulo pos += ret; 590281681Srpaulo } 591281681Srpaulo 592281681Srpaulo bss_basic_rate_set = os_malloc(elems.supp_rates_len + 593281681Srpaulo elems.ext_supp_rates_len); 594281681Srpaulo if (bss_basic_rate_set == NULL) 595281681Srpaulo return -1; 596281681Srpaulo 597281681Srpaulo bss_basic_rate_set_len = 0; 598281681Srpaulo for (i = 0; i < elems.supp_rates_len; i++) { 599281681Srpaulo if (elems.supp_rates[i] & 0x80) { 600281681Srpaulo bss_basic_rate_set[bss_basic_rate_set_len++] = 601281681Srpaulo (elems.supp_rates[i] & 0x7f) * 5; 602281681Srpaulo } 603281681Srpaulo } 604281681Srpaulo for (i = 0; i < elems.ext_supp_rates_len; i++) { 605281681Srpaulo if (elems.ext_supp_rates[i] & 0x80) { 606281681Srpaulo bss_basic_rate_set[bss_basic_rate_set_len++] = 607281681Srpaulo (elems.ext_supp_rates[i] & 0x7f) * 5; 608281681Srpaulo } 609281681Srpaulo } 610281681Srpaulo if (bss_basic_rate_set_len > 0) { 611281681Srpaulo ret = os_snprintf(pos, end - pos, "bss_basic_rate_set=%d", 612281681Srpaulo bss_basic_rate_set[0]); 613281681Srpaulo if (os_snprintf_error(end - pos, ret)) 614289549Srpaulo goto fail; 615281681Srpaulo pos += ret; 616281681Srpaulo 617281681Srpaulo for (i = 1; i < bss_basic_rate_set_len; i++) { 618281681Srpaulo ret = os_snprintf(pos, end - pos, " %d", 619281681Srpaulo bss_basic_rate_set[i]); 620281681Srpaulo if (os_snprintf_error(end - pos, ret)) 621289549Srpaulo goto fail; 622281681Srpaulo pos += ret; 623281681Srpaulo } 624281681Srpaulo 625281681Srpaulo ret = os_snprintf(pos, end - pos, "\n"); 626281681Srpaulo if (os_snprintf_error(end - pos, ret)) 627289549Srpaulo goto fail; 628281681Srpaulo pos += ret; 629281681Srpaulo } 630289549Srpaulofail: 631281681Srpaulo os_free(bss_basic_rate_set); 632281681Srpaulo 633281681Srpaulo return pos - buf; 634281681Srpaulo} 635281681Srpaulo 636281681Srpaulo 637281681Srpauloint wpas_mesh_scan_result_text(const u8 *ies, size_t ies_len, char *buf, 638281681Srpaulo char *end) 639281681Srpaulo{ 640281681Srpaulo return mesh_attr_text(ies, ies_len, buf, end); 641281681Srpaulo} 642281681Srpaulo 643281681Srpaulo 644281681Srpaulostatic int wpas_mesh_get_ifname(struct wpa_supplicant *wpa_s, char *ifname, 645281681Srpaulo size_t len) 646281681Srpaulo{ 647281681Srpaulo char *ifname_ptr = wpa_s->ifname; 648281681Srpaulo int res; 649281681Srpaulo 650281681Srpaulo res = os_snprintf(ifname, len, "mesh-%s-%d", ifname_ptr, 651281681Srpaulo wpa_s->mesh_if_idx); 652281681Srpaulo if (os_snprintf_error(len, res) || 653281681Srpaulo (os_strlen(ifname) >= IFNAMSIZ && 654281681Srpaulo os_strlen(wpa_s->ifname) < IFNAMSIZ)) { 655281681Srpaulo /* Try to avoid going over the IFNAMSIZ length limit */ 656281681Srpaulo res = os_snprintf(ifname, len, "mesh-%d", wpa_s->mesh_if_idx); 657281681Srpaulo if (os_snprintf_error(len, res)) 658281681Srpaulo return -1; 659281681Srpaulo } 660281681Srpaulo wpa_s->mesh_if_idx++; 661281681Srpaulo return 0; 662281681Srpaulo} 663281681Srpaulo 664281681Srpaulo 665281681Srpauloint wpas_mesh_add_interface(struct wpa_supplicant *wpa_s, char *ifname, 666281681Srpaulo size_t len) 667281681Srpaulo{ 668281681Srpaulo struct wpa_interface iface; 669281681Srpaulo struct wpa_supplicant *mesh_wpa_s; 670281681Srpaulo u8 addr[ETH_ALEN]; 671281681Srpaulo 672281681Srpaulo if (ifname[0] == '\0' && wpas_mesh_get_ifname(wpa_s, ifname, len) < 0) 673281681Srpaulo return -1; 674281681Srpaulo 675281681Srpaulo if (wpa_drv_if_add(wpa_s, WPA_IF_MESH, ifname, NULL, NULL, NULL, addr, 676281681Srpaulo NULL) < 0) { 677281681Srpaulo wpa_printf(MSG_ERROR, 678281681Srpaulo "mesh: Failed to create new mesh interface"); 679281681Srpaulo return -1; 680281681Srpaulo } 681281681Srpaulo wpa_printf(MSG_INFO, "mesh: Created virtual interface %s addr " 682281681Srpaulo MACSTR, ifname, MAC2STR(addr)); 683281681Srpaulo 684281681Srpaulo os_memset(&iface, 0, sizeof(iface)); 685281681Srpaulo iface.ifname = ifname; 686281681Srpaulo iface.driver = wpa_s->driver->name; 687281681Srpaulo iface.driver_param = wpa_s->conf->driver_param; 688281681Srpaulo iface.ctrl_interface = wpa_s->conf->ctrl_interface; 689281681Srpaulo 690281681Srpaulo mesh_wpa_s = wpa_supplicant_add_iface(wpa_s->global, &iface, wpa_s); 691281681Srpaulo if (!mesh_wpa_s) { 692281681Srpaulo wpa_printf(MSG_ERROR, 693281681Srpaulo "mesh: Failed to create new wpa_supplicant interface"); 694337817Scy wpa_drv_if_remove(wpa_s, WPA_IF_MESH, ifname); 695281681Srpaulo return -1; 696281681Srpaulo } 697281681Srpaulo mesh_wpa_s->mesh_if_created = 1; 698281681Srpaulo return 0; 699281681Srpaulo} 700337817Scy 701337817Scy 702337817Scyint wpas_mesh_peer_remove(struct wpa_supplicant *wpa_s, const u8 *addr) 703337817Scy{ 704337817Scy return mesh_mpm_close_peer(wpa_s, addr); 705337817Scy} 706337817Scy 707337817Scy 708337817Scyint wpas_mesh_peer_add(struct wpa_supplicant *wpa_s, const u8 *addr, 709337817Scy int duration) 710337817Scy{ 711337817Scy return mesh_mpm_connect_peer(wpa_s, addr, duration); 712337817Scy} 713