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, &params->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, &params->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