diff options
author | Avri Altman <avri.altman@intel.com> | 2015-08-18 09:52:07 -0400 |
---|---|---|
committer | Johannes Berg <johannes.berg@intel.com> | 2015-09-04 08:25:46 -0400 |
commit | 22f66895e60cfc55b92f6fa93f05bb3fbdbd0bed (patch) | |
tree | a76a60a63d227291e39e7b9d46579f435292d2f3 | |
parent | 98a1f8282b8c37378c1b947d661a58942331ca90 (diff) |
mac80211: protect non-HT BSS when HT TDLS traffic exists
HT TDLS traffic should be protected in a non-HT BSS to avoid
collisions. Therefore, when TDLS peers join/leave, check if
protection is (now) needed and set the ht_operation_mode of
the virtual interface according to the HT capabilities of the
TDLS peer(s).
This works because a non-HT BSS connection never sets (or
otherwise uses) the ht_operation_mode; it just means that
drivers must be aware that this field applies to all HT
traffic for this virtual interface, not just the traffic
within the BSS. Document that.
Signed-off-by: Avri Altman <avri.altman@intel.com>
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
-rw-r--r-- | include/net/mac80211.h | 4 | ||||
-rw-r--r-- | net/mac80211/tdls.c | 70 |
2 files changed, 70 insertions, 4 deletions
diff --git a/include/net/mac80211.h b/include/net/mac80211.h index e3314e516681..bfc569498bfa 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h | |||
@@ -477,7 +477,9 @@ struct ieee80211_event { | |||
477 | * @chandef: Channel definition for this BSS -- the hardware might be | 477 | * @chandef: Channel definition for this BSS -- the hardware might be |
478 | * configured a higher bandwidth than this BSS uses, for example. | 478 | * configured a higher bandwidth than this BSS uses, for example. |
479 | * @ht_operation_mode: HT operation mode like in &struct ieee80211_ht_operation. | 479 | * @ht_operation_mode: HT operation mode like in &struct ieee80211_ht_operation. |
480 | * This field is only valid when the channel type is one of the HT types. | 480 | * This field is only valid when the channel is a wide HT/VHT channel. |
481 | * Note that with TDLS this can be the case (channel is HT, protection must | ||
482 | * be used from this field) even when the BSS association isn't using HT. | ||
481 | * @cqm_rssi_thold: Connection quality monitor RSSI threshold, a zero value | 483 | * @cqm_rssi_thold: Connection quality monitor RSSI threshold, a zero value |
482 | * implies disabled | 484 | * implies disabled |
483 | * @cqm_rssi_hyst: Connection quality monitor RSSI hysteresis | 485 | * @cqm_rssi_hyst: Connection quality monitor RSSI hysteresis |
diff --git a/net/mac80211/tdls.c b/net/mac80211/tdls.c index aee701a5649e..4e202d0679b2 100644 --- a/net/mac80211/tdls.c +++ b/net/mac80211/tdls.c | |||
@@ -1249,6 +1249,58 @@ static void iee80211_tdls_recalc_chanctx(struct ieee80211_sub_if_data *sdata) | |||
1249 | mutex_unlock(&local->chanctx_mtx); | 1249 | mutex_unlock(&local->chanctx_mtx); |
1250 | } | 1250 | } |
1251 | 1251 | ||
1252 | static int iee80211_tdls_have_ht_peers(struct ieee80211_sub_if_data *sdata) | ||
1253 | { | ||
1254 | struct sta_info *sta; | ||
1255 | bool result = false; | ||
1256 | |||
1257 | rcu_read_lock(); | ||
1258 | list_for_each_entry_rcu(sta, &sdata->local->sta_list, list) { | ||
1259 | if (!sta->sta.tdls || sta->sdata != sdata || !sta->uploaded || | ||
1260 | !test_sta_flag(sta, WLAN_STA_AUTHORIZED) || | ||
1261 | !test_sta_flag(sta, WLAN_STA_TDLS_PEER_AUTH) || | ||
1262 | !sta->sta.ht_cap.ht_supported) | ||
1263 | continue; | ||
1264 | result = true; | ||
1265 | break; | ||
1266 | } | ||
1267 | rcu_read_unlock(); | ||
1268 | |||
1269 | return result; | ||
1270 | } | ||
1271 | |||
1272 | static void | ||
1273 | iee80211_tdls_recalc_ht_protection(struct ieee80211_sub_if_data *sdata, | ||
1274 | struct sta_info *sta) | ||
1275 | { | ||
1276 | struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; | ||
1277 | bool tdls_ht; | ||
1278 | u16 protection = IEEE80211_HT_OP_MODE_PROTECTION_NONHT_MIXED | | ||
1279 | IEEE80211_HT_OP_MODE_NON_GF_STA_PRSNT | | ||
1280 | IEEE80211_HT_OP_MODE_NON_HT_STA_PRSNT; | ||
1281 | u16 opmode; | ||
1282 | |||
1283 | /* Nothing to do if the BSS connection uses HT */ | ||
1284 | if (!(ifmgd->flags & IEEE80211_STA_DISABLE_HT)) | ||
1285 | return; | ||
1286 | |||
1287 | tdls_ht = (sta && sta->sta.ht_cap.ht_supported) || | ||
1288 | iee80211_tdls_have_ht_peers(sdata); | ||
1289 | |||
1290 | opmode = sdata->vif.bss_conf.ht_operation_mode; | ||
1291 | |||
1292 | if (tdls_ht) | ||
1293 | opmode |= protection; | ||
1294 | else | ||
1295 | opmode &= ~protection; | ||
1296 | |||
1297 | if (opmode == sdata->vif.bss_conf.ht_operation_mode) | ||
1298 | return; | ||
1299 | |||
1300 | sdata->vif.bss_conf.ht_operation_mode = opmode; | ||
1301 | ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_HT); | ||
1302 | } | ||
1303 | |||
1252 | int ieee80211_tdls_oper(struct wiphy *wiphy, struct net_device *dev, | 1304 | int ieee80211_tdls_oper(struct wiphy *wiphy, struct net_device *dev, |
1253 | const u8 *peer, enum nl80211_tdls_operation oper) | 1305 | const u8 *peer, enum nl80211_tdls_operation oper) |
1254 | { | 1306 | { |
@@ -1274,6 +1326,10 @@ int ieee80211_tdls_oper(struct wiphy *wiphy, struct net_device *dev, | |||
1274 | return -ENOTSUPP; | 1326 | return -ENOTSUPP; |
1275 | } | 1327 | } |
1276 | 1328 | ||
1329 | /* protect possible bss_conf changes and avoid concurrency in | ||
1330 | * ieee80211_bss_info_change_notify() | ||
1331 | */ | ||
1332 | sdata_lock(sdata); | ||
1277 | mutex_lock(&local->mtx); | 1333 | mutex_lock(&local->mtx); |
1278 | tdls_dbg(sdata, "TDLS oper %d peer %pM\n", oper, peer); | 1334 | tdls_dbg(sdata, "TDLS oper %d peer %pM\n", oper, peer); |
1279 | 1335 | ||
@@ -1287,16 +1343,18 @@ int ieee80211_tdls_oper(struct wiphy *wiphy, struct net_device *dev, | |||
1287 | 1343 | ||
1288 | iee80211_tdls_recalc_chanctx(sdata); | 1344 | iee80211_tdls_recalc_chanctx(sdata); |
1289 | 1345 | ||
1290 | rcu_read_lock(); | 1346 | mutex_lock(&local->sta_mtx); |
1291 | sta = sta_info_get(sdata, peer); | 1347 | sta = sta_info_get(sdata, peer); |
1292 | if (!sta) { | 1348 | if (!sta) { |
1293 | rcu_read_unlock(); | 1349 | mutex_unlock(&local->sta_mtx); |
1294 | ret = -ENOLINK; | 1350 | ret = -ENOLINK; |
1295 | break; | 1351 | break; |
1296 | } | 1352 | } |
1297 | 1353 | ||
1354 | iee80211_tdls_recalc_ht_protection(sdata, sta); | ||
1355 | |||
1298 | set_sta_flag(sta, WLAN_STA_TDLS_PEER_AUTH); | 1356 | set_sta_flag(sta, WLAN_STA_TDLS_PEER_AUTH); |
1299 | rcu_read_unlock(); | 1357 | mutex_unlock(&local->sta_mtx); |
1300 | 1358 | ||
1301 | WARN_ON_ONCE(is_zero_ether_addr(sdata->u.mgd.tdls_peer) || | 1359 | WARN_ON_ONCE(is_zero_ether_addr(sdata->u.mgd.tdls_peer) || |
1302 | !ether_addr_equal(sdata->u.mgd.tdls_peer, peer)); | 1360 | !ether_addr_equal(sdata->u.mgd.tdls_peer, peer)); |
@@ -1318,6 +1376,11 @@ int ieee80211_tdls_oper(struct wiphy *wiphy, struct net_device *dev, | |||
1318 | ieee80211_flush_queues(local, sdata, false); | 1376 | ieee80211_flush_queues(local, sdata, false); |
1319 | 1377 | ||
1320 | ret = sta_info_destroy_addr(sdata, peer); | 1378 | ret = sta_info_destroy_addr(sdata, peer); |
1379 | |||
1380 | mutex_lock(&local->sta_mtx); | ||
1381 | iee80211_tdls_recalc_ht_protection(sdata, NULL); | ||
1382 | mutex_unlock(&local->sta_mtx); | ||
1383 | |||
1321 | iee80211_tdls_recalc_chanctx(sdata); | 1384 | iee80211_tdls_recalc_chanctx(sdata); |
1322 | break; | 1385 | break; |
1323 | default: | 1386 | default: |
@@ -1335,6 +1398,7 @@ int ieee80211_tdls_oper(struct wiphy *wiphy, struct net_device *dev, | |||
1335 | &sdata->u.mgd.request_smps_work); | 1398 | &sdata->u.mgd.request_smps_work); |
1336 | 1399 | ||
1337 | mutex_unlock(&local->mtx); | 1400 | mutex_unlock(&local->mtx); |
1401 | sdata_unlock(sdata); | ||
1338 | return ret; | 1402 | return ret; |
1339 | } | 1403 | } |
1340 | 1404 | ||