Commit 17e6a59a authored by Arik Nemtsov's avatar Arik Nemtsov Committed by Johannes Berg
Browse files

mac80211: cleanup TDLS state during failed setup



When setting up a TDLS session, register a delayed work to remove
the peer if setup times out. Prevent concurrent setups to support this
capacity.

Signed-off-by: default avatarArik Nemtsov <arikx.nemtsov@intel.com>
Signed-off-by: default avatarJohannes Berg <johannes.berg@intel.com>
parent 68885a54
Loading
Loading
Loading
Loading
+4 −0
Original line number Diff line number Diff line
@@ -794,6 +794,9 @@ struct ieee80211_sub_if_data {
	bool radar_required;
	struct delayed_work dfs_cac_timer_work;

	u8 tdls_peer[ETH_ALEN] __aligned(2);
	struct delayed_work tdls_peer_del_work;

	/*
	 * AP this belongs to: self in AP mode and
	 * corresponding AP in VLAN mode, NULL for
@@ -1878,3 +1881,4 @@ extern const struct ethtool_ops ieee80211_ethtool_ops;
#endif

#endif /* IEEE80211_I_H */
void ieee80211_tdls_peer_del_work(struct work_struct *wk);
+2 −0
Original line number Diff line number Diff line
@@ -1672,6 +1672,8 @@ int ieee80211_if_add(struct ieee80211_local *local, const char *name,
			  ieee80211_dfs_cac_timer_work);
	INIT_DELAYED_WORK(&sdata->dec_tailroom_needed_wk,
			  ieee80211_delayed_tailroom_dec);
	INIT_DELAYED_WORK(&sdata->tdls_peer_del_work,
			  ieee80211_tdls_peer_del_work);

	for (i = 0; i < IEEE80211_NUM_BANDS; i++) {
		struct ieee80211_supported_band *sband;
+109 −24
Original line number Diff line number Diff line
@@ -10,6 +10,27 @@
#include <linux/ieee80211.h>
#include "ieee80211_i.h"

/* give usermode some time for retries in setting up the TDLS session */
#define TDLS_PEER_SETUP_TIMEOUT	(15 * HZ)

void ieee80211_tdls_peer_del_work(struct work_struct *wk)
{
	struct ieee80211_sub_if_data *sdata;
	struct ieee80211_local *local;

	sdata = container_of(wk, struct ieee80211_sub_if_data,
			     tdls_peer_del_work.work);
	local = sdata->local;

	mutex_lock(&local->mtx);
	if (!is_zero_ether_addr(sdata->tdls_peer)) {
		tdls_dbg(sdata, "TDLS del peer %pM\n", sdata->tdls_peer);
		sta_info_destroy_addr(sdata, sdata->tdls_peer);
		eth_zero_addr(sdata->tdls_peer);
	}
	mutex_unlock(&local->mtx);
}

static void ieee80211_tdls_add_ext_capab(struct sk_buff *skb)
{
	u8 *pos = (void *)skb_put(skb, 7);
@@ -168,10 +189,12 @@ ieee80211_prep_tdls_direct(struct wiphy *wiphy, struct net_device *dev,
	return 0;
}

int ieee80211_tdls_mgmt(struct wiphy *wiphy, struct net_device *dev,
			const u8 *peer, u8 action_code, u8 dialog_token,
			u16 status_code, u32 peer_capability,
			const u8 *extra_ies, size_t extra_ies_len)
static int
ieee80211_tdls_prep_mgmt_packet(struct wiphy *wiphy, struct net_device *dev,
				const u8 *peer, u8 action_code,
				u8 dialog_token, u16 status_code,
				u32 peer_capability, const u8 *extra_ies,
				size_t extra_ies_len)
{
	struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
	struct ieee80211_local *local = sdata->local;
@@ -179,17 +202,6 @@ int ieee80211_tdls_mgmt(struct wiphy *wiphy, struct net_device *dev,
	bool send_direct;
	int ret;

	if (!(wiphy->flags & WIPHY_FLAG_SUPPORTS_TDLS))
		return -ENOTSUPP;

	/* make sure we are in managed mode, and associated */
	if (sdata->vif.type != NL80211_IFTYPE_STATION ||
	    !sdata->u.mgd.associated)
		return -EINVAL;

	tdls_dbg(sdata, "TDLS mgmt action %d peer %pM\n",
		 action_code, peer);

	skb = dev_alloc_skb(local->hw.extra_tx_headroom +
			    max(sizeof(struct ieee80211_mgmt),
				sizeof(struct ieee80211_tdls_data)) +
@@ -284,11 +296,64 @@ fail:
	return ret;
}

int ieee80211_tdls_mgmt(struct wiphy *wiphy, struct net_device *dev,
			const u8 *peer, u8 action_code, u8 dialog_token,
			u16 status_code, u32 peer_capability,
			const u8 *extra_ies, size_t extra_ies_len)
{
	struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
	struct ieee80211_local *local = sdata->local;
	int ret;

	if (!(wiphy->flags & WIPHY_FLAG_SUPPORTS_TDLS))
		return -ENOTSUPP;

	/* make sure we are in managed mode, and associated */
	if (sdata->vif.type != NL80211_IFTYPE_STATION ||
	    !sdata->u.mgd.associated)
		return -EINVAL;

	mutex_lock(&local->mtx);

	/* we don't support concurrent TDLS peer setups */
	if (!is_zero_ether_addr(sdata->tdls_peer) &&
	    !ether_addr_equal(sdata->tdls_peer, peer) &&
	    (action_code == WLAN_TDLS_SETUP_REQUEST ||
	     action_code == WLAN_TDLS_SETUP_RESPONSE)) {
		ret = -EBUSY;
		goto exit;
	}

	ret = ieee80211_tdls_prep_mgmt_packet(wiphy, dev, peer, action_code,
					      dialog_token, status_code,
					      peer_capability, extra_ies,
					      extra_ies_len);
	if (ret < 0)
		goto exit;

	if (action_code == WLAN_TDLS_SETUP_REQUEST ||
	    action_code == WLAN_TDLS_SETUP_RESPONSE) {
		memcpy(sdata->tdls_peer, peer, ETH_ALEN);
		ieee80211_queue_delayed_work(&sdata->local->hw,
					     &sdata->tdls_peer_del_work,
					     TDLS_PEER_SETUP_TIMEOUT);
	}

exit:
	mutex_unlock(&local->mtx);

	tdls_dbg(sdata, "TDLS mgmt action %d peer %pM status %d\n",
		 action_code, peer, ret);
	return ret;
}

int ieee80211_tdls_oper(struct wiphy *wiphy, struct net_device *dev,
			const u8 *peer, enum nl80211_tdls_operation oper)
{
	struct sta_info *sta;
	struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
	struct ieee80211_local *local = sdata->local;
	int ret;

	if (!(wiphy->flags & WIPHY_FLAG_SUPPORTS_TDLS))
		return -ENOTSUPP;
@@ -296,6 +361,18 @@ int ieee80211_tdls_oper(struct wiphy *wiphy, struct net_device *dev,
	if (sdata->vif.type != NL80211_IFTYPE_STATION)
		return -EINVAL;

	switch (oper) {
	case NL80211_TDLS_ENABLE_LINK:
	case NL80211_TDLS_DISABLE_LINK:
		break;
	case NL80211_TDLS_TEARDOWN:
	case NL80211_TDLS_SETUP:
	case NL80211_TDLS_DISCOVERY_REQ:
		/* We don't support in-driver setup/teardown/discovery */
		return -ENOTSUPP;
	}

	mutex_lock(&local->mtx);
	tdls_dbg(sdata, "TDLS oper %d peer %pM\n", oper, peer);

	switch (oper) {
@@ -304,22 +381,30 @@ int ieee80211_tdls_oper(struct wiphy *wiphy, struct net_device *dev,
		sta = sta_info_get(sdata, peer);
		if (!sta) {
			rcu_read_unlock();
			return -ENOLINK;
			ret = -ENOLINK;
			break;
		}

		set_sta_flag(sta, WLAN_STA_TDLS_PEER_AUTH);
		rcu_read_unlock();

		WARN_ON_ONCE(is_zero_ether_addr(sdata->tdls_peer) ||
			     !ether_addr_equal(sdata->tdls_peer, peer));
		ret = 0;
		break;
	case NL80211_TDLS_DISABLE_LINK:
		return sta_info_destroy_addr(sdata, peer);
	case NL80211_TDLS_TEARDOWN:
	case NL80211_TDLS_SETUP:
	case NL80211_TDLS_DISCOVERY_REQ:
		/* We don't support in-driver setup/teardown/discovery */
		return -ENOTSUPP;
		ret = sta_info_destroy_addr(sdata, peer);
		break;
	default:
		return -ENOTSUPP;
		ret = -ENOTSUPP;
		break;
	}

	return 0;
	if (ret == 0 && ether_addr_equal(sdata->tdls_peer, peer)) {
		cancel_delayed_work(&sdata->tdls_peer_del_work);
		eth_zero_addr(sdata->tdls_peer);
	}

	mutex_unlock(&local->mtx);
	return ret;
}