diff options
Diffstat (limited to 'net/mac80211/tdls.c')
-rw-r--r-- | net/mac80211/tdls.c | 155 |
1 files changed, 132 insertions, 23 deletions
diff --git a/net/mac80211/tdls.c b/net/mac80211/tdls.c index c9f9752217ac..fff0d864adfa 100644 --- a/net/mac80211/tdls.c +++ b/net/mac80211/tdls.c | |||
@@ -136,6 +136,24 @@ ieee80211_tdls_add_supp_channels(struct ieee80211_sub_if_data *sdata, | |||
136 | *pos = 2 * subband_cnt; | 136 | *pos = 2 * subband_cnt; |
137 | } | 137 | } |
138 | 138 | ||
139 | static void ieee80211_tdls_add_oper_classes(struct ieee80211_sub_if_data *sdata, | ||
140 | struct sk_buff *skb) | ||
141 | { | ||
142 | u8 *pos; | ||
143 | u8 op_class; | ||
144 | |||
145 | if (!ieee80211_chandef_to_operating_class(&sdata->vif.bss_conf.chandef, | ||
146 | &op_class)) | ||
147 | return; | ||
148 | |||
149 | pos = skb_put(skb, 4); | ||
150 | *pos++ = WLAN_EID_SUPPORTED_REGULATORY_CLASSES; | ||
151 | *pos++ = 2; /* len */ | ||
152 | |||
153 | *pos++ = op_class; | ||
154 | *pos++ = op_class; /* give current operating class as alternate too */ | ||
155 | } | ||
156 | |||
139 | static void ieee80211_tdls_add_bss_coex_ie(struct sk_buff *skb) | 157 | static void ieee80211_tdls_add_bss_coex_ie(struct sk_buff *skb) |
140 | { | 158 | { |
141 | u8 *pos = (void *)skb_put(skb, 3); | 159 | u8 *pos = (void *)skb_put(skb, 3); |
@@ -193,6 +211,17 @@ static void ieee80211_tdls_add_link_ie(struct ieee80211_sub_if_data *sdata, | |||
193 | memcpy(lnkid->resp_sta, rsp_addr, ETH_ALEN); | 211 | memcpy(lnkid->resp_sta, rsp_addr, ETH_ALEN); |
194 | } | 212 | } |
195 | 213 | ||
214 | static void | ||
215 | ieee80211_tdls_add_aid(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb) | ||
216 | { | ||
217 | struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; | ||
218 | u8 *pos = (void *)skb_put(skb, 4); | ||
219 | |||
220 | *pos++ = WLAN_EID_AID; | ||
221 | *pos++ = 2; /* len */ | ||
222 | put_unaligned_le16(ifmgd->aid, pos); | ||
223 | } | ||
224 | |||
196 | /* translate numbering in the WMM parameter IE to the mac80211 notation */ | 225 | /* translate numbering in the WMM parameter IE to the mac80211 notation */ |
197 | static enum ieee80211_ac_numbers ieee80211_ac_from_wmm(int ac) | 226 | static enum ieee80211_ac_numbers ieee80211_ac_from_wmm(int ac) |
198 | { | 227 | { |
@@ -271,21 +300,11 @@ ieee80211_tdls_add_setup_start_ies(struct ieee80211_sub_if_data *sdata, | |||
271 | struct ieee80211_local *local = sdata->local; | 300 | struct ieee80211_local *local = sdata->local; |
272 | struct ieee80211_supported_band *sband; | 301 | struct ieee80211_supported_band *sband; |
273 | struct ieee80211_sta_ht_cap ht_cap; | 302 | struct ieee80211_sta_ht_cap ht_cap; |
303 | struct ieee80211_sta_vht_cap vht_cap; | ||
274 | struct sta_info *sta = NULL; | 304 | struct sta_info *sta = NULL; |
275 | size_t offset = 0, noffset; | 305 | size_t offset = 0, noffset; |
276 | u8 *pos; | 306 | u8 *pos; |
277 | 307 | ||
278 | rcu_read_lock(); | ||
279 | |||
280 | /* we should have the peer STA if we're already responding */ | ||
281 | if (action_code == WLAN_TDLS_SETUP_RESPONSE) { | ||
282 | sta = sta_info_get(sdata, peer); | ||
283 | if (WARN_ON_ONCE(!sta)) { | ||
284 | rcu_read_unlock(); | ||
285 | return; | ||
286 | } | ||
287 | } | ||
288 | |||
289 | ieee80211_add_srates_ie(sdata, skb, false, band); | 308 | ieee80211_add_srates_ie(sdata, skb, false, band); |
290 | ieee80211_add_ext_srates_ie(sdata, skb, false, band); | 309 | ieee80211_add_ext_srates_ie(sdata, skb, false, band); |
291 | ieee80211_tdls_add_supp_channels(sdata, skb); | 310 | ieee80211_tdls_add_supp_channels(sdata, skb); |
@@ -338,6 +357,19 @@ ieee80211_tdls_add_setup_start_ies(struct ieee80211_sub_if_data *sdata, | |||
338 | offset = noffset; | 357 | offset = noffset; |
339 | } | 358 | } |
340 | 359 | ||
360 | rcu_read_lock(); | ||
361 | |||
362 | /* we should have the peer STA if we're already responding */ | ||
363 | if (action_code == WLAN_TDLS_SETUP_RESPONSE) { | ||
364 | sta = sta_info_get(sdata, peer); | ||
365 | if (WARN_ON_ONCE(!sta)) { | ||
366 | rcu_read_unlock(); | ||
367 | return; | ||
368 | } | ||
369 | } | ||
370 | |||
371 | ieee80211_tdls_add_oper_classes(sdata, skb); | ||
372 | |||
341 | /* | 373 | /* |
342 | * with TDLS we can switch channels, and HT-caps are not necessarily | 374 | * with TDLS we can switch channels, and HT-caps are not necessarily |
343 | * the same on all bands. The specification limits the setup to a | 375 | * the same on all bands. The specification limits the setup to a |
@@ -346,7 +378,9 @@ ieee80211_tdls_add_setup_start_ies(struct ieee80211_sub_if_data *sdata, | |||
346 | sband = local->hw.wiphy->bands[band]; | 378 | sband = local->hw.wiphy->bands[band]; |
347 | memcpy(&ht_cap, &sband->ht_cap, sizeof(ht_cap)); | 379 | memcpy(&ht_cap, &sband->ht_cap, sizeof(ht_cap)); |
348 | 380 | ||
349 | if (action_code == WLAN_TDLS_SETUP_REQUEST && ht_cap.ht_supported) { | 381 | if ((action_code == WLAN_TDLS_SETUP_REQUEST || |
382 | action_code == WLAN_PUB_ACTION_TDLS_DISCOVER_RES) && | ||
383 | ht_cap.ht_supported) { | ||
350 | ieee80211_apply_htcap_overrides(sdata, &ht_cap); | 384 | ieee80211_apply_htcap_overrides(sdata, &ht_cap); |
351 | 385 | ||
352 | /* disable SMPS in TDLS initiator */ | 386 | /* disable SMPS in TDLS initiator */ |
@@ -368,12 +402,63 @@ ieee80211_tdls_add_setup_start_ies(struct ieee80211_sub_if_data *sdata, | |||
368 | ieee80211_ie_build_ht_cap(pos, &ht_cap, ht_cap.cap); | 402 | ieee80211_ie_build_ht_cap(pos, &ht_cap, ht_cap.cap); |
369 | } | 403 | } |
370 | 404 | ||
371 | rcu_read_unlock(); | ||
372 | |||
373 | if (ht_cap.ht_supported && | 405 | if (ht_cap.ht_supported && |
374 | (ht_cap.cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40)) | 406 | (ht_cap.cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40)) |
375 | ieee80211_tdls_add_bss_coex_ie(skb); | 407 | ieee80211_tdls_add_bss_coex_ie(skb); |
376 | 408 | ||
409 | ieee80211_tdls_add_link_ie(sdata, skb, peer, initiator); | ||
410 | |||
411 | /* add any custom IEs that go before VHT capabilities */ | ||
412 | if (extra_ies_len) { | ||
413 | static const u8 before_vht_cap[] = { | ||
414 | WLAN_EID_SUPP_RATES, | ||
415 | WLAN_EID_COUNTRY, | ||
416 | WLAN_EID_EXT_SUPP_RATES, | ||
417 | WLAN_EID_SUPPORTED_CHANNELS, | ||
418 | WLAN_EID_RSN, | ||
419 | WLAN_EID_EXT_CAPABILITY, | ||
420 | WLAN_EID_QOS_CAPA, | ||
421 | WLAN_EID_FAST_BSS_TRANSITION, | ||
422 | WLAN_EID_TIMEOUT_INTERVAL, | ||
423 | WLAN_EID_SUPPORTED_REGULATORY_CLASSES, | ||
424 | WLAN_EID_MULTI_BAND, | ||
425 | }; | ||
426 | noffset = ieee80211_ie_split(extra_ies, extra_ies_len, | ||
427 | before_vht_cap, | ||
428 | ARRAY_SIZE(before_vht_cap), | ||
429 | offset); | ||
430 | pos = skb_put(skb, noffset - offset); | ||
431 | memcpy(pos, extra_ies + offset, noffset - offset); | ||
432 | offset = noffset; | ||
433 | } | ||
434 | |||
435 | /* build the VHT-cap similarly to the HT-cap */ | ||
436 | memcpy(&vht_cap, &sband->vht_cap, sizeof(vht_cap)); | ||
437 | if ((action_code == WLAN_TDLS_SETUP_REQUEST || | ||
438 | action_code == WLAN_PUB_ACTION_TDLS_DISCOVER_RES) && | ||
439 | vht_cap.vht_supported) { | ||
440 | ieee80211_apply_vhtcap_overrides(sdata, &vht_cap); | ||
441 | |||
442 | /* the AID is present only when VHT is implemented */ | ||
443 | if (action_code == WLAN_TDLS_SETUP_REQUEST) | ||
444 | ieee80211_tdls_add_aid(sdata, skb); | ||
445 | |||
446 | pos = skb_put(skb, sizeof(struct ieee80211_vht_cap) + 2); | ||
447 | ieee80211_ie_build_vht_cap(pos, &vht_cap, vht_cap.cap); | ||
448 | } else if (action_code == WLAN_TDLS_SETUP_RESPONSE && | ||
449 | vht_cap.vht_supported && sta->sta.vht_cap.vht_supported) { | ||
450 | /* the peer caps are already intersected with our own */ | ||
451 | memcpy(&vht_cap, &sta->sta.vht_cap, sizeof(vht_cap)); | ||
452 | |||
453 | /* the AID is present only when VHT is implemented */ | ||
454 | ieee80211_tdls_add_aid(sdata, skb); | ||
455 | |||
456 | pos = skb_put(skb, sizeof(struct ieee80211_vht_cap) + 2); | ||
457 | ieee80211_ie_build_vht_cap(pos, &vht_cap, vht_cap.cap); | ||
458 | } | ||
459 | |||
460 | rcu_read_unlock(); | ||
461 | |||
377 | /* add any remaining IEs */ | 462 | /* add any remaining IEs */ |
378 | if (extra_ies_len) { | 463 | if (extra_ies_len) { |
379 | noffset = extra_ies_len; | 464 | noffset = extra_ies_len; |
@@ -381,7 +466,6 @@ ieee80211_tdls_add_setup_start_ies(struct ieee80211_sub_if_data *sdata, | |||
381 | memcpy(pos, extra_ies + offset, noffset - offset); | 466 | memcpy(pos, extra_ies + offset, noffset - offset); |
382 | } | 467 | } |
383 | 468 | ||
384 | ieee80211_tdls_add_link_ie(sdata, skb, peer, initiator); | ||
385 | } | 469 | } |
386 | 470 | ||
387 | static void | 471 | static void |
@@ -394,6 +478,7 @@ ieee80211_tdls_add_setup_cfm_ies(struct ieee80211_sub_if_data *sdata, | |||
394 | struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; | 478 | struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; |
395 | size_t offset = 0, noffset; | 479 | size_t offset = 0, noffset; |
396 | struct sta_info *sta, *ap_sta; | 480 | struct sta_info *sta, *ap_sta; |
481 | enum ieee80211_band band = ieee80211_get_sdata_band(sdata); | ||
397 | u8 *pos; | 482 | u8 *pos; |
398 | 483 | ||
399 | rcu_read_lock(); | 484 | rcu_read_lock(); |
@@ -453,6 +538,21 @@ ieee80211_tdls_add_setup_cfm_ies(struct ieee80211_sub_if_data *sdata, | |||
453 | } | 538 | } |
454 | } | 539 | } |
455 | 540 | ||
541 | ieee80211_tdls_add_link_ie(sdata, skb, peer, initiator); | ||
542 | |||
543 | /* only include VHT-operation if not on the 2.4GHz band */ | ||
544 | if (band != IEEE80211_BAND_2GHZ && !ap_sta->sta.vht_cap.vht_supported && | ||
545 | sta->sta.vht_cap.vht_supported) { | ||
546 | struct ieee80211_chanctx_conf *chanctx_conf = | ||
547 | rcu_dereference(sdata->vif.chanctx_conf); | ||
548 | if (!WARN_ON(!chanctx_conf)) { | ||
549 | pos = skb_put(skb, 2 + | ||
550 | sizeof(struct ieee80211_vht_operation)); | ||
551 | ieee80211_ie_build_vht_oper(pos, &sta->sta.vht_cap, | ||
552 | &chanctx_conf->def); | ||
553 | } | ||
554 | } | ||
555 | |||
456 | rcu_read_unlock(); | 556 | rcu_read_unlock(); |
457 | 557 | ||
458 | /* add any remaining IEs */ | 558 | /* add any remaining IEs */ |
@@ -461,8 +561,6 @@ ieee80211_tdls_add_setup_cfm_ies(struct ieee80211_sub_if_data *sdata, | |||
461 | pos = skb_put(skb, noffset - offset); | 561 | pos = skb_put(skb, noffset - offset); |
462 | memcpy(pos, extra_ies + offset, noffset - offset); | 562 | memcpy(pos, extra_ies + offset, noffset - offset); |
463 | } | 563 | } |
464 | |||
465 | ieee80211_tdls_add_link_ie(sdata, skb, peer, initiator); | ||
466 | } | 564 | } |
467 | 565 | ||
468 | static void | 566 | static void |
@@ -708,8 +806,12 @@ ieee80211_tdls_build_mgmt_packet_data(struct ieee80211_sub_if_data *sdata, | |||
708 | 26 + /* max(WMM-info, WMM-param) */ | 806 | 26 + /* max(WMM-info, WMM-param) */ |
709 | 2 + max(sizeof(struct ieee80211_ht_cap), | 807 | 2 + max(sizeof(struct ieee80211_ht_cap), |
710 | sizeof(struct ieee80211_ht_operation)) + | 808 | sizeof(struct ieee80211_ht_operation)) + |
809 | 2 + max(sizeof(struct ieee80211_vht_cap), | ||
810 | sizeof(struct ieee80211_vht_operation)) + | ||
711 | 50 + /* supported channels */ | 811 | 50 + /* supported channels */ |
712 | 3 + /* 40/20 BSS coex */ | 812 | 3 + /* 40/20 BSS coex */ |
813 | 4 + /* AID */ | ||
814 | 4 + /* oper classes */ | ||
713 | extra_ies_len + | 815 | extra_ies_len + |
714 | sizeof(struct ieee80211_tdls_lnkie)); | 816 | sizeof(struct ieee80211_tdls_lnkie)); |
715 | if (!skb) | 817 | if (!skb) |
@@ -907,7 +1009,7 @@ ieee80211_tdls_mgmt_setup(struct wiphy *wiphy, struct net_device *dev, | |||
907 | if (!is_zero_ether_addr(sdata->u.mgd.tdls_peer) && | 1009 | if (!is_zero_ether_addr(sdata->u.mgd.tdls_peer) && |
908 | !ether_addr_equal(sdata->u.mgd.tdls_peer, peer)) { | 1010 | !ether_addr_equal(sdata->u.mgd.tdls_peer, peer)) { |
909 | ret = -EBUSY; | 1011 | ret = -EBUSY; |
910 | goto exit; | 1012 | goto out_unlock; |
911 | } | 1013 | } |
912 | 1014 | ||
913 | /* | 1015 | /* |
@@ -922,27 +1024,34 @@ ieee80211_tdls_mgmt_setup(struct wiphy *wiphy, struct net_device *dev, | |||
922 | if (!sta_info_get(sdata, peer)) { | 1024 | if (!sta_info_get(sdata, peer)) { |
923 | rcu_read_unlock(); | 1025 | rcu_read_unlock(); |
924 | ret = -ENOLINK; | 1026 | ret = -ENOLINK; |
925 | goto exit; | 1027 | goto out_unlock; |
926 | } | 1028 | } |
927 | rcu_read_unlock(); | 1029 | rcu_read_unlock(); |
928 | } | 1030 | } |
929 | 1031 | ||
930 | ieee80211_flush_queues(local, sdata, false); | 1032 | ieee80211_flush_queues(local, sdata, false); |
1033 | memcpy(sdata->u.mgd.tdls_peer, peer, ETH_ALEN); | ||
1034 | mutex_unlock(&local->mtx); | ||
931 | 1035 | ||
1036 | /* we cannot take the mutex while preparing the setup packet */ | ||
932 | ret = ieee80211_tdls_prep_mgmt_packet(wiphy, dev, peer, action_code, | 1037 | ret = ieee80211_tdls_prep_mgmt_packet(wiphy, dev, peer, action_code, |
933 | dialog_token, status_code, | 1038 | dialog_token, status_code, |
934 | peer_capability, initiator, | 1039 | peer_capability, initiator, |
935 | extra_ies, extra_ies_len, 0, | 1040 | extra_ies, extra_ies_len, 0, |
936 | NULL); | 1041 | NULL); |
937 | if (ret < 0) | 1042 | if (ret < 0) { |
938 | goto exit; | 1043 | mutex_lock(&local->mtx); |
1044 | eth_zero_addr(sdata->u.mgd.tdls_peer); | ||
1045 | mutex_unlock(&local->mtx); | ||
1046 | return ret; | ||
1047 | } | ||
939 | 1048 | ||
940 | memcpy(sdata->u.mgd.tdls_peer, peer, ETH_ALEN); | ||
941 | ieee80211_queue_delayed_work(&sdata->local->hw, | 1049 | ieee80211_queue_delayed_work(&sdata->local->hw, |
942 | &sdata->u.mgd.tdls_peer_del_work, | 1050 | &sdata->u.mgd.tdls_peer_del_work, |
943 | TDLS_PEER_SETUP_TIMEOUT); | 1051 | TDLS_PEER_SETUP_TIMEOUT); |
1052 | return 0; | ||
944 | 1053 | ||
945 | exit: | 1054 | out_unlock: |
946 | mutex_unlock(&local->mtx); | 1055 | mutex_unlock(&local->mtx); |
947 | return ret; | 1056 | return ret; |
948 | } | 1057 | } |