[PATCH v2 3/5] TDLS: propagate enable/disable channel-switch commands to drv

Ilan Peer ilan.peer at intel.com
Mon Dec 29 00:20:51 EST 2014


From: Arik Nemtsov <arik at wizery.com>

The supplicant code does not try to control the actual channel of the
radio at any point. It simply passes the target peer and channel
parameters to the driver. It's the driver's responsibility to periodically
initiate TDLS channel-switch operations when TDLS channel-switching is
enabled.

Allow enable/disable operations to be invoked via the control interface.

Change-Id: Ib4c71b79ea2045b122b1068ad08c0ac319c576cf
Signed-off-by: Arik Nemtsov <arikx.nemtsov at intel.com>
Reviewed-on: https://gerrit.rds.intel.com/r/43843
Reviewed-by: Ilan Peer <ilan.peer at intel.com>
Tested-by: ArikX Nemtsov <arik at wizery.com>
---
 src/drivers/driver.h        |  27 +++++++++++
 src/rsn_supp/tdls.c         |  94 ++++++++++++++++++++++++++++++++++++++-
 src/rsn_supp/wpa.h          |   9 ++++
 src/rsn_supp/wpa_i.h        |  20 +++++++++
 wpa_supplicant/ctrl_iface.c | 106 ++++++++++++++++++++++++++++++++++++++++++++
 wpa_supplicant/driver_i.h   |  22 +++++++++
 wpa_supplicant/wpas_glue.c  |  25 +++++++++++
 7 files changed, 301 insertions(+), 2 deletions(-)

diff --git a/src/drivers/driver.h b/src/drivers/driver.h
index 5295b1d..3b7d973 100644
--- a/src/drivers/driver.h
+++ b/src/drivers/driver.h
@@ -2905,6 +2905,33 @@ struct wpa_driver_ops {
 	int (*del_tx_ts)(void *priv, u8 tsid, const u8 *addr);
 
 	/**
+	 * tdls_enable_channel_switch - enable channel-switching with TDLS peer
+	 * @priv: Private driver interface data
+	 * @addr: MAC address of the TDLS peer
+	 * @oper_class: Operating class of the switch channel
+	 * @params: Channel specification
+	 * Returns: 0 on success, -1 on failure
+	 *
+	 * The function indicates to driver that it can start switching to a
+	 * different channel with a specified TDLS peer. The switching is
+	 * assumed on until canceled with the @tdls_cancel_switch_channel API
+	 */
+	int (*tdls_enable_channel_switch)(void *priv, const u8 *addr,
+					  u8 oper_class,
+					  const struct hostapd_freq_params *params);
+
+	/**
+	 * tdls_disable_channel_switch - disable ch-switching with TDLS peer
+	 * @priv: Private driver interface data
+	 * @addr: MAC address of the TDLS peer
+	 * Returns: 0 on success, -1 on failure
+	 *
+	 * This function indicates to the driver that it should stop switching
+	 * with a given TDLS peer.
+	 */
+	int (*tdls_disable_channel_switch)(void *priv, const u8 *addr);
+
+	/**
 	 * start_dfs_cac - Listen for radar interference on the channel
 	 * @priv: Private driver interface data
 	 * @freq: Channel parameters
diff --git a/src/rsn_supp/tdls.c b/src/rsn_supp/tdls.c
index 2310f5f..1e09100 100644
--- a/src/rsn_supp/tdls.c
+++ b/src/rsn_supp/tdls.c
@@ -148,6 +148,9 @@ struct wpa_tdls_peer {
 	size_t supp_oper_classes_len;
 
 	u8 wmm_capable;
+
+	/* channel switch currently enabled */
+	int chan_switch_enabled;
 };
 
 
@@ -687,6 +690,7 @@ static void wpa_tdls_peer_clear(struct wpa_sm *sm, struct wpa_tdls_peer *peer)
 	peer->qos_info = 0;
 	peer->wmm_capable = 0;
 	peer->tpk_set = peer->tpk_success = 0;
+	peer->chan_switch_enabled = 0;
 	os_memset(&peer->tpk, 0, sizeof(peer->tpk));
 	os_memset(peer->inonce, 0, WPA_NONCE_LEN);
 	os_memset(peer->rnonce, 0, WPA_NONCE_LEN);
@@ -742,6 +746,13 @@ static int wpa_tdls_send_teardown(struct wpa_sm *sm, const u8 *addr,
 		return 0;
 	}
 
+	/* cancel active channel switch before teardown */
+	if (peer->chan_switch_enabled) {
+		wpa_printf(MSG_DEBUG, "TDLS: first returning " MACSTR " to "
+			   "base channel", MAC2STR(addr));
+		wpa_sm_tdls_disable_channel_switch(sm, peer->addr);
+	}
+
 	dialog_token = peer->dtoken;
 
 	wpa_printf(MSG_DEBUG, "TDLS: TDLS Teardown for " MACSTR,
@@ -858,9 +869,11 @@ void wpa_tdls_disable_unreachable_link(struct wpa_sm *sm, const u8 *addr)
 
 	if (wpa_tdls_is_external_setup(sm)) {
 		/*
-		 * Disable the link, send a teardown packet through the
-		 * AP, and then reset link data.
+		 * Get us on the base channel, disable the link, send a
+		 * teardown packet through the AP, and then reset link data.
 		 */
+		if (peer->chan_switch_enabled)
+			wpa_sm_tdls_disable_channel_switch(sm, peer->addr);
 		wpa_sm_tdls_oper(sm, TDLS_DISABLE_LINK, addr);
 		wpa_tdls_send_teardown(sm, addr,
 				       WLAN_REASON_TDLS_TEARDOWN_UNREACHABLE);
@@ -2902,3 +2915,80 @@ int wpa_tdls_is_external_setup(struct wpa_sm *sm)
 {
 	return sm->tdls_external_setup;
 }
+
+
+int wpa_tdls_enable_chan_switch(struct wpa_sm *sm, const u8 *addr,
+				u8 oper_class,
+				struct hostapd_freq_params *freq_params)
+{
+	struct wpa_tdls_peer *peer;
+	int ret;
+
+	if (sm->tdls_disabled || !sm->tdls_supported)
+		return -1;
+
+	if (!sm->tdls_chan_switch) {
+		wpa_printf(MSG_DEBUG, "TDLS: Channel switching not supported "
+			   "by the driver");
+		return -1;
+	}
+
+	if (sm->tdls_chan_switch_prohibited) {
+		wpa_printf(MSG_DEBUG, "TDLS: TDLS chan switch is prohibited "
+			   "in this BSS - reject request to switch channel");
+		return -1;
+	}
+
+	for (peer = sm->tdls; peer; peer = peer->next) {
+		if (os_memcmp(peer->addr, addr, ETH_ALEN) == 0)
+			break;
+	}
+
+	if (peer == NULL || !peer->tpk_success) {
+		wpa_printf(MSG_ERROR, "TDLS: peer " MACSTR " not found for "
+			   "channel switch", MAC2STR(addr));
+		return -1;
+	}
+
+	if (peer->chan_switch_enabled) {
+		wpa_printf(MSG_ERROR, "TDLS: peer " MACSTR " already in "
+			   "channel switch", MAC2STR(addr));
+		return -1;
+	}
+
+	ret = wpa_sm_tdls_enable_channel_switch(sm, peer->addr,
+						oper_class, freq_params);
+	if (!ret)
+		peer->chan_switch_enabled = 1;
+
+	return ret;
+}
+
+
+int wpa_tdls_disable_chan_switch(struct wpa_sm *sm, const u8 *addr)
+{
+	struct wpa_tdls_peer *peer;
+
+	if (sm->tdls_disabled || !sm->tdls_supported)
+		return -1;
+
+	for (peer = sm->tdls; peer; peer = peer->next) {
+		if (os_memcmp(peer->addr, addr, ETH_ALEN) == 0)
+			break;
+	}
+
+	if (!peer)
+		return -1;
+
+	if (!peer->chan_switch_enabled) {
+		wpa_printf(MSG_ERROR, "TDLS: channel switch not enabled for "
+			    MACSTR, MAC2STR(addr));
+		return -1;
+	}
+
+	/* ignore the return value */
+	wpa_sm_tdls_disable_channel_switch(sm, peer->addr);
+
+	peer->chan_switch_enabled = 0;
+	return 0;
+}
diff --git a/src/rsn_supp/wpa.h b/src/rsn_supp/wpa.h
index 110677f..ed7f4ce 100644
--- a/src/rsn_supp/wpa.h
+++ b/src/rsn_supp/wpa.h
@@ -17,6 +17,7 @@
 struct wpa_sm;
 struct eapol_sm;
 struct wpa_config_blob;
+struct hostapd_freq_params;
 
 struct wpa_sm_ctx {
 	void *ctx; /* pointer to arbitrary upper level context */
@@ -67,6 +68,10 @@ struct wpa_sm_ctx {
 				size_t supp_channels_len,
 				const u8 *supp_oper_classes,
 				size_t supp_oper_classes_len);
+	int (*tdls_enable_channel_switch)(void *ctx, const u8 *addr,
+					  u8 oper_class,
+					  const struct hostapd_freq_params *params);
+	int (*tdls_disable_channel_switch)(void *ctx, const u8 *addr);
 #endif /* CONFIG_TDLS */
 	void (*set_rekey_offload)(void *ctx, const u8 *kek, const u8 *kck,
 				  const u8 *replay_ctr);
@@ -404,6 +409,10 @@ void wpa_tdls_enable(struct wpa_sm *sm, int enabled);
 void wpa_tdls_disable_unreachable_link(struct wpa_sm *sm, const u8 *addr);
 const char * wpa_tdls_get_link_status(struct wpa_sm *sm, const u8 *addr);
 int wpa_tdls_is_external_setup(struct wpa_sm *sm);
+int wpa_tdls_enable_chan_switch(struct wpa_sm *sm, const u8 *addr,
+				u8 oper_class,
+				struct hostapd_freq_params *freq_params);
+int wpa_tdls_disable_chan_switch(struct wpa_sm *sm, const u8 *addr);
 
 int wpa_wnmsleep_install_key(struct wpa_sm *sm, u8 subelem_id, u8 *buf);
 
diff --git a/src/rsn_supp/wpa_i.h b/src/rsn_supp/wpa_i.h
index a7f5619..07f3692 100644
--- a/src/rsn_supp/wpa_i.h
+++ b/src/rsn_supp/wpa_i.h
@@ -315,6 +315,26 @@ wpa_sm_tdls_peer_addset(struct wpa_sm *sm, const u8 *addr, int add,
 						 supp_oper_classes_len);
 	return -1;
 }
+
+static inline int
+wpa_sm_tdls_enable_channel_switch(struct wpa_sm *sm, const u8 *addr,
+				  u8 oper_class,
+				  const struct hostapd_freq_params *freq_params)
+{
+	if (sm->ctx->tdls_enable_channel_switch)
+		return sm->ctx->tdls_enable_channel_switch(sm->ctx->ctx, addr,
+							   oper_class,
+							   freq_params);
+	return -1;
+}
+
+static inline int
+wpa_sm_tdls_disable_channel_switch(struct wpa_sm *sm, const u8 *addr)
+{
+	if (sm->ctx->tdls_disable_channel_switch)
+		return sm->ctx->tdls_disable_channel_switch(sm->ctx->ctx, addr);
+	return -1;
+}
 #endif /* CONFIG_TDLS */
 
 static inline int wpa_sm_key_mgmt_set_pmk(struct wpa_sm *sm,
diff --git a/wpa_supplicant/ctrl_iface.c b/wpa_supplicant/ctrl_iface.c
index e47a82c..dd62e11 100644
--- a/wpa_supplicant/ctrl_iface.c
+++ b/wpa_supplicant/ctrl_iface.c
@@ -653,6 +653,104 @@ static int ctrl_iface_get_capability_tdls(
 	return ret;
 }
 
+
+static int wpa_supplicant_ctrl_iface_tdls_chan_switch(
+	struct wpa_supplicant *wpa_s, char *cmd)
+{
+	u8 peer[ETH_ALEN];
+	struct hostapd_freq_params freq_params;
+	u8 oper_class;
+	char *pos, *end;
+
+	if (!wpa_tdls_is_external_setup(wpa_s->wpa)) {
+		wpa_printf(MSG_ERROR,
+			   "tdls_chanswitch: only supported with external setup");
+		return -1;
+	}
+
+	os_memset(&freq_params, 0, sizeof(freq_params));
+
+	pos = os_strchr(cmd, ' ');
+	if (pos == NULL)
+		return -1;
+	*pos++ = '\0';
+
+	oper_class = strtol(pos, &end, 10);
+	if (pos == end) {
+		wpa_printf(MSG_ERROR,
+			   "tdls_chanswitch: invalid op class provided");
+		return -1;
+	}
+
+	pos = end;
+	freq_params.freq = atoi(pos);
+	if (freq_params.freq == 0) {
+		wpa_printf(MSG_ERROR, "tdls_chanswitch: invalid freq provided");
+		return -1;
+	}
+
+#define SET_FREQ_SETTING(str) \
+	do { \
+		const char *pos2 = os_strstr(pos, " " #str "="); \
+		if (pos2) { \
+			pos2 += sizeof(" " #str "=") - 1; \
+			freq_params.str = atoi(pos2); \
+		} \
+	} while (0)
+
+	SET_FREQ_SETTING(center_freq1);
+	SET_FREQ_SETTING(center_freq2);
+	SET_FREQ_SETTING(bandwidth);
+	SET_FREQ_SETTING(sec_channel_offset);
+#undef SET_FREQ_SETTING
+
+	freq_params.ht_enabled = !!os_strstr(pos, " ht");
+	freq_params.vht_enabled = !!os_strstr(pos, " vht");
+
+	if (hwaddr_aton(cmd, peer)) {
+		wpa_printf(MSG_DEBUG, "CTRL_IFACE TDLS_CHAN_SWITCH: invalid "
+			   "address '%s'", cmd);
+		return -1;
+	}
+
+	wpa_printf(MSG_DEBUG, "CTRL_IFACE TDLS_CHAN_SWITCH " MACSTR
+		   " OP CLASS %d FREQ %d CENTER1 %d CENTER2 %d BW %d"
+		   " SEC_OFFSET %d%s%s",
+		   MAC2STR(peer), oper_class, freq_params.freq,
+		   freq_params.center_freq1, freq_params.center_freq2,
+		   freq_params.bandwidth, freq_params.sec_channel_offset,
+		   freq_params.ht_enabled ? " HT" : "",
+		   freq_params.vht_enabled ? " VHT" : "");
+
+	return wpa_tdls_enable_chan_switch(wpa_s->wpa, peer, oper_class,
+					   &freq_params);
+}
+
+
+static int wpa_supplicant_ctrl_iface_tdls_cancel_chan_switch(
+	struct wpa_supplicant *wpa_s, char *cmd)
+{
+	u8 peer[ETH_ALEN];
+
+	if (!wpa_tdls_is_external_setup(wpa_s->wpa)) {
+		wpa_printf(MSG_ERROR,
+			   "tdls_chanswitch: only supported with external setup");
+		return -1;
+	}
+
+	if (hwaddr_aton(cmd, peer)) {
+		wpa_printf(MSG_DEBUG,
+			   "CTRL_IFACE TDLS_CANCEL_CHAN_SWITCH: invalid "
+			   "address '%s'", cmd);
+		return -1;
+	}
+
+	wpa_printf(MSG_DEBUG, "CTRL_IFACE TDLS_CANCEL_CHAN_SWITCH " MACSTR,
+		   MAC2STR(peer));
+
+	return wpa_tdls_disable_chan_switch(wpa_s->wpa, peer);
+}
+
 #endif /* CONFIG_TDLS */
 
 
@@ -7548,6 +7646,14 @@ char * wpa_supplicant_ctrl_iface_process(struct wpa_supplicant *wpa_s,
 	} else if (os_strncmp(buf, "TDLS_TEARDOWN ", 14) == 0) {
 		if (wpa_supplicant_ctrl_iface_tdls_teardown(wpa_s, buf + 14))
 			reply_len = -1;
+	} else if (os_strncmp(buf, "TDLS_CHAN_SWITCH ", 17) == 0) {
+		if (wpa_supplicant_ctrl_iface_tdls_chan_switch(wpa_s,
+							       buf + 17))
+			reply_len = -1;
+	} else if (os_strncmp(buf, "TDLS_CANCEL_CHAN_SWITCH ", 24) == 0) {
+		if (wpa_supplicant_ctrl_iface_tdls_cancel_chan_switch(wpa_s,
+								buf + 24))
+			reply_len = -1;
 #endif /* CONFIG_TDLS */
 	} else if (os_strcmp(buf, "WMM_AC_STATUS") == 0) {
 		reply_len = wpas_wmm_ac_status(wpa_s, reply, reply_size);
diff --git a/wpa_supplicant/driver_i.h b/wpa_supplicant/driver_i.h
index a92c5f7..f614afe 100644
--- a/wpa_supplicant/driver_i.h
+++ b/wpa_supplicant/driver_i.h
@@ -607,6 +607,28 @@ static inline int wpa_drv_del_ts(struct wpa_supplicant *wpa_s, u8 tid,
 	return wpa_s->driver->del_tx_ts(wpa_s->drv_priv, tid, address);
 }
 
+static inline int
+wpa_drv_tdls_enable_channel_switch(struct wpa_supplicant *wpa_s, const u8 *addr,
+				   u8 oper_class,
+				   const struct hostapd_freq_params *freq_params)
+{
+	if (!wpa_s->driver->tdls_enable_channel_switch)
+		return -1;
+	return wpa_s->driver->tdls_enable_channel_switch(wpa_s->drv_priv, addr,
+							 oper_class,
+							 freq_params);
+}
+
+static inline int
+wpa_drv_tdls_disable_channel_switch(struct wpa_supplicant *wpa_s,
+				    const u8 *addr)
+{
+	if (!wpa_s->driver->tdls_disable_channel_switch)
+		return -1;
+	return wpa_s->driver->tdls_disable_channel_switch(wpa_s->drv_priv,
+							  addr);
+}
+
 static inline int wpa_drv_wnm_oper(struct wpa_supplicant *wpa_s,
 				   enum wnm_oper oper, const u8 *peer,
 				   u8 *buf, u16 *buf_len)
diff --git a/wpa_supplicant/wpas_glue.c b/wpa_supplicant/wpas_glue.c
index 3f45e08..b42080e 100644
--- a/wpa_supplicant/wpas_glue.c
+++ b/wpa_supplicant/wpas_glue.c
@@ -697,6 +697,27 @@ static int wpa_supplicant_tdls_peer_addset(
 	return wpa_drv_sta_add(wpa_s, &params);
 }
 
+
+static int
+wpa_supplicant_tdls_enable_channel_switch(void *ctx, const u8 *addr,
+					  u8 oper_class,
+					  const struct hostapd_freq_params *params)
+{
+	struct wpa_supplicant *wpa_s = ctx;
+
+	return wpa_drv_tdls_enable_channel_switch(wpa_s, addr, oper_class,
+						  params);
+}
+
+
+static int
+wpa_supplicant_tdls_disable_channel_switch(void *ctx, const u8 *addr)
+{
+	struct wpa_supplicant *wpa_s = ctx;
+
+	return wpa_drv_tdls_disable_channel_switch(wpa_s, addr);
+}
+
 #endif /* CONFIG_TDLS */
 
 #endif /* CONFIG_NO_WPA */
@@ -1009,6 +1030,10 @@ int wpa_supplicant_init_wpa(struct wpa_supplicant *wpa_s)
 	ctx->send_tdls_mgmt = wpa_supplicant_send_tdls_mgmt;
 	ctx->tdls_oper = wpa_supplicant_tdls_oper;
 	ctx->tdls_peer_addset = wpa_supplicant_tdls_peer_addset;
+	ctx->tdls_enable_channel_switch =
+				wpa_supplicant_tdls_enable_channel_switch;
+	ctx->tdls_disable_channel_switch =
+				wpa_supplicant_tdls_disable_channel_switch;
 #endif /* CONFIG_TDLS */
 	ctx->set_rekey_offload = wpa_supplicant_set_rekey_offload;
 	ctx->key_mgmt_set_pmk = wpa_supplicant_key_mgmt_set_pmk;
-- 
1.8.3.2



More information about the HostAP mailing list