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

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
...@@ -794,6 +794,9 @@ struct ieee80211_sub_if_data { ...@@ -794,6 +794,9 @@ struct ieee80211_sub_if_data {
bool radar_required; bool radar_required;
struct delayed_work dfs_cac_timer_work; 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 * AP this belongs to: self in AP mode and
* corresponding AP in VLAN mode, NULL for * corresponding AP in VLAN mode, NULL for
...@@ -1878,3 +1881,4 @@ extern const struct ethtool_ops ieee80211_ethtool_ops; ...@@ -1878,3 +1881,4 @@ extern const struct ethtool_ops ieee80211_ethtool_ops;
#endif #endif
#endif /* IEEE80211_I_H */ #endif /* IEEE80211_I_H */
void ieee80211_tdls_peer_del_work(struct work_struct *wk);
...@@ -1672,6 +1672,8 @@ int ieee80211_if_add(struct ieee80211_local *local, const char *name, ...@@ -1672,6 +1672,8 @@ int ieee80211_if_add(struct ieee80211_local *local, const char *name,
ieee80211_dfs_cac_timer_work); ieee80211_dfs_cac_timer_work);
INIT_DELAYED_WORK(&sdata->dec_tailroom_needed_wk, INIT_DELAYED_WORK(&sdata->dec_tailroom_needed_wk,
ieee80211_delayed_tailroom_dec); 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++) { for (i = 0; i < IEEE80211_NUM_BANDS; i++) {
struct ieee80211_supported_band *sband; struct ieee80211_supported_band *sband;
......
...@@ -10,6 +10,27 @@ ...@@ -10,6 +10,27 @@
#include <linux/ieee80211.h> #include <linux/ieee80211.h>
#include "ieee80211_i.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) static void ieee80211_tdls_add_ext_capab(struct sk_buff *skb)
{ {
u8 *pos = (void *)skb_put(skb, 7); u8 *pos = (void *)skb_put(skb, 7);
...@@ -168,10 +189,12 @@ ieee80211_prep_tdls_direct(struct wiphy *wiphy, struct net_device *dev, ...@@ -168,10 +189,12 @@ ieee80211_prep_tdls_direct(struct wiphy *wiphy, struct net_device *dev,
return 0; return 0;
} }
int ieee80211_tdls_mgmt(struct wiphy *wiphy, struct net_device *dev, static int
const u8 *peer, u8 action_code, u8 dialog_token, ieee80211_tdls_prep_mgmt_packet(struct wiphy *wiphy, struct net_device *dev,
u16 status_code, u32 peer_capability, const u8 *peer, u8 action_code,
const u8 *extra_ies, size_t extra_ies_len) 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_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
struct ieee80211_local *local = sdata->local; struct ieee80211_local *local = sdata->local;
...@@ -179,17 +202,6 @@ int ieee80211_tdls_mgmt(struct wiphy *wiphy, struct net_device *dev, ...@@ -179,17 +202,6 @@ int ieee80211_tdls_mgmt(struct wiphy *wiphy, struct net_device *dev,
bool send_direct; bool send_direct;
int ret; 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 + skb = dev_alloc_skb(local->hw.extra_tx_headroom +
max(sizeof(struct ieee80211_mgmt), max(sizeof(struct ieee80211_mgmt),
sizeof(struct ieee80211_tdls_data)) + sizeof(struct ieee80211_tdls_data)) +
...@@ -284,11 +296,64 @@ int ieee80211_tdls_mgmt(struct wiphy *wiphy, struct net_device *dev, ...@@ -284,11 +296,64 @@ int ieee80211_tdls_mgmt(struct wiphy *wiphy, struct net_device *dev,
return ret; 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, int ieee80211_tdls_oper(struct wiphy *wiphy, struct net_device *dev,
const u8 *peer, enum nl80211_tdls_operation oper) const u8 *peer, enum nl80211_tdls_operation oper)
{ {
struct sta_info *sta; struct sta_info *sta;
struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); 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)) if (!(wiphy->flags & WIPHY_FLAG_SUPPORTS_TDLS))
return -ENOTSUPP; return -ENOTSUPP;
...@@ -296,6 +361,18 @@ int ieee80211_tdls_oper(struct wiphy *wiphy, struct net_device *dev, ...@@ -296,6 +361,18 @@ int ieee80211_tdls_oper(struct wiphy *wiphy, struct net_device *dev,
if (sdata->vif.type != NL80211_IFTYPE_STATION) if (sdata->vif.type != NL80211_IFTYPE_STATION)
return -EINVAL; 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); tdls_dbg(sdata, "TDLS oper %d peer %pM\n", oper, peer);
switch (oper) { switch (oper) {
...@@ -304,22 +381,30 @@ int ieee80211_tdls_oper(struct wiphy *wiphy, struct net_device *dev, ...@@ -304,22 +381,30 @@ int ieee80211_tdls_oper(struct wiphy *wiphy, struct net_device *dev,
sta = sta_info_get(sdata, peer); sta = sta_info_get(sdata, peer);
if (!sta) { if (!sta) {
rcu_read_unlock(); rcu_read_unlock();
return -ENOLINK; ret = -ENOLINK;
break;
} }
set_sta_flag(sta, WLAN_STA_TDLS_PEER_AUTH); set_sta_flag(sta, WLAN_STA_TDLS_PEER_AUTH);
rcu_read_unlock(); rcu_read_unlock();
WARN_ON_ONCE(is_zero_ether_addr(sdata->tdls_peer) ||
!ether_addr_equal(sdata->tdls_peer, peer));
ret = 0;
break; break;
case NL80211_TDLS_DISABLE_LINK: case NL80211_TDLS_DISABLE_LINK:
return sta_info_destroy_addr(sdata, peer); ret = sta_info_destroy_addr(sdata, peer);
case NL80211_TDLS_TEARDOWN: break;
case NL80211_TDLS_SETUP:
case NL80211_TDLS_DISCOVERY_REQ:
/* We don't support in-driver setup/teardown/discovery */
return -ENOTSUPP;
default: 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;
} }
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment