diff options
author | Arik Nemtsov <arik@wizery.com> | 2014-06-11 16:18:20 +0200 |
---|---|---|
committer | Johannes Berg <johannes.berg@intel.com> | 2014-06-23 14:24:55 +0200 |
commit | 17e6a59a365ac993c18f762d3a259635957f1026 (patch) | |
tree | df1184615286d6a3a4e212fb128ea72125d8d2cf /net/mac80211/tdls.c | |
parent | mac80211: set auth flags after other station info (diff) | |
download | linux-17e6a59a365ac993c18f762d3a259635957f1026.tar.xz linux-17e6a59a365ac993c18f762d3a259635957f1026.zip |
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: Arik Nemtsov <arikx.nemtsov@intel.com>
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
Diffstat (limited to 'net/mac80211/tdls.c')
-rw-r--r-- | net/mac80211/tdls.c | 133 |
1 files changed, 109 insertions, 24 deletions
diff --git a/net/mac80211/tdls.c b/net/mac80211/tdls.c index 652813b2d3df..cafcbde70018 100644 --- a/net/mac80211/tdls.c +++ b/net/mac80211/tdls.c @@ -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; } |