| Dmitry Shmidt | 6c0da2b | 2015-01-05 13:08:17 -0800 | [diff] [blame] | 1 | /* | 
|  | 2 | * WPA Supplicant - Basic mesh mode routines | 
|  | 3 | * Copyright (c) 2013-2014, cozybit, Inc.  All rights reserved. | 
|  | 4 | * | 
|  | 5 | * This software may be distributed under the terms of the BSD license. | 
|  | 6 | * See README for more details. | 
|  | 7 | */ | 
|  | 8 |  | 
|  | 9 | #include "utils/includes.h" | 
|  | 10 |  | 
|  | 11 | #include "utils/common.h" | 
|  | 12 | #include "utils/eloop.h" | 
|  | 13 | #include "utils/uuid.h" | 
|  | 14 | #include "common/ieee802_11_defs.h" | 
|  | 15 | #include "common/wpa_ctrl.h" | 
|  | 16 | #include "ap/sta_info.h" | 
|  | 17 | #include "ap/hostapd.h" | 
|  | 18 | #include "ap/ieee802_11.h" | 
|  | 19 | #include "config_ssid.h" | 
|  | 20 | #include "config.h" | 
|  | 21 | #include "wpa_supplicant_i.h" | 
|  | 22 | #include "driver_i.h" | 
|  | 23 | #include "notify.h" | 
|  | 24 | #include "ap.h" | 
|  | 25 | #include "mesh_mpm.h" | 
|  | 26 | #include "mesh_rsn.h" | 
|  | 27 | #include "mesh.h" | 
|  | 28 |  | 
|  | 29 |  | 
|  | 30 | static void wpa_supplicant_mesh_deinit(struct wpa_supplicant *wpa_s) | 
|  | 31 | { | 
|  | 32 | wpa_supplicant_mesh_iface_deinit(wpa_s, wpa_s->ifmsh); | 
|  | 33 | wpa_s->ifmsh = NULL; | 
|  | 34 | wpa_s->current_ssid = NULL; | 
|  | 35 | os_free(wpa_s->mesh_rsn); | 
|  | 36 | wpa_s->mesh_rsn = NULL; | 
|  | 37 | /* TODO: leave mesh (stop beacon). This will happen on link down | 
|  | 38 | * anyway, so it's not urgent */ | 
|  | 39 | } | 
|  | 40 |  | 
|  | 41 |  | 
|  | 42 | void wpa_supplicant_mesh_iface_deinit(struct wpa_supplicant *wpa_s, | 
|  | 43 | struct hostapd_iface *ifmsh) | 
|  | 44 | { | 
|  | 45 | if (!ifmsh) | 
|  | 46 | return; | 
|  | 47 |  | 
|  | 48 | if (ifmsh->mconf) { | 
|  | 49 | mesh_mpm_deinit(wpa_s, ifmsh); | 
| Dmitry Shmidt | d80a401 | 2015-11-05 16:35:40 -0800 | [diff] [blame] | 50 | if (ifmsh->mconf->rsn_ie) { | 
|  | 51 | ifmsh->mconf->rsn_ie = NULL; | 
| Dmitry Shmidt | 6c0da2b | 2015-01-05 13:08:17 -0800 | [diff] [blame] | 52 | /* We cannot free this struct | 
|  | 53 | * because wpa_authenticator on | 
|  | 54 | * hostapd side is also using it | 
|  | 55 | * for now just set to NULL and | 
|  | 56 | * let hostapd code free it. | 
|  | 57 | */ | 
|  | 58 | } | 
|  | 59 | os_free(ifmsh->mconf); | 
|  | 60 | ifmsh->mconf = NULL; | 
|  | 61 | } | 
|  | 62 |  | 
|  | 63 | /* take care of shared data */ | 
|  | 64 | hostapd_interface_deinit(ifmsh); | 
|  | 65 | hostapd_interface_free(ifmsh); | 
|  | 66 | } | 
|  | 67 |  | 
|  | 68 |  | 
| Dmitry Shmidt | d5ab1b5 | 2016-06-21 12:38:41 -0700 | [diff] [blame] | 69 | static struct mesh_conf * mesh_config_create(struct wpa_supplicant *wpa_s, | 
|  | 70 | struct wpa_ssid *ssid) | 
| Dmitry Shmidt | 6c0da2b | 2015-01-05 13:08:17 -0800 | [diff] [blame] | 71 | { | 
|  | 72 | struct mesh_conf *conf; | 
| Dmitry Shmidt | d5ab1b5 | 2016-06-21 12:38:41 -0700 | [diff] [blame] | 73 | int cipher; | 
| Dmitry Shmidt | 6c0da2b | 2015-01-05 13:08:17 -0800 | [diff] [blame] | 74 |  | 
|  | 75 | conf = os_zalloc(sizeof(struct mesh_conf)); | 
|  | 76 | if (!conf) | 
|  | 77 | return NULL; | 
|  | 78 |  | 
|  | 79 | os_memcpy(conf->meshid, ssid->ssid, ssid->ssid_len); | 
|  | 80 | conf->meshid_len = ssid->ssid_len; | 
|  | 81 |  | 
|  | 82 | if (ssid->key_mgmt & WPA_KEY_MGMT_SAE) | 
|  | 83 | conf->security |= MESH_CONF_SEC_AUTH | | 
|  | 84 | MESH_CONF_SEC_AMPE; | 
|  | 85 | else | 
|  | 86 | conf->security |= MESH_CONF_SEC_NONE; | 
| Dmitry Shmidt | d5ab1b5 | 2016-06-21 12:38:41 -0700 | [diff] [blame] | 87 | conf->ieee80211w = ssid->ieee80211w; | 
|  | 88 | if (conf->ieee80211w == MGMT_FRAME_PROTECTION_DEFAULT) { | 
|  | 89 | if (wpa_s->drv_enc & WPA_DRIVER_CAPA_ENC_BIP) | 
|  | 90 | conf->ieee80211w = wpa_s->conf->pmf; | 
|  | 91 | else | 
|  | 92 | conf->ieee80211w = NO_MGMT_FRAME_PROTECTION; | 
|  | 93 | } | 
|  | 94 |  | 
|  | 95 | cipher = wpa_pick_pairwise_cipher(ssid->pairwise_cipher, 0); | 
|  | 96 | if (cipher < 0 || cipher == WPA_CIPHER_TKIP) { | 
|  | 97 | wpa_msg(wpa_s, MSG_INFO, "mesh: Invalid pairwise cipher"); | 
|  | 98 | os_free(conf); | 
|  | 99 | return NULL; | 
|  | 100 | } | 
|  | 101 | conf->pairwise_cipher = cipher; | 
|  | 102 |  | 
|  | 103 | cipher = wpa_pick_group_cipher(ssid->group_cipher); | 
|  | 104 | if (cipher < 0 || cipher == WPA_CIPHER_TKIP || | 
|  | 105 | cipher == WPA_CIPHER_GTK_NOT_USED) { | 
|  | 106 | wpa_msg(wpa_s, MSG_INFO, "mesh: Invalid group cipher"); | 
|  | 107 | os_free(conf); | 
|  | 108 | return NULL; | 
|  | 109 | } | 
|  | 110 |  | 
|  | 111 | conf->group_cipher = cipher; | 
|  | 112 | if (conf->ieee80211w != NO_MGMT_FRAME_PROTECTION) | 
|  | 113 | conf->mgmt_group_cipher = WPA_CIPHER_AES_128_CMAC; | 
| Dmitry Shmidt | 6c0da2b | 2015-01-05 13:08:17 -0800 | [diff] [blame] | 114 |  | 
|  | 115 | /* defaults */ | 
|  | 116 | conf->mesh_pp_id = MESH_PATH_PROTOCOL_HWMP; | 
|  | 117 | conf->mesh_pm_id = MESH_PATH_METRIC_AIRTIME; | 
|  | 118 | conf->mesh_cc_id = 0; | 
|  | 119 | conf->mesh_sp_id = MESH_SYNC_METHOD_NEIGHBOR_OFFSET; | 
|  | 120 | conf->mesh_auth_id = (conf->security & MESH_CONF_SEC_AUTH) ? 1 : 0; | 
|  | 121 | conf->dot11MeshMaxRetries = ssid->dot11MeshMaxRetries; | 
|  | 122 | conf->dot11MeshRetryTimeout = ssid->dot11MeshRetryTimeout; | 
|  | 123 | conf->dot11MeshConfirmTimeout = ssid->dot11MeshConfirmTimeout; | 
|  | 124 | conf->dot11MeshHoldingTimeout = ssid->dot11MeshHoldingTimeout; | 
|  | 125 |  | 
|  | 126 | return conf; | 
|  | 127 | } | 
|  | 128 |  | 
|  | 129 |  | 
|  | 130 | static void wpas_mesh_copy_groups(struct hostapd_data *bss, | 
|  | 131 | struct wpa_supplicant *wpa_s) | 
|  | 132 | { | 
|  | 133 | int num_groups; | 
|  | 134 | size_t groups_size; | 
|  | 135 |  | 
|  | 136 | for (num_groups = 0; wpa_s->conf->sae_groups[num_groups] > 0; | 
|  | 137 | num_groups++) | 
|  | 138 | ; | 
|  | 139 |  | 
|  | 140 | groups_size = (num_groups + 1) * sizeof(wpa_s->conf->sae_groups[0]); | 
|  | 141 | bss->conf->sae_groups = os_malloc(groups_size); | 
|  | 142 | if (bss->conf->sae_groups) | 
|  | 143 | os_memcpy(bss->conf->sae_groups, wpa_s->conf->sae_groups, | 
|  | 144 | groups_size); | 
|  | 145 | } | 
|  | 146 |  | 
|  | 147 |  | 
|  | 148 | static int wpa_supplicant_mesh_init(struct wpa_supplicant *wpa_s, | 
| Dmitry Shmidt | 2933359 | 2017-01-09 12:27:11 -0800 | [diff] [blame] | 149 | struct wpa_ssid *ssid, | 
|  | 150 | struct hostapd_freq_params *freq) | 
| Dmitry Shmidt | 6c0da2b | 2015-01-05 13:08:17 -0800 | [diff] [blame] | 151 | { | 
|  | 152 | struct hostapd_iface *ifmsh; | 
|  | 153 | struct hostapd_data *bss; | 
|  | 154 | struct hostapd_config *conf; | 
|  | 155 | struct mesh_conf *mconf; | 
|  | 156 | int basic_rates_erp[] = { 10, 20, 55, 60, 110, 120, 240, -1 }; | 
|  | 157 | static int default_groups[] = { 19, 20, 21, 25, 26, -1 }; | 
|  | 158 | size_t len; | 
|  | 159 | int rate_len; | 
| Dmitry Shmidt | 2933359 | 2017-01-09 12:27:11 -0800 | [diff] [blame] | 160 | int frequency; | 
| Dmitry Shmidt | 6c0da2b | 2015-01-05 13:08:17 -0800 | [diff] [blame] | 161 |  | 
|  | 162 | if (!wpa_s->conf->user_mpm) { | 
|  | 163 | /* not much for us to do here */ | 
|  | 164 | wpa_msg(wpa_s, MSG_WARNING, | 
|  | 165 | "user_mpm is not enabled in configuration"); | 
|  | 166 | return 0; | 
|  | 167 | } | 
|  | 168 |  | 
|  | 169 | wpa_s->ifmsh = ifmsh = os_zalloc(sizeof(*wpa_s->ifmsh)); | 
|  | 170 | if (!ifmsh) | 
|  | 171 | return -ENOMEM; | 
|  | 172 |  | 
|  | 173 | ifmsh->drv_flags = wpa_s->drv_flags; | 
|  | 174 | ifmsh->num_bss = 1; | 
|  | 175 | ifmsh->bss = os_calloc(wpa_s->ifmsh->num_bss, | 
|  | 176 | sizeof(struct hostapd_data *)); | 
|  | 177 | if (!ifmsh->bss) | 
|  | 178 | goto out_free; | 
|  | 179 |  | 
| Dmitry Shmidt | ebd93af | 2017-02-21 13:40:44 -0800 | [diff] [blame] | 180 | ifmsh->bss[0] = bss = hostapd_alloc_bss_data(NULL, NULL, NULL); | 
| Dmitry Shmidt | 6c0da2b | 2015-01-05 13:08:17 -0800 | [diff] [blame] | 181 | if (!bss) | 
|  | 182 | goto out_free; | 
|  | 183 |  | 
|  | 184 | os_memcpy(bss->own_addr, wpa_s->own_addr, ETH_ALEN); | 
|  | 185 | bss->driver = wpa_s->driver; | 
|  | 186 | bss->drv_priv = wpa_s->drv_priv; | 
|  | 187 | bss->iface = ifmsh; | 
|  | 188 | bss->mesh_sta_free_cb = mesh_mpm_free_sta; | 
| Dmitry Shmidt | 2933359 | 2017-01-09 12:27:11 -0800 | [diff] [blame] | 189 | frequency = ssid->frequency; | 
|  | 190 | if (frequency != freq->freq && | 
|  | 191 | frequency == freq->freq + freq->sec_channel_offset * 20) { | 
|  | 192 | wpa_printf(MSG_DEBUG, "mesh: pri/sec channels switched"); | 
|  | 193 | frequency = freq->freq; | 
|  | 194 | } | 
|  | 195 | wpa_s->assoc_freq = frequency; | 
| Dmitry Shmidt | 6c0da2b | 2015-01-05 13:08:17 -0800 | [diff] [blame] | 196 | wpa_s->current_ssid = ssid; | 
|  | 197 |  | 
|  | 198 | /* setup an AP config for auth processing */ | 
|  | 199 | conf = hostapd_config_defaults(); | 
|  | 200 | if (!conf) | 
|  | 201 | goto out_free; | 
|  | 202 |  | 
|  | 203 | bss->conf = *conf->bss; | 
|  | 204 | bss->conf->start_disabled = 1; | 
|  | 205 | bss->conf->mesh = MESH_ENABLED; | 
| Dmitry Shmidt | 2f74e36 | 2015-01-21 13:19:05 -0800 | [diff] [blame] | 206 | bss->conf->ap_max_inactivity = wpa_s->conf->mesh_max_inactivity; | 
| Dmitry Shmidt | 6c0da2b | 2015-01-05 13:08:17 -0800 | [diff] [blame] | 207 | bss->iconf = conf; | 
|  | 208 | ifmsh->conf = conf; | 
|  | 209 |  | 
|  | 210 | ifmsh->bss[0]->max_plinks = wpa_s->conf->max_peer_links; | 
| Dmitry Shmidt | d80a401 | 2015-11-05 16:35:40 -0800 | [diff] [blame] | 211 | ifmsh->bss[0]->dot11RSNASAERetransPeriod = | 
|  | 212 | wpa_s->conf->dot11RSNASAERetransPeriod; | 
| Dmitry Shmidt | 6c0da2b | 2015-01-05 13:08:17 -0800 | [diff] [blame] | 213 | os_strlcpy(bss->conf->iface, wpa_s->ifname, sizeof(bss->conf->iface)); | 
|  | 214 |  | 
| Dmitry Shmidt | d5ab1b5 | 2016-06-21 12:38:41 -0700 | [diff] [blame] | 215 | mconf = mesh_config_create(wpa_s, ssid); | 
| Dmitry Shmidt | 6c0da2b | 2015-01-05 13:08:17 -0800 | [diff] [blame] | 216 | if (!mconf) | 
|  | 217 | goto out_free; | 
|  | 218 | ifmsh->mconf = mconf; | 
|  | 219 |  | 
|  | 220 | /* need conf->hw_mode for supported rates. */ | 
| Dmitry Shmidt | 2933359 | 2017-01-09 12:27:11 -0800 | [diff] [blame] | 221 | conf->hw_mode = ieee80211_freq_to_chan(frequency, &conf->channel); | 
| Dmitry Shmidt | 6c0da2b | 2015-01-05 13:08:17 -0800 | [diff] [blame] | 222 | if (conf->hw_mode == NUM_HOSTAPD_MODES) { | 
|  | 223 | wpa_printf(MSG_ERROR, "Unsupported mesh mode frequency: %d MHz", | 
| Dmitry Shmidt | 2933359 | 2017-01-09 12:27:11 -0800 | [diff] [blame] | 224 | frequency); | 
| Dmitry Shmidt | 6c0da2b | 2015-01-05 13:08:17 -0800 | [diff] [blame] | 225 | goto out_free; | 
|  | 226 | } | 
| Dmitry Shmidt | 0e58d9b | 2015-12-22 10:59:44 -0800 | [diff] [blame] | 227 | if (ssid->ht40) | 
|  | 228 | conf->secondary_channel = ssid->ht40; | 
|  | 229 | if (conf->hw_mode == HOSTAPD_MODE_IEEE80211A && ssid->vht) { | 
|  | 230 | conf->vht_oper_chwidth = ssid->max_oper_chwidth; | 
|  | 231 | switch (conf->vht_oper_chwidth) { | 
|  | 232 | case VHT_CHANWIDTH_80MHZ: | 
|  | 233 | case VHT_CHANWIDTH_80P80MHZ: | 
|  | 234 | ieee80211_freq_to_chan( | 
| Dmitry Shmidt | 2933359 | 2017-01-09 12:27:11 -0800 | [diff] [blame] | 235 | frequency, | 
| Dmitry Shmidt | 0e58d9b | 2015-12-22 10:59:44 -0800 | [diff] [blame] | 236 | &conf->vht_oper_centr_freq_seg0_idx); | 
|  | 237 | conf->vht_oper_centr_freq_seg0_idx += ssid->ht40 * 2; | 
|  | 238 | break; | 
|  | 239 | case VHT_CHANWIDTH_160MHZ: | 
|  | 240 | ieee80211_freq_to_chan( | 
| Dmitry Shmidt | 2933359 | 2017-01-09 12:27:11 -0800 | [diff] [blame] | 241 | frequency, | 
| Dmitry Shmidt | 0e58d9b | 2015-12-22 10:59:44 -0800 | [diff] [blame] | 242 | &conf->vht_oper_centr_freq_seg0_idx); | 
|  | 243 | conf->vht_oper_centr_freq_seg0_idx += ssid->ht40 * 2; | 
|  | 244 | conf->vht_oper_centr_freq_seg0_idx += 40 / 5; | 
|  | 245 | break; | 
|  | 246 | } | 
|  | 247 | ieee80211_freq_to_chan(ssid->vht_center_freq2, | 
|  | 248 | &conf->vht_oper_centr_freq_seg1_idx); | 
|  | 249 | } | 
| Dmitry Shmidt | 6c0da2b | 2015-01-05 13:08:17 -0800 | [diff] [blame] | 250 |  | 
|  | 251 | if (ssid->mesh_basic_rates == NULL) { | 
|  | 252 | /* | 
|  | 253 | * XXX: Hack! This is so an MPM which correctly sets the ERP | 
|  | 254 | * mandatory rates as BSSBasicRateSet doesn't reject us. We | 
|  | 255 | * could add a new hw_mode HOSTAPD_MODE_IEEE80211G_ERP, but | 
|  | 256 | * this is way easier. This also makes our BSSBasicRateSet | 
|  | 257 | * advertised in beacons match the one in peering frames, sigh. | 
|  | 258 | */ | 
|  | 259 | if (conf->hw_mode == HOSTAPD_MODE_IEEE80211G) { | 
|  | 260 | conf->basic_rates = os_malloc(sizeof(basic_rates_erp)); | 
|  | 261 | if (!conf->basic_rates) | 
|  | 262 | goto out_free; | 
|  | 263 | os_memcpy(conf->basic_rates, basic_rates_erp, | 
|  | 264 | sizeof(basic_rates_erp)); | 
|  | 265 | } | 
|  | 266 | } else { | 
|  | 267 | rate_len = 0; | 
|  | 268 | while (1) { | 
|  | 269 | if (ssid->mesh_basic_rates[rate_len] < 1) | 
|  | 270 | break; | 
|  | 271 | rate_len++; | 
|  | 272 | } | 
|  | 273 | conf->basic_rates = os_calloc(rate_len + 1, sizeof(int)); | 
|  | 274 | if (conf->basic_rates == NULL) | 
|  | 275 | goto out_free; | 
|  | 276 | os_memcpy(conf->basic_rates, ssid->mesh_basic_rates, | 
|  | 277 | rate_len * sizeof(int)); | 
|  | 278 | conf->basic_rates[rate_len] = -1; | 
|  | 279 | } | 
|  | 280 |  | 
|  | 281 | if (hostapd_setup_interface(ifmsh)) { | 
|  | 282 | wpa_printf(MSG_ERROR, | 
|  | 283 | "Failed to initialize hostapd interface for mesh"); | 
|  | 284 | return -1; | 
|  | 285 | } | 
|  | 286 |  | 
|  | 287 | if (wpa_drv_init_mesh(wpa_s)) { | 
|  | 288 | wpa_msg(wpa_s, MSG_ERROR, "Failed to init mesh in driver"); | 
|  | 289 | return -1; | 
|  | 290 | } | 
|  | 291 |  | 
|  | 292 | if (mconf->security != MESH_CONF_SEC_NONE) { | 
|  | 293 | if (ssid->passphrase == NULL) { | 
|  | 294 | wpa_printf(MSG_ERROR, | 
|  | 295 | "mesh: Passphrase for SAE not configured"); | 
|  | 296 | goto out_free; | 
|  | 297 | } | 
|  | 298 |  | 
|  | 299 | bss->conf->wpa = ssid->proto; | 
|  | 300 | bss->conf->wpa_key_mgmt = ssid->key_mgmt; | 
|  | 301 |  | 
|  | 302 | if (wpa_s->conf->sae_groups && | 
|  | 303 | wpa_s->conf->sae_groups[0] > 0) { | 
|  | 304 | wpas_mesh_copy_groups(bss, wpa_s); | 
|  | 305 | } else { | 
|  | 306 | bss->conf->sae_groups = | 
|  | 307 | os_malloc(sizeof(default_groups)); | 
|  | 308 | if (!bss->conf->sae_groups) | 
|  | 309 | goto out_free; | 
|  | 310 | os_memcpy(bss->conf->sae_groups, default_groups, | 
|  | 311 | sizeof(default_groups)); | 
|  | 312 | } | 
|  | 313 |  | 
|  | 314 | len = os_strlen(ssid->passphrase); | 
|  | 315 | bss->conf->ssid.wpa_passphrase = | 
|  | 316 | dup_binstr(ssid->passphrase, len); | 
|  | 317 |  | 
|  | 318 | wpa_s->mesh_rsn = mesh_rsn_auth_init(wpa_s, mconf); | 
|  | 319 | if (!wpa_s->mesh_rsn) | 
|  | 320 | goto out_free; | 
|  | 321 | } | 
|  | 322 |  | 
|  | 323 | wpa_supplicant_conf_ap_ht(wpa_s, ssid, conf); | 
|  | 324 |  | 
|  | 325 | return 0; | 
|  | 326 | out_free: | 
|  | 327 | wpa_supplicant_mesh_deinit(wpa_s); | 
|  | 328 | return -ENOMEM; | 
|  | 329 | } | 
|  | 330 |  | 
|  | 331 |  | 
|  | 332 | void wpa_mesh_notify_peer(struct wpa_supplicant *wpa_s, const u8 *addr, | 
|  | 333 | const u8 *ies, size_t ie_len) | 
|  | 334 | { | 
|  | 335 | struct ieee802_11_elems elems; | 
|  | 336 |  | 
|  | 337 | wpa_msg(wpa_s, MSG_INFO, | 
|  | 338 | "new peer notification for " MACSTR, MAC2STR(addr)); | 
|  | 339 |  | 
|  | 340 | if (ieee802_11_parse_elems(ies, ie_len, &elems, 0) == ParseFailed) { | 
|  | 341 | wpa_msg(wpa_s, MSG_INFO, "Could not parse beacon from " MACSTR, | 
|  | 342 | MAC2STR(addr)); | 
|  | 343 | return; | 
|  | 344 | } | 
|  | 345 | wpa_mesh_new_mesh_peer(wpa_s, addr, &elems); | 
|  | 346 | } | 
|  | 347 |  | 
|  | 348 |  | 
|  | 349 | void wpa_supplicant_mesh_add_scan_ie(struct wpa_supplicant *wpa_s, | 
|  | 350 | struct wpabuf **extra_ie) | 
|  | 351 | { | 
|  | 352 | /* EID + 0-length (wildcard) mesh-id */ | 
|  | 353 | size_t ielen = 2; | 
|  | 354 |  | 
|  | 355 | if (wpabuf_resize(extra_ie, ielen) == 0) { | 
|  | 356 | wpabuf_put_u8(*extra_ie, WLAN_EID_MESH_ID); | 
|  | 357 | wpabuf_put_u8(*extra_ie, 0); | 
|  | 358 | } | 
|  | 359 | } | 
|  | 360 |  | 
|  | 361 |  | 
|  | 362 | int wpa_supplicant_join_mesh(struct wpa_supplicant *wpa_s, | 
|  | 363 | struct wpa_ssid *ssid) | 
|  | 364 | { | 
|  | 365 | struct wpa_driver_mesh_join_params params; | 
|  | 366 | int ret = 0; | 
|  | 367 |  | 
|  | 368 | if (!ssid || !ssid->ssid || !ssid->ssid_len || !ssid->frequency) { | 
|  | 369 | ret = -ENOENT; | 
|  | 370 | goto out; | 
|  | 371 | } | 
|  | 372 |  | 
|  | 373 | wpa_supplicant_mesh_deinit(wpa_s); | 
|  | 374 |  | 
| Dmitry Shmidt | d5ab1b5 | 2016-06-21 12:38:41 -0700 | [diff] [blame] | 375 | wpa_s->pairwise_cipher = WPA_CIPHER_NONE; | 
|  | 376 | wpa_s->group_cipher = WPA_CIPHER_NONE; | 
|  | 377 | wpa_s->mgmt_group_cipher = 0; | 
| Dmitry Shmidt | d7ff03d | 2015-12-04 14:49:35 -0800 | [diff] [blame] | 378 |  | 
| Dmitry Shmidt | 6c0da2b | 2015-01-05 13:08:17 -0800 | [diff] [blame] | 379 | os_memset(¶ms, 0, sizeof(params)); | 
|  | 380 | params.meshid = ssid->ssid; | 
|  | 381 | params.meshid_len = ssid->ssid_len; | 
| Dmitry Shmidt | ff787d5 | 2015-01-12 13:01:47 -0800 | [diff] [blame] | 382 | ibss_mesh_setup_freq(wpa_s, ssid, ¶ms.freq); | 
|  | 383 | wpa_s->mesh_ht_enabled = !!params.freq.ht_enabled; | 
| Dmitry Shmidt | d7ff03d | 2015-12-04 14:49:35 -0800 | [diff] [blame] | 384 | wpa_s->mesh_vht_enabled = !!params.freq.vht_enabled; | 
| Dmitry Shmidt | 0e58d9b | 2015-12-22 10:59:44 -0800 | [diff] [blame] | 385 | if (params.freq.ht_enabled && params.freq.sec_channel_offset) | 
|  | 386 | ssid->ht40 = params.freq.sec_channel_offset; | 
|  | 387 | if (wpa_s->mesh_vht_enabled) { | 
|  | 388 | ssid->vht = 1; | 
|  | 389 | switch (params.freq.bandwidth) { | 
|  | 390 | case 80: | 
|  | 391 | if (params.freq.center_freq2) { | 
|  | 392 | ssid->max_oper_chwidth = VHT_CHANWIDTH_80P80MHZ; | 
|  | 393 | ssid->vht_center_freq2 = | 
|  | 394 | params.freq.center_freq2; | 
|  | 395 | } else { | 
|  | 396 | ssid->max_oper_chwidth = VHT_CHANWIDTH_80MHZ; | 
|  | 397 | } | 
|  | 398 | break; | 
|  | 399 | case 160: | 
|  | 400 | ssid->max_oper_chwidth = VHT_CHANWIDTH_160MHZ; | 
|  | 401 | break; | 
|  | 402 | default: | 
|  | 403 | ssid->max_oper_chwidth = VHT_CHANWIDTH_USE_HT; | 
|  | 404 | break; | 
|  | 405 | } | 
|  | 406 | } | 
| Dmitry Shmidt | 6c0da2b | 2015-01-05 13:08:17 -0800 | [diff] [blame] | 407 | if (ssid->beacon_int > 0) | 
|  | 408 | params.beacon_int = ssid->beacon_int; | 
|  | 409 | else if (wpa_s->conf->beacon_int > 0) | 
|  | 410 | params.beacon_int = wpa_s->conf->beacon_int; | 
| Dmitry Shmidt | 58d12ad | 2016-07-28 10:07:03 -0700 | [diff] [blame] | 411 | if (ssid->dtim_period > 0) | 
|  | 412 | params.dtim_period = ssid->dtim_period; | 
|  | 413 | else if (wpa_s->conf->dtim_period > 0) | 
|  | 414 | params.dtim_period = wpa_s->conf->dtim_period; | 
| Dmitry Shmidt | d13095b | 2016-08-22 14:02:19 -0700 | [diff] [blame] | 415 | params.conf.max_peer_links = wpa_s->conf->max_peer_links; | 
| Dmitry Shmidt | 6c0da2b | 2015-01-05 13:08:17 -0800 | [diff] [blame] | 416 |  | 
|  | 417 | if (ssid->key_mgmt & WPA_KEY_MGMT_SAE) { | 
|  | 418 | params.flags |= WPA_DRIVER_MESH_FLAG_SAE_AUTH; | 
|  | 419 | params.flags |= WPA_DRIVER_MESH_FLAG_AMPE; | 
|  | 420 | wpa_s->conf->user_mpm = 1; | 
|  | 421 | } | 
|  | 422 |  | 
|  | 423 | if (wpa_s->conf->user_mpm) { | 
|  | 424 | params.flags |= WPA_DRIVER_MESH_FLAG_USER_MPM; | 
| Dmitry Shmidt | d13095b | 2016-08-22 14:02:19 -0700 | [diff] [blame] | 425 | params.conf.auto_plinks = 0; | 
| Dmitry Shmidt | 6c0da2b | 2015-01-05 13:08:17 -0800 | [diff] [blame] | 426 | } else { | 
|  | 427 | params.flags |= WPA_DRIVER_MESH_FLAG_DRIVER_MPM; | 
| Dmitry Shmidt | d13095b | 2016-08-22 14:02:19 -0700 | [diff] [blame] | 428 | params.conf.auto_plinks = 1; | 
| Dmitry Shmidt | 6c0da2b | 2015-01-05 13:08:17 -0800 | [diff] [blame] | 429 | } | 
| Dmitry Shmidt | 2f74e36 | 2015-01-21 13:19:05 -0800 | [diff] [blame] | 430 | params.conf.peer_link_timeout = wpa_s->conf->mesh_max_inactivity; | 
| Dmitry Shmidt | 6c0da2b | 2015-01-05 13:08:17 -0800 | [diff] [blame] | 431 |  | 
| Dmitry Shmidt | 2933359 | 2017-01-09 12:27:11 -0800 | [diff] [blame] | 432 | if (wpa_supplicant_mesh_init(wpa_s, ssid, ¶ms.freq)) { | 
| Dmitry Shmidt | 6c0da2b | 2015-01-05 13:08:17 -0800 | [diff] [blame] | 433 | wpa_msg(wpa_s, MSG_ERROR, "Failed to init mesh"); | 
| Dmitry Shmidt | 4dd28dc | 2015-03-10 11:21:43 -0700 | [diff] [blame] | 434 | wpa_drv_leave_mesh(wpa_s); | 
| Dmitry Shmidt | 6c0da2b | 2015-01-05 13:08:17 -0800 | [diff] [blame] | 435 | ret = -1; | 
|  | 436 | goto out; | 
|  | 437 | } | 
|  | 438 |  | 
| Dmitry Shmidt | d5ab1b5 | 2016-06-21 12:38:41 -0700 | [diff] [blame] | 439 | if (ssid->key_mgmt & WPA_KEY_MGMT_SAE) { | 
|  | 440 | wpa_s->pairwise_cipher = wpa_s->mesh_rsn->pairwise_cipher; | 
|  | 441 | wpa_s->group_cipher = wpa_s->mesh_rsn->group_cipher; | 
|  | 442 | wpa_s->mgmt_group_cipher = wpa_s->mesh_rsn->mgmt_group_cipher; | 
|  | 443 | } | 
|  | 444 |  | 
| Dmitry Shmidt | 6c0da2b | 2015-01-05 13:08:17 -0800 | [diff] [blame] | 445 | if (wpa_s->ifmsh) { | 
| Dmitry Shmidt | d80a401 | 2015-11-05 16:35:40 -0800 | [diff] [blame] | 446 | params.ies = wpa_s->ifmsh->mconf->rsn_ie; | 
|  | 447 | params.ie_len = wpa_s->ifmsh->mconf->rsn_ie_len; | 
| Dmitry Shmidt | 6c0da2b | 2015-01-05 13:08:17 -0800 | [diff] [blame] | 448 | params.basic_rates = wpa_s->ifmsh->basic_rates; | 
| Dmitry Shmidt | d13095b | 2016-08-22 14:02:19 -0700 | [diff] [blame] | 449 | params.conf.flags |= WPA_DRIVER_MESH_CONF_FLAG_HT_OP_MODE; | 
|  | 450 | params.conf.ht_opmode = wpa_s->ifmsh->bss[0]->iface->ht_op_mode; | 
| Dmitry Shmidt | 6c0da2b | 2015-01-05 13:08:17 -0800 | [diff] [blame] | 451 | } | 
|  | 452 |  | 
|  | 453 | wpa_msg(wpa_s, MSG_INFO, "joining mesh %s", | 
|  | 454 | wpa_ssid_txt(ssid->ssid, ssid->ssid_len)); | 
|  | 455 | ret = wpa_drv_join_mesh(wpa_s, ¶ms); | 
|  | 456 | if (ret) | 
| Dmitry Shmidt | d5ab1b5 | 2016-06-21 12:38:41 -0700 | [diff] [blame] | 457 | wpa_msg(wpa_s, MSG_ERROR, "mesh join error=%d", ret); | 
| Dmitry Shmidt | 6c0da2b | 2015-01-05 13:08:17 -0800 | [diff] [blame] | 458 |  | 
|  | 459 | /* hostapd sets the interface down until we associate */ | 
|  | 460 | wpa_drv_set_operstate(wpa_s, 1); | 
|  | 461 |  | 
| Dmitry Shmidt | 4ae50e6 | 2016-06-27 13:48:39 -0700 | [diff] [blame] | 462 | if (!ret) | 
|  | 463 | wpa_supplicant_set_state(wpa_s, WPA_COMPLETED); | 
|  | 464 |  | 
| Dmitry Shmidt | 6c0da2b | 2015-01-05 13:08:17 -0800 | [diff] [blame] | 465 | out: | 
|  | 466 | return ret; | 
|  | 467 | } | 
|  | 468 |  | 
|  | 469 |  | 
|  | 470 | int wpa_supplicant_leave_mesh(struct wpa_supplicant *wpa_s) | 
|  | 471 | { | 
|  | 472 | int ret = 0; | 
|  | 473 |  | 
|  | 474 | wpa_msg(wpa_s, MSG_INFO, "leaving mesh"); | 
|  | 475 |  | 
|  | 476 | /* Need to send peering close messages first */ | 
|  | 477 | wpa_supplicant_mesh_deinit(wpa_s); | 
|  | 478 |  | 
|  | 479 | ret = wpa_drv_leave_mesh(wpa_s); | 
|  | 480 | if (ret) | 
|  | 481 | wpa_msg(wpa_s, MSG_ERROR, "mesh leave error=%d", ret); | 
|  | 482 |  | 
|  | 483 | wpa_drv_set_operstate(wpa_s, 1); | 
|  | 484 |  | 
|  | 485 | return ret; | 
|  | 486 | } | 
|  | 487 |  | 
|  | 488 |  | 
|  | 489 | static int mesh_attr_text(const u8 *ies, size_t ies_len, char *buf, char *end) | 
|  | 490 | { | 
|  | 491 | struct ieee802_11_elems elems; | 
|  | 492 | char *mesh_id, *pos = buf; | 
|  | 493 | u8 *bss_basic_rate_set; | 
|  | 494 | int bss_basic_rate_set_len, ret, i; | 
|  | 495 |  | 
|  | 496 | if (ieee802_11_parse_elems(ies, ies_len, &elems, 0) == ParseFailed) | 
|  | 497 | return -1; | 
|  | 498 |  | 
|  | 499 | if (elems.mesh_id_len < 1) | 
|  | 500 | return 0; | 
|  | 501 |  | 
|  | 502 | mesh_id = os_malloc(elems.mesh_id_len + 1); | 
|  | 503 | if (mesh_id == NULL) | 
|  | 504 | return -1; | 
|  | 505 |  | 
|  | 506 | os_memcpy(mesh_id, elems.mesh_id, elems.mesh_id_len); | 
|  | 507 | mesh_id[elems.mesh_id_len] = '\0'; | 
|  | 508 | ret = os_snprintf(pos, end - pos, "mesh_id=%s\n", mesh_id); | 
|  | 509 | os_free(mesh_id); | 
|  | 510 | if (os_snprintf_error(end - pos, ret)) | 
|  | 511 | return pos - buf; | 
|  | 512 | pos += ret; | 
|  | 513 |  | 
|  | 514 | if (elems.mesh_config_len > 6) { | 
|  | 515 | ret = os_snprintf(pos, end - pos, | 
|  | 516 | "active_path_selection_protocol_id=0x%02x\n" | 
|  | 517 | "active_path_selection_metric_id=0x%02x\n" | 
|  | 518 | "congestion_control_mode_id=0x%02x\n" | 
|  | 519 | "synchronization_method_id=0x%02x\n" | 
|  | 520 | "authentication_protocol_id=0x%02x\n" | 
|  | 521 | "mesh_formation_info=0x%02x\n" | 
|  | 522 | "mesh_capability=0x%02x\n", | 
|  | 523 | elems.mesh_config[0], elems.mesh_config[1], | 
|  | 524 | elems.mesh_config[2], elems.mesh_config[3], | 
|  | 525 | elems.mesh_config[4], elems.mesh_config[5], | 
|  | 526 | elems.mesh_config[6]); | 
|  | 527 | if (os_snprintf_error(end - pos, ret)) | 
|  | 528 | return pos - buf; | 
|  | 529 | pos += ret; | 
|  | 530 | } | 
|  | 531 |  | 
|  | 532 | bss_basic_rate_set = os_malloc(elems.supp_rates_len + | 
|  | 533 | elems.ext_supp_rates_len); | 
|  | 534 | if (bss_basic_rate_set == NULL) | 
|  | 535 | return -1; | 
|  | 536 |  | 
|  | 537 | bss_basic_rate_set_len = 0; | 
|  | 538 | for (i = 0; i < elems.supp_rates_len; i++) { | 
|  | 539 | if (elems.supp_rates[i] & 0x80) { | 
|  | 540 | bss_basic_rate_set[bss_basic_rate_set_len++] = | 
|  | 541 | (elems.supp_rates[i] & 0x7f) * 5; | 
|  | 542 | } | 
|  | 543 | } | 
|  | 544 | for (i = 0; i < elems.ext_supp_rates_len; i++) { | 
|  | 545 | if (elems.ext_supp_rates[i] & 0x80) { | 
|  | 546 | bss_basic_rate_set[bss_basic_rate_set_len++] = | 
|  | 547 | (elems.ext_supp_rates[i] & 0x7f) * 5; | 
|  | 548 | } | 
|  | 549 | } | 
|  | 550 | if (bss_basic_rate_set_len > 0) { | 
|  | 551 | ret = os_snprintf(pos, end - pos, "bss_basic_rate_set=%d", | 
|  | 552 | bss_basic_rate_set[0]); | 
|  | 553 | if (os_snprintf_error(end - pos, ret)) | 
| Dmitry Shmidt | 9d9e602 | 2015-04-23 10:34:55 -0700 | [diff] [blame] | 554 | goto fail; | 
| Dmitry Shmidt | 6c0da2b | 2015-01-05 13:08:17 -0800 | [diff] [blame] | 555 | pos += ret; | 
|  | 556 |  | 
|  | 557 | for (i = 1; i < bss_basic_rate_set_len; i++) { | 
|  | 558 | ret = os_snprintf(pos, end - pos, " %d", | 
|  | 559 | bss_basic_rate_set[i]); | 
|  | 560 | if (os_snprintf_error(end - pos, ret)) | 
| Dmitry Shmidt | 9d9e602 | 2015-04-23 10:34:55 -0700 | [diff] [blame] | 561 | goto fail; | 
| Dmitry Shmidt | 6c0da2b | 2015-01-05 13:08:17 -0800 | [diff] [blame] | 562 | pos += ret; | 
|  | 563 | } | 
|  | 564 |  | 
|  | 565 | ret = os_snprintf(pos, end - pos, "\n"); | 
|  | 566 | if (os_snprintf_error(end - pos, ret)) | 
| Dmitry Shmidt | 9d9e602 | 2015-04-23 10:34:55 -0700 | [diff] [blame] | 567 | goto fail; | 
| Dmitry Shmidt | 6c0da2b | 2015-01-05 13:08:17 -0800 | [diff] [blame] | 568 | pos += ret; | 
|  | 569 | } | 
| Dmitry Shmidt | 9d9e602 | 2015-04-23 10:34:55 -0700 | [diff] [blame] | 570 | fail: | 
| Dmitry Shmidt | 6c0da2b | 2015-01-05 13:08:17 -0800 | [diff] [blame] | 571 | os_free(bss_basic_rate_set); | 
|  | 572 |  | 
|  | 573 | return pos - buf; | 
|  | 574 | } | 
|  | 575 |  | 
|  | 576 |  | 
|  | 577 | int wpas_mesh_scan_result_text(const u8 *ies, size_t ies_len, char *buf, | 
|  | 578 | char *end) | 
|  | 579 | { | 
|  | 580 | return mesh_attr_text(ies, ies_len, buf, end); | 
|  | 581 | } | 
|  | 582 |  | 
|  | 583 |  | 
|  | 584 | static int wpas_mesh_get_ifname(struct wpa_supplicant *wpa_s, char *ifname, | 
|  | 585 | size_t len) | 
|  | 586 | { | 
|  | 587 | char *ifname_ptr = wpa_s->ifname; | 
|  | 588 | int res; | 
|  | 589 |  | 
|  | 590 | res = os_snprintf(ifname, len, "mesh-%s-%d", ifname_ptr, | 
|  | 591 | wpa_s->mesh_if_idx); | 
|  | 592 | if (os_snprintf_error(len, res) || | 
|  | 593 | (os_strlen(ifname) >= IFNAMSIZ && | 
|  | 594 | os_strlen(wpa_s->ifname) < IFNAMSIZ)) { | 
|  | 595 | /* Try to avoid going over the IFNAMSIZ length limit */ | 
|  | 596 | res = os_snprintf(ifname, len, "mesh-%d", wpa_s->mesh_if_idx); | 
|  | 597 | if (os_snprintf_error(len, res)) | 
|  | 598 | return -1; | 
|  | 599 | } | 
|  | 600 | wpa_s->mesh_if_idx++; | 
|  | 601 | return 0; | 
|  | 602 | } | 
|  | 603 |  | 
|  | 604 |  | 
|  | 605 | int wpas_mesh_add_interface(struct wpa_supplicant *wpa_s, char *ifname, | 
|  | 606 | size_t len) | 
|  | 607 | { | 
|  | 608 | struct wpa_interface iface; | 
|  | 609 | struct wpa_supplicant *mesh_wpa_s; | 
|  | 610 | u8 addr[ETH_ALEN]; | 
|  | 611 |  | 
|  | 612 | if (ifname[0] == '\0' && wpas_mesh_get_ifname(wpa_s, ifname, len) < 0) | 
|  | 613 | return -1; | 
|  | 614 |  | 
|  | 615 | if (wpa_drv_if_add(wpa_s, WPA_IF_MESH, ifname, NULL, NULL, NULL, addr, | 
|  | 616 | NULL) < 0) { | 
|  | 617 | wpa_printf(MSG_ERROR, | 
|  | 618 | "mesh: Failed to create new mesh interface"); | 
|  | 619 | return -1; | 
|  | 620 | } | 
|  | 621 | wpa_printf(MSG_INFO, "mesh: Created virtual interface %s addr " | 
|  | 622 | MACSTR, ifname, MAC2STR(addr)); | 
|  | 623 |  | 
|  | 624 | os_memset(&iface, 0, sizeof(iface)); | 
|  | 625 | iface.ifname = ifname; | 
|  | 626 | iface.driver = wpa_s->driver->name; | 
|  | 627 | iface.driver_param = wpa_s->conf->driver_param; | 
|  | 628 | iface.ctrl_interface = wpa_s->conf->ctrl_interface; | 
|  | 629 |  | 
| Dmitry Shmidt | 203eadb | 2015-03-05 14:16:04 -0800 | [diff] [blame] | 630 | mesh_wpa_s = wpa_supplicant_add_iface(wpa_s->global, &iface, wpa_s); | 
| Dmitry Shmidt | 6c0da2b | 2015-01-05 13:08:17 -0800 | [diff] [blame] | 631 | if (!mesh_wpa_s) { | 
|  | 632 | wpa_printf(MSG_ERROR, | 
|  | 633 | "mesh: Failed to create new wpa_supplicant interface"); | 
| Dmitry Shmidt | d5ab1b5 | 2016-06-21 12:38:41 -0700 | [diff] [blame] | 634 | wpa_drv_if_remove(wpa_s, WPA_IF_MESH, ifname); | 
| Dmitry Shmidt | 6c0da2b | 2015-01-05 13:08:17 -0800 | [diff] [blame] | 635 | return -1; | 
|  | 636 | } | 
|  | 637 | mesh_wpa_s->mesh_if_created = 1; | 
| Dmitry Shmidt | 6c0da2b | 2015-01-05 13:08:17 -0800 | [diff] [blame] | 638 | return 0; | 
|  | 639 | } | 
| Dmitry Shmidt | e466304 | 2016-04-04 10:07:49 -0700 | [diff] [blame] | 640 |  | 
|  | 641 |  | 
|  | 642 | int wpas_mesh_peer_remove(struct wpa_supplicant *wpa_s, const u8 *addr) | 
|  | 643 | { | 
|  | 644 | return mesh_mpm_close_peer(wpa_s, addr); | 
|  | 645 | } | 
|  | 646 |  | 
|  | 647 |  | 
|  | 648 | int wpas_mesh_peer_add(struct wpa_supplicant *wpa_s, const u8 *addr, | 
|  | 649 | int duration) | 
|  | 650 | { | 
|  | 651 | return mesh_mpm_connect_peer(wpa_s, addr, duration); | 
|  | 652 | } |