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>
This commit is contained in:
Arik Nemtsov 2014-06-11 17:18:20 +03:00 коммит произвёл Johannes Berg
Родитель 68885a54cd
Коммит 17e6a59a36
3 изменённых файлов: 115 добавлений и 24 удалений

Просмотреть файл

@ -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);

Просмотреть файл

@ -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;

Просмотреть файл

@ -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;
}