diff options
author | Yu Wang <yyuwang@codeaurora.org> | 2019-05-10 05:04:52 -0400 |
---|---|---|
committer | Johannes Berg <johannes.berg@intel.com> | 2019-05-24 05:26:44 -0400 |
commit | 79c92ca42b5a3e0ea172ea2ce8df8e125af237da (patch) | |
tree | 08884f0a15aadc6b1edf04c21e1d2c3a32a840e2 | |
parent | 32b5a2c9950b9284000059d752f7afa164deb15e (diff) |
mac80211: handle deauthentication/disassociation from TDLS peer
When receiving a deauthentication/disassociation frame from a TDLS
peer, a station should not disconnect the current AP, but only
disable the current TDLS link if it's enabled.
Without this change, a TDLS issue can be reproduced by following the
steps as below:
1. STA-1 and STA-2 are connected to AP, bidirection traffic is running
between STA-1 and STA-2.
2. Set up TDLS link between STA-1 and STA-2, stay for a while, then
teardown TDLS link.
3. Repeat step #2 and monitor the connection between STA and AP.
During the test, one STA may send a deauthentication/disassociation
frame to another, after TDLS teardown, with reason code 6/7, which
means: Class 2/3 frame received from nonassociated STA.
On receive this frame, the receiver STA will disconnect the current
AP and then reconnect. It's not a expected behavior, purpose of this
frame should be disabling the TDLS link, not the link with AP.
Cc: stable@vger.kernel.org
Signed-off-by: Yu Wang <yyuwang@codeaurora.org>
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
-rw-r--r-- | net/mac80211/ieee80211_i.h | 3 | ||||
-rw-r--r-- | net/mac80211/mlme.c | 12 | ||||
-rw-r--r-- | net/mac80211/tdls.c | 23 |
3 files changed, 37 insertions, 1 deletions
diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index 073a8235ae1b..a8af4aafa117 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h | |||
@@ -2225,6 +2225,9 @@ void ieee80211_tdls_cancel_channel_switch(struct wiphy *wiphy, | |||
2225 | const u8 *addr); | 2225 | const u8 *addr); |
2226 | void ieee80211_teardown_tdls_peers(struct ieee80211_sub_if_data *sdata); | 2226 | void ieee80211_teardown_tdls_peers(struct ieee80211_sub_if_data *sdata); |
2227 | void ieee80211_tdls_chsw_work(struct work_struct *wk); | 2227 | void ieee80211_tdls_chsw_work(struct work_struct *wk); |
2228 | void ieee80211_tdls_handle_disconnect(struct ieee80211_sub_if_data *sdata, | ||
2229 | const u8 *peer, u16 reason); | ||
2230 | const char *ieee80211_get_reason_code_string(u16 reason_code); | ||
2228 | 2231 | ||
2229 | extern const struct ethtool_ops ieee80211_ethtool_ops; | 2232 | extern const struct ethtool_ops ieee80211_ethtool_ops; |
2230 | 2233 | ||
diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index b7a9fe3d5fcb..383b0df100e4 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c | |||
@@ -2963,7 +2963,7 @@ static void ieee80211_rx_mgmt_auth(struct ieee80211_sub_if_data *sdata, | |||
2963 | #define case_WLAN(type) \ | 2963 | #define case_WLAN(type) \ |
2964 | case WLAN_REASON_##type: return #type | 2964 | case WLAN_REASON_##type: return #type |
2965 | 2965 | ||
2966 | static const char *ieee80211_get_reason_code_string(u16 reason_code) | 2966 | const char *ieee80211_get_reason_code_string(u16 reason_code) |
2967 | { | 2967 | { |
2968 | switch (reason_code) { | 2968 | switch (reason_code) { |
2969 | case_WLAN(UNSPECIFIED); | 2969 | case_WLAN(UNSPECIFIED); |
@@ -3028,6 +3028,11 @@ static void ieee80211_rx_mgmt_deauth(struct ieee80211_sub_if_data *sdata, | |||
3028 | if (len < 24 + 2) | 3028 | if (len < 24 + 2) |
3029 | return; | 3029 | return; |
3030 | 3030 | ||
3031 | if (!ether_addr_equal(mgmt->bssid, mgmt->sa)) { | ||
3032 | ieee80211_tdls_handle_disconnect(sdata, mgmt->sa, reason_code); | ||
3033 | return; | ||
3034 | } | ||
3035 | |||
3031 | if (ifmgd->associated && | 3036 | if (ifmgd->associated && |
3032 | ether_addr_equal(mgmt->bssid, ifmgd->associated->bssid)) { | 3037 | ether_addr_equal(mgmt->bssid, ifmgd->associated->bssid)) { |
3033 | const u8 *bssid = ifmgd->associated->bssid; | 3038 | const u8 *bssid = ifmgd->associated->bssid; |
@@ -3077,6 +3082,11 @@ static void ieee80211_rx_mgmt_disassoc(struct ieee80211_sub_if_data *sdata, | |||
3077 | 3082 | ||
3078 | reason_code = le16_to_cpu(mgmt->u.disassoc.reason_code); | 3083 | reason_code = le16_to_cpu(mgmt->u.disassoc.reason_code); |
3079 | 3084 | ||
3085 | if (!ether_addr_equal(mgmt->bssid, mgmt->sa)) { | ||
3086 | ieee80211_tdls_handle_disconnect(sdata, mgmt->sa, reason_code); | ||
3087 | return; | ||
3088 | } | ||
3089 | |||
3080 | sdata_info(sdata, "disassociated from %pM (Reason: %u=%s)\n", | 3090 | sdata_info(sdata, "disassociated from %pM (Reason: %u=%s)\n", |
3081 | mgmt->sa, reason_code, | 3091 | mgmt->sa, reason_code, |
3082 | ieee80211_get_reason_code_string(reason_code)); | 3092 | ieee80211_get_reason_code_string(reason_code)); |
diff --git a/net/mac80211/tdls.c b/net/mac80211/tdls.c index 24c37f91ca46..ba8fe48952d9 100644 --- a/net/mac80211/tdls.c +++ b/net/mac80211/tdls.c | |||
@@ -1994,3 +1994,26 @@ void ieee80211_tdls_chsw_work(struct work_struct *wk) | |||
1994 | } | 1994 | } |
1995 | rtnl_unlock(); | 1995 | rtnl_unlock(); |
1996 | } | 1996 | } |
1997 | |||
1998 | void ieee80211_tdls_handle_disconnect(struct ieee80211_sub_if_data *sdata, | ||
1999 | const u8 *peer, u16 reason) | ||
2000 | { | ||
2001 | struct ieee80211_sta *sta; | ||
2002 | |||
2003 | rcu_read_lock(); | ||
2004 | sta = ieee80211_find_sta(&sdata->vif, peer); | ||
2005 | if (!sta || !sta->tdls) { | ||
2006 | rcu_read_unlock(); | ||
2007 | return; | ||
2008 | } | ||
2009 | rcu_read_unlock(); | ||
2010 | |||
2011 | tdls_dbg(sdata, "disconnected from TDLS peer %pM (Reason: %u=%s)\n", | ||
2012 | peer, reason, | ||
2013 | ieee80211_get_reason_code_string(reason)); | ||
2014 | |||
2015 | ieee80211_tdls_oper_request(&sdata->vif, peer, | ||
2016 | NL80211_TDLS_TEARDOWN, | ||
2017 | WLAN_REASON_TDLS_TEARDOWN_UNREACHABLE, | ||
2018 | GFP_ATOMIC); | ||
2019 | } | ||