2014-06-11 14:18:36

by Arik Nemtsov

[permalink] [raw]
Subject: [PATCH 01/10] mac80211: clarify TDLS Tx handling

Rename the flags used in the Tx path and add an explanation for the
reasons to drop, send directly or through the AP.

Signed-off-by: Arik Nemtsov <[email protected]>
Reviewed-by: Johannes Berg <[email protected]>
---
net/mac80211/tx.c | 33 +++++++++++++++++----------------
1 file changed, 17 insertions(+), 16 deletions(-)

diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c
index 0a519a5..023c74d 100644
--- a/net/mac80211/tx.c
+++ b/net/mac80211/tx.c
@@ -1813,7 +1813,7 @@ netdev_tx_t ieee80211_subif_start_xmit(struct sk_buff *skb,
int nh_pos, h_pos;
struct sta_info *sta = NULL;
bool wme_sta = false, authorized = false, tdls_auth = false;
- bool tdls_direct = false;
+ bool tdls_peer = false, tdls_setup_frame = false;
bool multicast;
u32 info_flags = 0;
u16 info_id = 0;
@@ -1955,34 +1955,35 @@ netdev_tx_t ieee80211_subif_start_xmit(struct sk_buff *skb,
#endif
case NL80211_IFTYPE_STATION:
if (sdata->wdev.wiphy->flags & WIPHY_FLAG_SUPPORTS_TDLS) {
- bool tdls_peer = false;
-
sta = sta_info_get(sdata, skb->data);
if (sta) {
authorized = test_sta_flag(sta,
WLAN_STA_AUTHORIZED);
wme_sta = test_sta_flag(sta, WLAN_STA_WME);
tdls_peer = test_sta_flag(sta,
- WLAN_STA_TDLS_PEER);
+ WLAN_STA_TDLS_PEER);
tdls_auth = test_sta_flag(sta,
WLAN_STA_TDLS_PEER_AUTH);
}

- /*
- * If the TDLS link is enabled, send everything
- * directly. Otherwise, allow TDLS setup frames
- * to be transmitted indirectly.
- */
- tdls_direct = tdls_peer && (tdls_auth ||
- !(ethertype == ETH_P_TDLS && skb->len > 14 &&
- skb->data[14] == WLAN_TDLS_SNAP_RFTYPE));
+ if (tdls_peer)
+ tdls_setup_frame =
+ ethertype == ETH_P_TDLS &&
+ skb->len > 14 &&
+ skb->data[14] == WLAN_TDLS_SNAP_RFTYPE;
}

- if (tdls_direct) {
- /* link during setup - throw out frames to peer */
- if (!tdls_auth)
- goto fail_rcu;
+ /*
+ * TDLS link during setup - throw out frames to peer. We allow
+ * TDLS-setup frames to unauthorized peers for the special case
+ * of a link teardown after a TDLS sta is removed due to being
+ * unreachable.
+ */
+ if (tdls_peer && !tdls_auth && !tdls_setup_frame)
+ goto fail_rcu;

+ /* send direct packets to authorized TDLS peers */
+ if (tdls_peer && tdls_auth) {
/* DA SA BSSID */
memcpy(hdr.addr1, skb->data, ETH_ALEN);
memcpy(hdr.addr2, skb->data + ETH_ALEN, ETH_ALEN);
--
1.9.1



2014-06-29 10:43:22

by Jouni Malinen

[permalink] [raw]
Subject: Re: [PATCH 05/10] mac80211: use TDLS initiator in tdls_mgmt operations

On Wed, Jun 11, 2014 at 05:18:22PM +0300, Arik Nemtsov wrote:
> The TDLS initiator is set once during link setup. If determines the
> address ordering in the link identifier IE.
> Use the value from userspace in order to have a correct teardown packet.
> With the current code, a teardown from the responder side fails the TDLS
> MIC check because of a bad link identifier IE.

This series, and in particular, this patch, seems to break number of
TDLS use cases (well, almost all use of TDLS) with "old" user space
tools. This should be fixed or reverted until the changes are in state
that avoid such breaking of the old user interface. We cannot assume
that everyone will be updating kernel and wpa_supplicant in sync.

I would recommend running through full mac80211_hwsim test cases from
hostap.git when doing any changes to existing nl80211
commands/attributes or how they are used.

> diff --git a/net/mac80211/tdls.c b/net/mac80211/tdls.c
> @@ -242,27 +243,42 @@ ieee80211_tdls_prep_mgmt_packet(struct wiphy *wiphy, struct net_device *dev,
> - /* the TDLS link IE is always added last */
> + /* sanity check for initiator */

These sanity checks do not work with old user space since initiator will
be hardcoded to false for those cases.

> switch (action_code) {
> case WLAN_TDLS_SETUP_REQUEST:
> case WLAN_TDLS_SETUP_CONFIRM:
> - case WLAN_TDLS_TEARDOWN:
> case WLAN_TDLS_DISCOVERY_REQUEST:
> - /* we are the initiator */
> - ieee80211_tdls_add_link_ie(skb, sdata->vif.addr, peer,
> - sdata->u.mgd.bssid);
> + if (!initiator) {
> + ret = -EINVAL;
> + goto fail;
> + }

This will reject all new request frames unless user space tools are
modified to add NL80211_ATTR_TDLS_INITIATOR.

For most of these, it should be possible to set initiator based on
action_code if the attribute had been designed to support backwards
compatibility, but it wasn't, i.e., there is no way of knowing whether
the user space is aware of this new attribute since it is a flag
attribute. For example, an u32 encoding an enum that indicates whether
the device is the initiator would have allowed cfg80211 to figure out on
its own which role the device is in if the new attribute is not
included.

Is it already too late to change the nl80211 attribute since this hit
wireless-testing.git? I was about to commit wpa_supplicant changes to
start using this new attribute, but I don't think I'll do it now to
avoid getting any actual users added for this until the kernel side
design gets fixed to address existing user space behavior.


PS.

This location and patch is not the only one that breaks TDLS tests.
'mac80211: make sure TDLS peer STA exists during setup' is also breaking
things at least for special test functionality (e.g.,
ap_wpa2_tdls_bssid_mismatch and ap_wpa2_tdls_concurrent_init). I'm not
sure what to do about those, though.

--
Jouni Malinen PGP id EFC895FA

2014-06-11 14:18:48

by Arik Nemtsov

[permalink] [raw]
Subject: [PATCH 09/10] mac80211: make sure TDLS peer STA exists during setup

Make sure userspace added a TDLS peer station before invoking the
transmission of the first setup frame. This ensures packets to the peer
won't go through the AP path.

Signed-off-by: Arik Nemtsov <[email protected]>
Reviewed-by: Johannes Berg <[email protected]>
---
net/mac80211/tdls.c | 13 +++++++++++++
1 file changed, 13 insertions(+)

diff --git a/net/mac80211/tdls.c b/net/mac80211/tdls.c
index 009601a..f100c12 100644
--- a/net/mac80211/tdls.c
+++ b/net/mac80211/tdls.c
@@ -332,6 +332,19 @@ ieee80211_tdls_mgmt_setup(struct wiphy *wiphy, struct net_device *dev,
goto exit;
}

+ /*
+ * make sure we have a STA representing the peer so we drop or buffer
+ * non-TDLS-setup frames to the peer. We can't send other packets
+ * during setup through the AP path
+ */
+ rcu_read_lock();
+ if (!sta_info_get(sdata, peer)) {
+ rcu_read_unlock();
+ ret = -ENOLINK;
+ goto exit;
+ }
+ rcu_read_unlock();
+
ieee80211_flush_queues(local, sdata);

ret = ieee80211_tdls_prep_mgmt_packet(wiphy, dev, peer, action_code,
--
1.9.1


2014-06-29 18:08:36

by Jouni Malinen

[permalink] [raw]
Subject: Re: [PATCH 05/10] mac80211: use TDLS initiator in tdls_mgmt operations

On Sun, Jun 29, 2014 at 06:23:01PM +0300, Arik Nemtsov wrote:
> Well I have to say I didn't really consider mac80211 TDLS
> functionality "ready for prime time" when I've discovered this bug.
> It's something pretty basic.
> The breakage of older userspace was semi-deliberate here.

It may have lacked some functionality, but it is being deployed at least
in number of test setups.. Any such breakage of already exposed kernel
functionality needs to really be clearly identified in the commit
message, so that it is obvious to whoever may be reviewing the changes.

> But your logic is sound. If you consider this important, we can fix this.

I'm more concerned about non-mac80211 drivers since there are number of
those with TDLS support in deployed devices. However, ath9k enabled TDLS
support already in 2011 and as far as I know, it was working more or
less fine for single channel use cases since that point in time (even if
some of the teardown operations may have failed based on these new
patches and frames getting delivered through incorrect path, etc.).

While I may not be able to point to any specific deployment of
mac80211-based TDLS implementation, I don't think that it is acceptable
to break functionality that has been enabled for multiple years.. So
yes, I think that these patches will need to be fixed or reverted to
avoid such regressions.

--
Jouni Malinen PGP id EFC895FA

2014-06-11 14:18:50

by Arik Nemtsov

[permalink] [raw]
Subject: [PATCH 10/10] mac80211: protect TDLS discovery session

After sending a TDLS discovery-request, we expect a reply to arrive on
the AP's channel. We must stay on the channel (no PSM, scan, etc.), since
a TDLS setup-response is a direct packet not buffered by the AP.
Add a new mac80211 driver callback to allow discovery session protection.

Signed-off-by: Arik Nemtsov <[email protected]>
Reviewed-by: Johannes Berg <[email protected]>
Reviewed-by: Emmanuel Grumbach <[email protected]>
---
include/net/mac80211.h | 12 ++++++++++++
net/mac80211/driver-ops.h | 16 ++++++++++++++++
net/mac80211/tdls.c | 10 +++++++++-
net/mac80211/trace.h | 7 +++++++
4 files changed, 44 insertions(+), 1 deletion(-)

diff --git a/include/net/mac80211.h b/include/net/mac80211.h
index 8d876dc..18c2bdb 100644
--- a/include/net/mac80211.h
+++ b/include/net/mac80211.h
@@ -2764,6 +2764,15 @@ enum ieee80211_roc_type {
* mac80211 will transmit the frame right away.
* The callback is optional and can (should!) sleep.
*
+ * @mgd_protect_tdls_discover: Protect a TDLS discovery session. After sending
+ * a TDLS discovery-request, we expect a reply to arrive on the AP's
+ * channel. We must stay on the channel (no PSM, scan, etc.), since a TDLS
+ * setup-response is a direct packet not buffered by the AP.
+ * mac80211 will call this function just before the transmission of a TDLS
+ * discovery-request. The recommended period of protection is at least
+ * 2 * (DTIM period).
+ * The callback is optional and can sleep.
+ *
* @add_chanctx: Notifies device driver about new channel context creation.
* @remove_chanctx: Notifies device driver about channel context destruction.
* @change_chanctx: Notifies device driver about channel context changes that
@@ -2981,6 +2990,9 @@ struct ieee80211_ops {
void (*mgd_prepare_tx)(struct ieee80211_hw *hw,
struct ieee80211_vif *vif);

+ void (*mgd_protect_tdls_discover)(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif);
+
int (*add_chanctx)(struct ieee80211_hw *hw,
struct ieee80211_chanctx_conf *ctx);
void (*remove_chanctx)(struct ieee80211_hw *hw,
diff --git a/net/mac80211/driver-ops.h b/net/mac80211/driver-ops.h
index bd782dc..2265bd7 100644
--- a/net/mac80211/driver-ops.h
+++ b/net/mac80211/driver-ops.h
@@ -970,6 +970,22 @@ static inline void drv_mgd_prepare_tx(struct ieee80211_local *local,
trace_drv_return_void(local);
}

+static inline void
+drv_mgd_protect_tdls_discover(struct ieee80211_local *local,
+ struct ieee80211_sub_if_data *sdata)
+{
+ might_sleep();
+
+ if (!check_sdata_in_driver(sdata))
+ return;
+ WARN_ON_ONCE(sdata->vif.type != NL80211_IFTYPE_STATION);
+
+ trace_drv_mgd_protect_tdls_discover(local, sdata);
+ if (local->ops->mgd_protect_tdls_discover)
+ local->ops->mgd_protect_tdls_discover(&local->hw, &sdata->vif);
+ trace_drv_return_void(local);
+}
+
static inline int drv_add_chanctx(struct ieee80211_local *local,
struct ieee80211_chanctx *ctx)
{
diff --git a/net/mac80211/tdls.c b/net/mac80211/tdls.c
index f100c12..a937cd9 100644
--- a/net/mac80211/tdls.c
+++ b/net/mac80211/tdls.c
@@ -10,6 +10,7 @@
#include <linux/ieee80211.h>
#include <net/cfg80211.h>
#include "ieee80211_i.h"
+#include "driver-ops.h"

/* give usermode some time for retries in setting up the TDLS session */
#define TDLS_PEER_SETUP_TIMEOUT (15 * HZ)
@@ -444,8 +445,15 @@ int ieee80211_tdls_mgmt(struct wiphy *wiphy, struct net_device *dev,
peer_capability, initiator,
extra_ies, extra_ies_len);
break;
- case WLAN_TDLS_SETUP_CONFIRM:
case WLAN_TDLS_DISCOVERY_REQUEST:
+ /*
+ * Protect the discovery so we can hear the TDLS discovery
+ * response frame. It is transmitted directly and not buffered
+ * by the AP.
+ */
+ drv_mgd_protect_tdls_discover(sdata->local, sdata);
+ /* fall-through */
+ case WLAN_TDLS_SETUP_CONFIRM:
case WLAN_PUB_ACTION_TDLS_DISCOVER_RES:
/* no special handling */
ret = ieee80211_tdls_prep_mgmt_packet(wiphy, dev, peer,
diff --git a/net/mac80211/trace.h b/net/mac80211/trace.h
index b22d696..e7a52a3 100644
--- a/net/mac80211/trace.h
+++ b/net/mac80211/trace.h
@@ -1330,6 +1330,13 @@ DEFINE_EVENT(local_sdata_evt, drv_mgd_prepare_tx,
TP_ARGS(local, sdata)
);

+DEFINE_EVENT(local_sdata_evt, drv_mgd_protect_tdls_discover,
+ TP_PROTO(struct ieee80211_local *local,
+ struct ieee80211_sub_if_data *sdata),
+
+ TP_ARGS(local, sdata)
+);
+
DECLARE_EVENT_CLASS(local_chanctx,
TP_PROTO(struct ieee80211_local *local,
struct ieee80211_chanctx *ctx),
--
1.9.1


2014-06-11 14:18:42

by Arik Nemtsov

[permalink] [raw]
Subject: [PATCH 05/10] mac80211: use TDLS initiator in tdls_mgmt operations

The TDLS initiator is set once during link setup. If determines the
address ordering in the link identifier IE.
Use the value from userspace in order to have a correct teardown packet.
With the current code, a teardown from the responder side fails the TDLS
MIC check because of a bad link identifier IE.

Signed-off-by: Arik Nemtsov <[email protected]>
Reviewed-by: Johannes Berg <[email protected]>
---
net/mac80211/tdls.c | 40 ++++++++++++++++++++++++++++------------
1 file changed, 28 insertions(+), 12 deletions(-)

diff --git a/net/mac80211/tdls.c b/net/mac80211/tdls.c
index 0b3ca2c..c4a9af3 100644
--- a/net/mac80211/tdls.c
+++ b/net/mac80211/tdls.c
@@ -193,13 +193,14 @@ 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)
+ u32 peer_capability, bool initiator,
+ 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;
struct sk_buff *skb = NULL;
bool send_direct;
+ const u8 *init_addr, *rsp_addr;
int ret;

skb = dev_alloc_skb(local->hw.extra_tx_headroom +
@@ -242,27 +243,42 @@ ieee80211_tdls_prep_mgmt_packet(struct wiphy *wiphy, struct net_device *dev,
if (extra_ies_len)
memcpy(skb_put(skb, extra_ies_len), extra_ies, extra_ies_len);

- /* the TDLS link IE is always added last */
+ /* sanity check for initiator */
switch (action_code) {
case WLAN_TDLS_SETUP_REQUEST:
case WLAN_TDLS_SETUP_CONFIRM:
- case WLAN_TDLS_TEARDOWN:
case WLAN_TDLS_DISCOVERY_REQUEST:
- /* we are the initiator */
- ieee80211_tdls_add_link_ie(skb, sdata->vif.addr, peer,
- sdata->u.mgd.bssid);
+ if (!initiator) {
+ ret = -EINVAL;
+ goto fail;
+ }
break;
case WLAN_TDLS_SETUP_RESPONSE:
case WLAN_PUB_ACTION_TDLS_DISCOVER_RES:
- /* we are the responder */
- ieee80211_tdls_add_link_ie(skb, peer, sdata->vif.addr,
- sdata->u.mgd.bssid);
+ if (initiator) {
+ ret = -EINVAL;
+ goto fail;
+ }
+ break;
+ case WLAN_TDLS_TEARDOWN:
+ /* any value is ok */
break;
default:
ret = -ENOTSUPP;
goto fail;
}

+ if (initiator) {
+ init_addr = sdata->vif.addr;
+ rsp_addr = peer;
+ } else {
+ init_addr = peer;
+ rsp_addr = sdata->vif.addr;
+ }
+
+ ieee80211_tdls_add_link_ie(skb, init_addr, rsp_addr,
+ sdata->u.mgd.bssid);
+
if (send_direct) {
ieee80211_tx_skb(sdata, skb);
return 0;
@@ -327,8 +343,8 @@ int ieee80211_tdls_mgmt(struct wiphy *wiphy, struct net_device *dev,

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

--
1.9.1


2014-06-11 14:18:43

by Arik Nemtsov

[permalink] [raw]
Subject: [PATCH 04/10] cfg80211: pass TDLS initiator in tdls_mgmt operations

The TDLS initiator is set once during link setup. If determines the
address ordering in the link identifier IE.

Fix dependent drivers - mwifiex and mac80211.

Signed-off-by: Arik Nemtsov <[email protected]>
Reviewed-by: Johannes Berg <[email protected]>
---
drivers/net/wireless/mwifiex/cfg80211.c | 3 ++-
include/net/cfg80211.h | 2 +-
include/uapi/linux/nl80211.h | 5 +++++
net/mac80211/ieee80211_i.h | 3 ++-
net/mac80211/tdls.c | 3 ++-
net/wireless/nl80211.c | 4 ++++
net/wireless/rdev-ops.h | 6 +++---
net/wireless/trace.h | 10 +++++++---
8 files changed, 26 insertions(+), 10 deletions(-)

diff --git a/drivers/net/wireless/mwifiex/cfg80211.c b/drivers/net/wireless/mwifiex/cfg80211.c
index e95dec9..149d2e6 100644
--- a/drivers/net/wireless/mwifiex/cfg80211.c
+++ b/drivers/net/wireless/mwifiex/cfg80211.c
@@ -2631,7 +2631,8 @@ static int
mwifiex_cfg80211_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)
+ bool initiator, const u8 *extra_ies,
+ size_t extra_ies_len)
{
struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev);
int ret;
diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h
index 5c17b1f..9d7b59e 100644
--- a/include/net/cfg80211.h
+++ b/include/net/cfg80211.h
@@ -2508,7 +2508,7 @@ struct cfg80211_ops {
int (*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 *buf, size_t len);
+ bool initiator, const u8 *buf, size_t len);
int (*tdls_oper)(struct wiphy *wiphy, struct net_device *dev,
const u8 *peer, enum nl80211_tdls_operation oper);

diff --git a/include/uapi/linux/nl80211.h b/include/uapi/linux/nl80211.h
index fb0efa1..407be5c 100644
--- a/include/uapi/linux/nl80211.h
+++ b/include/uapi/linux/nl80211.h
@@ -1591,6 +1591,9 @@ enum nl80211_commands {
* creation then the new interface will be owned by the netlink socket
* that created it and will be destroyed when the socket is closed
*
+ * @NL80211_ATTR_TDLS_INITIATOR: flag attribute indicating the current end is
+ * the TDLS link initiator.
+ *
* @NL80211_ATTR_MAX: highest attribute number currently defined
* @__NL80211_ATTR_AFTER_LAST: internal use
*/
@@ -1931,6 +1934,8 @@ enum nl80211_attrs {
NL80211_ATTR_CSA_C_OFFSETS_TX,
NL80211_ATTR_MAX_CSA_COUNTERS,

+ NL80211_ATTR_TDLS_INITIATOR,
+
/* add attributes here, update the policy in nl80211.c */

__NL80211_ATTR_AFTER_LAST,
diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h
index b0d1d16..2e3d18c 100644
--- a/net/mac80211/ieee80211_i.h
+++ b/net/mac80211/ieee80211_i.h
@@ -1854,7 +1854,8 @@ int ieee80211_max_num_channels(struct ieee80211_local *local);
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);
+ bool initiator, const u8 *extra_ies,
+ size_t extra_ies_len);
int ieee80211_tdls_oper(struct wiphy *wiphy, struct net_device *dev,
const u8 *peer, enum nl80211_tdls_operation oper);

diff --git a/net/mac80211/tdls.c b/net/mac80211/tdls.c
index cafcbde..0b3ca2c 100644
--- a/net/mac80211/tdls.c
+++ b/net/mac80211/tdls.c
@@ -299,7 +299,8 @@ fail:
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)
+ bool initiator, 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;
diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c
index ba4f172..8f46b8f 100644
--- a/net/wireless/nl80211.c
+++ b/net/wireless/nl80211.c
@@ -337,6 +337,7 @@ static const struct nla_policy nl80211_policy[NL80211_ATTR_MAX+1] = {
[NL80211_ATTR_TDLS_OPERATION] = { .type = NLA_U8 },
[NL80211_ATTR_TDLS_SUPPORT] = { .type = NLA_FLAG },
[NL80211_ATTR_TDLS_EXTERNAL_SETUP] = { .type = NLA_FLAG },
+ [NL80211_ATTR_TDLS_INITIATOR] = { .type = NLA_FLAG },
[NL80211_ATTR_DONT_WAIT_FOR_ACK] = { .type = NLA_FLAG },
[NL80211_ATTR_PROBE_RESP] = { .type = NLA_BINARY,
.len = IEEE80211_MAX_DATA_LEN },
@@ -7365,6 +7366,7 @@ static int nl80211_tdls_mgmt(struct sk_buff *skb, struct genl_info *info)
u32 peer_capability = 0;
u16 status_code;
u8 *peer;
+ bool initiator;

if (!(rdev->wiphy.flags & WIPHY_FLAG_SUPPORTS_TDLS) ||
!rdev->ops->tdls_mgmt)
@@ -7381,12 +7383,14 @@ static int nl80211_tdls_mgmt(struct sk_buff *skb, struct genl_info *info)
action_code = nla_get_u8(info->attrs[NL80211_ATTR_TDLS_ACTION]);
status_code = nla_get_u16(info->attrs[NL80211_ATTR_STATUS_CODE]);
dialog_token = nla_get_u8(info->attrs[NL80211_ATTR_TDLS_DIALOG_TOKEN]);
+ initiator = nla_get_flag(info->attrs[NL80211_ATTR_TDLS_INITIATOR]);
if (info->attrs[NL80211_ATTR_TDLS_PEER_CAPABILITY])
peer_capability =
nla_get_u32(info->attrs[NL80211_ATTR_TDLS_PEER_CAPABILITY]);

return rdev_tdls_mgmt(rdev, dev, peer, action_code,
dialog_token, status_code, peer_capability,
+ initiator,
nla_data(info->attrs[NL80211_ATTR_IE]),
nla_len(info->attrs[NL80211_ATTR_IE]));
}
diff --git a/net/wireless/rdev-ops.h b/net/wireless/rdev-ops.h
index f552b0a..56c2240 100644
--- a/net/wireless/rdev-ops.h
+++ b/net/wireless/rdev-ops.h
@@ -751,15 +751,15 @@ static inline int rdev_tdls_mgmt(struct cfg80211_registered_device *rdev,
struct net_device *dev, u8 *peer,
u8 action_code, u8 dialog_token,
u16 status_code, u32 peer_capability,
- const u8 *buf, size_t len)
+ bool initiator, const u8 *buf, size_t len)
{
int ret;
trace_rdev_tdls_mgmt(&rdev->wiphy, dev, peer, action_code,
dialog_token, status_code, peer_capability,
- buf, len);
+ initiator, buf, len);
ret = rdev->ops->tdls_mgmt(&rdev->wiphy, dev, peer, action_code,
dialog_token, status_code, peer_capability,
- buf, len);
+ initiator, buf, len);
trace_rdev_return_int(&rdev->wiphy, ret);
return ret;
}
diff --git a/net/wireless/trace.h b/net/wireless/trace.h
index 174559a..85474ee 100644
--- a/net/wireless/trace.h
+++ b/net/wireless/trace.h
@@ -1454,9 +1454,9 @@ TRACE_EVENT(rdev_tdls_mgmt,
TP_PROTO(struct wiphy *wiphy, struct net_device *netdev,
u8 *peer, u8 action_code, u8 dialog_token,
u16 status_code, u32 peer_capability,
- const u8 *buf, size_t len),
+ bool initiator, const u8 *buf, size_t len),
TP_ARGS(wiphy, netdev, peer, action_code, dialog_token, status_code,
- peer_capability, buf, len),
+ peer_capability, initiator, buf, len),
TP_STRUCT__entry(
WIPHY_ENTRY
NETDEV_ENTRY
@@ -1465,6 +1465,7 @@ TRACE_EVENT(rdev_tdls_mgmt,
__field(u8, dialog_token)
__field(u16, status_code)
__field(u32, peer_capability)
+ __field(bool, initiator)
__dynamic_array(u8, buf, len)
),
TP_fast_assign(
@@ -1475,13 +1476,16 @@ TRACE_EVENT(rdev_tdls_mgmt,
__entry->dialog_token = dialog_token;
__entry->status_code = status_code;
__entry->peer_capability = peer_capability;
+ __entry->initiator = initiator;
memcpy(__get_dynamic_array(buf), buf, len);
),
TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT ", " MAC_PR_FMT ", action_code: %u, "
- "dialog_token: %u, status_code: %u, peer_capability: %u buf: %#.2x ",
+ "dialog_token: %u, status_code: %u, peer_capability: %u "
+ "initiator: %s buf: %#.2x ",
WIPHY_PR_ARG, NETDEV_PR_ARG, MAC_PR_ARG(peer),
__entry->action_code, __entry->dialog_token,
__entry->status_code, __entry->peer_capability,
+ BOOL_TO_STR(__entry->initiator),
((u8 *)__get_dynamic_array(buf))[0])
);

--
1.9.1


2014-06-11 14:18:38

by Arik Nemtsov

[permalink] [raw]
Subject: [PATCH 02/10] mac80211: set auth flags after other station info

For TDLS, the AUTHORIZED flag arrives with all other important station
info (supported rates, HT/VHT caps, ...). Make sure to set the station
state in the low-level driver after transferring this information to
the mac80211 STA entry.
This aligns the STA information during sta_state callbacks with the
non-TDLS case.

Signed-off-by: Arik Nemtsov <[email protected]>
Reviewed-by: Emmanuel Grumbach <[email protected]>
---
net/mac80211/cfg.c | 16 +++++++++++++---
1 file changed, 13 insertions(+), 3 deletions(-)

diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c
index af3eac4..6c67073 100644
--- a/net/mac80211/cfg.c
+++ b/net/mac80211/cfg.c
@@ -1020,9 +1020,12 @@ static int sta_apply_parameters(struct ieee80211_local *local,
}
}

- ret = sta_apply_auth_flags(local, sta, mask, set);
- if (ret)
- return ret;
+ /* auth flags will be set later for TDLS stations */
+ if (!test_sta_flag(sta, WLAN_STA_TDLS_PEER)) {
+ ret = sta_apply_auth_flags(local, sta, mask, set);
+ if (ret)
+ return ret;
+ }

if (mask & BIT(NL80211_STA_FLAG_SHORT_PREAMBLE)) {
if (set & BIT(NL80211_STA_FLAG_SHORT_PREAMBLE))
@@ -1159,6 +1162,13 @@ static int sta_apply_parameters(struct ieee80211_local *local,
#endif
}

+ /* set the STA state after all sta info from usermode has been set */
+ if (test_sta_flag(sta, WLAN_STA_TDLS_PEER)) {
+ ret = sta_apply_auth_flags(local, sta, mask, set);
+ if (ret)
+ return ret;
+ }
+
return 0;
}

--
1.9.1


2014-06-29 15:23:17

by Arik Nemtsov

[permalink] [raw]
Subject: Re: [PATCH 05/10] mac80211: use TDLS initiator in tdls_mgmt operations

On Sun, Jun 29, 2014 at 1:43 PM, Jouni Malinen <[email protected]> wrote:
> On Wed, Jun 11, 2014 at 05:18:22PM +0300, Arik Nemtsov wrote:
>> The TDLS initiator is set once during link setup. If determines the
>> address ordering in the link identifier IE.
>> Use the value from userspace in order to have a correct teardown packet.
>> With the current code, a teardown from the responder side fails the TDLS
>> MIC check because of a bad link identifier IE.
>
> This series, and in particular, this patch, seems to break number of
> TDLS use cases (well, almost all use of TDLS) with "old" user space
> tools. This should be fixed or reverted until the changes are in state
> that avoid such breaking of the old user interface. We cannot assume
> that everyone will be updating kernel and wpa_supplicant in sync.
>
> I would recommend running through full mac80211_hwsim test cases from
> hostap.git when doing any changes to existing nl80211
> commands/attributes or how they are used.

Well I have to say I didn't really consider mac80211 TDLS
functionality "ready for prime time" when I've discovered this bug.
It's something pretty basic.
The breakage of older userspace was semi-deliberate here.

But your logic is sound. If you consider this important, we can fix this.

>
>> diff --git a/net/mac80211/tdls.c b/net/mac80211/tdls.c
>> @@ -242,27 +243,42 @@ ieee80211_tdls_prep_mgmt_packet(struct wiphy *wiphy, struct net_device *dev,
>> - /* the TDLS link IE is always added last */
>> + /* sanity check for initiator */
>
> These sanity checks do not work with old user space since initiator will
> be hardcoded to false for those cases.
>
>> switch (action_code) {
>> case WLAN_TDLS_SETUP_REQUEST:
>> case WLAN_TDLS_SETUP_CONFIRM:
>> - case WLAN_TDLS_TEARDOWN:
>> case WLAN_TDLS_DISCOVERY_REQUEST:
>> - /* we are the initiator */
>> - ieee80211_tdls_add_link_ie(skb, sdata->vif.addr, peer,
>> - sdata->u.mgd.bssid);
>> + if (!initiator) {
>> + ret = -EINVAL;
>> + goto fail;
>> + }
>
> This will reject all new request frames unless user space tools are
> modified to add NL80211_ATTR_TDLS_INITIATOR.
>
> For most of these, it should be possible to set initiator based on
> action_code if the attribute had been designed to support backwards
> compatibility, but it wasn't, i.e., there is no way of knowing whether
> the user space is aware of this new attribute since it is a flag
> attribute. For example, an u32 encoding an enum that indicates whether
> the device is the initiator would have allowed cfg80211 to figure out on
> its own which role the device is in if the new attribute is not
> included.
>
> Is it already too late to change the nl80211 attribute since this hit
> wireless-testing.git? I was about to commit wpa_supplicant changes to
> start using this new attribute, but I don't think I'll do it now to
> avoid getting any actual users added for this until the kernel side
> design gets fixed to address existing user space behavior.

We can just put a patch on top of mac80211 to remove the sanity
testing of "initiator" and just act as if it's false when we can't
infer the value (as before). That would leave the wpa_s patch as is.
I don't see any value in changing the nl80211 to something other than
a flag, if you don't want old userspace to fail anyway..

>
>
> PS.
>
> This location and patch is not the only one that breaks TDLS tests.
> 'mac80211: make sure TDLS peer STA exists during setup' is also breaking
> things at least for special test functionality (e.g.,
> ap_wpa2_tdls_bssid_mismatch and ap_wpa2_tdls_concurrent_init). I'm not
> sure what to do about those, though.

Actually Johannes asked for that one :) But I'm not sure this patch is
the culprit, makes more sense for this:
17e6a59 mac80211: cleanup TDLS state during failed setup

I'll take a look.

Arik

2014-06-11 14:18:39

by Arik Nemtsov

[permalink] [raw]
Subject: [PATCH 03/10] 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 <[email protected]>
Reviewed-by: Johannes Berg <[email protected]>
---
net/mac80211/ieee80211_i.h | 4 ++
net/mac80211/iface.c | 2 +
net/mac80211/tdls.c | 133 +++++++++++++++++++++++++++++++++++++--------
3 files changed, 115 insertions(+), 24 deletions(-)

diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h
index 677e9c8..b0d1d16 100644
--- a/net/mac80211/ieee80211_i.h
+++ b/net/mac80211/ieee80211_i.h
@@ -793,6 +793,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
@@ -1865,3 +1868,4 @@ extern const struct ethtool_ops ieee80211_ethtool_ops;
#endif

#endif /* IEEE80211_I_H */
+void ieee80211_tdls_peer_del_work(struct work_struct *wk);
diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c
index 00762a1..57e01dd 100644
--- a/net/mac80211/iface.c
+++ b/net/mac80211/iface.c
@@ -1671,6 +1671,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;
diff --git a/net/mac80211/tdls.c b/net/mac80211/tdls.c
index 652813b..cafcbde 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;
}
--
1.9.1


2014-06-11 14:18:45

by Arik Nemtsov

[permalink] [raw]
Subject: [PATCH 07/10] mac80211: implement proper Tx path flushing for TDLS

As the spec mandates, flush data in the AP path before transmitting the
first setup frame. Data packets transmitted during setup are already
dropped in the Tx path.

For the teardown flow, flush all packets in the direct path before
transmitting the teardown frame. Un-authorize the peer sta after teardown
is sent, forcing all subsequent Tx to the peer through the AP.

Make sure to flush the queues when disabling the link to get the
teardown packet out.

Signed-off-by: Arik Nemtsov <[email protected]>
Reviewed-by: Johannes Berg <[email protected]>
---
net/mac80211/ieee80211_i.h | 1 +
net/mac80211/tdls.c | 59 ++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 60 insertions(+)

diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h
index 2e3d18c..851ca7c 100644
--- a/net/mac80211/ieee80211_i.h
+++ b/net/mac80211/ieee80211_i.h
@@ -924,6 +924,7 @@ enum queue_stop_reason {
IEEE80211_QUEUE_STOP_REASON_SKB_ADD,
IEEE80211_QUEUE_STOP_REASON_OFFCHANNEL,
IEEE80211_QUEUE_STOP_REASON_FLUSH,
+ IEEE80211_QUEUE_STOP_REASON_TDLS_TEARDOWN,
};

#ifdef CONFIG_MAC80211_LEDS
diff --git a/net/mac80211/tdls.c b/net/mac80211/tdls.c
index 92d203a..da7eb7e 100644
--- a/net/mac80211/tdls.c
+++ b/net/mac80211/tdls.c
@@ -331,6 +331,8 @@ ieee80211_tdls_mgmt_setup(struct wiphy *wiphy, struct net_device *dev,
goto exit;
}

+ ieee80211_flush_queues(local, sdata);
+
ret = ieee80211_tdls_prep_mgmt_packet(wiphy, dev, peer, action_code,
dialog_token, status_code,
peer_capability, initiator,
@@ -348,6 +350,54 @@ exit:
return ret;
}

+static int
+ieee80211_tdls_mgmt_teardown(struct wiphy *wiphy, struct net_device *dev,
+ const u8 *peer, u8 action_code, u8 dialog_token,
+ u16 status_code, u32 peer_capability,
+ bool initiator, 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;
+ struct sta_info *sta;
+ int ret;
+
+ /*
+ * No packets can be transmitted to the peer via the AP during setup -
+ * the STA is set as a TDLS peer, but is not authorized.
+ * During teardown, we prevent direct transmissions by stopping the
+ * queues and flushing all direct packets.
+ */
+ ieee80211_stop_queues_by_reason(&local->hw,
+ IEEE80211_MAX_QUEUE_MAP,
+ IEEE80211_QUEUE_STOP_REASON_TDLS_TEARDOWN);
+ ieee80211_flush_queues(local, sdata);
+
+ ret = ieee80211_tdls_prep_mgmt_packet(wiphy, dev, peer, action_code,
+ dialog_token, status_code,
+ peer_capability, initiator,
+ extra_ies, extra_ies_len);
+ if (ret < 0)
+ sdata_err(sdata, "Failed sending TDLS teardown packet %d\n",
+ ret);
+
+ /*
+ * Remove the STA AUTH flag to force further traffic through the AP. If
+ * the STA was unreachable, it was already removed.
+ */
+ rcu_read_lock();
+ sta = sta_info_get(sdata, peer);
+ if (sta)
+ clear_sta_flag(sta, WLAN_STA_TDLS_PEER_AUTH);
+ rcu_read_unlock();
+
+ ieee80211_wake_queues_by_reason(&local->hw,
+ IEEE80211_MAX_QUEUE_MAP,
+ IEEE80211_QUEUE_STOP_REASON_TDLS_TEARDOWN);
+
+ 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,
@@ -374,6 +424,12 @@ int ieee80211_tdls_mgmt(struct wiphy *wiphy, struct net_device *dev,
extra_ies, extra_ies_len);
break;
case WLAN_TDLS_TEARDOWN:
+ ret = ieee80211_tdls_mgmt_teardown(wiphy, dev, peer,
+ action_code, dialog_token,
+ status_code,
+ peer_capability, initiator,
+ extra_ies, extra_ies_len);
+ break;
case WLAN_TDLS_SETUP_CONFIRM:
case WLAN_TDLS_DISCOVERY_REQUEST:
case WLAN_PUB_ACTION_TDLS_DISCOVER_RES:
@@ -442,6 +498,9 @@ int ieee80211_tdls_oper(struct wiphy *wiphy, struct net_device *dev,
ret = 0;
break;
case NL80211_TDLS_DISABLE_LINK:
+ /* flush a potentially queued teardown packet */
+ ieee80211_flush_queues(local, sdata);
+
ret = sta_info_destroy_addr(sdata, peer);
break;
default:
--
1.9.1


2014-06-23 12:30:08

by Johannes Berg

[permalink] [raw]
Subject: Re: [PATCH 01/10] mac80211: clarify TDLS Tx handling

Applied all - had to make some adjustments in patch 7 and while at it
made it use the per-vif functionality Luca added.

johannes


2014-06-11 14:18:43

by Arik Nemtsov

[permalink] [raw]
Subject: [PATCH 06/10] mac80211: split tdls_mgmt function

There are setup/teardown specific actions to be done that accompany
the sending of a TDLS management packet. Split the main function to
simplify future additions.

Signed-off-by: Arik Nemtsov <[email protected]>
Reviewed-by: Johannes Berg <[email protected]>
---
net/mac80211/tdls.c | 78 +++++++++++++++++++++++++++++++++++++----------------
1 file changed, 55 insertions(+), 23 deletions(-)

diff --git a/net/mac80211/tdls.c b/net/mac80211/tdls.c
index c4a9af3..92d203a 100644
--- a/net/mac80211/tdls.c
+++ b/net/mac80211/tdls.c
@@ -312,31 +312,21 @@ 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,
- bool initiator, const u8 *extra_ies,
- size_t extra_ies_len)
+static int
+ieee80211_tdls_mgmt_setup(struct wiphy *wiphy, struct net_device *dev,
+ const u8 *peer, u8 action_code, u8 dialog_token,
+ u16 status_code, u32 peer_capability, bool initiator,
+ 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)) {
+ !ether_addr_equal(sdata->tdls_peer, peer)) {
ret = -EBUSY;
goto exit;
}
@@ -348,16 +338,58 @@ int ieee80211_tdls_mgmt(struct wiphy *wiphy, struct net_device *dev,
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);
- }
+ 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);
+ 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,
+ bool initiator, const u8 *extra_ies,
+ size_t extra_ies_len)
+{
+ struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
+ 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;
+
+ switch (action_code) {
+ case WLAN_TDLS_SETUP_REQUEST:
+ case WLAN_TDLS_SETUP_RESPONSE:
+ ret = ieee80211_tdls_mgmt_setup(wiphy, dev, peer, action_code,
+ dialog_token, status_code,
+ peer_capability, initiator,
+ extra_ies, extra_ies_len);
+ break;
+ case WLAN_TDLS_TEARDOWN:
+ case WLAN_TDLS_SETUP_CONFIRM:
+ case WLAN_TDLS_DISCOVERY_REQUEST:
+ case WLAN_PUB_ACTION_TDLS_DISCOVER_RES:
+ /* no special handling */
+ ret = ieee80211_tdls_prep_mgmt_packet(wiphy, dev, peer,
+ action_code,
+ dialog_token,
+ status_code,
+ peer_capability,
+ initiator, extra_ies,
+ extra_ies_len);
+ break;
+ default:
+ ret = -EOPNOTSUPP;
+ break;
+ }

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


2014-06-23 12:31:55

by Arik Nemtsov

[permalink] [raw]
Subject: Re: [PATCH 01/10] mac80211: clarify TDLS Tx handling

On Mon, Jun 23, 2014 at 3:29 PM, Johannes Berg
<[email protected]> wrote:
>
> Applied all - had to make some adjustments in patch 7 and while at it
> made it use the per-vif functionality Luca added.

Thanks!

Arik

2014-06-11 14:18:46

by Arik Nemtsov

[permalink] [raw]
Subject: [PATCH 08/10] mac80211: add API to request TDLS operation from userspace

Write a mac80211 to the cfg80211 API for requesting a userspace TDLS
operation. Define TDLS specific reason codes that can be used here.

Signed-off-by: Arik Nemtsov <[email protected]>
Reviewed-by: Johannes Berg <[email protected]>
---
include/linux/ieee80211.h | 3 +++
include/net/mac80211.h | 13 +++++++++++++
net/mac80211/tdls.c | 17 +++++++++++++++++
3 files changed, 33 insertions(+)

diff --git a/include/linux/ieee80211.h b/include/linux/ieee80211.h
index 6bff13f..75d17e1 100644
--- a/include/linux/ieee80211.h
+++ b/include/linux/ieee80211.h
@@ -1621,6 +1621,9 @@ enum ieee80211_reasoncode {
WLAN_REASON_INVALID_RSN_IE_CAP = 22,
WLAN_REASON_IEEE8021X_FAILED = 23,
WLAN_REASON_CIPHER_SUITE_REJECTED = 24,
+ /* TDLS (802.11z) */
+ WLAN_REASON_TDLS_TEARDOWN_UNREACHABLE = 25,
+ WLAN_REASON_TDLS_TEARDOWN_UNSPECIFIED = 26,
/* 802.11e */
WLAN_REASON_DISASSOC_UNSPECIFIED_QOS = 32,
WLAN_REASON_DISASSOC_QAP_NO_BANDWIDTH = 33,
diff --git a/include/net/mac80211.h b/include/net/mac80211.h
index 421b6ec..8d876dc 100644
--- a/include/net/mac80211.h
+++ b/include/net/mac80211.h
@@ -4815,4 +4815,17 @@ int ieee80211_parse_p2p_noa(const struct ieee80211_p2p_noa_attr *attr,
*/
void ieee80211_update_p2p_noa(struct ieee80211_noa_data *data, u32 tsf);

+/**
+ * ieee80211_tdls_oper - request userspace to perform a TDLS operation
+ * @vif: virtual interface
+ * @peer: the peer's destination address
+ * @oper: the requested TDLS operation
+ * @reason_code: reason code for the operation, valid for TDLS teardown
+ * @gfp: allocation flags
+ *
+ * See cfg80211_tdls_oper_request().
+ */
+void ieee80211_tdls_oper_request(struct ieee80211_vif *vif, const u8 *peer,
+ enum nl80211_tdls_operation oper,
+ u16 reason_code, gfp_t gfp);
#endif /* MAC80211_H */
diff --git a/net/mac80211/tdls.c b/net/mac80211/tdls.c
index da7eb7e..009601a 100644
--- a/net/mac80211/tdls.c
+++ b/net/mac80211/tdls.c
@@ -8,6 +8,7 @@
*/

#include <linux/ieee80211.h>
+#include <net/cfg80211.h>
#include "ieee80211_i.h"

/* give usermode some time for retries in setting up the TDLS session */
@@ -516,3 +517,19 @@ int ieee80211_tdls_oper(struct wiphy *wiphy, struct net_device *dev,
mutex_unlock(&local->mtx);
return ret;
}
+
+void ieee80211_tdls_oper_request(struct ieee80211_vif *vif, const u8 *peer,
+ enum nl80211_tdls_operation oper,
+ u16 reason_code, gfp_t gfp)
+{
+ struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif);
+
+ if (vif->type != NL80211_IFTYPE_STATION || !vif->bss_conf.assoc) {
+ sdata_err(sdata, "Discarding TDLS oper %d - not STA or disconnected\n",
+ oper);
+ return;
+ }
+
+ cfg80211_tdls_oper_request(sdata->dev, peer, oper, reason_code, gfp);
+}
+EXPORT_SYMBOL(ieee80211_tdls_oper_request);
--
1.9.1