Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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>

authored by

Chun-Yeow Yeoh and committed by
Johannes Berg
8f2535b9 c0f17eb9

+134 -3
+20
include/linux/ieee80211.h
··· 697 697 } __packed; 698 698 699 699 /** 700 + * struct ieee80211_mesh_chansw_params_ie - mesh channel switch parameters IE 701 + * 702 + * This structure represents the "Mesh Channel Switch Paramters element" 703 + */ 704 + struct ieee80211_mesh_chansw_params_ie { 705 + u8 mesh_ttl; 706 + u8 mesh_flags; 707 + __le16 mesh_reason; 708 + __le16 mesh_pre_value; 709 + } __packed; 710 + 711 + /** 700 712 * struct ieee80211_wide_bw_chansw_ie - wide bandwidth channel switch IE 701 713 */ 702 714 struct ieee80211_wide_bw_chansw_ie { ··· 761 749 IEEE80211_MESHCONF_CAPAB_TBTT_ADJUSTING = 0x20, 762 750 IEEE80211_MESHCONF_CAPAB_POWER_SAVE_LEVEL = 0x40, 763 751 }; 752 + 753 + /** 754 + * mesh channel switch parameters element's flag indicator 755 + * 756 + */ 757 + #define WLAN_EID_CHAN_SWITCH_PARAM_TX_RESTRICT BIT(0) 758 + #define WLAN_EID_CHAN_SWITCH_PARAM_INITIATOR BIT(1) 759 + #define WLAN_EID_CHAN_SWITCH_PARAM_REASON BIT(2) 764 760 765 761 /** 766 762 * struct ieee80211_rann_ie
+11
net/mac80211/Kconfig
··· 259 259 260 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 273 config MAC80211_MESH_PS_DEBUG 263 274 bool "Verbose mesh powersave debugging" 264 275 depends on MAC80211_DEBUG_MENU
+10
net/mac80211/debug.h
··· 44 44 #define MAC80211_MESH_SYNC_DEBUG 0 45 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 53 #ifdef CONFIG_MAC80211_MESH_PS_DEBUG 48 54 #define MAC80211_MESH_PS_DEBUG 1 49 55 #else ··· 161 155 162 156 #define msync_dbg(sdata, fmt, ...) \ 163 157 _sdata_dbg(MAC80211_MESH_SYNC_DEBUG, \ 158 + sdata, fmt, ##__VA_ARGS__) 159 + 160 + #define mcsa_dbg(sdata, fmt, ...) \ 161 + _sdata_dbg(MAC80211_MESH_CSA_DEBUG, \ 164 162 sdata, fmt, ##__VA_ARGS__) 165 163 166 164 #define mps_dbg(sdata, fmt, ...) \
+4
net/mac80211/ieee80211_i.h
··· 603 603 int ps_peers_light_sleep; 604 604 int ps_peers_deep_sleep; 605 605 struct ps_data ps; 606 + /* Channel Switching Support */ 607 + bool chsw_init; 608 + u16 pre_value; 606 609 }; 607 610 608 611 #ifdef CONFIG_MAC80211_MESH ··· 1255 1252 const struct ieee80211_timeout_interval_ie *timeout_int; 1256 1253 const u8 *opmode_notif; 1257 1254 const struct ieee80211_sec_chan_offs_ie *sec_chan_offs; 1255 + const struct ieee80211_mesh_chansw_params_ie *mesh_chansw_params_ie; 1258 1256 1259 1257 /* length of them, respectively */ 1260 1258 u8 ssid_len;
+80 -3
net/mac80211/mesh.c
··· 920 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 999 static void ieee80211_mesh_rx_mgmt_action(struct ieee80211_sub_if_data *sdata, 924 1000 struct ieee80211_mgmt *mgmt, 925 1001 size_t len, ··· 1014 938 case WLAN_CATEGORY_MESH_ACTION: 1015 939 if (mesh_action_is_path_sel(mgmt)) 1016 940 mesh_rx_path_sel_frame(sdata, mgmt, len); 941 + break; 942 + case WLAN_CATEGORY_SPECTRUM_MGMT: 943 + mesh_rx_csa_frame(sdata, mgmt, len); 1017 944 break; 1018 945 } 1019 946 } ··· 1135 1056 (unsigned long) sdata); 1136 1057 1137 1058 ifmsh->accepting_plinks = true; 1138 - ifmsh->preq_id = 0; 1139 - ifmsh->sn = 0; 1140 - ifmsh->num_gates = 0; 1141 1059 atomic_set(&ifmsh->mpaths, 0); 1142 1060 mesh_rmc_init(sdata); 1143 1061 ifmsh->last_preq = jiffies; 1144 1062 ifmsh->next_perr = jiffies; 1063 + ifmsh->chsw_init = false; 1145 1064 /* Allocate all mesh structures when creating the first mesh interface. */ 1146 1065 if (!mesh_allocated) 1147 1066 ieee80211s_init();
+9
net/mac80211/util.c
··· 740 740 case WLAN_EID_TIMEOUT_INTERVAL: 741 741 case WLAN_EID_SECONDARY_CHANNEL_OFFSET: 742 742 case WLAN_EID_WIDE_BW_CHANNEL_SWITCH: 743 + case WLAN_EID_CHAN_SWITCH_PARAM: 743 744 /* 744 745 * not listing WLAN_EID_CHANNEL_SWITCH_WRAPPER -- it seems possible 745 746 * that if the content gets bigger it might be needed more than once ··· 905 904 break; 906 905 } 907 906 elems->sec_chan_offs = (void *)pos; 907 + break; 908 + case WLAN_EID_CHAN_SWITCH_PARAM: 909 + if (elen != 910 + sizeof(*elems->mesh_chansw_params_ie)) { 911 + elem_parse_failed = true; 912 + break; 913 + } 914 + elems->mesh_chansw_params_ie = (void *)pos; 908 915 break; 909 916 case WLAN_EID_WIDE_BW_CHANNEL_SWITCH: 910 917 if (!action ||