diff mbox series

[v3,03/12] wpa_supplicant: Add Multi-AP backhaul STA support

Message ID 20181205102401.17810-4-arnout@mind.be
State Accepted
Headers show
Series [v3,01/12] hostapd: Add Multi-AP protocol support | expand

Commit Message

Arnout Vandecappelle Dec. 5, 2018, 10:23 a.m. UTC
From: Venkateswara Naralasetty <vnaralas@codeaurora.org>

Advertise vendor specific Multi-AP IE in (Re)Association request
and process Multi-AP IE from (Re)Association response frames if the
user enables Multi-AP fuctionality. If the (Re)Association response does
not contain the Multi-AP IE, deassociate.

This patch introduces a new configuration parameter
'multi_ap_backhaul_sta' to enable/disable Multi-AP functionality.

Enable 4addr mode after association (if the Association Response
contains the Multi-AP IE). Also enable the bridge in that case. This is
necessary because wpa_supplicant only enables the bridge in
wpa_drv_if_add(), which only gets called when an interface is added
through the control interface, not when it is configured from the
command line.

Signed-off-by: Venkateswara Naralasetty <vnaralas@codeaurora.org>
Signed-off-by: Jouni Malinen <jouni@codeaurora.org>
Signed-off-by: Arnout Vandecappelle (Essensium/Mind) <arnout@mind.be>
---
v3 (Arnout): Improve commit message

v2:
 * changed configuration from multi_ap_enabled to multiap_backhaul_sta
 * made multiap_backhaul_sta configuration static
 * used single helper function to build all different cases of
   Multi-AP IE contents

---
I believe that adding the bridge in set_4addr_mode is actually wrong.
The real problem IMO is that the -b command-line option doesn't work in
station mode.

Even more, I think the 4addr mode should not be set like this. Instead,
I think both the bridge and the 4addr mode should be controlled by
whatever entity creates the VIF. So if the VIF is already created
externally (i.e. command-line configuration), it must already have been
set to 4addr mode and added to the bridge. While if wpa_supplicant
creates the interface, it must set 4addr mode when creating the
interface (i.e. the wds argument of if_add() should be set).
---
 src/drivers/driver.h               | 11 +++++++
 src/drivers/driver_nl80211.c       | 45 +++++++++++++++++++++++++
 wpa_supplicant/Makefile            |  4 +++
 wpa_supplicant/config.c            |  3 ++
 wpa_supplicant/config_ssid.h       |  8 +++++
 wpa_supplicant/defconfig           |  3 ++
 wpa_supplicant/driver_i.h          | 10 ++++++
 wpa_supplicant/events.c            | 53 ++++++++++++++++++++++++++++++
 wpa_supplicant/sme.c               | 14 +++++++-
 wpa_supplicant/wpa_supplicant.c    | 29 ++++++++++++++++
 wpa_supplicant/wpa_supplicant.conf |  4 +++
 wpa_supplicant/wpa_supplicant_i.h  |  6 ++++
 12 files changed, 189 insertions(+), 1 deletion(-)
diff mbox series

Patch

diff --git a/src/drivers/driver.h b/src/drivers/driver.h
index 4ac9f16a0..3ae52d43f 100644
--- a/src/drivers/driver.h
+++ b/src/drivers/driver.h
@@ -4092,6 +4092,17 @@  struct wpa_driver_ops {
 	 */
 	int (*send_external_auth_status)(void *priv,
 					 struct external_auth *params);
+
+#ifdef CONFIG_MULTI_AP
+	/**
+	 * set_4addr_mode - Set 4address mode
+	 * @priv: Private driver interface data
+	 * @val: 0 - disabale 4addr mode
+	 *	 1 - enable 4addr mode
+	 * @brige_ifname - Bridge interface name
+	 */
+	int (*set_4addr_mode)(void *priv, const char *dridge_ifname, int val);
+#endif /* CONFIG_MULTI_AP */
 };
 
 /**
diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c
index b2c4120eb..0b2e79653 100644
--- a/src/drivers/driver_nl80211.c
+++ b/src/drivers/driver_nl80211.c
@@ -10647,6 +10647,48 @@  fail:
 	return ret;
 }
 
+#ifdef CONFIG_MULTI_AP
+static int nl80211_set_4addr_mode(void *priv, const char *bridge_ifname,
+				  int val)
+{
+	struct i802_bss *bss = priv;
+	struct wpa_driver_nl80211_data *drv = bss->drv;
+	struct nl_msg *msg;
+	int ret = -ENOBUFS;
+
+	msg = nl80211_cmd_msg(drv->first_bss, 0, NL80211_CMD_SET_INTERFACE);
+	if (!msg || nla_put_u8(msg, NL80211_ATTR_4ADDR, val))
+		goto fail;
+
+	if (bridge_ifname[0] && bss->added_if_into_bridge && !val) {
+		if (linux_br_del_if(drv->global->ioctl_sock,
+				    bridge_ifname, bss->ifname)) {
+			wpa_printf(MSG_ERROR, "nl80211: Failed to remove the "
+					"interface %s to a bridge %s",
+					bss->ifname, bridge_ifname);
+			return -1;
+		}
+		bss->added_if_into_bridge = 0;
+	}
+
+	ret = send_and_recv_msgs(drv, msg, NULL, NULL);
+	msg = NULL;
+	if (!ret) {
+		if (bridge_ifname[0] && val) {
+			if (i802_check_bridge(drv, bss,
+					      bridge_ifname, bss->ifname) < 0)
+				return -1;
+		}
+		return 0;
+	}
+
+fail:
+	nlmsg_free(msg);
+	wpa_printf(MSG_ERROR, "nl80211: Failed to enable/disable 4addr");
+
+	return ret;
+}
+#endif /* CONFIG_MULTI_AP */
 
 const struct wpa_driver_ops wpa_driver_nl80211_ops = {
 	.name = "nl80211",
@@ -10776,4 +10818,7 @@  const struct wpa_driver_ops wpa_driver_nl80211_ops = {
 	.get_ext_capab = nl80211_get_ext_capab,
 	.update_connect_params = nl80211_update_connection_params,
 	.send_external_auth_status = nl80211_send_external_auth_status,
+#ifdef CONFIG_MULTI_AP
+	.set_4addr_mode = nl80211_set_4addr_mode,
+#endif /* CONFIG_MULTI_AP */
 };
diff --git a/wpa_supplicant/Makefile b/wpa_supplicant/Makefile
index c2e93e20b..52892eaaf 100644
--- a/wpa_supplicant/Makefile
+++ b/wpa_supplicant/Makefile
@@ -96,6 +96,10 @@  CONFIG_NO_RANDOM_POOL=
 CONFIG_OPENSSL_CMAC=y
 endif
 
+ifdef CONFIG_MULTI_AP
+CFLAGS += -DCONFIG_MULTI_AP
+endif
+
 OBJS = config.o
 OBJS += notify.o
 OBJS += bss.o
diff --git a/wpa_supplicant/config.c b/wpa_supplicant/config.c
index c43960697..bd73bcfc8 100644
--- a/wpa_supplicant/config.c
+++ b/wpa_supplicant/config.c
@@ -2320,6 +2320,9 @@  static const struct parse_data ssid_fields[] = {
 #endif /* CONFIG_DPP */
 	{ INT_RANGE(owe_group, 0, 65535) },
 	{ INT_RANGE(owe_only, 0, 1) },
+#ifdef CONFIG_MULTI_AP
+	{ INT_RANGE(multiap_backhaul_sta, 0, 1) },
+#endif
 };
 
 #undef OFFSET
diff --git a/wpa_supplicant/config_ssid.h b/wpa_supplicant/config_ssid.h
index d2a52d760..2dc3fbff3 100644
--- a/wpa_supplicant/config_ssid.h
+++ b/wpa_supplicant/config_ssid.h
@@ -937,6 +937,14 @@  struct wpa_ssid {
 	 * the selection attempts for OWE BSS exceed the configured threshold.
 	 */
 	int owe_transition_bss_select_count;
+
+#ifdef CONFIG_MULTI_AP
+	/*
+	 * 0 = normal station
+	 * 1 = backhaul station
+	 */
+	int multiap_backhaul_sta;
+#endif /* CONFIG_MULTI_AP */
 };
 
 #endif /* CONFIG_SSID_H */
diff --git a/wpa_supplicant/defconfig b/wpa_supplicant/defconfig
index 08f585779..3c3081c95 100644
--- a/wpa_supplicant/defconfig
+++ b/wpa_supplicant/defconfig
@@ -593,3 +593,6 @@  CONFIG_BACKEND=file
 # Opportunistic Wireless Encryption (OWE)
 # Experimental implementation of draft-harkins-owe-07.txt
 #CONFIG_OWE=y
+
+#Multi-AP protocol support
+#CONFIG_MULTI_AP=y
diff --git a/wpa_supplicant/driver_i.h b/wpa_supplicant/driver_i.h
index 078de23f7..9c1a26d8b 100644
--- a/wpa_supplicant/driver_i.h
+++ b/wpa_supplicant/driver_i.h
@@ -30,6 +30,16 @@  static inline void wpa_drv_deinit(struct wpa_supplicant *wpa_s)
 		wpa_s->driver->deinit(wpa_s->drv_priv);
 }
 
+#ifdef CONFIG_MULTI_AP
+static inline int wpa_drv_set_4addr_mode(struct wpa_supplicant *wpa_s, int val)
+{
+	if (wpa_s->driver->set_4addr_mode)
+		return wpa_s->driver->set_4addr_mode(wpa_s->drv_priv,
+						     wpa_s->bridge_ifname, val);
+	return -1;
+}
+#endif /* CONFIG_MULTI_AP */
+
 static inline int wpa_drv_set_param(struct wpa_supplicant *wpa_s,
 				    const char *param)
 {
diff --git a/wpa_supplicant/events.c b/wpa_supplicant/events.c
index dd6dd5267..62100e7b4 100644
--- a/wpa_supplicant/events.c
+++ b/wpa_supplicant/events.c
@@ -2266,6 +2266,55 @@  static void interworking_process_assoc_resp(struct wpa_supplicant *wpa_s,
 
 #endif /* CONFIG_INTERWORKING */
 
+#ifdef CONFIG_MULTI_AP
+static void multi_ap_process_assoc_resp(struct wpa_supplicant *wpa_s,
+					const u8 *ies, size_t ies_len)
+{
+	struct ieee802_11_elems elems;
+	const u8 *map_sub_elem, *pos;
+	size_t len;
+
+	if (!wpa_s->current_ssid->multiap_backhaul_sta)
+		return;
+
+	if (ies == NULL)
+		return;
+
+	if (ieee802_11_parse_elems(ies, ies_len, &elems, 1) == ParseFailed)
+		return;
+
+	if (elems.multi_ap && (elems.multi_ap_len >= 7)) {
+		pos = elems.multi_ap + 4;
+		len = elems.multi_ap_len - 4;
+
+		map_sub_elem = get_ie(pos, len, MULTI_AP_SUB_ELEM_TYPE);
+		if (!map_sub_elem || map_sub_elem[1] < 1) {
+			wpa_printf(MSG_INFO, "invalid Multi-AP sub elem type\n");
+			goto fail;
+		}
+
+		if (!(map_sub_elem[2] & MULTI_AP_BACKHAUL_BSS)) {
+			wpa_printf(MSG_INFO, "AP doesn't support BACKHAUL BSS");
+			goto fail;
+		}
+
+		if (wpa_drv_set_4addr_mode(wpa_s, 1)) {
+			wpa_printf(MSG_ERROR, "failed to sed 4addr mode\n");
+			goto fail;
+		}
+		wpa_s->enabled_4addr_mode = 1;
+	} else {
+		wpa_printf(MSG_INFO, "AP doesn't support Multi-AP protocol");
+		goto fail;
+	}
+
+	return;
+
+fail:
+	wpa_supplicant_deauthenticate(wpa_s, WLAN_REASON_DEAUTH_LEAVING);
+	return;
+}
+#endif /* CONFIG_MULTI_AP */
 
 #ifdef CONFIG_FST
 static int wpas_fst_update_mbie(struct wpa_supplicant *wpa_s,
@@ -2343,6 +2392,10 @@  static int wpa_supplicant_event_associnfo(struct wpa_supplicant *wpa_s,
 		    get_ie(data->assoc_info.resp_ies,
 			   data->assoc_info.resp_ies_len, WLAN_EID_VHT_CAP))
 			wpa_s->ieee80211ac = 1;
+#ifdef CONFIG_MULTI_AP
+		multi_ap_process_assoc_resp(wpa_s, data->assoc_info.resp_ies,
+					    data->assoc_info.resp_ies_len);
+#endif /* CONFIG_MULTI_AP */
 	}
 	if (data->assoc_info.beacon_ies)
 		wpa_hexdump(MSG_DEBUG, "beacon_ies",
diff --git a/wpa_supplicant/sme.c b/wpa_supplicant/sme.c
index d569acad8..1c444ff86 100644
--- a/wpa_supplicant/sme.c
+++ b/wpa_supplicant/sme.c
@@ -39,7 +39,6 @@  static void sme_obss_scan_timeout(void *eloop_ctx, void *timeout_ctx);
 static void sme_stop_sa_query(struct wpa_supplicant *wpa_s);
 #endif /* CONFIG_IEEE80211W */
 
-
 #ifdef CONFIG_SAE
 
 static int index_within_array(const int *array, int idx)
@@ -1548,6 +1547,19 @@  void sme_associate(struct wpa_supplicant *wpa_s, enum wpas_mode mode,
 	}
 #endif /* CONFIG_OWE */
 
+#ifdef CONFIG_MULTI_AP
+	if (wpa_s->conf->ssid->multiap_backhaul_sta) {
+		if (wpa_s->sme.assoc_req_ie_len + 9 >
+		    sizeof(wpa_s->sme.assoc_req_ie)) {
+			wpa_printf(MSG_ERROR,
+					"Multi-AP: Not enough buffer for Assoc req frame element");
+			return;
+		}
+		u8 *pos = wpa_s->sme.assoc_req_ie + wpa_s->sme.assoc_req_ie_len;
+		wpa_add_multi_ap_info_ie(pos, &wpa_s->sme.assoc_req_ie_len);
+	}
+#endif /* CONFIG_MULTI_AP */
+
 	params.bssid = bssid;
 	params.ssid = wpa_s->sme.ssid;
 	params.ssid_len = wpa_s->sme.ssid_len;
diff --git a/wpa_supplicant/wpa_supplicant.c b/wpa_supplicant/wpa_supplicant.c
index 0fe2adb6b..357a4e193 100644
--- a/wpa_supplicant/wpa_supplicant.c
+++ b/wpa_supplicant/wpa_supplicant.c
@@ -285,6 +285,23 @@  void wpa_supplicant_cancel_auth_timeout(struct wpa_supplicant *wpa_s)
 	wpa_s->last_con_fail_realm_len = 0;
 }
 
+#ifdef CONFIG_MULTI_AP
+void wpa_add_multi_ap_info_ie(u8 *pos, size_t *len)
+{
+	u8 *buf = pos;
+
+	*buf++ = WLAN_EID_VENDOR_SPECIFIC;
+	*buf++ = 7; /* len */
+	WPA_PUT_BE24(buf, OUI_WFA);
+	buf += 3;
+	*buf++ = MULTI_AP_OUI_TYPE;
+	*buf++ = MULTI_AP_SUB_ELEM_TYPE;
+	*buf++ = 1; /*sub element len */
+	*buf++ = MULTI_AP_BACKHAUL_STA;
+
+	*len += (buf - pos);
+}
+#endif /* CONFIG_MULTI_AP */
 
 /**
  * wpa_supplicant_initiate_eapol - Configure EAPOL state machine
@@ -2808,6 +2825,11 @@  static u8 * wpas_populate_assoc_ies(
 	}
 #endif /* CONFIG_IEEE80211R */
 
+#ifdef CONFIG_MULTI_AP
+	if (ssid->multiap_backhaul_sta && ((max_wpa_ie_len - wpa_ie_len) > 9))
+		wpa_add_multi_ap_info_ie(wpa_ie, &wpa_ie_len);
+#endif /* CONFIG_MULTI_AP */
+
 	params->wpa_ie = wpa_ie;
 	params->wpa_ie_len = wpa_ie_len;
 	params->auth_alg = algs;
@@ -3286,6 +3308,13 @@  void wpa_supplicant_deauthenticate(struct wpa_supplicant *wpa_s,
 		zero_addr = 1;
 	}
 
+#ifdef CONFIG_MULTI_AP
+	if (wpa_s->enabled_4addr_mode) {
+		if (wpa_drv_set_4addr_mode(wpa_s, 0))
+			wpa_s->enabled_4addr_mode = 0;
+	}
+#endif
+
 #ifdef CONFIG_TDLS
 	wpa_tdls_teardown_peers(wpa_s->wpa);
 #endif /* CONFIG_TDLS */
diff --git a/wpa_supplicant/wpa_supplicant.conf b/wpa_supplicant/wpa_supplicant.conf
index 4f5916025..1d5c6a175 100644
--- a/wpa_supplicant/wpa_supplicant.conf
+++ b/wpa_supplicant/wpa_supplicant.conf
@@ -1422,6 +1422,10 @@  fast_reauth=1
 # Transitioning between states).
 #fst_llt=100
 
+# Set satation to use as BACKHAUL STA.
+# default = 0
+# multiap_backhaul_sta = 1 (configure as backhaul STA).
+
 # Example blocks:
 
 # Simple case: WPA-PSK, PSK as an ASCII passphrase, allow all valid ciphers
diff --git a/wpa_supplicant/wpa_supplicant_i.h b/wpa_supplicant/wpa_supplicant_i.h
index 8b749f44e..0687182bc 100644
--- a/wpa_supplicant/wpa_supplicant_i.h
+++ b/wpa_supplicant/wpa_supplicant_i.h
@@ -1234,6 +1234,9 @@  struct wpa_supplicant {
 	unsigned int disable_fils:1;
 #endif /* CONFIG_FILS */
 	unsigned int ieee80211ac:1;
+#ifdef CONFIG_MULTI_AP
+	char enabled_4addr_mode;
+#endif
 };
 
 
@@ -1493,4 +1496,7 @@  int wpas_ctrl_iface_get_pref_freq_list_override(struct wpa_supplicant *wpa_s,
 int wpa_is_fils_supported(struct wpa_supplicant *wpa_s);
 int wpa_is_fils_sk_pfs_supported(struct wpa_supplicant *wpa_s);
 
+#ifdef CONFIG_MULTI_AP
+void wpa_add_multi_ap_info_ie(u8 *pos, size_t *len);
+#endif
 #endif /* WPA_SUPPLICANT_I_H */