diff options
author | Chun-Yeow Yeoh <yeohchunyeow@cozybit.com> | 2013-10-14 22:08:27 -0400 |
---|---|---|
committer | Johannes Berg <johannes.berg@intel.com> | 2013-10-28 10:05:28 -0400 |
commit | 8f2535b92d685c68db4bc699dd78462a646f6ef9 (patch) | |
tree | e64579c3dfc09f81fbf47f2a76d316721a4da2e3 /net/mac80211 | |
parent | c0f17eb9b2d4d322c099a0700437209149224583 (diff) |
mac80211: process the CSA frame for mesh accordingly
Process the CSA frame according to the procedures define in IEEE Std
802.11-2012 section 10.9.8.4.3 as follow:
* The mesh channel switch parameters element (MCSP) must be availabe.
* If the MCSP's TTL is 1, drop the frame but still process the CSA.
* If the MCSP's precedence value is less than or equal to the current
precedence value, drop the frame and do not process the CSA.
* The CSA frame is forwarded after TTL is decremented by 1 and the
initiator field is set to 0. Transmit restrict field and others
are maintained as is.
* No beacon or probe response frame are handled here.
Also, introduce the debug message used for mesh CSA purpose.
Signed-off-by: Chun-Yeow Yeoh <yeohchunyeow@cozybit.com>
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
Diffstat (limited to 'net/mac80211')
-rw-r--r-- | net/mac80211/Kconfig | 11 | ||||
-rw-r--r-- | net/mac80211/debug.h | 10 | ||||
-rw-r--r-- | net/mac80211/ieee80211_i.h | 4 | ||||
-rw-r--r-- | net/mac80211/mesh.c | 83 | ||||
-rw-r--r-- | net/mac80211/util.c | 9 |
5 files changed, 114 insertions, 3 deletions
diff --git a/net/mac80211/Kconfig b/net/mac80211/Kconfig index dc31ec3db404..97b5dcad5025 100644 --- a/net/mac80211/Kconfig +++ b/net/mac80211/Kconfig | |||
@@ -259,6 +259,17 @@ config MAC80211_MESH_SYNC_DEBUG | |||
259 | 259 | ||
260 | Do not select this option. | 260 | Do not select this option. |
261 | 261 | ||
262 | config MAC80211_MESH_CSA_DEBUG | ||
263 | bool "Verbose mesh channel switch debugging" | ||
264 | depends on MAC80211_DEBUG_MENU | ||
265 | depends on MAC80211_MESH | ||
266 | ---help--- | ||
267 | Selecting this option causes mac80211 to print out very verbose mesh | ||
268 | channel switch debugging messages (when mac80211 is taking part in a | ||
269 | mesh network). | ||
270 | |||
271 | Do not select this option. | ||
272 | |||
262 | config MAC80211_MESH_PS_DEBUG | 273 | config MAC80211_MESH_PS_DEBUG |
263 | bool "Verbose mesh powersave debugging" | 274 | bool "Verbose mesh powersave debugging" |
264 | depends on MAC80211_DEBUG_MENU | 275 | depends on MAC80211_DEBUG_MENU |
diff --git a/net/mac80211/debug.h b/net/mac80211/debug.h index 4ccc5ed6237d..493d68061f0c 100644 --- a/net/mac80211/debug.h +++ b/net/mac80211/debug.h | |||
@@ -44,6 +44,12 @@ | |||
44 | #define MAC80211_MESH_SYNC_DEBUG 0 | 44 | #define MAC80211_MESH_SYNC_DEBUG 0 |
45 | #endif | 45 | #endif |
46 | 46 | ||
47 | #ifdef CONFIG_MAC80211_MESH_CSA_DEBUG | ||
48 | #define MAC80211_MESH_CSA_DEBUG 1 | ||
49 | #else | ||
50 | #define MAC80211_MESH_CSA_DEBUG 0 | ||
51 | #endif | ||
52 | |||
47 | #ifdef CONFIG_MAC80211_MESH_PS_DEBUG | 53 | #ifdef CONFIG_MAC80211_MESH_PS_DEBUG |
48 | #define MAC80211_MESH_PS_DEBUG 1 | 54 | #define MAC80211_MESH_PS_DEBUG 1 |
49 | #else | 55 | #else |
@@ -157,6 +163,10 @@ do { \ | |||
157 | _sdata_dbg(MAC80211_MESH_SYNC_DEBUG, \ | 163 | _sdata_dbg(MAC80211_MESH_SYNC_DEBUG, \ |
158 | sdata, fmt, ##__VA_ARGS__) | 164 | sdata, fmt, ##__VA_ARGS__) |
159 | 165 | ||
166 | #define mcsa_dbg(sdata, fmt, ...) \ | ||
167 | _sdata_dbg(MAC80211_MESH_CSA_DEBUG, \ | ||
168 | sdata, fmt, ##__VA_ARGS__) | ||
169 | |||
160 | #define mps_dbg(sdata, fmt, ...) \ | 170 | #define mps_dbg(sdata, fmt, ...) \ |
161 | _sdata_dbg(MAC80211_MESH_PS_DEBUG, \ | 171 | _sdata_dbg(MAC80211_MESH_PS_DEBUG, \ |
162 | sdata, fmt, ##__VA_ARGS__) | 172 | sdata, fmt, ##__VA_ARGS__) |
diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index cbaea32bccf1..4ebbcc6f67e0 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h | |||
@@ -603,6 +603,9 @@ struct ieee80211_if_mesh { | |||
603 | int ps_peers_light_sleep; | 603 | int ps_peers_light_sleep; |
604 | int ps_peers_deep_sleep; | 604 | int ps_peers_deep_sleep; |
605 | struct ps_data ps; | 605 | struct ps_data ps; |
606 | /* Channel Switching Support */ | ||
607 | bool chsw_init; | ||
608 | u16 pre_value; | ||
606 | }; | 609 | }; |
607 | 610 | ||
608 | #ifdef CONFIG_MAC80211_MESH | 611 | #ifdef CONFIG_MAC80211_MESH |
@@ -1252,6 +1255,7 @@ struct ieee802_11_elems { | |||
1252 | const struct ieee80211_timeout_interval_ie *timeout_int; | 1255 | const struct ieee80211_timeout_interval_ie *timeout_int; |
1253 | const u8 *opmode_notif; | 1256 | const u8 *opmode_notif; |
1254 | const struct ieee80211_sec_chan_offs_ie *sec_chan_offs; | 1257 | const struct ieee80211_sec_chan_offs_ie *sec_chan_offs; |
1258 | const struct ieee80211_mesh_chansw_params_ie *mesh_chansw_params_ie; | ||
1255 | 1259 | ||
1256 | /* length of them, respectively */ | 1260 | /* length of them, respectively */ |
1257 | u8 ssid_len; | 1261 | u8 ssid_len; |
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(); |
diff --git a/net/mac80211/util.c b/net/mac80211/util.c index 65ebe0c5e835..523783cedf6e 100644 --- a/net/mac80211/util.c +++ b/net/mac80211/util.c | |||
@@ -740,6 +740,7 @@ u32 ieee802_11_parse_elems_crc(const u8 *start, size_t len, bool action, | |||
740 | case WLAN_EID_TIMEOUT_INTERVAL: | 740 | case WLAN_EID_TIMEOUT_INTERVAL: |
741 | case WLAN_EID_SECONDARY_CHANNEL_OFFSET: | 741 | case WLAN_EID_SECONDARY_CHANNEL_OFFSET: |
742 | case WLAN_EID_WIDE_BW_CHANNEL_SWITCH: | 742 | case WLAN_EID_WIDE_BW_CHANNEL_SWITCH: |
743 | case WLAN_EID_CHAN_SWITCH_PARAM: | ||
743 | /* | 744 | /* |
744 | * not listing WLAN_EID_CHANNEL_SWITCH_WRAPPER -- it seems possible | 745 | * not listing WLAN_EID_CHANNEL_SWITCH_WRAPPER -- it seems possible |
745 | * that if the content gets bigger it might be needed more than once | 746 | * that if the content gets bigger it might be needed more than once |
@@ -905,6 +906,14 @@ u32 ieee802_11_parse_elems_crc(const u8 *start, size_t len, bool action, | |||
905 | } | 906 | } |
906 | elems->sec_chan_offs = (void *)pos; | 907 | elems->sec_chan_offs = (void *)pos; |
907 | break; | 908 | break; |
909 | case WLAN_EID_CHAN_SWITCH_PARAM: | ||
910 | if (elen != | ||
911 | sizeof(*elems->mesh_chansw_params_ie)) { | ||
912 | elem_parse_failed = true; | ||
913 | break; | ||
914 | } | ||
915 | elems->mesh_chansw_params_ie = (void *)pos; | ||
916 | break; | ||
908 | case WLAN_EID_WIDE_BW_CHANNEL_SWITCH: | 917 | case WLAN_EID_WIDE_BW_CHANNEL_SWITCH: |
909 | if (!action || | 918 | if (!action || |
910 | elen != sizeof(*elems->wide_bw_chansw_ie)) { | 919 | elen != sizeof(*elems->wide_bw_chansw_ie)) { |