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:
parent
68885a54cd
commit
17e6a59a36
3 changed files with 115 additions and 24 deletions
|
@ -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;
|
||||||
#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,
|
||||||
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 @@
|
||||||
#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,
|
||||||
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,
|
||||||
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 @@ fail:
|
||||||
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,
|
||||||
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,
|
||||||
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;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue