.. | .. |
---|
| 1 | +// SPDX-License-Identifier: GPL-2.0-only |
---|
1 | 2 | /* |
---|
2 | 3 | * Copyright (c) 2008, 2009 open80211s Ltd. |
---|
| 4 | + * Copyright (C) 2018 - 2020 Intel Corporation |
---|
3 | 5 | * Authors: Luis Carlos Cobo <luisca@cozybit.com> |
---|
4 | 6 | * Javier Cardona <javier@cozybit.com> |
---|
5 | | - * |
---|
6 | | - * This program is free software; you can redistribute it and/or modify |
---|
7 | | - * it under the terms of the GNU General Public License version 2 as |
---|
8 | | - * published by the Free Software Foundation. |
---|
9 | 7 | */ |
---|
10 | 8 | |
---|
11 | 9 | #include <linux/slab.h> |
---|
.. | .. |
---|
65 | 63 | u32 basic_rates = 0; |
---|
66 | 64 | struct cfg80211_chan_def sta_chan_def; |
---|
67 | 65 | struct ieee80211_supported_band *sband; |
---|
| 66 | + u32 vht_cap_info = 0; |
---|
68 | 67 | |
---|
69 | 68 | /* |
---|
70 | 69 | * As support for each feature is added, check for matching |
---|
.. | .. |
---|
98 | 97 | cfg80211_chandef_create(&sta_chan_def, sdata->vif.bss_conf.chandef.chan, |
---|
99 | 98 | NL80211_CHAN_NO_HT); |
---|
100 | 99 | ieee80211_chandef_ht_oper(ie->ht_operation, &sta_chan_def); |
---|
101 | | - ieee80211_chandef_vht_oper(ie->vht_operation, &sta_chan_def); |
---|
| 100 | + |
---|
| 101 | + if (ie->vht_cap_elem) |
---|
| 102 | + vht_cap_info = le32_to_cpu(ie->vht_cap_elem->vht_cap_info); |
---|
| 103 | + |
---|
| 104 | + ieee80211_chandef_vht_oper(&sdata->local->hw, vht_cap_info, |
---|
| 105 | + ie->vht_operation, ie->ht_operation, |
---|
| 106 | + &sta_chan_def); |
---|
| 107 | + ieee80211_chandef_he_6ghz_oper(sdata, ie->he_operation, &sta_chan_def); |
---|
102 | 108 | |
---|
103 | 109 | if (!cfg80211_chandef_compatible(&sdata->vif.bss_conf.chandef, |
---|
104 | 110 | &sta_chan_def)) |
---|
.. | .. |
---|
251 | 257 | struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh; |
---|
252 | 258 | u8 *pos, neighbors; |
---|
253 | 259 | u8 meshconf_len = sizeof(struct ieee80211_meshconf_ie); |
---|
| 260 | + bool is_connected_to_gate = ifmsh->num_gates > 0 || |
---|
| 261 | + ifmsh->mshcfg.dot11MeshGateAnnouncementProtocol || |
---|
| 262 | + ifmsh->mshcfg.dot11MeshConnectedToMeshGate; |
---|
| 263 | + bool is_connected_to_as = ifmsh->mshcfg.dot11MeshConnectedToAuthServer; |
---|
254 | 264 | |
---|
255 | 265 | if (skb_tailroom(skb) < 2 + meshconf_len) |
---|
256 | 266 | return -ENOMEM; |
---|
.. | .. |
---|
275 | 285 | /* Mesh Formation Info - number of neighbors */ |
---|
276 | 286 | neighbors = atomic_read(&ifmsh->estab_plinks); |
---|
277 | 287 | neighbors = min_t(int, neighbors, IEEE80211_MAX_MESH_PEERINGS); |
---|
278 | | - *pos++ = neighbors << 1; |
---|
| 288 | + *pos++ = (is_connected_to_as << 7) | |
---|
| 289 | + (neighbors << 1) | |
---|
| 290 | + is_connected_to_gate; |
---|
279 | 291 | /* Mesh capability */ |
---|
280 | 292 | *pos = 0x00; |
---|
281 | 293 | *pos |= ifmsh->mshcfg.dot11MeshForwarding ? |
---|
.. | .. |
---|
412 | 424 | if (!sband) |
---|
413 | 425 | return -EINVAL; |
---|
414 | 426 | |
---|
| 427 | + /* HT not allowed in 6 GHz */ |
---|
| 428 | + if (sband->band == NL80211_BAND_6GHZ) |
---|
| 429 | + return 0; |
---|
| 430 | + |
---|
415 | 431 | if (!sband->ht_cap.ht_supported || |
---|
416 | 432 | sdata->vif.bss_conf.chandef.width == NL80211_CHAN_WIDTH_20_NOHT || |
---|
417 | 433 | sdata->vif.bss_conf.chandef.width == NL80211_CHAN_WIDTH_5 || |
---|
.. | .. |
---|
449 | 465 | sband = local->hw.wiphy->bands[channel->band]; |
---|
450 | 466 | ht_cap = &sband->ht_cap; |
---|
451 | 467 | |
---|
| 468 | + /* HT not allowed in 6 GHz */ |
---|
| 469 | + if (sband->band == NL80211_BAND_6GHZ) |
---|
| 470 | + return 0; |
---|
| 471 | + |
---|
452 | 472 | if (!ht_cap->ht_supported || |
---|
453 | 473 | sdata->vif.bss_conf.chandef.width == NL80211_CHAN_WIDTH_20_NOHT || |
---|
454 | 474 | sdata->vif.bss_conf.chandef.width == NL80211_CHAN_WIDTH_5 || |
---|
.. | .. |
---|
475 | 495 | sband = ieee80211_get_sband(sdata); |
---|
476 | 496 | if (!sband) |
---|
477 | 497 | return -EINVAL; |
---|
| 498 | + |
---|
| 499 | + /* VHT not allowed in 6 GHz */ |
---|
| 500 | + if (sband->band == NL80211_BAND_6GHZ) |
---|
| 501 | + return 0; |
---|
478 | 502 | |
---|
479 | 503 | if (!sband->vht_cap.vht_supported || |
---|
480 | 504 | sdata->vif.bss_conf.chandef.width == NL80211_CHAN_WIDTH_20_NOHT || |
---|
.. | .. |
---|
513 | 537 | sband = local->hw.wiphy->bands[channel->band]; |
---|
514 | 538 | vht_cap = &sband->vht_cap; |
---|
515 | 539 | |
---|
| 540 | + /* VHT not allowed in 6 GHz */ |
---|
| 541 | + if (sband->band == NL80211_BAND_6GHZ) |
---|
| 542 | + return 0; |
---|
| 543 | + |
---|
516 | 544 | if (!vht_cap->vht_supported || |
---|
517 | 545 | sdata->vif.bss_conf.chandef.width == NL80211_CHAN_WIDTH_20_NOHT || |
---|
518 | 546 | sdata->vif.bss_conf.chandef.width == NL80211_CHAN_WIDTH_5 || |
---|
.. | .. |
---|
526 | 554 | ieee80211_ie_build_vht_oper(pos, vht_cap, |
---|
527 | 555 | &sdata->vif.bss_conf.chandef); |
---|
528 | 556 | |
---|
| 557 | + return 0; |
---|
| 558 | +} |
---|
| 559 | + |
---|
| 560 | +int mesh_add_he_cap_ie(struct ieee80211_sub_if_data *sdata, |
---|
| 561 | + struct sk_buff *skb, u8 ie_len) |
---|
| 562 | +{ |
---|
| 563 | + const struct ieee80211_sta_he_cap *he_cap; |
---|
| 564 | + struct ieee80211_supported_band *sband; |
---|
| 565 | + u8 *pos; |
---|
| 566 | + |
---|
| 567 | + sband = ieee80211_get_sband(sdata); |
---|
| 568 | + if (!sband) |
---|
| 569 | + return -EINVAL; |
---|
| 570 | + |
---|
| 571 | + he_cap = ieee80211_get_he_iftype_cap(sband, NL80211_IFTYPE_MESH_POINT); |
---|
| 572 | + |
---|
| 573 | + if (!he_cap || |
---|
| 574 | + sdata->vif.bss_conf.chandef.width == NL80211_CHAN_WIDTH_20_NOHT || |
---|
| 575 | + sdata->vif.bss_conf.chandef.width == NL80211_CHAN_WIDTH_5 || |
---|
| 576 | + sdata->vif.bss_conf.chandef.width == NL80211_CHAN_WIDTH_10) |
---|
| 577 | + return 0; |
---|
| 578 | + |
---|
| 579 | + if (skb_tailroom(skb) < ie_len) |
---|
| 580 | + return -ENOMEM; |
---|
| 581 | + |
---|
| 582 | + pos = skb_put(skb, ie_len); |
---|
| 583 | + ieee80211_ie_build_he_cap(pos, he_cap, pos + ie_len); |
---|
| 584 | + |
---|
| 585 | + return 0; |
---|
| 586 | +} |
---|
| 587 | + |
---|
| 588 | +int mesh_add_he_oper_ie(struct ieee80211_sub_if_data *sdata, |
---|
| 589 | + struct sk_buff *skb) |
---|
| 590 | +{ |
---|
| 591 | + const struct ieee80211_sta_he_cap *he_cap; |
---|
| 592 | + struct ieee80211_supported_band *sband; |
---|
| 593 | + u32 len; |
---|
| 594 | + u8 *pos; |
---|
| 595 | + |
---|
| 596 | + sband = ieee80211_get_sband(sdata); |
---|
| 597 | + if (!sband) |
---|
| 598 | + return -EINVAL; |
---|
| 599 | + |
---|
| 600 | + he_cap = ieee80211_get_he_iftype_cap(sband, NL80211_IFTYPE_MESH_POINT); |
---|
| 601 | + if (!he_cap || |
---|
| 602 | + sdata->vif.bss_conf.chandef.width == NL80211_CHAN_WIDTH_20_NOHT || |
---|
| 603 | + sdata->vif.bss_conf.chandef.width == NL80211_CHAN_WIDTH_5 || |
---|
| 604 | + sdata->vif.bss_conf.chandef.width == NL80211_CHAN_WIDTH_10) |
---|
| 605 | + return 0; |
---|
| 606 | + |
---|
| 607 | + len = 2 + 1 + sizeof(struct ieee80211_he_operation); |
---|
| 608 | + if (sdata->vif.bss_conf.chandef.chan->band == NL80211_BAND_6GHZ) |
---|
| 609 | + len += sizeof(struct ieee80211_he_6ghz_oper); |
---|
| 610 | + |
---|
| 611 | + if (skb_tailroom(skb) < len) |
---|
| 612 | + return -ENOMEM; |
---|
| 613 | + |
---|
| 614 | + pos = skb_put(skb, len); |
---|
| 615 | + ieee80211_ie_build_he_oper(pos, &sdata->vif.bss_conf.chandef); |
---|
| 616 | + |
---|
| 617 | + return 0; |
---|
| 618 | +} |
---|
| 619 | + |
---|
| 620 | +int mesh_add_he_6ghz_cap_ie(struct ieee80211_sub_if_data *sdata, |
---|
| 621 | + struct sk_buff *skb) |
---|
| 622 | +{ |
---|
| 623 | + struct ieee80211_supported_band *sband; |
---|
| 624 | + const struct ieee80211_sband_iftype_data *iftd; |
---|
| 625 | + |
---|
| 626 | + sband = ieee80211_get_sband(sdata); |
---|
| 627 | + if (!sband) |
---|
| 628 | + return -EINVAL; |
---|
| 629 | + |
---|
| 630 | + iftd = ieee80211_get_sband_iftype_data(sband, |
---|
| 631 | + NL80211_IFTYPE_MESH_POINT); |
---|
| 632 | + /* The device doesn't support HE in mesh mode or at all */ |
---|
| 633 | + if (!iftd) |
---|
| 634 | + return 0; |
---|
| 635 | + |
---|
| 636 | + ieee80211_ie_build_he_6ghz_cap(sdata, skb); |
---|
529 | 637 | return 0; |
---|
530 | 638 | } |
---|
531 | 639 | |
---|
.. | .. |
---|
564 | 672 | * @hdr: 802.11 frame header |
---|
565 | 673 | * @fc: frame control field |
---|
566 | 674 | * @meshda: destination address in the mesh |
---|
567 | | - * @meshsa: source address address in the mesh. Same as TA, as frame is |
---|
| 675 | + * @meshsa: source address in the mesh. Same as TA, as frame is |
---|
568 | 676 | * locally originated. |
---|
569 | 677 | * |
---|
570 | 678 | * Return the length of the 802.11 (does not include a mesh control header) |
---|
.. | .. |
---|
674 | 782 | struct ieee80211_chanctx_conf *chanctx_conf; |
---|
675 | 783 | struct mesh_csa_settings *csa; |
---|
676 | 784 | enum nl80211_band band; |
---|
| 785 | + u8 ie_len_he_cap; |
---|
677 | 786 | u8 *pos; |
---|
678 | 787 | struct ieee80211_sub_if_data *sdata; |
---|
679 | 788 | int hdr_len = offsetofend(struct ieee80211_mgmt, u.beacon); |
---|
.. | .. |
---|
684 | 793 | band = chanctx_conf->def.chan->band; |
---|
685 | 794 | rcu_read_unlock(); |
---|
686 | 795 | |
---|
| 796 | + ie_len_he_cap = ieee80211_ie_len_he_cap(sdata, |
---|
| 797 | + NL80211_IFTYPE_MESH_POINT); |
---|
687 | 798 | head_len = hdr_len + |
---|
688 | 799 | 2 + /* NULL SSID */ |
---|
689 | 800 | /* Channel Switch Announcement */ |
---|
.. | .. |
---|
703 | 814 | 2 + sizeof(__le16) + /* awake window */ |
---|
704 | 815 | 2 + sizeof(struct ieee80211_vht_cap) + |
---|
705 | 816 | 2 + sizeof(struct ieee80211_vht_operation) + |
---|
| 817 | + ie_len_he_cap + |
---|
| 818 | + 2 + 1 + sizeof(struct ieee80211_he_operation) + |
---|
| 819 | + sizeof(struct ieee80211_he_6ghz_oper) + |
---|
| 820 | + 2 + 1 + sizeof(struct ieee80211_he_6ghz_capa) + |
---|
706 | 821 | ifmsh->ie_len; |
---|
707 | 822 | |
---|
708 | 823 | bcn = kzalloc(sizeof(*bcn) + head_len + tail_len, GFP_KERNEL); |
---|
.. | .. |
---|
749 | 864 | *pos++ = 0x0; |
---|
750 | 865 | *pos++ = ieee80211_frequency_to_channel( |
---|
751 | 866 | csa->settings.chandef.chan->center_freq); |
---|
752 | | - bcn->csa_current_counter = csa->settings.count; |
---|
753 | | - bcn->csa_counter_offsets[0] = hdr_len + 6; |
---|
| 867 | + bcn->cntdwn_current_counter = csa->settings.count; |
---|
| 868 | + bcn->cntdwn_counter_offsets[0] = hdr_len + 6; |
---|
754 | 869 | *pos++ = csa->settings.count; |
---|
755 | 870 | *pos++ = WLAN_EID_CHAN_SWITCH_PARAM; |
---|
756 | 871 | *pos++ = 6; |
---|
.. | .. |
---|
820 | 935 | mesh_add_awake_window_ie(sdata, skb) || |
---|
821 | 936 | mesh_add_vht_cap_ie(sdata, skb) || |
---|
822 | 937 | mesh_add_vht_oper_ie(sdata, skb) || |
---|
| 938 | + mesh_add_he_cap_ie(sdata, skb, ie_len_he_cap) || |
---|
| 939 | + mesh_add_he_oper_ie(sdata, skb) || |
---|
| 940 | + mesh_add_he_6ghz_cap_ie(sdata, skb) || |
---|
823 | 941 | mesh_add_vendor_ies(sdata, skb)) |
---|
824 | 942 | goto out_free; |
---|
825 | 943 | |
---|
.. | .. |
---|
929 | 1047 | /* stop the beacon */ |
---|
930 | 1048 | ifmsh->mesh_id_len = 0; |
---|
931 | 1049 | sdata->vif.bss_conf.enable_beacon = false; |
---|
| 1050 | + sdata->beacon_rate_set = false; |
---|
932 | 1051 | clear_bit(SDATA_STATE_OFFCHANNEL_BEACON_STOPPED, &sdata->state); |
---|
933 | 1052 | ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_BEACON_ENABLED); |
---|
934 | 1053 | |
---|
.. | .. |
---|
979 | 1098 | struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh; |
---|
980 | 1099 | struct ieee80211_supported_band *sband; |
---|
981 | 1100 | int err; |
---|
982 | | - u32 sta_flags; |
---|
| 1101 | + u32 sta_flags, vht_cap_info = 0; |
---|
983 | 1102 | |
---|
984 | 1103 | sdata_assert_lock(sdata); |
---|
985 | 1104 | |
---|
.. | .. |
---|
991 | 1110 | switch (sdata->vif.bss_conf.chandef.width) { |
---|
992 | 1111 | case NL80211_CHAN_WIDTH_20_NOHT: |
---|
993 | 1112 | sta_flags |= IEEE80211_STA_DISABLE_HT; |
---|
994 | | - /* fall through */ |
---|
| 1113 | + fallthrough; |
---|
995 | 1114 | case NL80211_CHAN_WIDTH_20: |
---|
996 | 1115 | sta_flags |= IEEE80211_STA_DISABLE_40MHZ; |
---|
997 | | - /* fall through */ |
---|
| 1116 | + fallthrough; |
---|
998 | 1117 | case NL80211_CHAN_WIDTH_40: |
---|
999 | 1118 | sta_flags |= IEEE80211_STA_DISABLE_VHT; |
---|
1000 | 1119 | break; |
---|
.. | .. |
---|
1002 | 1121 | break; |
---|
1003 | 1122 | } |
---|
1004 | 1123 | |
---|
| 1124 | + if (elems->vht_cap_elem) |
---|
| 1125 | + vht_cap_info = |
---|
| 1126 | + le32_to_cpu(elems->vht_cap_elem->vht_cap_info); |
---|
| 1127 | + |
---|
1005 | 1128 | memset(¶ms, 0, sizeof(params)); |
---|
1006 | 1129 | err = ieee80211_parse_ch_switch_ie(sdata, elems, sband->band, |
---|
| 1130 | + vht_cap_info, |
---|
1007 | 1131 | sta_flags, sdata->vif.addr, |
---|
1008 | 1132 | &csa_ie); |
---|
1009 | 1133 | if (err < 0) |
---|
.. | .. |
---|
1101 | 1225 | if (baselen > len) |
---|
1102 | 1226 | return; |
---|
1103 | 1227 | |
---|
1104 | | - ieee802_11_parse_elems(pos, len - baselen, false, &elems); |
---|
| 1228 | + ieee802_11_parse_elems(pos, len - baselen, false, &elems, mgmt->bssid, |
---|
| 1229 | + NULL); |
---|
1105 | 1230 | |
---|
1106 | 1231 | if (!elems.mesh_id) |
---|
1107 | 1232 | return; |
---|
.. | .. |
---|
1165 | 1290 | return; |
---|
1166 | 1291 | |
---|
1167 | 1292 | ieee802_11_parse_elems(mgmt->u.probe_resp.variable, len - baselen, |
---|
1168 | | - false, &elems); |
---|
| 1293 | + false, &elems, mgmt->bssid, NULL); |
---|
1169 | 1294 | |
---|
1170 | 1295 | /* ignore non-mesh or secure / unsecure mismatch */ |
---|
1171 | 1296 | if ((!elems.mesh_id || !elems.mesh_config) || |
---|
.. | .. |
---|
1189 | 1314 | if (!sdata->u.mesh.user_mpm || |
---|
1190 | 1315 | sdata->u.mesh.mshcfg.rssi_threshold == 0 || |
---|
1191 | 1316 | sdata->u.mesh.mshcfg.rssi_threshold < rx_status->signal) |
---|
1192 | | - mesh_neighbour_update(sdata, mgmt->sa, &elems); |
---|
| 1317 | + mesh_neighbour_update(sdata, mgmt->sa, &elems, |
---|
| 1318 | + rx_status); |
---|
| 1319 | + |
---|
| 1320 | + if (ifmsh->csa_role != IEEE80211_MESH_CSA_ROLE_INIT && |
---|
| 1321 | + !sdata->vif.csa_active) |
---|
| 1322 | + ieee80211_mesh_process_chnswitch(sdata, &elems, true); |
---|
1193 | 1323 | } |
---|
1194 | 1324 | |
---|
1195 | 1325 | if (ifmsh->sync_ops) |
---|
1196 | 1326 | ifmsh->sync_ops->rx_bcn_presp(sdata, |
---|
1197 | 1327 | stype, mgmt, &elems, rx_status); |
---|
1198 | | - |
---|
1199 | | - if (ifmsh->csa_role != IEEE80211_MESH_CSA_ROLE_INIT && |
---|
1200 | | - !sdata->vif.csa_active) |
---|
1201 | | - ieee80211_mesh_process_chnswitch(sdata, &elems, true); |
---|
1202 | 1328 | } |
---|
1203 | 1329 | |
---|
1204 | 1330 | int ieee80211_mesh_finish_csa(struct ieee80211_sub_if_data *sdata) |
---|
.. | .. |
---|
1303 | 1429 | pos = mgmt->u.action.u.chan_switch.variable; |
---|
1304 | 1430 | baselen = offsetof(struct ieee80211_mgmt, |
---|
1305 | 1431 | u.action.u.chan_switch.variable); |
---|
1306 | | - ieee802_11_parse_elems(pos, len - baselen, true, &elems); |
---|
| 1432 | + ieee802_11_parse_elems(pos, len - baselen, true, &elems, |
---|
| 1433 | + mgmt->bssid, NULL); |
---|
| 1434 | + |
---|
| 1435 | + if (!mesh_matches_local(sdata, &elems)) |
---|
| 1436 | + return; |
---|
1307 | 1437 | |
---|
1308 | 1438 | ifmsh->chsw_ttl = elems.mesh_chansw_params_ie->mesh_ttl; |
---|
1309 | 1439 | if (!--ifmsh->chsw_ttl) |
---|