This series adds HT and QoS support for mac80211 drivers implementing TDLS.
It also includes some error path fixes, as well as a compatibility fix for
older usermode (as requested by Jouni Malinen).
A couple of wpa_supplicant patches are also required to properly propagate
WMM capability to kernel and fix a recently introduced issue into the hwsim
TDLS tests. These will be sent soon.
Arik Nemtsov (14):
mac80211: track TDLS initiator internally
mac80211: fix error path for TDLS setup
mac80211: consolidate TDLS IE treatment
mac80211: split extra TDLS IEs in setup frames
mac80211: avoid adding some IEs on TDLS setup failure packets
mac80211: set TDLS capab to zero on failure frames
mac80211: add QoS IE during TDLS setup start
mac80211: add TDLS QoS param IE on setup-confirm
mac80211: move TDLS data to mgd private part
mac80211: support HT for TDLS stations
mac80211: set Rx highest rate in ht_cap
mac80211: disable VHT for TDLS
cfg80211: fix TDLS setup with VHT peers
mac80211: ignore frames between TDLS peers when operating as AP
Liad Kaufman (1):
mac80211: make sure TDLS teardown packet is sent on time
include/linux/ieee80211.h | 20 ++
net/mac80211/ht.c | 10 +-
net/mac80211/ibss.c | 13 +-
net/mac80211/ieee80211_i.h | 7 +-
net/mac80211/iface.c | 2 -
net/mac80211/mlme.c | 14 +-
net/mac80211/rx.c | 8 +
net/mac80211/sta_info.h | 3 +
net/mac80211/tdls.c | 459 ++++++++++++++++++++++++++++++++++++++-------
net/mac80211/util.c | 15 ++
net/mac80211/vht.c | 4 +
net/wireless/nl80211.c | 3 +-
12 files changed, 462 insertions(+), 96 deletions(-)
--
1.9.1
Infer the TDLS initiator and track it in mac80211 via a STA flag. This
avoids breaking old userspace that doesn't pass it via nl80211 APIs.
The only case where userspace will need to pass the initiator is when the
STA is removed due to unreachability before a teardown packet is sent.
Support for unreachability was only recently added to wpa_supplicant, so
it won't be a problem in practice.
Signed-off-by: Arik Nemtsov <[email protected]>
Reviewed-by: Johannes Berg <[email protected]>
---
net/mac80211/sta_info.h | 3 +++
net/mac80211/tdls.c | 35 ++++++++++++++++++++++++-----------
2 files changed, 27 insertions(+), 11 deletions(-)
diff --git a/net/mac80211/sta_info.h b/net/mac80211/sta_info.h
index 2a04361..e37f009 100644
--- a/net/mac80211/sta_info.h
+++ b/net/mac80211/sta_info.h
@@ -47,6 +47,8 @@
* @WLAN_STA_TDLS_PEER: Station is a TDLS peer.
* @WLAN_STA_TDLS_PEER_AUTH: This TDLS peer is authorized to send direct
* packets. This means the link is enabled.
+ * @WLAN_STA_TDLS_INITIATOR: We are the initiator of the TDLS link with this
+ * station.
* @WLAN_STA_UAPSD: Station requested unscheduled SP while driver was
* keeping station in power-save mode, reply when the driver
* unblocks the station.
@@ -76,6 +78,7 @@ enum ieee80211_sta_info_flags {
WLAN_STA_PSPOLL,
WLAN_STA_TDLS_PEER,
WLAN_STA_TDLS_PEER_AUTH,
+ WLAN_STA_TDLS_INITIATOR,
WLAN_STA_UAPSD,
WLAN_STA_SP,
WLAN_STA_4ADDR_EVENT,
diff --git a/net/mac80211/tdls.c b/net/mac80211/tdls.c
index f718533..b01b310 100644
--- a/net/mac80211/tdls.c
+++ b/net/mac80211/tdls.c
@@ -203,6 +203,7 @@ ieee80211_tdls_prep_mgmt_packet(struct wiphy *wiphy, struct net_device *dev,
struct sk_buff *skb = NULL;
bool send_direct;
const u8 *init_addr, *rsp_addr;
+ struct sta_info *sta;
int ret;
skb = dev_alloc_skb(local->hw.extra_tx_headroom +
@@ -245,32 +246,40 @@ 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);
- /* sanity check for initiator */
+ rcu_read_lock();
+ sta = sta_info_get(sdata, peer);
+
+ /* infer the initiator if we can, to support old userspace */
switch (action_code) {
case WLAN_TDLS_SETUP_REQUEST:
+ if (sta)
+ set_sta_flag(sta, WLAN_STA_TDLS_INITIATOR);
+ /* fall-through */
case WLAN_TDLS_SETUP_CONFIRM:
case WLAN_TDLS_DISCOVERY_REQUEST:
- if (!initiator) {
- ret = -EINVAL;
- goto fail;
- }
+ initiator = true;
break;
case WLAN_TDLS_SETUP_RESPONSE:
+ /*
+ * In some testing scenarios, we send a request and response.
+ * Make the last packet sent take effect for the initiator
+ * value.
+ */
+ if (sta)
+ clear_sta_flag(sta, WLAN_STA_TDLS_INITIATOR);
+ /* fall-through */
case WLAN_PUB_ACTION_TDLS_DISCOVER_RES:
- if (initiator) {
- ret = -EINVAL;
- goto fail;
- }
+ initiator = false;
break;
case WLAN_TDLS_TEARDOWN:
/* any value is ok */
break;
default:
ret = -ENOTSUPP;
- goto fail;
+ break;
}
- if (initiator) {
+ if (initiator || (sta && test_sta_flag(sta, WLAN_STA_TDLS_INITIATOR))) {
init_addr = sdata->vif.addr;
rsp_addr = peer;
} else {
@@ -278,6 +287,10 @@ ieee80211_tdls_prep_mgmt_packet(struct wiphy *wiphy, struct net_device *dev,
rsp_addr = sdata->vif.addr;
}
+ rcu_read_unlock();
+ if (ret < 0)
+ goto fail;
+
ieee80211_tdls_add_link_ie(skb, init_addr, rsp_addr,
sdata->u.mgd.bssid);
--
1.9.1
We can only be a station for TDLS connections. Also fix a bug where
a delayed work could be left scheduled if the station interface was
brought down during TDLS setup.
Signed-off-by: Arik Nemtsov <[email protected]>
Reviewed-by: Emmanuel Grumbach <[email protected]>
---
net/mac80211/ieee80211_i.h | 6 +++---
net/mac80211/iface.c | 2 --
net/mac80211/mlme.c | 3 +++
net/mac80211/tdls.c | 28 ++++++++++++++--------------
4 files changed, 20 insertions(+), 19 deletions(-)
diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h
index cb87476..0d8539c 100644
--- a/net/mac80211/ieee80211_i.h
+++ b/net/mac80211/ieee80211_i.h
@@ -503,6 +503,9 @@ struct ieee80211_if_managed {
struct ieee80211_ht_cap ht_capa_mask; /* Valid parts of ht_capa */
struct ieee80211_vht_cap vht_capa; /* configured VHT overrides */
struct ieee80211_vht_cap vht_capa_mask; /* Valid parts of vht_capa */
+
+ u8 tdls_peer[ETH_ALEN] __aligned(2);
+ struct delayed_work tdls_peer_del_work;
};
struct ieee80211_if_ibss {
@@ -815,9 +818,6 @@ 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
diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c
index bbf51b2..2a12b8a 100644
--- a/net/mac80211/iface.c
+++ b/net/mac80211/iface.c
@@ -1672,8 +1672,6 @@ 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/mlme.c b/net/mac80211/mlme.c
index d863ff8..fcc0748 100644
--- a/net/mac80211/mlme.c
+++ b/net/mac80211/mlme.c
@@ -3713,6 +3713,8 @@ void ieee80211_sta_setup_sdata(struct ieee80211_sub_if_data *sdata)
INIT_WORK(&ifmgd->csa_connection_drop_work,
ieee80211_csa_connection_drop_work);
INIT_WORK(&ifmgd->request_smps_work, ieee80211_request_smps_mgd_work);
+ INIT_DELAYED_WORK(&ifmgd->tdls_peer_del_work,
+ ieee80211_tdls_peer_del_work);
setup_timer(&ifmgd->timer, ieee80211_sta_timer,
(unsigned long) sdata);
setup_timer(&ifmgd->bcn_mon_timer, ieee80211_sta_bcn_mon_timer,
@@ -4576,6 +4578,7 @@ void ieee80211_mgd_stop(struct ieee80211_sub_if_data *sdata)
cancel_work_sync(&ifmgd->request_smps_work);
cancel_work_sync(&ifmgd->csa_connection_drop_work);
cancel_work_sync(&ifmgd->chswitch_work);
+ cancel_delayed_work_sync(&ifmgd->tdls_peer_del_work);
sdata_lock(sdata);
if (ifmgd->assoc_data) {
diff --git a/net/mac80211/tdls.c b/net/mac80211/tdls.c
index 72eebea..c59b8f4 100644
--- a/net/mac80211/tdls.c
+++ b/net/mac80211/tdls.c
@@ -22,14 +22,14 @@ void ieee80211_tdls_peer_del_work(struct work_struct *wk)
struct ieee80211_local *local;
sdata = container_of(wk, struct ieee80211_sub_if_data,
- tdls_peer_del_work.work);
+ u.mgd.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);
+ if (!is_zero_ether_addr(sdata->u.mgd.tdls_peer)) {
+ tdls_dbg(sdata, "TDLS del peer %pM\n", sdata->u.mgd.tdls_peer);
+ sta_info_destroy_addr(sdata, sdata->u.mgd.tdls_peer);
+ eth_zero_addr(sdata->u.mgd.tdls_peer);
}
mutex_unlock(&local->mtx);
}
@@ -561,8 +561,8 @@ ieee80211_tdls_mgmt_setup(struct wiphy *wiphy, struct net_device *dev,
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)) {
+ if (!is_zero_ether_addr(sdata->u.mgd.tdls_peer) &&
+ !ether_addr_equal(sdata->u.mgd.tdls_peer, peer)) {
ret = -EBUSY;
goto exit;
}
@@ -593,9 +593,9 @@ ieee80211_tdls_mgmt_setup(struct wiphy *wiphy, struct net_device *dev,
if (ret < 0)
goto exit;
- memcpy(sdata->tdls_peer, peer, ETH_ALEN);
+ memcpy(sdata->u.mgd.tdls_peer, peer, ETH_ALEN);
ieee80211_queue_delayed_work(&sdata->local->hw,
- &sdata->tdls_peer_del_work,
+ &sdata->u.mgd.tdls_peer_del_work,
TDLS_PEER_SETUP_TIMEOUT);
exit:
@@ -751,8 +751,8 @@ int ieee80211_tdls_oper(struct wiphy *wiphy, struct net_device *dev,
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));
+ WARN_ON_ONCE(is_zero_ether_addr(sdata->u.mgd.tdls_peer) ||
+ !ether_addr_equal(sdata->u.mgd.tdls_peer, peer));
ret = 0;
break;
case NL80211_TDLS_DISABLE_LINK:
@@ -766,9 +766,9 @@ int ieee80211_tdls_oper(struct wiphy *wiphy, struct net_device *dev,
break;
}
- if (ret == 0 && ether_addr_equal(sdata->tdls_peer, peer)) {
- cancel_delayed_work(&sdata->tdls_peer_del_work);
- eth_zero_addr(sdata->tdls_peer);
+ if (ret == 0 && ether_addr_equal(sdata->u.mgd.tdls_peer, peer)) {
+ cancel_delayed_work(&sdata->u.mgd.tdls_peer_del_work);
+ eth_zero_addr(sdata->u.mgd.tdls_peer);
}
mutex_unlock(&local->mtx);
--
1.9.1
Add all information elements for TDLS discovery and setup in the same
function.
Signed-off-by: Arik Nemtsov <[email protected]>
Reviewed-by: Liad Kaufman <[email protected]>
Reviewed-by: Johannes Berg <[email protected]>
---
net/mac80211/tdls.c | 69 +++++++++++++++++++++++++++++++----------------------
1 file changed, 41 insertions(+), 28 deletions(-)
diff --git a/net/mac80211/tdls.c b/net/mac80211/tdls.c
index 53c235d..b61448a 100644
--- a/net/mac80211/tdls.c
+++ b/net/mac80211/tdls.c
@@ -78,13 +78,49 @@ static void ieee80211_tdls_add_link_ie(struct sk_buff *skb, const u8 *src_addr,
memcpy(lnkid->resp_sta, peer, ETH_ALEN);
}
+static void ieee80211_tdls_add_ies(struct ieee80211_sub_if_data *sdata,
+ struct sk_buff *skb, const u8 *peer,
+ u8 action_code, bool initiator,
+ const u8 *extra_ies, size_t extra_ies_len)
+{
+ const u8 *init_addr, *rsp_addr;
+ enum ieee80211_band band = ieee80211_get_sdata_band(sdata);
+
+ switch (action_code) {
+ case WLAN_TDLS_SETUP_REQUEST:
+ case WLAN_TDLS_SETUP_RESPONSE:
+ case WLAN_PUB_ACTION_TDLS_DISCOVER_RES:
+ ieee80211_add_srates_ie(sdata, skb, false, band);
+ ieee80211_add_ext_srates_ie(sdata, skb, false, band);
+ ieee80211_tdls_add_ext_capab(skb);
+ break;
+ case WLAN_TDLS_SETUP_CONFIRM:
+ case WLAN_TDLS_TEARDOWN:
+ case WLAN_TDLS_DISCOVERY_REQUEST:
+ break;
+ }
+
+ if (initiator) {
+ init_addr = sdata->vif.addr;
+ rsp_addr = peer;
+ } else {
+ init_addr = peer;
+ rsp_addr = sdata->vif.addr;
+ }
+
+ if (extra_ies_len)
+ memcpy(skb_put(skb, extra_ies_len), extra_ies, extra_ies_len);
+
+ ieee80211_tdls_add_link_ie(skb, init_addr, rsp_addr,
+ sdata->u.mgd.bssid);
+}
+
static int
ieee80211_prep_tdls_encap_data(struct wiphy *wiphy, struct net_device *dev,
const u8 *peer, u8 action_code, u8 dialog_token,
u16 status_code, struct sk_buff *skb)
{
struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
- enum ieee80211_band band = ieee80211_get_sdata_band(sdata);
struct ieee80211_tdls_data *tf;
tf = (void *)skb_put(skb, offsetof(struct ieee80211_tdls_data, u));
@@ -103,10 +139,6 @@ ieee80211_prep_tdls_encap_data(struct wiphy *wiphy, struct net_device *dev,
tf->u.setup_req.dialog_token = dialog_token;
tf->u.setup_req.capability =
cpu_to_le16(ieee80211_get_tdls_sta_capab(sdata));
-
- ieee80211_add_srates_ie(sdata, skb, false, band);
- ieee80211_add_ext_srates_ie(sdata, skb, false, band);
- ieee80211_tdls_add_ext_capab(skb);
break;
case WLAN_TDLS_SETUP_RESPONSE:
tf->category = WLAN_CATEGORY_TDLS;
@@ -117,10 +149,6 @@ ieee80211_prep_tdls_encap_data(struct wiphy *wiphy, struct net_device *dev,
tf->u.setup_resp.dialog_token = dialog_token;
tf->u.setup_resp.capability =
cpu_to_le16(ieee80211_get_tdls_sta_capab(sdata));
-
- ieee80211_add_srates_ie(sdata, skb, false, band);
- ieee80211_add_ext_srates_ie(sdata, skb, false, band);
- ieee80211_tdls_add_ext_capab(skb);
break;
case WLAN_TDLS_SETUP_CONFIRM:
tf->category = WLAN_CATEGORY_TDLS;
@@ -157,7 +185,6 @@ ieee80211_prep_tdls_direct(struct wiphy *wiphy, struct net_device *dev,
u16 status_code, struct sk_buff *skb)
{
struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
- enum ieee80211_band band = ieee80211_get_sdata_band(sdata);
struct ieee80211_mgmt *mgmt;
mgmt = (void *)skb_put(skb, 24);
@@ -179,10 +206,6 @@ ieee80211_prep_tdls_direct(struct wiphy *wiphy, struct net_device *dev,
dialog_token;
mgmt->u.action.u.tdls_discover_resp.capability =
cpu_to_le16(ieee80211_get_tdls_sta_capab(sdata));
-
- ieee80211_add_srates_ie(sdata, skb, false, band);
- ieee80211_add_ext_srates_ie(sdata, skb, false, band);
- ieee80211_tdls_add_ext_capab(skb);
break;
default:
return -EINVAL;
@@ -202,7 +225,6 @@ ieee80211_tdls_prep_mgmt_packet(struct wiphy *wiphy, struct net_device *dev,
struct ieee80211_local *local = sdata->local;
struct sk_buff *skb = NULL;
bool send_direct;
- const u8 *init_addr, *rsp_addr;
struct sta_info *sta;
int ret;
@@ -243,9 +265,6 @@ ieee80211_tdls_prep_mgmt_packet(struct wiphy *wiphy, struct net_device *dev,
if (ret < 0)
goto fail;
- if (extra_ies_len)
- memcpy(skb_put(skb, extra_ies_len), extra_ies, extra_ies_len);
-
rcu_read_lock();
sta = sta_info_get(sdata, peer);
@@ -279,21 +298,15 @@ ieee80211_tdls_prep_mgmt_packet(struct wiphy *wiphy, struct net_device *dev,
break;
}
- if (initiator || (sta && test_sta_flag(sta, WLAN_STA_TDLS_INITIATOR))) {
- init_addr = sdata->vif.addr;
- rsp_addr = peer;
- } else {
- init_addr = peer;
- rsp_addr = sdata->vif.addr;
- }
+ if (sta && test_sta_flag(sta, WLAN_STA_TDLS_INITIATOR))
+ initiator = true;
rcu_read_unlock();
if (ret < 0)
goto fail;
- ieee80211_tdls_add_link_ie(skb, init_addr, rsp_addr,
- sdata->u.mgd.bssid);
-
+ ieee80211_tdls_add_ies(sdata, skb, peer, action_code, initiator,
+ extra_ies, extra_ies_len);
if (send_direct) {
ieee80211_tx_skb(sdata, skb);
return 0;
--
1.9.1
When TDLS QoS is supported by the the peer and the local card, add
the WMM parameter IE to the setup-confirm frame. Take the QoS settings
from the current AP, or if unsupported, use the default values from
the specification. This behavior is mandated by IEEE802.11-2012 section
10.22.4.
Signed-off-by: Arik Nemtsov <[email protected]>
Reviewed-by: Liad Kaufman <[email protected]>
Reviewed-by: Johannes Berg <[email protected]>
---
include/linux/ieee80211.h | 20 ++++++++
net/mac80211/tdls.c | 124 ++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 144 insertions(+)
diff --git a/include/linux/ieee80211.h b/include/linux/ieee80211.h
index 75d17e1..63ab3873 100644
--- a/include/linux/ieee80211.h
+++ b/include/linux/ieee80211.h
@@ -1001,6 +1001,26 @@ struct ieee80211_vendor_ie {
u8 oui_type;
} __packed;
+struct ieee80211_wmm_ac_param {
+ u8 aci_aifsn; /* AIFSN, ACM, ACI */
+ u8 cw; /* ECWmin, ECWmax (CW = 2^ECW - 1) */
+ __le16 txop_limit;
+} __packed;
+
+struct ieee80211_wmm_param_ie {
+ u8 element_id; /* Element ID: 221 (0xdd); */
+ u8 len; /* Length: 24 */
+ /* required fields for WMM version 1 */
+ u8 oui[3]; /* 00:50:f2 */
+ u8 oui_type; /* 2 */
+ u8 oui_subtype; /* 1 */
+ u8 version; /* 1 for WMM version 1.0 */
+ u8 qos_info; /* AP/STA specific QoS info */
+ u8 reserved; /* 0 */
+ /* AC_BE, AC_BK, AC_VI, AC_VO */
+ struct ieee80211_wmm_ac_param ac[4];
+} __packed;
+
/* Control frames */
struct ieee80211_rts {
__le16 frame_control;
diff --git a/net/mac80211/tdls.c b/net/mac80211/tdls.c
index bfd8fc4..72eebea 100644
--- a/net/mac80211/tdls.c
+++ b/net/mac80211/tdls.c
@@ -8,6 +8,7 @@
*/
#include <linux/ieee80211.h>
+#include <linux/log2.h>
#include <net/cfg80211.h>
#include "ieee80211_i.h"
#include "driver-ops.h"
@@ -93,6 +94,74 @@ static void ieee80211_tdls_add_link_ie(struct ieee80211_sub_if_data *sdata,
memcpy(lnkid->resp_sta, rsp_addr, ETH_ALEN);
}
+/* translate numbering in the WMM parameter IE to the mac80211 notation */
+static enum ieee80211_ac_numbers ieee80211_ac_from_wmm(int ac)
+{
+ switch (ac) {
+ default:
+ WARN_ON_ONCE(1);
+ case 0:
+ return IEEE80211_AC_BE;
+ case 1:
+ return IEEE80211_AC_BK;
+ case 2:
+ return IEEE80211_AC_VI;
+ case 3:
+ return IEEE80211_AC_VO;
+ }
+}
+
+static u8 ieee80211_wmm_aci_aifsn(int aifsn, bool acm, int aci)
+{
+ u8 ret;
+
+ ret = aifsn & 0x0f;
+ if (acm)
+ ret |= 0x10;
+ ret |= (aci << 5) & 0x60;
+ return ret;
+}
+
+static u8 ieee80211_wmm_ecw(u16 cw_min, u16 cw_max)
+{
+ return ((ilog2(cw_min + 1) << 0x0) & 0x0f) |
+ ((ilog2(cw_max + 1) << 0x4) & 0xf0);
+}
+
+static void ieee80211_tdls_add_wmm_param_ie(struct ieee80211_sub_if_data *sdata,
+ struct sk_buff *skb)
+{
+ struct ieee80211_wmm_param_ie *wmm;
+ struct ieee80211_tx_queue_params *txq;
+ int i;
+
+ wmm = (void *)skb_put(skb, sizeof(*wmm));
+ memset(wmm, 0, sizeof(*wmm));
+
+ wmm->element_id = WLAN_EID_VENDOR_SPECIFIC;
+ wmm->len = sizeof(*wmm) - 2;
+
+ wmm->oui[0] = 0x00; /* Microsoft OUI 00:50:F2 */
+ wmm->oui[1] = 0x50;
+ wmm->oui[2] = 0xf2;
+ wmm->oui_type = 2; /* WME */
+ wmm->oui_subtype = 1; /* WME param */
+ wmm->version = 1; /* WME ver */
+ wmm->qos_info = 0; /* U-APSD not in use */
+
+ /*
+ * Use the EDCA parameters defined for the BSS, or default if the AP
+ * doesn't support it, as mandated by 802.11-2012 section 10.22.4
+ */
+ for (i = 0; i < IEEE80211_NUM_ACS; i++) {
+ txq = &sdata->tx_conf[ieee80211_ac_from_wmm(i)];
+ wmm->ac[i].aci_aifsn = ieee80211_wmm_aci_aifsn(txq->aifs,
+ txq->acm, i);
+ wmm->ac[i].cw = ieee80211_wmm_ecw(txq->cw_min, txq->cw_max);
+ wmm->ac[i].txop_limit = cpu_to_le16(txq->txop);
+ }
+}
+
static void
ieee80211_tdls_add_setup_start_ies(struct ieee80211_sub_if_data *sdata,
struct sk_buff *skb, const u8 *peer,
@@ -165,6 +234,56 @@ ieee80211_tdls_add_setup_start_ies(struct ieee80211_sub_if_data *sdata,
ieee80211_tdls_add_link_ie(sdata, skb, peer, initiator);
}
+static void
+ieee80211_tdls_add_setup_cfm_ies(struct ieee80211_sub_if_data *sdata,
+ struct sk_buff *skb, const u8 *peer,
+ bool initiator, const u8 *extra_ies,
+ size_t extra_ies_len)
+{
+ struct ieee80211_local *local = sdata->local;
+ size_t offset = 0, noffset;
+ struct sta_info *sta;
+ u8 *pos;
+
+ rcu_read_lock();
+
+ sta = sta_info_get(sdata, peer);
+ if (WARN_ON_ONCE(!sta)) {
+ rcu_read_unlock();
+ return;
+ }
+
+ /* add any custom IEs that go before the QoS IE */
+ if (extra_ies_len) {
+ static const u8 before_qos[] = {
+ WLAN_EID_RSN,
+ };
+ noffset = ieee80211_ie_split(extra_ies, extra_ies_len,
+ before_qos,
+ ARRAY_SIZE(before_qos),
+ offset);
+ pos = skb_put(skb, noffset - offset);
+ memcpy(pos, extra_ies + offset, noffset - offset);
+ offset = noffset;
+ }
+
+ /* add the QoS param IE if both the peer and we support it */
+ if (local->hw.queues >= IEEE80211_NUM_ACS &&
+ test_sta_flag(sta, WLAN_STA_WME))
+ ieee80211_tdls_add_wmm_param_ie(sdata, skb);
+
+ /* add any remaining IEs */
+ if (extra_ies_len) {
+ noffset = extra_ies_len;
+ pos = skb_put(skb, noffset - offset);
+ memcpy(pos, extra_ies + offset, noffset - offset);
+ }
+
+ ieee80211_tdls_add_link_ie(sdata, skb, peer, initiator);
+
+ rcu_read_unlock();
+}
+
static void ieee80211_tdls_add_ies(struct ieee80211_sub_if_data *sdata,
struct sk_buff *skb, const u8 *peer,
u8 action_code, u16 status_code,
@@ -183,6 +302,11 @@ static void ieee80211_tdls_add_ies(struct ieee80211_sub_if_data *sdata,
extra_ies_len);
break;
case WLAN_TDLS_SETUP_CONFIRM:
+ if (status_code == 0)
+ ieee80211_tdls_add_setup_cfm_ies(sdata, skb, peer,
+ initiator, extra_ies,
+ extra_ies_len);
+ break;
case WLAN_TDLS_TEARDOWN:
case WLAN_TDLS_DISCOVERY_REQUEST:
if (extra_ies_len)
--
1.9.1
Most setup-specific information elements are not to be added when a
setup frame is sent with an error status code.
Signed-off-by: Arik Nemtsov <[email protected]>
Reviewed-by: Liad Kaufman <[email protected]>
Reviewed-by: Johannes Berg <[email protected]>
---
net/mac80211/tdls.c | 58 +++++++++++++++++++++++++++++------------------------
1 file changed, 32 insertions(+), 26 deletions(-)
diff --git a/net/mac80211/tdls.c b/net/mac80211/tdls.c
index 8d6c928..99d5ed3 100644
--- a/net/mac80211/tdls.c
+++ b/net/mac80211/tdls.c
@@ -63,26 +63,36 @@ static u16 ieee80211_get_tdls_sta_capab(struct ieee80211_sub_if_data *sdata)
return capab;
}
-static void ieee80211_tdls_add_link_ie(struct sk_buff *skb, const u8 *src_addr,
- const u8 *peer, const u8 *bssid)
+static void ieee80211_tdls_add_link_ie(struct ieee80211_sub_if_data *sdata,
+ struct sk_buff *skb, const u8 *peer,
+ bool initiator)
{
struct ieee80211_tdls_lnkie *lnkid;
+ const u8 *init_addr, *rsp_addr;
+
+ if (initiator) {
+ init_addr = sdata->vif.addr;
+ rsp_addr = peer;
+ } else {
+ init_addr = peer;
+ rsp_addr = sdata->vif.addr;
+ }
lnkid = (void *)skb_put(skb, sizeof(struct ieee80211_tdls_lnkie));
lnkid->ie_type = WLAN_EID_LINK_ID;
lnkid->ie_len = sizeof(struct ieee80211_tdls_lnkie) - 2;
- memcpy(lnkid->bssid, bssid, ETH_ALEN);
- memcpy(lnkid->init_sta, src_addr, ETH_ALEN);
- memcpy(lnkid->resp_sta, peer, ETH_ALEN);
+ memcpy(lnkid->bssid, sdata->u.mgd.bssid, ETH_ALEN);
+ memcpy(lnkid->init_sta, init_addr, ETH_ALEN);
+ memcpy(lnkid->resp_sta, rsp_addr, ETH_ALEN);
}
static void
ieee80211_tdls_add_setup_start_ies(struct ieee80211_sub_if_data *sdata,
struct sk_buff *skb, const u8 *peer,
- u8 action_code, const u8 *extra_ies,
- size_t extra_ies_len)
+ u8 action_code, bool initiator,
+ const u8 *extra_ies, size_t extra_ies_len)
{
enum ieee80211_band band = ieee80211_get_sdata_band(sdata);
size_t offset = 0, noffset;
@@ -140,22 +150,26 @@ ieee80211_tdls_add_setup_start_ies(struct ieee80211_sub_if_data *sdata,
pos = skb_put(skb, noffset - offset);
memcpy(pos, extra_ies + offset, noffset - offset);
}
+
+ ieee80211_tdls_add_link_ie(sdata, skb, peer, initiator);
}
static void ieee80211_tdls_add_ies(struct ieee80211_sub_if_data *sdata,
struct sk_buff *skb, const u8 *peer,
- u8 action_code, bool initiator,
- const u8 *extra_ies, size_t extra_ies_len)
+ u8 action_code, u16 status_code,
+ bool initiator, const u8 *extra_ies,
+ size_t extra_ies_len)
{
- const u8 *init_addr, *rsp_addr;
-
switch (action_code) {
case WLAN_TDLS_SETUP_REQUEST:
case WLAN_TDLS_SETUP_RESPONSE:
case WLAN_PUB_ACTION_TDLS_DISCOVER_RES:
- ieee80211_tdls_add_setup_start_ies(sdata, skb, peer,
- action_code, extra_ies,
- extra_ies_len);
+ if (status_code == 0)
+ ieee80211_tdls_add_setup_start_ies(sdata, skb, peer,
+ action_code,
+ initiator,
+ extra_ies,
+ extra_ies_len);
break;
case WLAN_TDLS_SETUP_CONFIRM:
case WLAN_TDLS_TEARDOWN:
@@ -163,19 +177,11 @@ static void ieee80211_tdls_add_ies(struct ieee80211_sub_if_data *sdata,
if (extra_ies_len)
memcpy(skb_put(skb, extra_ies_len), extra_ies,
extra_ies_len);
+ if (status_code == 0 || action_code == WLAN_TDLS_TEARDOWN)
+ ieee80211_tdls_add_link_ie(sdata, skb, peer, initiator);
break;
}
- 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);
}
static int
@@ -368,8 +374,8 @@ ieee80211_tdls_prep_mgmt_packet(struct wiphy *wiphy, struct net_device *dev,
if (ret < 0)
goto fail;
- ieee80211_tdls_add_ies(sdata, skb, peer, action_code, initiator,
- extra_ies, extra_ies_len);
+ ieee80211_tdls_add_ies(sdata, skb, peer, action_code, status_code,
+ initiator, extra_ies, extra_ies_len);
if (send_direct) {
ieee80211_tx_skb(sdata, skb);
return 0;
--
1.9.1
When sending setup-failure frames, set the capability field to zero, as
mandated by the specification (IEEE802.11-2012 8.5.13).
Signed-off-by: Arik Nemtsov <[email protected]>
Reviewed-by: Liad Kaufman <[email protected]>
Reviewed-by: Johannes Berg <[email protected]>
---
net/mac80211/tdls.c | 16 ++++++++++++----
1 file changed, 12 insertions(+), 4 deletions(-)
diff --git a/net/mac80211/tdls.c b/net/mac80211/tdls.c
index 99d5ed3..398a413 100644
--- a/net/mac80211/tdls.c
+++ b/net/mac80211/tdls.c
@@ -46,11 +46,16 @@ static void ieee80211_tdls_add_ext_capab(struct sk_buff *skb)
*pos++ = WLAN_EXT_CAPA5_TDLS_ENABLED;
}
-static u16 ieee80211_get_tdls_sta_capab(struct ieee80211_sub_if_data *sdata)
+static u16 ieee80211_get_tdls_sta_capab(struct ieee80211_sub_if_data *sdata,
+ u16 status_code)
{
struct ieee80211_local *local = sdata->local;
u16 capab;
+ /* The capability will be 0 when sending a failure code */
+ if (status_code != 0)
+ return 0;
+
capab = 0;
if (ieee80211_get_sdata_band(sdata) != IEEE80211_BAND_2GHZ)
return capab;
@@ -207,7 +212,8 @@ ieee80211_prep_tdls_encap_data(struct wiphy *wiphy, struct net_device *dev,
skb_put(skb, sizeof(tf->u.setup_req));
tf->u.setup_req.dialog_token = dialog_token;
tf->u.setup_req.capability =
- cpu_to_le16(ieee80211_get_tdls_sta_capab(sdata));
+ cpu_to_le16(ieee80211_get_tdls_sta_capab(sdata,
+ status_code));
break;
case WLAN_TDLS_SETUP_RESPONSE:
tf->category = WLAN_CATEGORY_TDLS;
@@ -217,7 +223,8 @@ ieee80211_prep_tdls_encap_data(struct wiphy *wiphy, struct net_device *dev,
tf->u.setup_resp.status_code = cpu_to_le16(status_code);
tf->u.setup_resp.dialog_token = dialog_token;
tf->u.setup_resp.capability =
- cpu_to_le16(ieee80211_get_tdls_sta_capab(sdata));
+ cpu_to_le16(ieee80211_get_tdls_sta_capab(sdata,
+ status_code));
break;
case WLAN_TDLS_SETUP_CONFIRM:
tf->category = WLAN_CATEGORY_TDLS;
@@ -274,7 +281,8 @@ ieee80211_prep_tdls_direct(struct wiphy *wiphy, struct net_device *dev,
mgmt->u.action.u.tdls_discover_resp.dialog_token =
dialog_token;
mgmt->u.action.u.tdls_discover_resp.capability =
- cpu_to_le16(ieee80211_get_tdls_sta_capab(sdata));
+ cpu_to_le16(ieee80211_get_tdls_sta_capab(sdata,
+ status_code));
break;
default:
return -EINVAL;
--
1.9.1
When building TDLS setup frames, use the IE order mandates in the
specification, splitting extra IEs coming from usermode.
Signed-off-by: Arik Nemtsov <[email protected]>
Reviewed-by: Johannes Berg <[email protected]>
---
net/mac80211/tdls.c | 77 ++++++++++++++++++++++++++++++++++++++++++++++++-----
1 file changed, 70 insertions(+), 7 deletions(-)
diff --git a/net/mac80211/tdls.c b/net/mac80211/tdls.c
index b61448a..8d6c928 100644
--- a/net/mac80211/tdls.c
+++ b/net/mac80211/tdls.c
@@ -78,25 +78,91 @@ static void ieee80211_tdls_add_link_ie(struct sk_buff *skb, const u8 *src_addr,
memcpy(lnkid->resp_sta, peer, ETH_ALEN);
}
+static void
+ieee80211_tdls_add_setup_start_ies(struct ieee80211_sub_if_data *sdata,
+ struct sk_buff *skb, const u8 *peer,
+ u8 action_code, const u8 *extra_ies,
+ size_t extra_ies_len)
+{
+ enum ieee80211_band band = ieee80211_get_sdata_band(sdata);
+ size_t offset = 0, noffset;
+ u8 *pos;
+
+ ieee80211_add_srates_ie(sdata, skb, false, band);
+ ieee80211_add_ext_srates_ie(sdata, skb, false, band);
+
+ /* add any custom IEs that go before Extended Capabilities */
+ if (extra_ies_len) {
+ static const u8 before_ext_cap[] = {
+ WLAN_EID_SUPP_RATES,
+ WLAN_EID_COUNTRY,
+ WLAN_EID_EXT_SUPP_RATES,
+ WLAN_EID_SUPPORTED_CHANNELS,
+ WLAN_EID_RSN,
+ };
+ noffset = ieee80211_ie_split(extra_ies, extra_ies_len,
+ before_ext_cap,
+ ARRAY_SIZE(before_ext_cap),
+ offset);
+ pos = skb_put(skb, noffset - offset);
+ memcpy(pos, extra_ies + offset, noffset - offset);
+ offset = noffset;
+ }
+
+ ieee80211_tdls_add_ext_capab(skb);
+
+ /* add any custom IEs that go before HT capabilities */
+ if (extra_ies_len) {
+ static const u8 before_ht_cap[] = {
+ WLAN_EID_SUPP_RATES,
+ WLAN_EID_COUNTRY,
+ WLAN_EID_EXT_SUPP_RATES,
+ WLAN_EID_SUPPORTED_CHANNELS,
+ WLAN_EID_RSN,
+ WLAN_EID_EXT_CAPABILITY,
+ WLAN_EID_QOS_CAPA,
+ WLAN_EID_FAST_BSS_TRANSITION,
+ WLAN_EID_TIMEOUT_INTERVAL,
+ WLAN_EID_SUPPORTED_REGULATORY_CLASSES,
+ };
+ noffset = ieee80211_ie_split(extra_ies, extra_ies_len,
+ before_ht_cap,
+ ARRAY_SIZE(before_ht_cap),
+ offset);
+ pos = skb_put(skb, noffset - offset);
+ memcpy(pos, extra_ies + offset, noffset - offset);
+ offset = noffset;
+ }
+
+ /* add any remaining IEs */
+ if (extra_ies_len) {
+ noffset = extra_ies_len;
+ pos = skb_put(skb, noffset - offset);
+ memcpy(pos, extra_ies + offset, noffset - offset);
+ }
+}
+
static void ieee80211_tdls_add_ies(struct ieee80211_sub_if_data *sdata,
struct sk_buff *skb, const u8 *peer,
u8 action_code, bool initiator,
const u8 *extra_ies, size_t extra_ies_len)
{
const u8 *init_addr, *rsp_addr;
- enum ieee80211_band band = ieee80211_get_sdata_band(sdata);
switch (action_code) {
case WLAN_TDLS_SETUP_REQUEST:
case WLAN_TDLS_SETUP_RESPONSE:
case WLAN_PUB_ACTION_TDLS_DISCOVER_RES:
- ieee80211_add_srates_ie(sdata, skb, false, band);
- ieee80211_add_ext_srates_ie(sdata, skb, false, band);
- ieee80211_tdls_add_ext_capab(skb);
+ ieee80211_tdls_add_setup_start_ies(sdata, skb, peer,
+ action_code, extra_ies,
+ extra_ies_len);
break;
case WLAN_TDLS_SETUP_CONFIRM:
case WLAN_TDLS_TEARDOWN:
case WLAN_TDLS_DISCOVERY_REQUEST:
+ if (extra_ies_len)
+ memcpy(skb_put(skb, extra_ies_len), extra_ies,
+ extra_ies_len);
break;
}
@@ -108,9 +174,6 @@ static void ieee80211_tdls_add_ies(struct ieee80211_sub_if_data *sdata,
rsp_addr = sdata->vif.addr;
}
- if (extra_ies_len)
- memcpy(skb_put(skb, extra_ies_len), extra_ies, extra_ies_len);
-
ieee80211_tdls_add_link_ie(skb, init_addr, rsp_addr,
sdata->u.mgd.bssid);
}
--
1.9.1
From: Liad Kaufman <[email protected]>
Since the teardown packet is created while the queues are
stopped, it isn't sent immediately, but rather is pending.
To be sure that when we flush the queues prior to destroying
the station we also send this packet - the tasklet handling
pending packets is invoked to flush the packets.
Signed-off-by: Liad Kaufman <[email protected]>
Reviewed-by: ArikX Nemtsov <[email protected]>
Reviewed-by: Johannes Berg <[email protected]>
---
net/mac80211/tdls.c | 11 +++++++++++
1 file changed, 11 insertions(+)
diff --git a/net/mac80211/tdls.c b/net/mac80211/tdls.c
index 50d0e06..1b21050 100644
--- a/net/mac80211/tdls.c
+++ b/net/mac80211/tdls.c
@@ -836,6 +836,17 @@ int ieee80211_tdls_oper(struct wiphy *wiphy, struct net_device *dev,
ret = 0;
break;
case NL80211_TDLS_DISABLE_LINK:
+ /*
+ * The teardown message in ieee80211_tdls_mgmt_teardown() was
+ * created while the queues were stopped, so it might still be
+ * pending. Before flushing the queues we need to be sure the
+ * message is handled by the tasklet handling pending messages,
+ * otherwise we might start destroying the station before
+ * sending the teardown packet.
+ * Note that this only forces the tasklet to flush pendings -
+ * not to stop the tasklet from rescheduling itself.
+ */
+ tasklet_kill(&local->tx_pending_tasklet);
/* flush a potentially queued teardown packet */
ieee80211_flush_queues(local, sdata);
--
1.9.1
Add the HT capabilities and HT operation information elements to TDLS
setup packets where appropriate.
Signed-off-by: Arik Nemtsov <[email protected]>
Reviewed-by: Emmanuel Grumbach <[email protected]>
---
net/mac80211/ht.c | 7 ++---
net/mac80211/tdls.c | 88 ++++++++++++++++++++++++++++++++++++++++++++++++++---
2 files changed, 87 insertions(+), 8 deletions(-)
diff --git a/net/mac80211/ht.c b/net/mac80211/ht.c
index 15702ff..568055c 100644
--- a/net/mac80211/ht.c
+++ b/net/mac80211/ht.c
@@ -150,13 +150,12 @@ bool ieee80211_ht_cap_ie_to_sta_ht_cap(struct ieee80211_sub_if_data *sdata,
/*
* If user has specified capability over-rides, take care
- * of that if the station we're setting up is the AP that
+ * of that if the station we're setting up is the AP or TDLS peer that
* we advertised a restricted capability set to. Override
* our own capabilities and then use those below.
*/
- if ((sdata->vif.type == NL80211_IFTYPE_STATION ||
- sdata->vif.type == NL80211_IFTYPE_ADHOC) &&
- !test_sta_flag(sta, WLAN_STA_TDLS_PEER))
+ if (sdata->vif.type == NL80211_IFTYPE_STATION ||
+ sdata->vif.type == NL80211_IFTYPE_ADHOC)
ieee80211_apply_htcap_overrides(sdata, &own_cap);
/*
diff --git a/net/mac80211/tdls.c b/net/mac80211/tdls.c
index c59b8f4..50d0e06 100644
--- a/net/mac80211/tdls.c
+++ b/net/mac80211/tdls.c
@@ -170,9 +170,23 @@ ieee80211_tdls_add_setup_start_ies(struct ieee80211_sub_if_data *sdata,
{
enum ieee80211_band band = ieee80211_get_sdata_band(sdata);
struct ieee80211_local *local = sdata->local;
+ struct ieee80211_supported_band *sband;
+ struct ieee80211_sta_ht_cap ht_cap;
+ struct sta_info *sta = NULL;
size_t offset = 0, noffset;
u8 *pos;
+ rcu_read_lock();
+
+ /* we should have the peer STA if we're already responding */
+ if (action_code == WLAN_TDLS_SETUP_RESPONSE) {
+ sta = sta_info_get(sdata, peer);
+ if (WARN_ON_ONCE(!sta)) {
+ rcu_read_unlock();
+ return;
+ }
+ }
+
ieee80211_add_srates_ie(sdata, skb, false, band);
ieee80211_add_ext_srates_ie(sdata, skb, false, band);
@@ -224,6 +238,38 @@ ieee80211_tdls_add_setup_start_ies(struct ieee80211_sub_if_data *sdata,
offset = noffset;
}
+ /*
+ * with TDLS we can switch channels, and HT-caps are not necessarily
+ * the same on all bands. The specification limits the setup to a
+ * single HT-cap, so use the current band for now.
+ */
+ sband = local->hw.wiphy->bands[band];
+ memcpy(&ht_cap, &sband->ht_cap, sizeof(ht_cap));
+ if ((action_code == WLAN_TDLS_SETUP_REQUEST ||
+ action_code == WLAN_TDLS_SETUP_RESPONSE) &&
+ ht_cap.ht_supported && (!sta || sta->sta.ht_cap.ht_supported)) {
+ if (action_code == WLAN_TDLS_SETUP_REQUEST) {
+ ieee80211_apply_htcap_overrides(sdata, &ht_cap);
+
+ /* disable SMPS in TDLS initiator */
+ ht_cap.cap |= (WLAN_HT_CAP_SM_PS_DISABLED
+ << IEEE80211_HT_CAP_SM_PS_SHIFT);
+ } else {
+ /* disable SMPS in TDLS responder */
+ sta->sta.ht_cap.cap |=
+ (WLAN_HT_CAP_SM_PS_DISABLED
+ << IEEE80211_HT_CAP_SM_PS_SHIFT);
+
+ /* the peer caps are already intersected with our own */
+ memcpy(&ht_cap, &sta->sta.ht_cap, sizeof(ht_cap));
+ }
+
+ pos = skb_put(skb, sizeof(struct ieee80211_ht_cap) + 2);
+ ieee80211_ie_build_ht_cap(pos, &ht_cap, ht_cap.cap);
+ }
+
+ rcu_read_unlock();
+
/* add any remaining IEs */
if (extra_ies_len) {
noffset = extra_ies_len;
@@ -241,14 +287,16 @@ ieee80211_tdls_add_setup_cfm_ies(struct ieee80211_sub_if_data *sdata,
size_t extra_ies_len)
{
struct ieee80211_local *local = sdata->local;
+ struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
size_t offset = 0, noffset;
- struct sta_info *sta;
+ struct sta_info *sta, *ap_sta;
u8 *pos;
rcu_read_lock();
sta = sta_info_get(sdata, peer);
- if (WARN_ON_ONCE(!sta)) {
+ ap_sta = sta_info_get(sdata, ifmgd->bssid);
+ if (WARN_ON_ONCE(!sta || !ap_sta)) {
rcu_read_unlock();
return;
}
@@ -272,6 +320,38 @@ ieee80211_tdls_add_setup_cfm_ies(struct ieee80211_sub_if_data *sdata,
test_sta_flag(sta, WLAN_STA_WME))
ieee80211_tdls_add_wmm_param_ie(sdata, skb);
+ /* add any custom IEs that go before HT operation */
+ if (extra_ies_len) {
+ static const u8 before_ht_op[] = {
+ WLAN_EID_RSN,
+ WLAN_EID_QOS_CAPA,
+ WLAN_EID_FAST_BSS_TRANSITION,
+ WLAN_EID_TIMEOUT_INTERVAL,
+ };
+ noffset = ieee80211_ie_split(extra_ies, extra_ies_len,
+ before_ht_op,
+ ARRAY_SIZE(before_ht_op),
+ offset);
+ pos = skb_put(skb, noffset - offset);
+ memcpy(pos, extra_ies + offset, noffset - offset);
+ offset = noffset;
+ }
+
+ /* if HT support is only added in TDLS, we need an HT-operation IE */
+ if (!ap_sta->sta.ht_cap.ht_supported && sta->sta.ht_cap.ht_supported) {
+ struct ieee80211_chanctx_conf *chanctx_conf =
+ rcu_dereference(sdata->vif.chanctx_conf);
+ if (!WARN_ON(!chanctx_conf)) {
+ pos = skb_put(skb, 2 +
+ sizeof(struct ieee80211_ht_operation));
+ /* send an empty HT operation IE */
+ ieee80211_ie_build_ht_oper(pos, &sta->sta.ht_cap,
+ &chanctx_conf->def, 0);
+ }
+ }
+
+ rcu_read_unlock();
+
/* add any remaining IEs */
if (extra_ies_len) {
noffset = extra_ies_len;
@@ -280,8 +360,6 @@ ieee80211_tdls_add_setup_cfm_ies(struct ieee80211_sub_if_data *sdata,
}
ieee80211_tdls_add_link_ie(sdata, skb, peer, initiator);
-
- rcu_read_unlock();
}
static void ieee80211_tdls_add_ies(struct ieee80211_sub_if_data *sdata,
@@ -441,6 +519,8 @@ ieee80211_tdls_prep_mgmt_packet(struct wiphy *wiphy, struct net_device *dev,
50 + /* supported rates */
7 + /* ext capab */
26 + /* max(WMM-info, WMM-param) */
+ 2 + max(sizeof(struct ieee80211_ht_cap),
+ sizeof(struct ieee80211_ht_operation)) +
extra_ies_len +
sizeof(struct ieee80211_tdls_lnkie));
if (!skb)
--
1.9.1
TDLS VHT support requires some more information elements during setup.
While these are not there, mask out the peer's VHT capabilities so that
VHT rates are not mistakenly used.
Signed-off-by: Arik Nemtsov <[email protected]>
Reviewed-by: Emmanuel Grumbach <[email protected]>
---
net/mac80211/vht.c | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/net/mac80211/vht.c b/net/mac80211/vht.c
index 9265adf..671ce0d 100644
--- a/net/mac80211/vht.c
+++ b/net/mac80211/vht.c
@@ -129,6 +129,10 @@ ieee80211_vht_cap_ie_to_sta_vht_cap(struct ieee80211_sub_if_data *sdata,
if (!vht_cap_ie || !sband->vht_cap.vht_supported)
return;
+ /* don't support VHT for TDLS peers for now */
+ if (test_sta_flag(sta, WLAN_STA_TDLS_PEER))
+ return;
+
/*
* A VHT STA must support 40 MHz, but if we verify that here
* then we break a few things - some APs (e.g. Netgear R6300v2
--
1.9.1
On Thu, 2014-07-17 at 17:14 +0300, Arik Nemtsov wrote:
> This series adds HT and QoS support for mac80211 drivers implementing TDLS.
>
> It also includes some error path fixes, as well as a compatibility fix for
> older usermode (as requested by Jouni Malinen).
All applied, thanks.
johannes
If the AP receives actions frames destined for other peers, it may
mistakenly toggle BA-sessions from itself to a peer.
Ignore TDLS data packets as well - the AP should not handle them.
Signed-off-by: Arik Nemtsov <[email protected]>
Reviewed-by: Emmanuel Grumbach <[email protected]>
---
net/mac80211/rx.c | 8 ++++++++
1 file changed, 8 insertions(+)
diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c
index 5f572be..5a786d4 100644
--- a/net/mac80211/rx.c
+++ b/net/mac80211/rx.c
@@ -3129,6 +3129,14 @@ static bool prepare_for_handlers(struct ieee80211_rx_data *rx,
if (!ieee80211_is_beacon(hdr->frame_control))
return false;
status->rx_flags &= ~IEEE80211_RX_RA_MATCH;
+ } else if (!ieee80211_has_tods(hdr->frame_control)) {
+ /* ignore data frames to TDLS-peers */
+ if (ieee80211_is_data(hdr->frame_control))
+ return false;
+ /* ignore action frames to TDLS-peers */
+ if (ieee80211_is_action(hdr->frame_control) &&
+ !ether_addr_equal(bssid, hdr->addr1))
+ return false;
}
break;
case NL80211_IFTYPE_WDS:
--
1.9.1
Some VHT TDLS peers (Google Nexus 5) include the VHT-AID IE in their
TDLS setup request/response. Usermode passes this aid as the station
aid, causing it to fail verifiction, since this happens in the
"set_station" stage. Make an exception for the TDLS use-case.
Signed-off-by: Arik Nemtsov <[email protected]>
Reviewed-by: Emmanuel Grumbach <[email protected]>
---
net/wireless/nl80211.c | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c
index 082f5c6..df7b133 100644
--- a/net/wireless/nl80211.c
+++ b/net/wireless/nl80211.c
@@ -3814,7 +3814,8 @@ int cfg80211_check_station_change(struct wiphy *wiphy,
{
if (params->listen_interval != -1)
return -EINVAL;
- if (params->aid)
+ if (params->aid &&
+ !(params->sta_flags_set & BIT(NL80211_STA_FLAG_TDLS_PEER)))
return -EINVAL;
/* When you run into this, adjust the code below for the new flag */
--
1.9.1
If QoS is supported by the card, add an appropriate IE to TDLS setup-
request and setup-response frames.
Consolidate the setting of the WMM info IE across mac80211.
Signed-off-by: Arik Nemtsov <[email protected]>
Reviewed-by: Liad Kaufman <[email protected]>
Reviewed-by: Johannes Berg <[email protected]>
---
net/mac80211/ibss.c | 13 ++-----------
net/mac80211/ieee80211_i.h | 1 +
net/mac80211/mlme.c | 11 +----------
net/mac80211/tdls.c | 7 +++++++
net/mac80211/util.c | 15 +++++++++++++++
5 files changed, 26 insertions(+), 21 deletions(-)
diff --git a/net/mac80211/ibss.c b/net/mac80211/ibss.c
index 713485f..9713dc5 100644
--- a/net/mac80211/ibss.c
+++ b/net/mac80211/ibss.c
@@ -189,17 +189,8 @@ ieee80211_ibss_build_presp(struct ieee80211_sub_if_data *sdata,
chandef, 0);
}
- if (local->hw.queues >= IEEE80211_NUM_ACS) {
- *pos++ = WLAN_EID_VENDOR_SPECIFIC;
- *pos++ = 7; /* len */
- *pos++ = 0x00; /* Microsoft OUI 00:50:F2 */
- *pos++ = 0x50;
- *pos++ = 0xf2;
- *pos++ = 2; /* WME */
- *pos++ = 0; /* WME info */
- *pos++ = 1; /* WME ver */
- *pos++ = 0; /* U-APSD no in use */
- }
+ if (local->hw.queues >= IEEE80211_NUM_ACS)
+ pos = ieee80211_add_wmm_info_ie(pos, 0); /* U-APSD not in use */
presp->head_len = pos - presp->head;
if (WARN_ON(presp->head_len > frame_len))
diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h
index 9e025e1..cb87476 100644
--- a/net/mac80211/ieee80211_i.h
+++ b/net/mac80211/ieee80211_i.h
@@ -1824,6 +1824,7 @@ int ieee80211_add_srates_ie(struct ieee80211_sub_if_data *sdata,
int ieee80211_add_ext_srates_ie(struct ieee80211_sub_if_data *sdata,
struct sk_buff *skb, bool need_basic,
enum ieee80211_band band);
+u8 *ieee80211_add_wmm_info_ie(u8 *buf, u8 qosinfo);
/* channel management */
void ieee80211_ht_oper_to_chandef(struct ieee80211_channel *control_chan,
diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c
index 931330b..d863ff8 100644
--- a/net/mac80211/mlme.c
+++ b/net/mac80211/mlme.c
@@ -830,16 +830,7 @@ static void ieee80211_send_assoc(struct ieee80211_sub_if_data *sdata)
qos_info = 0;
}
- pos = skb_put(skb, 9);
- *pos++ = WLAN_EID_VENDOR_SPECIFIC;
- *pos++ = 7; /* len */
- *pos++ = 0x00; /* Microsoft OUI 00:50:F2 */
- *pos++ = 0x50;
- *pos++ = 0xf2;
- *pos++ = 2; /* WME */
- *pos++ = 0; /* WME info */
- *pos++ = 1; /* WME ver */
- *pos++ = qos_info;
+ pos = ieee80211_add_wmm_info_ie(skb_put(skb, 9), qos_info);
}
/* add any remaining custom (i.e. vendor specific here) IEs */
diff --git a/net/mac80211/tdls.c b/net/mac80211/tdls.c
index 398a413..bfd8fc4 100644
--- a/net/mac80211/tdls.c
+++ b/net/mac80211/tdls.c
@@ -100,6 +100,7 @@ ieee80211_tdls_add_setup_start_ies(struct ieee80211_sub_if_data *sdata,
const u8 *extra_ies, size_t extra_ies_len)
{
enum ieee80211_band band = ieee80211_get_sdata_band(sdata);
+ struct ieee80211_local *local = sdata->local;
size_t offset = 0, noffset;
u8 *pos;
@@ -126,6 +127,11 @@ ieee80211_tdls_add_setup_start_ies(struct ieee80211_sub_if_data *sdata,
ieee80211_tdls_add_ext_capab(skb);
+ /* add the QoS element if we support it */
+ if (local->hw.queues >= IEEE80211_NUM_ACS &&
+ action_code != WLAN_PUB_ACTION_TDLS_DISCOVER_RES)
+ ieee80211_add_wmm_info_ie(skb_put(skb, 9), 0); /* no U-APSD */
+
/* add any custom IEs that go before HT capabilities */
if (extra_ies_len) {
static const u8 before_ht_cap[] = {
@@ -310,6 +316,7 @@ ieee80211_tdls_prep_mgmt_packet(struct wiphy *wiphy, struct net_device *dev,
sizeof(struct ieee80211_tdls_data)) +
50 + /* supported rates */
7 + /* ext capab */
+ 26 + /* max(WMM-info, WMM-param) */
extra_ies_len +
sizeof(struct ieee80211_tdls_lnkie));
if (!skb)
diff --git a/net/mac80211/util.c b/net/mac80211/util.c
index df1bb7e..725af7a 100644
--- a/net/mac80211/util.c
+++ b/net/mac80211/util.c
@@ -3083,3 +3083,18 @@ int ieee80211_max_num_channels(struct ieee80211_local *local)
return max_num_different_channels;
}
+
+u8 *ieee80211_add_wmm_info_ie(u8 *buf, u8 qosinfo)
+{
+ *buf++ = WLAN_EID_VENDOR_SPECIFIC;
+ *buf++ = 7; /* len */
+ *buf++ = 0x00; /* Microsoft OUI 00:50:F2 */
+ *buf++ = 0x50;
+ *buf++ = 0xf2;
+ *buf++ = 2; /* WME */
+ *buf++ = 0; /* WME info */
+ *buf++ = 1; /* WME ver */
+ *buf++ = qosinfo; /* U-APSD no in use */
+
+ return buf;
+}
--
1.9.1
The patch "8f02e6b mac80211: make sure TDLS peer STA exists during
setup" broke TDLS error paths where the STA doesn't exist when sending
the error.
Fix it by only testing for STA existence during a non-error flow.
Signed-off-by: Arik Nemtsov <[email protected]>
Reviewed-by: Johannes Berg <[email protected]>
---
net/mac80211/tdls.c | 16 ++++++++++------
1 file changed, 10 insertions(+), 6 deletions(-)
diff --git a/net/mac80211/tdls.c b/net/mac80211/tdls.c
index b01b310..53c235d 100644
--- a/net/mac80211/tdls.c
+++ b/net/mac80211/tdls.c
@@ -349,15 +349,19 @@ ieee80211_tdls_mgmt_setup(struct wiphy *wiphy, struct net_device *dev,
/*
* 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
+ * during setup through the AP path.
+ * Allow error packets to be sent - sometimes we don't even add a STA
+ * before failing the setup.
*/
- rcu_read_lock();
- if (!sta_info_get(sdata, peer)) {
+ if (status_code == 0) {
+ rcu_read_lock();
+ if (!sta_info_get(sdata, peer)) {
+ rcu_read_unlock();
+ ret = -ENOLINK;
+ goto exit;
+ }
rcu_read_unlock();
- ret = -ENOLINK;
- goto exit;
}
- rcu_read_unlock();
ieee80211_flush_queues(local, sdata);
--
1.9.1
Set for completeness mostly, currently unused in the code.
Signed-off-by: Arik Nemtsov <[email protected]>
Reviewed-by: Emmanuel Grumbach <[email protected]>
---
net/mac80211/ht.c | 3 +++
1 file changed, 3 insertions(+)
diff --git a/net/mac80211/ht.c b/net/mac80211/ht.c
index 568055c..ff630be 100644
--- a/net/mac80211/ht.c
+++ b/net/mac80211/ht.c
@@ -227,6 +227,9 @@ bool ieee80211_ht_cap_ie_to_sta_ht_cap(struct ieee80211_sub_if_data *sdata,
if (own_cap.mcs.rx_mask[32/8] & ht_cap_ie->mcs.rx_mask[32/8] & 1)
ht_cap.mcs.rx_mask[32/8] |= 1;
+ /* set Rx highest rate */
+ ht_cap.mcs.rx_highest = ht_cap_ie->mcs.rx_highest;
+
apply:
changed = memcmp(&sta->sta.ht_cap, &ht_cap, sizeof(ht_cap));
--
1.9.1