diff options
Diffstat (limited to 'net/mac80211/mesh.c')
-rw-r--r-- | net/mac80211/mesh.c | 83 |
1 files changed, 80 insertions, 3 deletions
diff --git a/net/mac80211/mesh.c b/net/mac80211/mesh.c index 707ac61d63e5..0a3ccaa275f9 100644 --- a/net/mac80211/mesh.c +++ b/net/mac80211/mesh.c | |||
@@ -920,6 +920,82 @@ static void ieee80211_mesh_rx_bcn_presp(struct ieee80211_sub_if_data *sdata, | |||
920 | stype, mgmt, &elems, rx_status); | 920 | stype, mgmt, &elems, rx_status); |
921 | } | 921 | } |
922 | 922 | ||
923 | static int mesh_fwd_csa_frame(struct ieee80211_sub_if_data *sdata, | ||
924 | struct ieee80211_mgmt *mgmt, size_t len) | ||
925 | { | ||
926 | struct ieee80211_mgmt *mgmt_fwd; | ||
927 | struct sk_buff *skb; | ||
928 | struct ieee80211_local *local = sdata->local; | ||
929 | u8 *pos = mgmt->u.action.u.chan_switch.variable; | ||
930 | size_t offset_ttl; | ||
931 | |||
932 | skb = dev_alloc_skb(local->tx_headroom + len); | ||
933 | if (!skb) | ||
934 | return -ENOMEM; | ||
935 | skb_reserve(skb, local->tx_headroom); | ||
936 | mgmt_fwd = (struct ieee80211_mgmt *) skb_put(skb, len); | ||
937 | |||
938 | /* offset_ttl is based on whether the secondary channel | ||
939 | * offset is available or not. Substract 1 from the mesh TTL | ||
940 | * and disable the initiator flag before forwarding. | ||
941 | */ | ||
942 | offset_ttl = (len < 42) ? 7 : 10; | ||
943 | *(pos + offset_ttl) -= 1; | ||
944 | *(pos + offset_ttl + 1) &= ~WLAN_EID_CHAN_SWITCH_PARAM_INITIATOR; | ||
945 | |||
946 | memcpy(mgmt_fwd, mgmt, len); | ||
947 | eth_broadcast_addr(mgmt_fwd->da); | ||
948 | memcpy(mgmt_fwd->sa, sdata->vif.addr, ETH_ALEN); | ||
949 | memcpy(mgmt_fwd->bssid, sdata->vif.addr, ETH_ALEN); | ||
950 | |||
951 | ieee80211_tx_skb(sdata, skb); | ||
952 | return 0; | ||
953 | } | ||
954 | |||
955 | static void mesh_rx_csa_frame(struct ieee80211_sub_if_data *sdata, | ||
956 | struct ieee80211_mgmt *mgmt, size_t len) | ||
957 | { | ||
958 | struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh; | ||
959 | struct ieee802_11_elems elems; | ||
960 | u16 pre_value; | ||
961 | bool block_tx, fwd_csa = true; | ||
962 | size_t baselen; | ||
963 | u8 *pos, ttl; | ||
964 | |||
965 | if (mgmt->u.action.u.measurement.action_code != | ||
966 | WLAN_ACTION_SPCT_CHL_SWITCH) | ||
967 | return; | ||
968 | |||
969 | pos = mgmt->u.action.u.chan_switch.variable; | ||
970 | baselen = offsetof(struct ieee80211_mgmt, | ||
971 | u.action.u.chan_switch.variable); | ||
972 | ieee802_11_parse_elems(pos, len - baselen, false, &elems); | ||
973 | |||
974 | ttl = elems.mesh_chansw_params_ie->mesh_ttl; | ||
975 | if (!--ttl) | ||
976 | fwd_csa = false; | ||
977 | |||
978 | pre_value = le16_to_cpu(elems.mesh_chansw_params_ie->mesh_pre_value); | ||
979 | if (ifmsh->pre_value >= pre_value) | ||
980 | return; | ||
981 | |||
982 | ifmsh->pre_value = pre_value; | ||
983 | |||
984 | /* forward or re-broadcast the CSA frame */ | ||
985 | if (fwd_csa) { | ||
986 | if (mesh_fwd_csa_frame(sdata, mgmt, len) < 0) | ||
987 | mcsa_dbg(sdata, "Failed to forward the CSA frame"); | ||
988 | } | ||
989 | |||
990 | /* block the Tx only after forwarding the CSA frame if required */ | ||
991 | block_tx = elems.mesh_chansw_params_ie->mesh_flags & | ||
992 | WLAN_EID_CHAN_SWITCH_PARAM_TX_RESTRICT; | ||
993 | if (block_tx) | ||
994 | ieee80211_stop_queues_by_reason(&sdata->local->hw, | ||
995 | IEEE80211_MAX_QUEUE_MAP, | ||
996 | IEEE80211_QUEUE_STOP_REASON_CSA); | ||
997 | } | ||
998 | |||
923 | static void ieee80211_mesh_rx_mgmt_action(struct ieee80211_sub_if_data *sdata, | 999 | static void ieee80211_mesh_rx_mgmt_action(struct ieee80211_sub_if_data *sdata, |
924 | struct ieee80211_mgmt *mgmt, | 1000 | struct ieee80211_mgmt *mgmt, |
925 | size_t len, | 1001 | size_t len, |
@@ -939,6 +1015,9 @@ static void ieee80211_mesh_rx_mgmt_action(struct ieee80211_sub_if_data *sdata, | |||
939 | if (mesh_action_is_path_sel(mgmt)) | 1015 | if (mesh_action_is_path_sel(mgmt)) |
940 | mesh_rx_path_sel_frame(sdata, mgmt, len); | 1016 | mesh_rx_path_sel_frame(sdata, mgmt, len); |
941 | break; | 1017 | break; |
1018 | case WLAN_CATEGORY_SPECTRUM_MGMT: | ||
1019 | mesh_rx_csa_frame(sdata, mgmt, len); | ||
1020 | break; | ||
942 | } | 1021 | } |
943 | } | 1022 | } |
944 | 1023 | ||
@@ -1056,13 +1135,11 @@ void ieee80211_mesh_init_sdata(struct ieee80211_sub_if_data *sdata) | |||
1056 | (unsigned long) sdata); | 1135 | (unsigned long) sdata); |
1057 | 1136 | ||
1058 | ifmsh->accepting_plinks = true; | 1137 | ifmsh->accepting_plinks = true; |
1059 | ifmsh->preq_id = 0; | ||
1060 | ifmsh->sn = 0; | ||
1061 | ifmsh->num_gates = 0; | ||
1062 | atomic_set(&ifmsh->mpaths, 0); | 1138 | atomic_set(&ifmsh->mpaths, 0); |
1063 | mesh_rmc_init(sdata); | 1139 | mesh_rmc_init(sdata); |
1064 | ifmsh->last_preq = jiffies; | 1140 | ifmsh->last_preq = jiffies; |
1065 | ifmsh->next_perr = jiffies; | 1141 | ifmsh->next_perr = jiffies; |
1142 | ifmsh->chsw_init = false; | ||
1066 | /* Allocate all mesh structures when creating the first mesh interface. */ | 1143 | /* Allocate all mesh structures when creating the first mesh interface. */ |
1067 | if (!mesh_allocated) | 1144 | if (!mesh_allocated) |
1068 | ieee80211s_init(); | 1145 | ieee80211s_init(); |