diff options
-rw-r--r-- | net/mac80211/mesh.c | 139 | ||||
-rw-r--r-- | net/mac80211/spectmgmt.c | 6 |
2 files changed, 136 insertions, 9 deletions
diff --git a/net/mac80211/mesh.c b/net/mac80211/mesh.c index 6eb31d6fd8e1..896fe3bd599e 100644 --- a/net/mac80211/mesh.c +++ b/net/mac80211/mesh.c | |||
@@ -850,6 +850,127 @@ void ieee80211_stop_mesh(struct ieee80211_sub_if_data *sdata) | |||
850 | ieee80211_configure_filter(local); | 850 | ieee80211_configure_filter(local); |
851 | } | 851 | } |
852 | 852 | ||
853 | static bool | ||
854 | ieee80211_mesh_process_chnswitch(struct ieee80211_sub_if_data *sdata, | ||
855 | struct ieee802_11_elems *elems, bool beacon) | ||
856 | { | ||
857 | struct cfg80211_csa_settings params; | ||
858 | struct ieee80211_csa_ie csa_ie; | ||
859 | struct ieee80211_chanctx_conf *chanctx_conf; | ||
860 | struct ieee80211_chanctx *chanctx; | ||
861 | struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh; | ||
862 | enum ieee80211_band band = ieee80211_get_sdata_band(sdata); | ||
863 | int err, num_chanctx; | ||
864 | u32 sta_flags; | ||
865 | |||
866 | if (sdata->vif.csa_active) | ||
867 | return true; | ||
868 | |||
869 | if (!ifmsh->mesh_id) | ||
870 | return false; | ||
871 | |||
872 | sta_flags = IEEE80211_STA_DISABLE_VHT; | ||
873 | switch (sdata->vif.bss_conf.chandef.width) { | ||
874 | case NL80211_CHAN_WIDTH_20_NOHT: | ||
875 | sta_flags |= IEEE80211_STA_DISABLE_HT; | ||
876 | case NL80211_CHAN_WIDTH_20: | ||
877 | sta_flags |= IEEE80211_STA_DISABLE_40MHZ; | ||
878 | break; | ||
879 | default: | ||
880 | break; | ||
881 | } | ||
882 | |||
883 | memset(¶ms, 0, sizeof(params)); | ||
884 | memset(&csa_ie, 0, sizeof(csa_ie)); | ||
885 | err = ieee80211_parse_ch_switch_ie(sdata, elems, beacon, band, | ||
886 | sta_flags, sdata->vif.addr, | ||
887 | &csa_ie); | ||
888 | if (err < 0) | ||
889 | return false; | ||
890 | if (err) | ||
891 | return false; | ||
892 | |||
893 | params.chandef = csa_ie.chandef; | ||
894 | params.count = csa_ie.count; | ||
895 | |||
896 | if (sdata->vif.bss_conf.chandef.chan->band != | ||
897 | params.chandef.chan->band) | ||
898 | return false; | ||
899 | |||
900 | if (!cfg80211_chandef_usable(sdata->local->hw.wiphy, ¶ms.chandef, | ||
901 | IEEE80211_CHAN_DISABLED)) { | ||
902 | sdata_info(sdata, | ||
903 | "mesh STA %pM switches to unsupported channel (%d MHz, width:%d, CF1/2: %d/%d MHz), aborting\n", | ||
904 | sdata->vif.addr, | ||
905 | params.chandef.chan->center_freq, | ||
906 | params.chandef.width, | ||
907 | params.chandef.center_freq1, | ||
908 | params.chandef.center_freq2); | ||
909 | return false; | ||
910 | } | ||
911 | |||
912 | err = cfg80211_chandef_dfs_required(sdata->local->hw.wiphy, | ||
913 | ¶ms.chandef); | ||
914 | if (err < 0) | ||
915 | return false; | ||
916 | if (err) { | ||
917 | params.radar_required = true; | ||
918 | /* TODO: DFS not (yet) supported */ | ||
919 | return false; | ||
920 | } | ||
921 | |||
922 | rcu_read_lock(); | ||
923 | chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf); | ||
924 | if (!chanctx_conf) | ||
925 | goto failed_chswitch; | ||
926 | |||
927 | /* don't handle for multi-VIF cases */ | ||
928 | chanctx = container_of(chanctx_conf, struct ieee80211_chanctx, conf); | ||
929 | if (chanctx->refcount > 1) | ||
930 | goto failed_chswitch; | ||
931 | |||
932 | num_chanctx = 0; | ||
933 | list_for_each_entry_rcu(chanctx, &sdata->local->chanctx_list, list) | ||
934 | num_chanctx++; | ||
935 | |||
936 | if (num_chanctx > 1) | ||
937 | goto failed_chswitch; | ||
938 | |||
939 | rcu_read_unlock(); | ||
940 | |||
941 | mcsa_dbg(sdata, | ||
942 | "received channel switch announcement to go to channel %d MHz\n", | ||
943 | params.chandef.chan->center_freq); | ||
944 | |||
945 | params.block_tx = csa_ie.mode & WLAN_EID_CHAN_SWITCH_PARAM_TX_RESTRICT; | ||
946 | if (beacon) | ||
947 | ifmsh->chsw_ttl = csa_ie.ttl - 1; | ||
948 | else | ||
949 | ifmsh->chsw_ttl = 0; | ||
950 | |||
951 | if (ifmsh->chsw_ttl > 0) | ||
952 | if (ieee80211_mesh_csa_beacon(sdata, ¶ms, false) < 0) | ||
953 | return false; | ||
954 | |||
955 | sdata->csa_radar_required = params.radar_required; | ||
956 | |||
957 | if (params.block_tx) | ||
958 | ieee80211_stop_queues_by_reason(&sdata->local->hw, | ||
959 | IEEE80211_MAX_QUEUE_MAP, | ||
960 | IEEE80211_QUEUE_STOP_REASON_CSA); | ||
961 | |||
962 | sdata->local->csa_chandef = params.chandef; | ||
963 | sdata->vif.csa_active = true; | ||
964 | |||
965 | ieee80211_bss_info_change_notify(sdata, err); | ||
966 | drv_channel_switch_beacon(sdata, ¶ms.chandef); | ||
967 | |||
968 | return true; | ||
969 | failed_chswitch: | ||
970 | rcu_read_unlock(); | ||
971 | return false; | ||
972 | } | ||
973 | |||
853 | static void | 974 | static void |
854 | ieee80211_mesh_rx_probe_req(struct ieee80211_sub_if_data *sdata, | 975 | ieee80211_mesh_rx_probe_req(struct ieee80211_sub_if_data *sdata, |
855 | struct ieee80211_mgmt *mgmt, size_t len) | 976 | struct ieee80211_mgmt *mgmt, size_t len) |
@@ -956,6 +1077,9 @@ static void ieee80211_mesh_rx_bcn_presp(struct ieee80211_sub_if_data *sdata, | |||
956 | if (ifmsh->sync_ops) | 1077 | if (ifmsh->sync_ops) |
957 | ifmsh->sync_ops->rx_bcn_presp(sdata, | 1078 | ifmsh->sync_ops->rx_bcn_presp(sdata, |
958 | stype, mgmt, &elems, rx_status); | 1079 | stype, mgmt, &elems, rx_status); |
1080 | |||
1081 | if (!ifmsh->chsw_init) | ||
1082 | ieee80211_mesh_process_chnswitch(sdata, &elems, true); | ||
959 | } | 1083 | } |
960 | 1084 | ||
961 | int ieee80211_mesh_finish_csa(struct ieee80211_sub_if_data *sdata) | 1085 | int ieee80211_mesh_finish_csa(struct ieee80211_sub_if_data *sdata) |
@@ -1056,7 +1180,7 @@ static void mesh_rx_csa_frame(struct ieee80211_sub_if_data *sdata, | |||
1056 | struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh; | 1180 | struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh; |
1057 | struct ieee802_11_elems elems; | 1181 | struct ieee802_11_elems elems; |
1058 | u16 pre_value; | 1182 | u16 pre_value; |
1059 | bool block_tx, fwd_csa = true; | 1183 | bool fwd_csa = true; |
1060 | size_t baselen; | 1184 | size_t baselen; |
1061 | u8 *pos, ttl; | 1185 | u8 *pos, ttl; |
1062 | 1186 | ||
@@ -1079,19 +1203,16 @@ static void mesh_rx_csa_frame(struct ieee80211_sub_if_data *sdata, | |||
1079 | 1203 | ||
1080 | ifmsh->pre_value = pre_value; | 1204 | ifmsh->pre_value = pre_value; |
1081 | 1205 | ||
1206 | if (!ieee80211_mesh_process_chnswitch(sdata, &elems, false)) { | ||
1207 | mcsa_dbg(sdata, "Failed to process CSA action frame"); | ||
1208 | return; | ||
1209 | } | ||
1210 | |||
1082 | /* forward or re-broadcast the CSA frame */ | 1211 | /* forward or re-broadcast the CSA frame */ |
1083 | if (fwd_csa) { | 1212 | if (fwd_csa) { |
1084 | if (mesh_fwd_csa_frame(sdata, mgmt, len) < 0) | 1213 | if (mesh_fwd_csa_frame(sdata, mgmt, len) < 0) |
1085 | mcsa_dbg(sdata, "Failed to forward the CSA frame"); | 1214 | mcsa_dbg(sdata, "Failed to forward the CSA frame"); |
1086 | } | 1215 | } |
1087 | |||
1088 | /* block the Tx only after forwarding the CSA frame if required */ | ||
1089 | block_tx = elems.mesh_chansw_params_ie->mesh_flags & | ||
1090 | WLAN_EID_CHAN_SWITCH_PARAM_TX_RESTRICT; | ||
1091 | if (block_tx) | ||
1092 | ieee80211_stop_queues_by_reason(&sdata->local->hw, | ||
1093 | IEEE80211_MAX_QUEUE_MAP, | ||
1094 | IEEE80211_QUEUE_STOP_REASON_CSA); | ||
1095 | } | 1216 | } |
1096 | 1217 | ||
1097 | static void ieee80211_mesh_rx_mgmt_action(struct ieee80211_sub_if_data *sdata, | 1218 | static void ieee80211_mesh_rx_mgmt_action(struct ieee80211_sub_if_data *sdata, |
diff --git a/net/mac80211/spectmgmt.c b/net/mac80211/spectmgmt.c index a298e129633b..a40da20b32e0 100644 --- a/net/mac80211/spectmgmt.c +++ b/net/mac80211/spectmgmt.c | |||
@@ -74,6 +74,12 @@ int ieee80211_parse_ch_switch_ie(struct ieee80211_sub_if_data *sdata, | |||
74 | return 1; | 74 | return 1; |
75 | } | 75 | } |
76 | 76 | ||
77 | /* Mesh Channel Switch Parameters Element */ | ||
78 | if (elems->mesh_chansw_params_ie) { | ||
79 | csa_ie->ttl = elems->mesh_chansw_params_ie->mesh_ttl; | ||
80 | csa_ie->mode = elems->mesh_chansw_params_ie->mesh_flags; | ||
81 | } | ||
82 | |||
77 | new_freq = ieee80211_channel_to_frequency(new_chan_no, new_band); | 83 | new_freq = ieee80211_channel_to_frequency(new_chan_no, new_band); |
78 | new_chan = ieee80211_get_channel(sdata->local->hw.wiphy, new_freq); | 84 | new_chan = ieee80211_get_channel(sdata->local->hw.wiphy, new_freq); |
79 | if (!new_chan || new_chan->flags & IEEE80211_CHAN_DISABLED) { | 85 | if (!new_chan || new_chan->flags & IEEE80211_CHAN_DISABLED) { |