Patchwork [PATCH/RFC,04/18] driver: add new NL80211 cmds to support mesh ifaces

login
register
mail settings
Submitter Bob Copeland
Date July 14, 2014, 5:19 a.m.
Message ID <1405315163-9492-5-git-send-email-me@bobcopeland.com>
Download mbox | patch
Permalink /patch/369483/
State Superseded
Headers show

Comments

Bob Copeland - July 14, 2014, 5:19 a.m.
- To support mesh mode, use u64 to fit all necessary driver flags.
- Create mesh_join and mesh_leave actions to kernel.
- Handle new_peer_candidate events from kernel.
- Register to receive necessary frames.
- Add plink_action_field and mesh_power_mode to hostapd_sta_add_params

Signed-off-by: Javier Lopez <jlopex@gmail.com>
Signed-off-by: Javier Cardona <javier@cozybit.com>
Signed-off-by: Jason Mobarak <x@jason.mobarak.name>
Signed-hostap: Bob Copeland <me@bobcopeland.com>
---
 src/common/defs.h                 |  25 ++++
 src/drivers/driver.h              |  90 +++++++++++-
 src/drivers/driver_common.c       |   1 +
 src/drivers/driver_nl80211.c      | 302 ++++++++++++++++++++++++++++++++++++--
 src/drivers/driver_wext.c         |   5 +-
 wpa_supplicant/driver_i.h         |  22 +++
 wpa_supplicant/wpa_supplicant_i.h |   2 +-
 7 files changed, 428 insertions(+), 19 deletions(-)

Patch

diff --git a/src/common/defs.h b/src/common/defs.h
index d4091e3..fb289b0 100644
--- a/src/common/defs.h
+++ b/src/common/defs.h
@@ -300,4 +300,29 @@  enum wpa_ctrl_req_type {
 /* Maximum number of EAP methods to store for EAP server user information */
 #define EAP_MAX_METHODS 8
 
+#ifdef CONFIG_MESH
+enum mesh_plink_state {
+	PLINK_LISTEN = 1,
+	PLINK_OPEN_SENT,
+	PLINK_OPEN_RCVD,
+	PLINK_CNF_RCVD,
+	PLINK_ESTAB,
+	PLINK_HOLDING,
+	PLINK_BLOCKED,
+};
+
+enum mesh_power_mode {
+	MESH_POWER_ACTIVE = 1,
+	MESH_POWER_LIGHT_SLEEP,
+	MESH_POWER_DEEP_SLEEP,
+};
+
+enum plink_action_field {
+	PLINK_OPEN = 1,
+	PLINK_CONFIRM,
+	PLINK_CLOSE
+};
+
+#endif /* CONFIG_MESH */
+
 #endif /* DEFS_H */
diff --git a/src/drivers/driver.h b/src/drivers/driver.h
index 352c163..f48ff84 100644
--- a/src/drivers/driver.h
+++ b/src/drivers/driver.h
@@ -170,6 +170,7 @@  struct hostapd_hw_modes {
 #define IEEE80211_MODE_INFRA	0
 #define IEEE80211_MODE_IBSS	1
 #define IEEE80211_MODE_AP	2
+#define IEEE80211_MODE_MESH	5
 
 #define IEEE80211_CAP_ESS	0x0001
 #define IEEE80211_CAP_IBSS	0x0002
@@ -893,6 +894,31 @@  struct wpa_driver_ap_params {
 	struct hostapd_freq_params *freq;
 };
 
+struct wpa_driver_mesh_bss_params {
+#define WPA_DRIVER_MESH_CONF_FLAG_AUTO_PLINKS	0x00000001
+	/*
+	 * TODO: Other mesh configuration parameters would go here.
+	 * See NL80211_MESHCONF_* for all the mesh config parameters.
+	 */
+	unsigned int flags;
+};
+
+struct wpa_driver_mesh_join_params {
+	const u8 *meshid;
+	int meshid_len;
+	int *basic_rates;
+	int mcast_rate;
+	u8 *ies;
+	int ie_len;
+	int freq;
+	struct wpa_driver_mesh_bss_params conf;
+#define WPA_DRIVER_MESH_FLAG_USER_MPM	0x00000001
+#define WPA_DRIVER_MESH_FLAG_DRIVER_MPM	0x00000002
+#define WPA_DRIVER_MESH_FLAG_SAE_AUTH	0x00000004
+#define WPA_DRIVER_MESH_FLAG_AMPE	0x00000008
+	unsigned int flags;
+};
+
 /**
  * struct wpa_driver_capa - Driver capability information
  */
@@ -1003,7 +1029,9 @@  struct wpa_driver_capa {
 #define WPA_DRIVER_FLAGS_QOS_MAPPING			0x40000000
 /* Driver supports CSA in AP mode */
 #define WPA_DRIVER_FLAGS_AP_CSA				0x80000000
-	unsigned int flags;
+/* Driver supports mesh */
+#define WPA_DRIVER_FLAGS_MESH			0x0000000100000000ULL
+	u64 flags;
 
 	int max_scan_ssids;
 	int max_sched_scan_ssids;
@@ -1081,6 +1109,12 @@  struct hostapd_sta_add_params {
 	int vht_opmode_enabled;
 	u8 vht_opmode;
 	u32 flags; /* bitmask of WPA_STA_* flags */
+	u32 flags_mask; /* unset bits in flags */
+#ifdef CONFIG_MESH
+	enum mesh_plink_state plink_state;
+	enum plink_action_field plink_action;
+	enum mesh_power_mode power_mode;
+#endif /* CONFIG_MESH */
 	int set; /* Set STA parameters instead of add */
 	u8 qosinfo;
 	const u8 *ext_capab;
@@ -1161,7 +1195,11 @@  enum wpa_driver_if_type {
 	 * WPA_IF_P2P_DEVICE - P2P Device interface is used to indentify the
 	 * abstracted P2P Device function in the driver
 	 */
-	WPA_IF_P2P_DEVICE
+	WPA_IF_P2P_DEVICE,
+	/*
+	 * WPA_IF_MESH - Mesh interface
+	 */
+	WPA_IF_MESH
 };
 
 struct wpa_init_params {
@@ -1199,6 +1237,7 @@  struct wpa_bss_params {
 #define WPA_STA_SHORT_PREAMBLE BIT(2)
 #define WPA_STA_MFP BIT(3)
 #define WPA_STA_TDLS_PEER BIT(4)
+#define WPA_STA_AUTHENTICATED BIT(5)
 
 enum tdls_oper {
 	TDLS_DISCOVERY_REQ,
@@ -3000,6 +3039,29 @@  struct wpa_driver_ops {
 	 */
 	int (*disable_transmit_sa)(void *priv, u32 channel, u8 an);
 #endif /* CONFIG_MACSEC */
+
+	/**
+	 * init_mesh - Driver specific initialization for mesh
+	 * @priv: Private driver interface data
+	 * Returns: 0 on success, -1 on failure
+	 */
+	int (*init_mesh)(void *priv);
+
+	/**
+	 * join_mesh - Join a mesh network
+	 * @priv: Private driver interface data
+	 * @params: Mesh configuration parameters
+	 * Returns: 0 on success, -1 on failure
+	 */
+	int (*join_mesh)(void *priv,
+			 struct wpa_driver_mesh_join_params *params);
+
+	/**
+	 * leave_mesh - Leave a mesh network
+	 * @priv: Private driver interface data
+	 * Returns 0 on success, -1 on failure
+	 */
+	int (*leave_mesh)(void *priv);
 };
 
 
@@ -3475,7 +3537,13 @@  enum wpa_event_type {
 	 * to reduce issues due to interference or internal co-existence
 	 * information in the driver.
 	 */
-	EVENT_AVOID_FREQUENCIES
+	EVENT_AVOID_FREQUENCIES,
+
+	/**
+	 * EVENT_NEW_PEER_CANDIDATE - new (unknown) mesh peer notification
+	 */
+	EVENT_NEW_PEER_CANDIDATE
+
 };
 
 
@@ -4114,6 +4182,22 @@  union wpa_event_data {
 	 * This is used as the data with EVENT_AVOID_FREQUENCIES.
 	 */
 	struct wpa_freq_range_list freq_range;
+
+	/**
+	 * struct mesh_peer
+	 *
+	 * @peer: peer address
+	 * @ies: beacon IEs
+	 * @ie_len: length of @ies
+	 *
+	 * Notification of new candidate mesh peer.
+	 */
+	struct mesh_peer {
+		u8 peer[ETH_ALEN];
+		u8 *ies;
+		int ie_len;
+	} mesh_peer;
+
 };
 
 /**
diff --git a/src/drivers/driver_common.c b/src/drivers/driver_common.c
index 3058cd5..63138ab 100644
--- a/src/drivers/driver_common.c
+++ b/src/drivers/driver_common.c
@@ -70,6 +70,7 @@  const char * event_to_string(enum wpa_event_type event)
 	E2S(DRIVER_CLIENT_POLL_OK);
 	E2S(EAPOL_TX_STATUS);
 	E2S(CH_SWITCH);
+	E2S(NEW_PEER_CANDIDATE);
 	E2S(WNM);
 	E2S(CONNECT_FAILED_REASON);
 	E2S(DFS_RADAR_DETECTED);
diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c
index 683123b..42097e2 100644
--- a/src/drivers/driver_nl80211.c
+++ b/src/drivers/driver_nl80211.c
@@ -567,7 +567,8 @@  static int is_ap_interface(enum nl80211_iftype nlmode)
 static int is_sta_interface(enum nl80211_iftype nlmode)
 {
 	return nlmode == NL80211_IFTYPE_STATION ||
-		nlmode == NL80211_IFTYPE_P2P_CLIENT;
+		nlmode == NL80211_IFTYPE_P2P_CLIENT ||
+		nlmode == NL80211_IFTYPE_MESH_POINT;
 }
 
 
@@ -577,6 +578,11 @@  static int is_p2p_net_interface(enum nl80211_iftype nlmode)
 		nlmode == NL80211_IFTYPE_P2P_GO;
 }
 
+static int is_mesh_interface(enum nl80211_iftype nlmode)
+{
+	return nlmode == NL80211_IFTYPE_MESH_POINT;
+}
+
 
 static void nl80211_mark_disconnected(struct wpa_driver_nl80211_data *drv)
 {
@@ -2495,6 +2501,37 @@  static void nl80211_cqm_event(struct wpa_driver_nl80211_data *drv,
 }
 
 
+static void nl80211_new_peer_candidate(struct wpa_driver_nl80211_data *drv,
+				       struct nlattr **tb)
+{
+	u8 *addr, *ie;
+	int ie_len;
+	union wpa_event_data data;
+
+	if (drv->nlmode != NL80211_IFTYPE_MESH_POINT)
+		return;
+
+	if (!tb[NL80211_ATTR_MAC] || !tb[NL80211_ATTR_IE])
+		return;
+
+	ie = nla_data(tb[NL80211_ATTR_IE]);
+	ie_len = nla_len(tb[NL80211_ATTR_IE]);
+	addr = nla_data(tb[NL80211_ATTR_MAC]);
+	wpa_printf(MSG_DEBUG, "nl80211: New peer candidate" MACSTR,
+		   MAC2STR(addr));
+
+	os_memset(&data, 0, sizeof(data));
+	os_memcpy(data.mesh_peer.peer, addr, ETH_ALEN);
+	if ((data.mesh_peer.ies = os_zalloc(ie_len)) == NULL) {
+		wpa_printf(MSG_ERROR, "nl80211: couldn't alloc peer IEs!");
+		return;
+	}
+	os_memcpy(data.mesh_peer.ies, ie, ie_len);
+	data.mesh_peer.ie_len = ie_len;
+	wpa_supplicant_event(drv->ctx, EVENT_NEW_PEER_CANDIDATE, &data);
+	os_free(data.mesh_peer.ies);
+}
+
 static void nl80211_new_station_event(struct wpa_driver_nl80211_data *drv,
 				      struct nlattr **tb)
 {
@@ -3149,6 +3186,9 @@  static void do_process_drv_event(struct i802_bss *bss, int cmd,
 	case NL80211_CMD_VENDOR:
 		nl80211_vendor_event(drv, tb);
 		break;
+	case NL80211_CMD_NEW_PEER_CANDIDATE:
+		nl80211_new_peer_candidate(drv, tb);
+		break;
 	default:
 		wpa_dbg(drv->ctx, MSG_DEBUG, "nl80211: Ignored unknown event "
 			"(cmd=%d)", cmd);
@@ -3452,6 +3492,9 @@  static void wiphy_info_supported_iftypes(struct wiphy_info_data *info,
 		case NL80211_IFTYPE_AP:
 			info->capa->flags |= WPA_DRIVER_FLAGS_AP;
 			break;
+		case NL80211_IFTYPE_MESH_POINT:
+			info->capa->flags |= WPA_DRIVER_FLAGS_MESH;
+			break;
 		case NL80211_IFTYPE_ADHOC:
 			info->capa->flags |= WPA_DRIVER_FLAGS_IBSS;
 			break;
@@ -4557,6 +4600,37 @@  static int nl80211_mgmt_subscribe_non_ap(struct i802_bss *bss)
 	return ret;
 }
 
+static int nl80211_mgmt_subscribe_mesh(struct i802_bss *bss)
+{
+	int ret = 0;
+
+	if (nl80211_alloc_mgmt_handle(bss))
+		return -1;
+
+	wpa_printf(MSG_DEBUG, "nl80211: Subscribe to mgmt frames with mesh "
+		   "handle %p", bss->nl_mgmt);
+
+	/* Auth frames for mesh SAE */
+	if (nl80211_register_frame(bss, bss->nl_mgmt,
+				   (WLAN_FC_TYPE_MGMT << 2) |
+				   (WLAN_FC_STYPE_AUTH << 4),
+				   NULL, 0) < 0)
+		ret = -1;
+
+	/* Mesh peering open */
+	if (nl80211_register_action_frame(bss, (u8 *) "\x0f\x01", 2) < 0)
+		ret = -1;
+	/* Mesh peering confirm */
+	if (nl80211_register_action_frame(bss, (u8 *) "\x0f\x02", 2) < 0)
+		ret = -1;
+	/* Mesh peering close */
+	if (nl80211_register_action_frame(bss, (u8 *) "\x0f\x03", 2) < 0)
+		ret = -1;
+
+	nl80211_mgmt_handle_register_eloop(bss);
+
+	return ret;
+}
 
 static int nl80211_register_spurious_class3(struct i802_bss *bss)
 {
@@ -4906,7 +4980,9 @@  static void wpa_driver_nl80211_deinit(struct i802_bss *bss)
 
 	if (!drv->start_iface_up)
 		(void) i802_set_iface_flags(bss, 0);
-	if (drv->nlmode != NL80211_IFTYPE_P2P_DEVICE) {
+
+	if (drv->nlmode != NL80211_IFTYPE_P2P_DEVICE &&
+	    drv->nlmode != NL80211_IFTYPE_MESH_POINT) {
 		if (!drv->hostapd || !drv->start_mode_ap)
 			wpa_driver_nl80211_set_mode(bss,
 						    NL80211_IFTYPE_STATION);
@@ -5785,7 +5861,18 @@  static int wpa_driver_nl80211_set_key(const char *ifname, struct i802_bss *bss,
 		wpa_hexdump(MSG_DEBUG, "nl80211: KEY_SEQ", seq, seq_len);
 	}
 
-	if (addr && !is_broadcast_ether_addr(addr)) {
+	if (!addr && key_len && set_tx) {
+		/* this is a group tx key */
+		if (alg == WPA_ALG_IGTK || alg == WPA_ALG_CCMP) {
+			wpa_printf(MSG_DEBUG, "   TX GTK");
+			NLA_PUT_U32(msg, NL80211_ATTR_KEY_TYPE,
+				    NL80211_KEYTYPE_GROUP);
+			if (alg == WPA_ALG_IGTK)
+				NLA_PUT_FLAG(msg, NL80211_ATTR_KEY_DEFAULT_MGMT);
+			else
+				NLA_PUT_FLAG(msg, NL80211_ATTR_KEY_DEFAULT);
+		}
+	} else if (addr && !is_broadcast_ether_addr(addr)) {
 		wpa_printf(MSG_DEBUG, "   addr=" MACSTR, MAC2STR(addr));
 		NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, addr);
 
@@ -7542,10 +7629,37 @@  static u32 sta_flags_nl80211(int flags)
 		f |= BIT(NL80211_STA_FLAG_MFP);
 	if (flags & WPA_STA_TDLS_PEER)
 		f |= BIT(NL80211_STA_FLAG_TDLS_PEER);
+	if (flags & WPA_STA_AUTHENTICATED)
+		f |= BIT(NL80211_STA_FLAG_AUTHENTICATED);
 
 	return f;
 }
 
+#ifdef CONFIG_MESH
+static u32 sta_plink_state_nl80211(enum mesh_plink_state state)
+{
+	switch (state) {
+	case PLINK_LISTEN:
+		return NL80211_PLINK_LISTEN;
+	case PLINK_OPEN_SENT:
+		return NL80211_PLINK_OPN_SNT;
+	case PLINK_OPEN_RCVD:
+		return NL80211_PLINK_OPN_RCVD;
+	case PLINK_CNF_RCVD:
+		return NL80211_PLINK_CNF_RCVD;
+	case PLINK_ESTAB:
+		return NL80211_PLINK_ESTAB;
+	case PLINK_HOLDING:
+		return NL80211_PLINK_HOLDING;
+	case PLINK_BLOCKED:
+		return NL80211_PLINK_BLOCKED;
+	default:
+		wpa_printf(MSG_ERROR, "nl80211: Invalid mesh plink state %d",
+			   state);
+	}
+	return -1;
+}
+#endif /* CONFIG_MESH */
 
 static int wpa_driver_nl80211_sta_add(void *priv,
 				      struct hostapd_sta_add_params *params)
@@ -7571,11 +7685,13 @@  static int wpa_driver_nl80211_sta_add(void *priv,
 
 	NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, if_nametoindex(bss->ifname));
 	NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, params->addr);
-	NLA_PUT(msg, NL80211_ATTR_STA_SUPPORTED_RATES, params->supp_rates_len,
-		params->supp_rates);
-	wpa_hexdump(MSG_DEBUG, "  * supported rates", params->supp_rates,
-		    params->supp_rates_len);
+
 	if (!params->set) {
+		NLA_PUT(msg, NL80211_ATTR_STA_SUPPORTED_RATES,
+			params->supp_rates_len, params->supp_rates);
+		wpa_hexdump(MSG_DEBUG, "  * supported rates",
+			    params->supp_rates, params->supp_rates_len);
+
 		if (params->aid) {
 			wpa_printf(MSG_DEBUG, "  * aid=%u", params->aid);
 			NLA_PUT_U16(msg, NL80211_ATTR_STA_AID, params->aid);
@@ -7620,8 +7736,12 @@  static int wpa_driver_nl80211_sta_add(void *priv,
 			   params->vht_opmode);
 	}
 
-	wpa_printf(MSG_DEBUG, "  * capability=0x%x", params->capability);
-	NLA_PUT_U16(msg, NL80211_ATTR_STA_CAPABILITY, params->capability);
+	if (params->capability) {
+		wpa_printf(MSG_DEBUG, "  * capability=0x%x",
+			   params->capability);
+		NLA_PUT_U16(msg, NL80211_ATTR_STA_CAPABILITY,
+			    params->capability);
+	}
 
 	if (params->ext_capab) {
 		wpa_hexdump(MSG_DEBUG, "  * ext_capab",
@@ -7647,12 +7767,18 @@  static int wpa_driver_nl80211_sta_add(void *priv,
 	}
 
 	os_memset(&upd, 0, sizeof(upd));
-	upd.mask = sta_flags_nl80211(params->flags);
-	upd.set = upd.mask;
+	upd.set = sta_flags_nl80211(params->flags);
+	upd.mask = upd.set | sta_flags_nl80211(params->flags_mask);
 	wpa_printf(MSG_DEBUG, "  * flags set=0x%x mask=0x%x",
 		   upd.set, upd.mask);
 	NLA_PUT(msg, NL80211_ATTR_STA_FLAGS2, sizeof(upd), &upd);
 
+#ifdef CONFIG_MESH
+	if (params->plink_state)
+		NLA_PUT_U8(msg, NL80211_ATTR_STA_PLINK_STATE,
+				sta_plink_state_nl80211(params->plink_state));
+#endif /* CONFIG_MESH */
+
 	if (params->flags & WPA_STA_WMM) {
 		struct nlattr *wme = nla_nest_start(msg, NL80211_ATTR_STA_WME);
 
@@ -8944,6 +9070,140 @@  static int wpa_driver_nl80211_connect(
 }
 
 
+static int wpa_driver_nl80211_init_mesh(void *priv)
+{
+	if (wpa_driver_nl80211_set_mode(priv, NL80211_IFTYPE_MESH_POINT)) {
+		wpa_printf(MSG_INFO, "nl80211: Failed to set interface into "
+				"mode mode");
+		return -1;
+	}
+	return 0;
+}
+
+
+static int
+wpa_driver_nl80211_join_mesh(void *priv,
+			     struct wpa_driver_mesh_join_params *params)
+{
+	struct i802_bss *bss = priv;
+	struct wpa_driver_nl80211_data *drv = bss->drv;
+	struct nl_msg *msg;
+	struct nlattr *container;
+	int ret = 0;
+
+	msg = nlmsg_alloc();
+	if (!msg)
+		return -1;
+
+	wpa_printf(MSG_DEBUG, "nl80211: mesh join (ifindex=%d)", drv->ifindex);
+	nl80211_cmd(drv, msg, 0, NL80211_CMD_JOIN_MESH);
+
+	NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex);
+	/* XXX: need chtype too in case we want HT */
+	if (params->freq) {
+		wpa_printf(MSG_DEBUG, "  * freq=%d", params->freq);
+		NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_FREQ, params->freq);
+	}
+
+	if (params->basic_rates) {
+		u8 rates[NL80211_MAX_SUPP_RATES];
+		u8 rates_len = 0;
+		int i;
+
+		for (i = 0; i < NL80211_MAX_SUPP_RATES; i++) {
+			if (params->basic_rates[i] < 0)
+				break;
+			rates[rates_len++] = params->basic_rates[i] / 5;
+		}
+
+		NLA_PUT(msg, NL80211_ATTR_BSS_BASIC_RATES, rates_len, rates);
+	}
+
+	if (params->meshid) {
+		wpa_hexdump_ascii(MSG_DEBUG, "  * SSID",
+				  params->meshid, params->meshid_len);
+		NLA_PUT(msg, NL80211_ATTR_MESH_ID, params->meshid_len,
+			params->meshid);
+	}
+
+	wpa_printf(MSG_DEBUG, "  * flags=%08X", params->flags);
+
+	container = nla_nest_start(msg, NL80211_ATTR_MESH_SETUP);
+	if (!container)
+		goto nla_put_failure;
+
+	if (params->ies) {
+		wpa_hexdump(MSG_DEBUG, "  * IEs",
+			    (unsigned char *) params->ies, params->ie_len);
+		NLA_PUT(msg, NL80211_MESH_SETUP_IE, params->ie_len,
+			params->ies);
+	}
+	/* WPA_DRIVER_MESH_FLAG_OPEN_AUTH is treated as default by nl80211 */
+	if (params->flags & WPA_DRIVER_MESH_FLAG_SAE_AUTH) {
+		NLA_PUT_U8(msg, NL80211_MESH_SETUP_AUTH_PROTOCOL, 0x1);
+		NLA_PUT_FLAG(msg, NL80211_MESH_SETUP_USERSPACE_AUTH);
+	}
+	if (params->flags & WPA_DRIVER_MESH_FLAG_AMPE)
+		NLA_PUT_FLAG(msg, NL80211_MESH_SETUP_USERSPACE_AMPE);
+	if (params->flags & WPA_DRIVER_MESH_FLAG_USER_MPM)
+		NLA_PUT_FLAG(msg, NL80211_MESH_SETUP_USERSPACE_MPM);
+	nla_nest_end(msg, container);
+
+	container = nla_nest_start(msg, NL80211_ATTR_MESH_CONFIG);
+	if (!container)
+		goto nla_put_failure;
+
+	if (!(params->conf.flags & WPA_DRIVER_MESH_CONF_FLAG_AUTO_PLINKS))
+		NLA_PUT_U32(msg, NL80211_MESHCONF_AUTO_OPEN_PLINKS, 0);
+	nla_nest_end(msg, container);
+
+	ret = send_and_recv_msgs(drv, msg, NULL, NULL);
+	msg = NULL;
+	if (ret) {
+		wpa_printf(MSG_DEBUG, "nl80211: mesh join failed: ret=%d "
+			   "(%s)", ret, strerror(-ret));
+		goto nla_put_failure;
+	}
+	ret = 0;
+	bss->freq = params->freq;
+	wpa_printf(MSG_DEBUG, "nl80211: mesh join request send successfully");
+
+
+nla_put_failure:
+	nlmsg_free(msg);
+	return ret;
+}
+
+static int wpa_driver_nl80211_leave_mesh(void *priv)
+{
+	struct i802_bss *bss = priv;
+	struct wpa_driver_nl80211_data *drv = bss->drv;
+	struct nl_msg *msg;
+	int ret = 0;
+
+	msg = nlmsg_alloc();
+	if (!msg)
+		return -1;
+
+	wpa_printf(MSG_DEBUG, "nl80211: mesh leave (ifindex=%d)", drv->ifindex);
+	nl80211_cmd(drv, msg, 0, NL80211_CMD_LEAVE_MESH);
+	NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex);
+
+	ret = send_and_recv_msgs(drv, msg, NULL, NULL);
+	msg = NULL;
+	if (ret) {
+		wpa_printf(MSG_DEBUG, "nl80211: mesh leave failed: ret=%d "
+			   "(%s)", ret, strerror(-ret));
+		goto nla_put_failure;
+	}
+	ret = 0;
+	wpa_printf(MSG_DEBUG, "nl80211: mesh leave request send successfully");
+
+nla_put_failure:
+	nlmsg_free(msg);
+	return ret;
+}
+
 static int wpa_driver_nl80211_associate(
 	void *priv, struct wpa_driver_associate_params *params)
 {
@@ -9149,7 +9409,12 @@  done:
 		nl80211_mgmt_unsubscribe(bss, "mode change");
 	}
 
+	if (is_mesh_interface(nlmode))
+		if (nl80211_mgmt_subscribe_mesh(bss))
+			return -1;
+
 	if (!bss->in_deinit && !is_ap_interface(nlmode) &&
+	    !is_mesh_interface(nlmode) &&
 	    nl80211_mgmt_subscribe_non_ap(bss) < 0)
 		wpa_printf(MSG_DEBUG, "nl80211: Failed to register Action "
 			   "frame processing - ignore for now");
@@ -9681,6 +9946,9 @@  static int i802_sta_deauth(void *priv, const u8 *own_addr, const u8 *addr,
 	struct wpa_driver_nl80211_data *drv = bss->drv;
 	struct ieee80211_mgmt mgmt;
 
+	if (is_mesh_interface(drv->nlmode))
+		return -1;
+
 	if (drv->device_ap_sme)
 		return wpa_driver_nl80211_sta_remove(bss, addr);
 
@@ -9705,6 +9973,9 @@  static int i802_sta_disassoc(void *priv, const u8 *own_addr, const u8 *addr,
 	struct wpa_driver_nl80211_data *drv = bss->drv;
 	struct ieee80211_mgmt mgmt;
 
+	if (is_mesh_interface(drv->nlmode))
+		return -1;
+
 	if (drv->device_ap_sme)
 		return wpa_driver_nl80211_sta_remove(bss, addr);
 
@@ -10031,6 +10302,8 @@  static enum nl80211_iftype wpa_driver_nl80211_if_type(
 		return NL80211_IFTYPE_P2P_GO;
 	case WPA_IF_P2P_DEVICE:
 		return NL80211_IFTYPE_P2P_DEVICE;
+	case WPA_IF_MESH:
+		return NL80211_IFTYPE_MESH_POINT;
 	}
 	return -1;
 }
@@ -12009,7 +12282,7 @@  static int wpa_driver_nl80211_status(void *priv, char *buf, size_t buflen)
 				  "capa.key_mgmt=0x%x\n"
 				  "capa.enc=0x%x\n"
 				  "capa.auth=0x%x\n"
-				  "capa.flags=0x%x\n"
+				  "capa.flags=0x%llx\n"
 				  "capa.max_scan_ssids=%d\n"
 				  "capa.max_sched_scan_ssids=%d\n"
 				  "capa.sched_scan_supported=%d\n"
@@ -12022,7 +12295,7 @@  static int wpa_driver_nl80211_status(void *priv, char *buf, size_t buflen)
 				  drv->capa.key_mgmt,
 				  drv->capa.enc,
 				  drv->capa.auth,
-				  drv->capa.flags,
+				  (unsigned long long) drv->capa.flags,
 				  drv->capa.max_scan_ssids,
 				  drv->capa.max_sched_scan_ssids,
 				  drv->capa.sched_scan_supported,
@@ -12360,6 +12633,9 @@  const struct wpa_driver_ops wpa_driver_nl80211_ops = {
 	.deauthenticate = driver_nl80211_deauthenticate,
 	.authenticate = driver_nl80211_authenticate,
 	.associate = wpa_driver_nl80211_associate,
+	.init_mesh = wpa_driver_nl80211_init_mesh,
+	.join_mesh = wpa_driver_nl80211_join_mesh,
+	.leave_mesh = wpa_driver_nl80211_leave_mesh,
 	.global_init = nl80211_global_init,
 	.global_deinit = nl80211_global_deinit,
 	.init2 = wpa_driver_nl80211_init,
diff --git a/src/drivers/driver_wext.c b/src/drivers/driver_wext.c
index a4f9cec..6a56549 100644
--- a/src/drivers/driver_wext.c
+++ b/src/drivers/driver_wext.c
@@ -1568,8 +1568,9 @@  static int wpa_driver_wext_get_range(void *priv)
 		drv->capa.max_scan_ssids = 1;
 
 		wpa_printf(MSG_DEBUG, "  capabilities: key_mgmt 0x%x enc 0x%x "
-			   "flags 0x%x",
-			   drv->capa.key_mgmt, drv->capa.enc, drv->capa.flags);
+			   "flags 0x%llx",
+			   drv->capa.key_mgmt, drv->capa.enc,
+			   (unsigned long long) drv->capa.flags);
 	} else {
 		wpa_printf(MSG_DEBUG, "SIOCGIWRANGE: too old (short) data - "
 			   "assuming WPA is not supported");
diff --git a/wpa_supplicant/driver_i.h b/wpa_supplicant/driver_i.h
index 00703d9..1bc4340 100644
--- a/wpa_supplicant/driver_i.h
+++ b/wpa_supplicant/driver_i.h
@@ -65,6 +65,28 @@  static inline int wpa_drv_associate(struct wpa_supplicant *wpa_s,
 	return -1;
 }
 
+static inline int wpa_drv_init_mesh(struct wpa_supplicant *wpa_s)
+{
+	if (wpa_s->driver->init_mesh)
+		return wpa_s->driver->init_mesh(wpa_s->drv_priv);
+	return -1;
+}
+
+static inline int wpa_drv_join_mesh(struct wpa_supplicant *wpa_s,
+				    struct wpa_driver_mesh_join_params *params)
+{
+	if (wpa_s->driver->join_mesh)
+		return wpa_s->driver->join_mesh(wpa_s->drv_priv, params);
+	return -1;
+}
+
+static inline int wpa_drv_leave_mesh(struct wpa_supplicant *wpa_s)
+{
+	if (wpa_s->driver->leave_mesh)
+		return wpa_s->driver->leave_mesh(wpa_s->drv_priv);
+	return -1;
+}
+
 static inline int wpa_drv_scan(struct wpa_supplicant *wpa_s,
 			       struct wpa_driver_scan_params *params)
 {
diff --git a/wpa_supplicant/wpa_supplicant_i.h b/wpa_supplicant/wpa_supplicant_i.h
index f35cd0d..20d5cbb 100644
--- a/wpa_supplicant/wpa_supplicant_i.h
+++ b/wpa_supplicant/wpa_supplicant_i.h
@@ -573,7 +573,7 @@  struct wpa_supplicant {
 	int scan_id[MAX_SCAN_ID];
 	unsigned int scan_id_count;
 
-	unsigned int drv_flags;
+	u64 drv_flags;
 	unsigned int drv_enc;
 
 	/*