Commit 32cb05bfe81ed00676a21d914db5729abe3f326f
Committed by
Johannes Berg
1 parent
e76d67f035
Exists in
smarc-imx_3.14.28_1.0.0_ga
and in
1 other branch
mac80211: mesh_plink: group basic fitness checks
The initial frame checks differ depending on whether this is a new peer or not, but they were all intermixed with sta checks as necessary. Group them together so the two cases are clearer. Signed-off-by: Bob Copeland <bob@cozybit.com> Signed-off-by: Johannes Berg <johannes.berg@intel.com>
Showing 1 changed file with 37 additions and 33 deletions Inline Diff
net/mac80211/mesh_plink.c
1 | /* | 1 | /* |
2 | * Copyright (c) 2008, 2009 open80211s Ltd. | 2 | * Copyright (c) 2008, 2009 open80211s Ltd. |
3 | * Author: Luis Carlos Cobo <luisca@cozybit.com> | 3 | * Author: Luis Carlos Cobo <luisca@cozybit.com> |
4 | * | 4 | * |
5 | * This program is free software; you can redistribute it and/or modify | 5 | * This program is free software; you can redistribute it and/or modify |
6 | * it under the terms of the GNU General Public License version 2 as | 6 | * it under the terms of the GNU General Public License version 2 as |
7 | * published by the Free Software Foundation. | 7 | * published by the Free Software Foundation. |
8 | */ | 8 | */ |
9 | #include <linux/gfp.h> | 9 | #include <linux/gfp.h> |
10 | #include <linux/kernel.h> | 10 | #include <linux/kernel.h> |
11 | #include <linux/random.h> | 11 | #include <linux/random.h> |
12 | #include "ieee80211_i.h" | 12 | #include "ieee80211_i.h" |
13 | #include "rate.h" | 13 | #include "rate.h" |
14 | #include "mesh.h" | 14 | #include "mesh.h" |
15 | 15 | ||
16 | #define PLINK_GET_LLID(p) (p + 2) | 16 | #define PLINK_GET_LLID(p) (p + 2) |
17 | #define PLINK_GET_PLID(p) (p + 4) | 17 | #define PLINK_GET_PLID(p) (p + 4) |
18 | 18 | ||
19 | #define mod_plink_timer(s, t) (mod_timer(&s->plink_timer, \ | 19 | #define mod_plink_timer(s, t) (mod_timer(&s->plink_timer, \ |
20 | jiffies + HZ * t / 1000)) | 20 | jiffies + HZ * t / 1000)) |
21 | 21 | ||
22 | /* We only need a valid sta if user configured a minimum rssi_threshold. */ | 22 | /* We only need a valid sta if user configured a minimum rssi_threshold. */ |
23 | #define rssi_threshold_check(sta, sdata) \ | 23 | #define rssi_threshold_check(sta, sdata) \ |
24 | (sdata->u.mesh.mshcfg.rssi_threshold == 0 ||\ | 24 | (sdata->u.mesh.mshcfg.rssi_threshold == 0 ||\ |
25 | (sta && (s8) -ewma_read(&sta->avg_signal) > \ | 25 | (sta && (s8) -ewma_read(&sta->avg_signal) > \ |
26 | sdata->u.mesh.mshcfg.rssi_threshold)) | 26 | sdata->u.mesh.mshcfg.rssi_threshold)) |
27 | 27 | ||
28 | enum plink_event { | 28 | enum plink_event { |
29 | PLINK_UNDEFINED, | 29 | PLINK_UNDEFINED, |
30 | OPN_ACPT, | 30 | OPN_ACPT, |
31 | OPN_RJCT, | 31 | OPN_RJCT, |
32 | OPN_IGNR, | 32 | OPN_IGNR, |
33 | CNF_ACPT, | 33 | CNF_ACPT, |
34 | CNF_RJCT, | 34 | CNF_RJCT, |
35 | CNF_IGNR, | 35 | CNF_IGNR, |
36 | CLS_ACPT, | 36 | CLS_ACPT, |
37 | CLS_IGNR | 37 | CLS_IGNR |
38 | }; | 38 | }; |
39 | 39 | ||
40 | static const char * const mplstates[] = { | 40 | static const char * const mplstates[] = { |
41 | [NL80211_PLINK_LISTEN] = "LISTEN", | 41 | [NL80211_PLINK_LISTEN] = "LISTEN", |
42 | [NL80211_PLINK_OPN_SNT] = "OPN-SNT", | 42 | [NL80211_PLINK_OPN_SNT] = "OPN-SNT", |
43 | [NL80211_PLINK_OPN_RCVD] = "OPN-RCVD", | 43 | [NL80211_PLINK_OPN_RCVD] = "OPN-RCVD", |
44 | [NL80211_PLINK_CNF_RCVD] = "CNF_RCVD", | 44 | [NL80211_PLINK_CNF_RCVD] = "CNF_RCVD", |
45 | [NL80211_PLINK_ESTAB] = "ESTAB", | 45 | [NL80211_PLINK_ESTAB] = "ESTAB", |
46 | [NL80211_PLINK_HOLDING] = "HOLDING", | 46 | [NL80211_PLINK_HOLDING] = "HOLDING", |
47 | [NL80211_PLINK_BLOCKED] = "BLOCKED" | 47 | [NL80211_PLINK_BLOCKED] = "BLOCKED" |
48 | }; | 48 | }; |
49 | 49 | ||
50 | static const char * const mplevents[] = { | 50 | static const char * const mplevents[] = { |
51 | [PLINK_UNDEFINED] = "NONE", | 51 | [PLINK_UNDEFINED] = "NONE", |
52 | [OPN_ACPT] = "OPN_ACPT", | 52 | [OPN_ACPT] = "OPN_ACPT", |
53 | [OPN_RJCT] = "OPN_RJCT", | 53 | [OPN_RJCT] = "OPN_RJCT", |
54 | [OPN_IGNR] = "OPN_IGNR", | 54 | [OPN_IGNR] = "OPN_IGNR", |
55 | [CNF_ACPT] = "CNF_ACPT", | 55 | [CNF_ACPT] = "CNF_ACPT", |
56 | [CNF_RJCT] = "CNF_RJCT", | 56 | [CNF_RJCT] = "CNF_RJCT", |
57 | [CNF_IGNR] = "CNF_IGNR", | 57 | [CNF_IGNR] = "CNF_IGNR", |
58 | [CLS_ACPT] = "CLS_ACPT", | 58 | [CLS_ACPT] = "CLS_ACPT", |
59 | [CLS_IGNR] = "CLS_IGNR" | 59 | [CLS_IGNR] = "CLS_IGNR" |
60 | }; | 60 | }; |
61 | 61 | ||
62 | static int mesh_plink_frame_tx(struct ieee80211_sub_if_data *sdata, | 62 | static int mesh_plink_frame_tx(struct ieee80211_sub_if_data *sdata, |
63 | enum ieee80211_self_protected_actioncode action, | 63 | enum ieee80211_self_protected_actioncode action, |
64 | u8 *da, __le16 llid, __le16 plid, __le16 reason); | 64 | u8 *da, __le16 llid, __le16 plid, __le16 reason); |
65 | 65 | ||
66 | /** | 66 | /** |
67 | * mesh_plink_fsm_restart - restart a mesh peer link finite state machine | 67 | * mesh_plink_fsm_restart - restart a mesh peer link finite state machine |
68 | * | 68 | * |
69 | * @sta: mesh peer link to restart | 69 | * @sta: mesh peer link to restart |
70 | * | 70 | * |
71 | * Locking: this function must be called holding sta->lock | 71 | * Locking: this function must be called holding sta->lock |
72 | */ | 72 | */ |
73 | static inline void mesh_plink_fsm_restart(struct sta_info *sta) | 73 | static inline void mesh_plink_fsm_restart(struct sta_info *sta) |
74 | { | 74 | { |
75 | sta->plink_state = NL80211_PLINK_LISTEN; | 75 | sta->plink_state = NL80211_PLINK_LISTEN; |
76 | sta->llid = sta->plid = sta->reason = 0; | 76 | sta->llid = sta->plid = sta->reason = 0; |
77 | sta->plink_retries = 0; | 77 | sta->plink_retries = 0; |
78 | } | 78 | } |
79 | 79 | ||
80 | /* | 80 | /* |
81 | * mesh_set_short_slot_time - enable / disable ERP short slot time. | 81 | * mesh_set_short_slot_time - enable / disable ERP short slot time. |
82 | * | 82 | * |
83 | * The standard indirectly mandates mesh STAs to turn off short slot time by | 83 | * The standard indirectly mandates mesh STAs to turn off short slot time by |
84 | * disallowing advertising this (802.11-2012 8.4.1.4), but that doesn't mean we | 84 | * disallowing advertising this (802.11-2012 8.4.1.4), but that doesn't mean we |
85 | * can't be sneaky about it. Enable short slot time if all mesh STAs in the | 85 | * can't be sneaky about it. Enable short slot time if all mesh STAs in the |
86 | * MBSS support ERP rates. | 86 | * MBSS support ERP rates. |
87 | * | 87 | * |
88 | * Returns BSS_CHANGED_ERP_SLOT or 0 for no change. | 88 | * Returns BSS_CHANGED_ERP_SLOT or 0 for no change. |
89 | */ | 89 | */ |
90 | static u32 mesh_set_short_slot_time(struct ieee80211_sub_if_data *sdata) | 90 | static u32 mesh_set_short_slot_time(struct ieee80211_sub_if_data *sdata) |
91 | { | 91 | { |
92 | struct ieee80211_local *local = sdata->local; | 92 | struct ieee80211_local *local = sdata->local; |
93 | enum ieee80211_band band = ieee80211_get_sdata_band(sdata); | 93 | enum ieee80211_band band = ieee80211_get_sdata_band(sdata); |
94 | struct ieee80211_supported_band *sband = local->hw.wiphy->bands[band]; | 94 | struct ieee80211_supported_band *sband = local->hw.wiphy->bands[band]; |
95 | struct sta_info *sta; | 95 | struct sta_info *sta; |
96 | u32 erp_rates = 0, changed = 0; | 96 | u32 erp_rates = 0, changed = 0; |
97 | int i; | 97 | int i; |
98 | bool short_slot = false; | 98 | bool short_slot = false; |
99 | 99 | ||
100 | if (band == IEEE80211_BAND_5GHZ) { | 100 | if (band == IEEE80211_BAND_5GHZ) { |
101 | /* (IEEE 802.11-2012 19.4.5) */ | 101 | /* (IEEE 802.11-2012 19.4.5) */ |
102 | short_slot = true; | 102 | short_slot = true; |
103 | goto out; | 103 | goto out; |
104 | } else if (band != IEEE80211_BAND_2GHZ || | 104 | } else if (band != IEEE80211_BAND_2GHZ || |
105 | (band == IEEE80211_BAND_2GHZ && | 105 | (band == IEEE80211_BAND_2GHZ && |
106 | local->hw.flags & IEEE80211_HW_2GHZ_SHORT_SLOT_INCAPABLE)) | 106 | local->hw.flags & IEEE80211_HW_2GHZ_SHORT_SLOT_INCAPABLE)) |
107 | goto out; | 107 | goto out; |
108 | 108 | ||
109 | for (i = 0; i < sband->n_bitrates; i++) | 109 | for (i = 0; i < sband->n_bitrates; i++) |
110 | if (sband->bitrates[i].flags & IEEE80211_RATE_ERP_G) | 110 | if (sband->bitrates[i].flags & IEEE80211_RATE_ERP_G) |
111 | erp_rates |= BIT(i); | 111 | erp_rates |= BIT(i); |
112 | 112 | ||
113 | if (!erp_rates) | 113 | if (!erp_rates) |
114 | goto out; | 114 | goto out; |
115 | 115 | ||
116 | rcu_read_lock(); | 116 | rcu_read_lock(); |
117 | list_for_each_entry_rcu(sta, &local->sta_list, list) { | 117 | list_for_each_entry_rcu(sta, &local->sta_list, list) { |
118 | if (sdata != sta->sdata || | 118 | if (sdata != sta->sdata || |
119 | sta->plink_state != NL80211_PLINK_ESTAB) | 119 | sta->plink_state != NL80211_PLINK_ESTAB) |
120 | continue; | 120 | continue; |
121 | 121 | ||
122 | short_slot = false; | 122 | short_slot = false; |
123 | if (erp_rates & sta->sta.supp_rates[band]) | 123 | if (erp_rates & sta->sta.supp_rates[band]) |
124 | short_slot = true; | 124 | short_slot = true; |
125 | else | 125 | else |
126 | break; | 126 | break; |
127 | } | 127 | } |
128 | rcu_read_unlock(); | 128 | rcu_read_unlock(); |
129 | 129 | ||
130 | out: | 130 | out: |
131 | if (sdata->vif.bss_conf.use_short_slot != short_slot) { | 131 | if (sdata->vif.bss_conf.use_short_slot != short_slot) { |
132 | sdata->vif.bss_conf.use_short_slot = short_slot; | 132 | sdata->vif.bss_conf.use_short_slot = short_slot; |
133 | changed = BSS_CHANGED_ERP_SLOT; | 133 | changed = BSS_CHANGED_ERP_SLOT; |
134 | mpl_dbg(sdata, "mesh_plink %pM: ERP short slot time %d\n", | 134 | mpl_dbg(sdata, "mesh_plink %pM: ERP short slot time %d\n", |
135 | sdata->vif.addr, short_slot); | 135 | sdata->vif.addr, short_slot); |
136 | } | 136 | } |
137 | return changed; | 137 | return changed; |
138 | } | 138 | } |
139 | 139 | ||
140 | /** | 140 | /** |
141 | * mesh_set_ht_prot_mode - set correct HT protection mode | 141 | * mesh_set_ht_prot_mode - set correct HT protection mode |
142 | * | 142 | * |
143 | * Section 9.23.3.5 of IEEE 80211-2012 describes the protection rules for HT | 143 | * Section 9.23.3.5 of IEEE 80211-2012 describes the protection rules for HT |
144 | * mesh STA in a MBSS. Three HT protection modes are supported for now, non-HT | 144 | * mesh STA in a MBSS. Three HT protection modes are supported for now, non-HT |
145 | * mixed mode, 20MHz-protection and no-protection mode. non-HT mixed mode is | 145 | * mixed mode, 20MHz-protection and no-protection mode. non-HT mixed mode is |
146 | * selected if any non-HT peers are present in our MBSS. 20MHz-protection mode | 146 | * selected if any non-HT peers are present in our MBSS. 20MHz-protection mode |
147 | * is selected if all peers in our 20/40MHz MBSS support HT and atleast one | 147 | * is selected if all peers in our 20/40MHz MBSS support HT and atleast one |
148 | * HT20 peer is present. Otherwise no-protection mode is selected. | 148 | * HT20 peer is present. Otherwise no-protection mode is selected. |
149 | */ | 149 | */ |
150 | static u32 mesh_set_ht_prot_mode(struct ieee80211_sub_if_data *sdata) | 150 | static u32 mesh_set_ht_prot_mode(struct ieee80211_sub_if_data *sdata) |
151 | { | 151 | { |
152 | struct ieee80211_local *local = sdata->local; | 152 | struct ieee80211_local *local = sdata->local; |
153 | struct sta_info *sta; | 153 | struct sta_info *sta; |
154 | u16 ht_opmode; | 154 | u16 ht_opmode; |
155 | bool non_ht_sta = false, ht20_sta = false; | 155 | bool non_ht_sta = false, ht20_sta = false; |
156 | 156 | ||
157 | switch (sdata->vif.bss_conf.chandef.width) { | 157 | switch (sdata->vif.bss_conf.chandef.width) { |
158 | case NL80211_CHAN_WIDTH_20_NOHT: | 158 | case NL80211_CHAN_WIDTH_20_NOHT: |
159 | case NL80211_CHAN_WIDTH_5: | 159 | case NL80211_CHAN_WIDTH_5: |
160 | case NL80211_CHAN_WIDTH_10: | 160 | case NL80211_CHAN_WIDTH_10: |
161 | return 0; | 161 | return 0; |
162 | default: | 162 | default: |
163 | break; | 163 | break; |
164 | } | 164 | } |
165 | 165 | ||
166 | rcu_read_lock(); | 166 | rcu_read_lock(); |
167 | list_for_each_entry_rcu(sta, &local->sta_list, list) { | 167 | list_for_each_entry_rcu(sta, &local->sta_list, list) { |
168 | if (sdata != sta->sdata || | 168 | if (sdata != sta->sdata || |
169 | sta->plink_state != NL80211_PLINK_ESTAB) | 169 | sta->plink_state != NL80211_PLINK_ESTAB) |
170 | continue; | 170 | continue; |
171 | 171 | ||
172 | if (sta->sta.bandwidth > IEEE80211_STA_RX_BW_20) | 172 | if (sta->sta.bandwidth > IEEE80211_STA_RX_BW_20) |
173 | continue; | 173 | continue; |
174 | 174 | ||
175 | if (!sta->sta.ht_cap.ht_supported) { | 175 | if (!sta->sta.ht_cap.ht_supported) { |
176 | mpl_dbg(sdata, "nonHT sta (%pM) is present\n", | 176 | mpl_dbg(sdata, "nonHT sta (%pM) is present\n", |
177 | sta->sta.addr); | 177 | sta->sta.addr); |
178 | non_ht_sta = true; | 178 | non_ht_sta = true; |
179 | break; | 179 | break; |
180 | } | 180 | } |
181 | 181 | ||
182 | mpl_dbg(sdata, "HT20 sta (%pM) is present\n", sta->sta.addr); | 182 | mpl_dbg(sdata, "HT20 sta (%pM) is present\n", sta->sta.addr); |
183 | ht20_sta = true; | 183 | ht20_sta = true; |
184 | } | 184 | } |
185 | rcu_read_unlock(); | 185 | rcu_read_unlock(); |
186 | 186 | ||
187 | if (non_ht_sta) | 187 | if (non_ht_sta) |
188 | ht_opmode = IEEE80211_HT_OP_MODE_PROTECTION_NONHT_MIXED; | 188 | ht_opmode = IEEE80211_HT_OP_MODE_PROTECTION_NONHT_MIXED; |
189 | else if (ht20_sta && | 189 | else if (ht20_sta && |
190 | sdata->vif.bss_conf.chandef.width > NL80211_CHAN_WIDTH_20) | 190 | sdata->vif.bss_conf.chandef.width > NL80211_CHAN_WIDTH_20) |
191 | ht_opmode = IEEE80211_HT_OP_MODE_PROTECTION_20MHZ; | 191 | ht_opmode = IEEE80211_HT_OP_MODE_PROTECTION_20MHZ; |
192 | else | 192 | else |
193 | ht_opmode = IEEE80211_HT_OP_MODE_PROTECTION_NONE; | 193 | ht_opmode = IEEE80211_HT_OP_MODE_PROTECTION_NONE; |
194 | 194 | ||
195 | if (sdata->vif.bss_conf.ht_operation_mode == ht_opmode) | 195 | if (sdata->vif.bss_conf.ht_operation_mode == ht_opmode) |
196 | return 0; | 196 | return 0; |
197 | 197 | ||
198 | sdata->vif.bss_conf.ht_operation_mode = ht_opmode; | 198 | sdata->vif.bss_conf.ht_operation_mode = ht_opmode; |
199 | sdata->u.mesh.mshcfg.ht_opmode = ht_opmode; | 199 | sdata->u.mesh.mshcfg.ht_opmode = ht_opmode; |
200 | mpl_dbg(sdata, "selected new HT protection mode %d\n", ht_opmode); | 200 | mpl_dbg(sdata, "selected new HT protection mode %d\n", ht_opmode); |
201 | return BSS_CHANGED_HT; | 201 | return BSS_CHANGED_HT; |
202 | } | 202 | } |
203 | 203 | ||
204 | /** | 204 | /** |
205 | * __mesh_plink_deactivate - deactivate mesh peer link | 205 | * __mesh_plink_deactivate - deactivate mesh peer link |
206 | * | 206 | * |
207 | * @sta: mesh peer link to deactivate | 207 | * @sta: mesh peer link to deactivate |
208 | * | 208 | * |
209 | * All mesh paths with this peer as next hop will be flushed | 209 | * All mesh paths with this peer as next hop will be flushed |
210 | * Returns beacon changed flag if the beacon content changed. | 210 | * Returns beacon changed flag if the beacon content changed. |
211 | * | 211 | * |
212 | * Locking: the caller must hold sta->lock | 212 | * Locking: the caller must hold sta->lock |
213 | */ | 213 | */ |
214 | static u32 __mesh_plink_deactivate(struct sta_info *sta) | 214 | static u32 __mesh_plink_deactivate(struct sta_info *sta) |
215 | { | 215 | { |
216 | struct ieee80211_sub_if_data *sdata = sta->sdata; | 216 | struct ieee80211_sub_if_data *sdata = sta->sdata; |
217 | u32 changed = 0; | 217 | u32 changed = 0; |
218 | 218 | ||
219 | if (sta->plink_state == NL80211_PLINK_ESTAB) | 219 | if (sta->plink_state == NL80211_PLINK_ESTAB) |
220 | changed = mesh_plink_dec_estab_count(sdata); | 220 | changed = mesh_plink_dec_estab_count(sdata); |
221 | sta->plink_state = NL80211_PLINK_BLOCKED; | 221 | sta->plink_state = NL80211_PLINK_BLOCKED; |
222 | mesh_path_flush_by_nexthop(sta); | 222 | mesh_path_flush_by_nexthop(sta); |
223 | 223 | ||
224 | ieee80211_mps_sta_status_update(sta); | 224 | ieee80211_mps_sta_status_update(sta); |
225 | changed |= ieee80211_mps_set_sta_local_pm(sta, | 225 | changed |= ieee80211_mps_set_sta_local_pm(sta, |
226 | NL80211_MESH_POWER_UNKNOWN); | 226 | NL80211_MESH_POWER_UNKNOWN); |
227 | 227 | ||
228 | return changed; | 228 | return changed; |
229 | } | 229 | } |
230 | 230 | ||
231 | /** | 231 | /** |
232 | * mesh_plink_deactivate - deactivate mesh peer link | 232 | * mesh_plink_deactivate - deactivate mesh peer link |
233 | * | 233 | * |
234 | * @sta: mesh peer link to deactivate | 234 | * @sta: mesh peer link to deactivate |
235 | * | 235 | * |
236 | * All mesh paths with this peer as next hop will be flushed | 236 | * All mesh paths with this peer as next hop will be flushed |
237 | */ | 237 | */ |
238 | u32 mesh_plink_deactivate(struct sta_info *sta) | 238 | u32 mesh_plink_deactivate(struct sta_info *sta) |
239 | { | 239 | { |
240 | struct ieee80211_sub_if_data *sdata = sta->sdata; | 240 | struct ieee80211_sub_if_data *sdata = sta->sdata; |
241 | u32 changed; | 241 | u32 changed; |
242 | 242 | ||
243 | spin_lock_bh(&sta->lock); | 243 | spin_lock_bh(&sta->lock); |
244 | changed = __mesh_plink_deactivate(sta); | 244 | changed = __mesh_plink_deactivate(sta); |
245 | sta->reason = cpu_to_le16(WLAN_REASON_MESH_PEER_CANCELED); | 245 | sta->reason = cpu_to_le16(WLAN_REASON_MESH_PEER_CANCELED); |
246 | mesh_plink_frame_tx(sdata, WLAN_SP_MESH_PEERING_CLOSE, | 246 | mesh_plink_frame_tx(sdata, WLAN_SP_MESH_PEERING_CLOSE, |
247 | sta->sta.addr, sta->llid, sta->plid, | 247 | sta->sta.addr, sta->llid, sta->plid, |
248 | sta->reason); | 248 | sta->reason); |
249 | spin_unlock_bh(&sta->lock); | 249 | spin_unlock_bh(&sta->lock); |
250 | 250 | ||
251 | return changed; | 251 | return changed; |
252 | } | 252 | } |
253 | 253 | ||
254 | static int mesh_plink_frame_tx(struct ieee80211_sub_if_data *sdata, | 254 | static int mesh_plink_frame_tx(struct ieee80211_sub_if_data *sdata, |
255 | enum ieee80211_self_protected_actioncode action, | 255 | enum ieee80211_self_protected_actioncode action, |
256 | u8 *da, __le16 llid, __le16 plid, __le16 reason) | 256 | u8 *da, __le16 llid, __le16 plid, __le16 reason) |
257 | { | 257 | { |
258 | struct ieee80211_local *local = sdata->local; | 258 | struct ieee80211_local *local = sdata->local; |
259 | struct sk_buff *skb; | 259 | struct sk_buff *skb; |
260 | struct ieee80211_tx_info *info; | 260 | struct ieee80211_tx_info *info; |
261 | struct ieee80211_mgmt *mgmt; | 261 | struct ieee80211_mgmt *mgmt; |
262 | bool include_plid = false; | 262 | bool include_plid = false; |
263 | u16 peering_proto = 0; | 263 | u16 peering_proto = 0; |
264 | u8 *pos, ie_len = 4; | 264 | u8 *pos, ie_len = 4; |
265 | int hdr_len = offsetof(struct ieee80211_mgmt, u.action.u.self_prot) + | 265 | int hdr_len = offsetof(struct ieee80211_mgmt, u.action.u.self_prot) + |
266 | sizeof(mgmt->u.action.u.self_prot); | 266 | sizeof(mgmt->u.action.u.self_prot); |
267 | int err = -ENOMEM; | 267 | int err = -ENOMEM; |
268 | 268 | ||
269 | skb = dev_alloc_skb(local->tx_headroom + | 269 | skb = dev_alloc_skb(local->tx_headroom + |
270 | hdr_len + | 270 | hdr_len + |
271 | 2 + /* capability info */ | 271 | 2 + /* capability info */ |
272 | 2 + /* AID */ | 272 | 2 + /* AID */ |
273 | 2 + 8 + /* supported rates */ | 273 | 2 + 8 + /* supported rates */ |
274 | 2 + (IEEE80211_MAX_SUPP_RATES - 8) + | 274 | 2 + (IEEE80211_MAX_SUPP_RATES - 8) + |
275 | 2 + sdata->u.mesh.mesh_id_len + | 275 | 2 + sdata->u.mesh.mesh_id_len + |
276 | 2 + sizeof(struct ieee80211_meshconf_ie) + | 276 | 2 + sizeof(struct ieee80211_meshconf_ie) + |
277 | 2 + sizeof(struct ieee80211_ht_cap) + | 277 | 2 + sizeof(struct ieee80211_ht_cap) + |
278 | 2 + sizeof(struct ieee80211_ht_operation) + | 278 | 2 + sizeof(struct ieee80211_ht_operation) + |
279 | 2 + 8 + /* peering IE */ | 279 | 2 + 8 + /* peering IE */ |
280 | sdata->u.mesh.ie_len); | 280 | sdata->u.mesh.ie_len); |
281 | if (!skb) | 281 | if (!skb) |
282 | return -1; | 282 | return -1; |
283 | info = IEEE80211_SKB_CB(skb); | 283 | info = IEEE80211_SKB_CB(skb); |
284 | skb_reserve(skb, local->tx_headroom); | 284 | skb_reserve(skb, local->tx_headroom); |
285 | mgmt = (struct ieee80211_mgmt *) skb_put(skb, hdr_len); | 285 | mgmt = (struct ieee80211_mgmt *) skb_put(skb, hdr_len); |
286 | memset(mgmt, 0, hdr_len); | 286 | memset(mgmt, 0, hdr_len); |
287 | mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT | | 287 | mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT | |
288 | IEEE80211_STYPE_ACTION); | 288 | IEEE80211_STYPE_ACTION); |
289 | memcpy(mgmt->da, da, ETH_ALEN); | 289 | memcpy(mgmt->da, da, ETH_ALEN); |
290 | memcpy(mgmt->sa, sdata->vif.addr, ETH_ALEN); | 290 | memcpy(mgmt->sa, sdata->vif.addr, ETH_ALEN); |
291 | memcpy(mgmt->bssid, sdata->vif.addr, ETH_ALEN); | 291 | memcpy(mgmt->bssid, sdata->vif.addr, ETH_ALEN); |
292 | mgmt->u.action.category = WLAN_CATEGORY_SELF_PROTECTED; | 292 | mgmt->u.action.category = WLAN_CATEGORY_SELF_PROTECTED; |
293 | mgmt->u.action.u.self_prot.action_code = action; | 293 | mgmt->u.action.u.self_prot.action_code = action; |
294 | 294 | ||
295 | if (action != WLAN_SP_MESH_PEERING_CLOSE) { | 295 | if (action != WLAN_SP_MESH_PEERING_CLOSE) { |
296 | enum ieee80211_band band = ieee80211_get_sdata_band(sdata); | 296 | enum ieee80211_band band = ieee80211_get_sdata_band(sdata); |
297 | 297 | ||
298 | /* capability info */ | 298 | /* capability info */ |
299 | pos = skb_put(skb, 2); | 299 | pos = skb_put(skb, 2); |
300 | memset(pos, 0, 2); | 300 | memset(pos, 0, 2); |
301 | if (action == WLAN_SP_MESH_PEERING_CONFIRM) { | 301 | if (action == WLAN_SP_MESH_PEERING_CONFIRM) { |
302 | /* AID */ | 302 | /* AID */ |
303 | pos = skb_put(skb, 2); | 303 | pos = skb_put(skb, 2); |
304 | memcpy(pos + 2, &plid, 2); | 304 | memcpy(pos + 2, &plid, 2); |
305 | } | 305 | } |
306 | if (ieee80211_add_srates_ie(sdata, skb, true, band) || | 306 | if (ieee80211_add_srates_ie(sdata, skb, true, band) || |
307 | ieee80211_add_ext_srates_ie(sdata, skb, true, band) || | 307 | ieee80211_add_ext_srates_ie(sdata, skb, true, band) || |
308 | mesh_add_rsn_ie(sdata, skb) || | 308 | mesh_add_rsn_ie(sdata, skb) || |
309 | mesh_add_meshid_ie(sdata, skb) || | 309 | mesh_add_meshid_ie(sdata, skb) || |
310 | mesh_add_meshconf_ie(sdata, skb)) | 310 | mesh_add_meshconf_ie(sdata, skb)) |
311 | goto free; | 311 | goto free; |
312 | } else { /* WLAN_SP_MESH_PEERING_CLOSE */ | 312 | } else { /* WLAN_SP_MESH_PEERING_CLOSE */ |
313 | info->flags |= IEEE80211_TX_CTL_NO_ACK; | 313 | info->flags |= IEEE80211_TX_CTL_NO_ACK; |
314 | if (mesh_add_meshid_ie(sdata, skb)) | 314 | if (mesh_add_meshid_ie(sdata, skb)) |
315 | goto free; | 315 | goto free; |
316 | } | 316 | } |
317 | 317 | ||
318 | /* Add Mesh Peering Management element */ | 318 | /* Add Mesh Peering Management element */ |
319 | switch (action) { | 319 | switch (action) { |
320 | case WLAN_SP_MESH_PEERING_OPEN: | 320 | case WLAN_SP_MESH_PEERING_OPEN: |
321 | break; | 321 | break; |
322 | case WLAN_SP_MESH_PEERING_CONFIRM: | 322 | case WLAN_SP_MESH_PEERING_CONFIRM: |
323 | ie_len += 2; | 323 | ie_len += 2; |
324 | include_plid = true; | 324 | include_plid = true; |
325 | break; | 325 | break; |
326 | case WLAN_SP_MESH_PEERING_CLOSE: | 326 | case WLAN_SP_MESH_PEERING_CLOSE: |
327 | if (plid) { | 327 | if (plid) { |
328 | ie_len += 2; | 328 | ie_len += 2; |
329 | include_plid = true; | 329 | include_plid = true; |
330 | } | 330 | } |
331 | ie_len += 2; /* reason code */ | 331 | ie_len += 2; /* reason code */ |
332 | break; | 332 | break; |
333 | default: | 333 | default: |
334 | err = -EINVAL; | 334 | err = -EINVAL; |
335 | goto free; | 335 | goto free; |
336 | } | 336 | } |
337 | 337 | ||
338 | if (WARN_ON(skb_tailroom(skb) < 2 + ie_len)) | 338 | if (WARN_ON(skb_tailroom(skb) < 2 + ie_len)) |
339 | goto free; | 339 | goto free; |
340 | 340 | ||
341 | pos = skb_put(skb, 2 + ie_len); | 341 | pos = skb_put(skb, 2 + ie_len); |
342 | *pos++ = WLAN_EID_PEER_MGMT; | 342 | *pos++ = WLAN_EID_PEER_MGMT; |
343 | *pos++ = ie_len; | 343 | *pos++ = ie_len; |
344 | memcpy(pos, &peering_proto, 2); | 344 | memcpy(pos, &peering_proto, 2); |
345 | pos += 2; | 345 | pos += 2; |
346 | memcpy(pos, &llid, 2); | 346 | memcpy(pos, &llid, 2); |
347 | pos += 2; | 347 | pos += 2; |
348 | if (include_plid) { | 348 | if (include_plid) { |
349 | memcpy(pos, &plid, 2); | 349 | memcpy(pos, &plid, 2); |
350 | pos += 2; | 350 | pos += 2; |
351 | } | 351 | } |
352 | if (action == WLAN_SP_MESH_PEERING_CLOSE) { | 352 | if (action == WLAN_SP_MESH_PEERING_CLOSE) { |
353 | memcpy(pos, &reason, 2); | 353 | memcpy(pos, &reason, 2); |
354 | pos += 2; | 354 | pos += 2; |
355 | } | 355 | } |
356 | 356 | ||
357 | if (action != WLAN_SP_MESH_PEERING_CLOSE) { | 357 | if (action != WLAN_SP_MESH_PEERING_CLOSE) { |
358 | if (mesh_add_ht_cap_ie(sdata, skb) || | 358 | if (mesh_add_ht_cap_ie(sdata, skb) || |
359 | mesh_add_ht_oper_ie(sdata, skb)) | 359 | mesh_add_ht_oper_ie(sdata, skb)) |
360 | goto free; | 360 | goto free; |
361 | } | 361 | } |
362 | 362 | ||
363 | if (mesh_add_vendor_ies(sdata, skb)) | 363 | if (mesh_add_vendor_ies(sdata, skb)) |
364 | goto free; | 364 | goto free; |
365 | 365 | ||
366 | ieee80211_tx_skb(sdata, skb); | 366 | ieee80211_tx_skb(sdata, skb); |
367 | return 0; | 367 | return 0; |
368 | free: | 368 | free: |
369 | kfree_skb(skb); | 369 | kfree_skb(skb); |
370 | return err; | 370 | return err; |
371 | } | 371 | } |
372 | 372 | ||
373 | static void mesh_sta_info_init(struct ieee80211_sub_if_data *sdata, | 373 | static void mesh_sta_info_init(struct ieee80211_sub_if_data *sdata, |
374 | struct sta_info *sta, | 374 | struct sta_info *sta, |
375 | struct ieee802_11_elems *elems, bool insert) | 375 | struct ieee802_11_elems *elems, bool insert) |
376 | { | 376 | { |
377 | struct ieee80211_local *local = sdata->local; | 377 | struct ieee80211_local *local = sdata->local; |
378 | enum ieee80211_band band = ieee80211_get_sdata_band(sdata); | 378 | enum ieee80211_band band = ieee80211_get_sdata_band(sdata); |
379 | struct ieee80211_supported_band *sband; | 379 | struct ieee80211_supported_band *sband; |
380 | u32 rates, basic_rates = 0, changed = 0; | 380 | u32 rates, basic_rates = 0, changed = 0; |
381 | 381 | ||
382 | sband = local->hw.wiphy->bands[band]; | 382 | sband = local->hw.wiphy->bands[band]; |
383 | rates = ieee80211_sta_get_rates(sdata, elems, band, &basic_rates); | 383 | rates = ieee80211_sta_get_rates(sdata, elems, band, &basic_rates); |
384 | 384 | ||
385 | spin_lock_bh(&sta->lock); | 385 | spin_lock_bh(&sta->lock); |
386 | sta->last_rx = jiffies; | 386 | sta->last_rx = jiffies; |
387 | 387 | ||
388 | /* rates and capabilities don't change during peering */ | 388 | /* rates and capabilities don't change during peering */ |
389 | if (sta->plink_state == NL80211_PLINK_ESTAB) | 389 | if (sta->plink_state == NL80211_PLINK_ESTAB) |
390 | goto out; | 390 | goto out; |
391 | 391 | ||
392 | if (sta->sta.supp_rates[band] != rates) | 392 | if (sta->sta.supp_rates[band] != rates) |
393 | changed |= IEEE80211_RC_SUPP_RATES_CHANGED; | 393 | changed |= IEEE80211_RC_SUPP_RATES_CHANGED; |
394 | sta->sta.supp_rates[band] = rates; | 394 | sta->sta.supp_rates[band] = rates; |
395 | 395 | ||
396 | if (ieee80211_ht_cap_ie_to_sta_ht_cap(sdata, sband, | 396 | if (ieee80211_ht_cap_ie_to_sta_ht_cap(sdata, sband, |
397 | elems->ht_cap_elem, sta)) | 397 | elems->ht_cap_elem, sta)) |
398 | changed |= IEEE80211_RC_BW_CHANGED; | 398 | changed |= IEEE80211_RC_BW_CHANGED; |
399 | 399 | ||
400 | /* HT peer is operating 20MHz-only */ | 400 | /* HT peer is operating 20MHz-only */ |
401 | if (elems->ht_operation && | 401 | if (elems->ht_operation && |
402 | !(elems->ht_operation->ht_param & | 402 | !(elems->ht_operation->ht_param & |
403 | IEEE80211_HT_PARAM_CHAN_WIDTH_ANY)) { | 403 | IEEE80211_HT_PARAM_CHAN_WIDTH_ANY)) { |
404 | if (sta->sta.bandwidth != IEEE80211_STA_RX_BW_20) | 404 | if (sta->sta.bandwidth != IEEE80211_STA_RX_BW_20) |
405 | changed |= IEEE80211_RC_BW_CHANGED; | 405 | changed |= IEEE80211_RC_BW_CHANGED; |
406 | sta->sta.bandwidth = IEEE80211_STA_RX_BW_20; | 406 | sta->sta.bandwidth = IEEE80211_STA_RX_BW_20; |
407 | } | 407 | } |
408 | 408 | ||
409 | if (insert) | 409 | if (insert) |
410 | rate_control_rate_init(sta); | 410 | rate_control_rate_init(sta); |
411 | else | 411 | else |
412 | rate_control_rate_update(local, sband, sta, changed); | 412 | rate_control_rate_update(local, sband, sta, changed); |
413 | out: | 413 | out: |
414 | spin_unlock_bh(&sta->lock); | 414 | spin_unlock_bh(&sta->lock); |
415 | } | 415 | } |
416 | 416 | ||
417 | static struct sta_info * | 417 | static struct sta_info * |
418 | __mesh_sta_info_alloc(struct ieee80211_sub_if_data *sdata, u8 *hw_addr) | 418 | __mesh_sta_info_alloc(struct ieee80211_sub_if_data *sdata, u8 *hw_addr) |
419 | { | 419 | { |
420 | struct sta_info *sta; | 420 | struct sta_info *sta; |
421 | 421 | ||
422 | if (sdata->local->num_sta >= MESH_MAX_PLINKS) | 422 | if (sdata->local->num_sta >= MESH_MAX_PLINKS) |
423 | return NULL; | 423 | return NULL; |
424 | 424 | ||
425 | sta = sta_info_alloc(sdata, hw_addr, GFP_KERNEL); | 425 | sta = sta_info_alloc(sdata, hw_addr, GFP_KERNEL); |
426 | if (!sta) | 426 | if (!sta) |
427 | return NULL; | 427 | return NULL; |
428 | 428 | ||
429 | sta->plink_state = NL80211_PLINK_LISTEN; | 429 | sta->plink_state = NL80211_PLINK_LISTEN; |
430 | 430 | ||
431 | sta_info_pre_move_state(sta, IEEE80211_STA_AUTH); | 431 | sta_info_pre_move_state(sta, IEEE80211_STA_AUTH); |
432 | sta_info_pre_move_state(sta, IEEE80211_STA_ASSOC); | 432 | sta_info_pre_move_state(sta, IEEE80211_STA_ASSOC); |
433 | sta_info_pre_move_state(sta, IEEE80211_STA_AUTHORIZED); | 433 | sta_info_pre_move_state(sta, IEEE80211_STA_AUTHORIZED); |
434 | 434 | ||
435 | set_sta_flag(sta, WLAN_STA_WME); | 435 | set_sta_flag(sta, WLAN_STA_WME); |
436 | 436 | ||
437 | return sta; | 437 | return sta; |
438 | } | 438 | } |
439 | 439 | ||
440 | static struct sta_info * | 440 | static struct sta_info * |
441 | mesh_sta_info_alloc(struct ieee80211_sub_if_data *sdata, u8 *addr, | 441 | mesh_sta_info_alloc(struct ieee80211_sub_if_data *sdata, u8 *addr, |
442 | struct ieee802_11_elems *elems) | 442 | struct ieee802_11_elems *elems) |
443 | { | 443 | { |
444 | struct sta_info *sta = NULL; | 444 | struct sta_info *sta = NULL; |
445 | 445 | ||
446 | /* Userspace handles station allocation */ | 446 | /* Userspace handles station allocation */ |
447 | if (sdata->u.mesh.user_mpm || | 447 | if (sdata->u.mesh.user_mpm || |
448 | sdata->u.mesh.security & IEEE80211_MESH_SEC_AUTHED) | 448 | sdata->u.mesh.security & IEEE80211_MESH_SEC_AUTHED) |
449 | cfg80211_notify_new_peer_candidate(sdata->dev, addr, | 449 | cfg80211_notify_new_peer_candidate(sdata->dev, addr, |
450 | elems->ie_start, | 450 | elems->ie_start, |
451 | elems->total_len, | 451 | elems->total_len, |
452 | GFP_KERNEL); | 452 | GFP_KERNEL); |
453 | else | 453 | else |
454 | sta = __mesh_sta_info_alloc(sdata, addr); | 454 | sta = __mesh_sta_info_alloc(sdata, addr); |
455 | 455 | ||
456 | return sta; | 456 | return sta; |
457 | } | 457 | } |
458 | 458 | ||
459 | /* | 459 | /* |
460 | * mesh_sta_info_get - return mesh sta info entry for @addr. | 460 | * mesh_sta_info_get - return mesh sta info entry for @addr. |
461 | * | 461 | * |
462 | * @sdata: local meshif | 462 | * @sdata: local meshif |
463 | * @addr: peer's address | 463 | * @addr: peer's address |
464 | * @elems: IEs from beacon or mesh peering frame. | 464 | * @elems: IEs from beacon or mesh peering frame. |
465 | * | 465 | * |
466 | * Return existing or newly allocated sta_info under RCU read lock. | 466 | * Return existing or newly allocated sta_info under RCU read lock. |
467 | * (re)initialize with given IEs. | 467 | * (re)initialize with given IEs. |
468 | */ | 468 | */ |
469 | static struct sta_info * | 469 | static struct sta_info * |
470 | mesh_sta_info_get(struct ieee80211_sub_if_data *sdata, | 470 | mesh_sta_info_get(struct ieee80211_sub_if_data *sdata, |
471 | u8 *addr, struct ieee802_11_elems *elems) __acquires(RCU) | 471 | u8 *addr, struct ieee802_11_elems *elems) __acquires(RCU) |
472 | { | 472 | { |
473 | struct sta_info *sta = NULL; | 473 | struct sta_info *sta = NULL; |
474 | 474 | ||
475 | rcu_read_lock(); | 475 | rcu_read_lock(); |
476 | sta = sta_info_get(sdata, addr); | 476 | sta = sta_info_get(sdata, addr); |
477 | if (sta) { | 477 | if (sta) { |
478 | mesh_sta_info_init(sdata, sta, elems, false); | 478 | mesh_sta_info_init(sdata, sta, elems, false); |
479 | } else { | 479 | } else { |
480 | rcu_read_unlock(); | 480 | rcu_read_unlock(); |
481 | /* can't run atomic */ | 481 | /* can't run atomic */ |
482 | sta = mesh_sta_info_alloc(sdata, addr, elems); | 482 | sta = mesh_sta_info_alloc(sdata, addr, elems); |
483 | if (!sta) { | 483 | if (!sta) { |
484 | rcu_read_lock(); | 484 | rcu_read_lock(); |
485 | return NULL; | 485 | return NULL; |
486 | } | 486 | } |
487 | 487 | ||
488 | mesh_sta_info_init(sdata, sta, elems, true); | 488 | mesh_sta_info_init(sdata, sta, elems, true); |
489 | 489 | ||
490 | if (sta_info_insert_rcu(sta)) | 490 | if (sta_info_insert_rcu(sta)) |
491 | return NULL; | 491 | return NULL; |
492 | } | 492 | } |
493 | 493 | ||
494 | return sta; | 494 | return sta; |
495 | } | 495 | } |
496 | 496 | ||
497 | /* | 497 | /* |
498 | * mesh_neighbour_update - update or initialize new mesh neighbor. | 498 | * mesh_neighbour_update - update or initialize new mesh neighbor. |
499 | * | 499 | * |
500 | * @sdata: local meshif | 500 | * @sdata: local meshif |
501 | * @addr: peer's address | 501 | * @addr: peer's address |
502 | * @elems: IEs from beacon or mesh peering frame | 502 | * @elems: IEs from beacon or mesh peering frame |
503 | * | 503 | * |
504 | * Initiates peering if appropriate. | 504 | * Initiates peering if appropriate. |
505 | */ | 505 | */ |
506 | void mesh_neighbour_update(struct ieee80211_sub_if_data *sdata, | 506 | void mesh_neighbour_update(struct ieee80211_sub_if_data *sdata, |
507 | u8 *hw_addr, | 507 | u8 *hw_addr, |
508 | struct ieee802_11_elems *elems) | 508 | struct ieee802_11_elems *elems) |
509 | { | 509 | { |
510 | struct sta_info *sta; | 510 | struct sta_info *sta; |
511 | u32 changed = 0; | 511 | u32 changed = 0; |
512 | 512 | ||
513 | sta = mesh_sta_info_get(sdata, hw_addr, elems); | 513 | sta = mesh_sta_info_get(sdata, hw_addr, elems); |
514 | if (!sta) | 514 | if (!sta) |
515 | goto out; | 515 | goto out; |
516 | 516 | ||
517 | if (mesh_peer_accepts_plinks(elems) && | 517 | if (mesh_peer_accepts_plinks(elems) && |
518 | sta->plink_state == NL80211_PLINK_LISTEN && | 518 | sta->plink_state == NL80211_PLINK_LISTEN && |
519 | sdata->u.mesh.accepting_plinks && | 519 | sdata->u.mesh.accepting_plinks && |
520 | sdata->u.mesh.mshcfg.auto_open_plinks && | 520 | sdata->u.mesh.mshcfg.auto_open_plinks && |
521 | rssi_threshold_check(sta, sdata)) | 521 | rssi_threshold_check(sta, sdata)) |
522 | changed = mesh_plink_open(sta); | 522 | changed = mesh_plink_open(sta); |
523 | 523 | ||
524 | ieee80211_mps_frame_release(sta, elems); | 524 | ieee80211_mps_frame_release(sta, elems); |
525 | out: | 525 | out: |
526 | rcu_read_unlock(); | 526 | rcu_read_unlock(); |
527 | ieee80211_mbss_info_change_notify(sdata, changed); | 527 | ieee80211_mbss_info_change_notify(sdata, changed); |
528 | } | 528 | } |
529 | 529 | ||
530 | static void mesh_plink_timer(unsigned long data) | 530 | static void mesh_plink_timer(unsigned long data) |
531 | { | 531 | { |
532 | struct sta_info *sta; | 532 | struct sta_info *sta; |
533 | __le16 llid, plid, reason; | 533 | __le16 llid, plid, reason; |
534 | struct ieee80211_sub_if_data *sdata; | 534 | struct ieee80211_sub_if_data *sdata; |
535 | struct mesh_config *mshcfg; | 535 | struct mesh_config *mshcfg; |
536 | enum ieee80211_self_protected_actioncode action = 0; | 536 | enum ieee80211_self_protected_actioncode action = 0; |
537 | 537 | ||
538 | /* | 538 | /* |
539 | * This STA is valid because sta_info_destroy() will | 539 | * This STA is valid because sta_info_destroy() will |
540 | * del_timer_sync() this timer after having made sure | 540 | * del_timer_sync() this timer after having made sure |
541 | * it cannot be readded (by deleting the plink.) | 541 | * it cannot be readded (by deleting the plink.) |
542 | */ | 542 | */ |
543 | sta = (struct sta_info *) data; | 543 | sta = (struct sta_info *) data; |
544 | 544 | ||
545 | if (sta->sdata->local->quiescing) | 545 | if (sta->sdata->local->quiescing) |
546 | return; | 546 | return; |
547 | 547 | ||
548 | spin_lock_bh(&sta->lock); | 548 | spin_lock_bh(&sta->lock); |
549 | if (sta->ignore_plink_timer) { | 549 | if (sta->ignore_plink_timer) { |
550 | sta->ignore_plink_timer = false; | 550 | sta->ignore_plink_timer = false; |
551 | spin_unlock_bh(&sta->lock); | 551 | spin_unlock_bh(&sta->lock); |
552 | return; | 552 | return; |
553 | } | 553 | } |
554 | mpl_dbg(sta->sdata, | 554 | mpl_dbg(sta->sdata, |
555 | "Mesh plink timer for %pM fired on state %s\n", | 555 | "Mesh plink timer for %pM fired on state %s\n", |
556 | sta->sta.addr, mplstates[sta->plink_state]); | 556 | sta->sta.addr, mplstates[sta->plink_state]); |
557 | reason = 0; | 557 | reason = 0; |
558 | llid = sta->llid; | 558 | llid = sta->llid; |
559 | plid = sta->plid; | 559 | plid = sta->plid; |
560 | sdata = sta->sdata; | 560 | sdata = sta->sdata; |
561 | mshcfg = &sdata->u.mesh.mshcfg; | 561 | mshcfg = &sdata->u.mesh.mshcfg; |
562 | 562 | ||
563 | switch (sta->plink_state) { | 563 | switch (sta->plink_state) { |
564 | case NL80211_PLINK_OPN_RCVD: | 564 | case NL80211_PLINK_OPN_RCVD: |
565 | case NL80211_PLINK_OPN_SNT: | 565 | case NL80211_PLINK_OPN_SNT: |
566 | /* retry timer */ | 566 | /* retry timer */ |
567 | if (sta->plink_retries < mshcfg->dot11MeshMaxRetries) { | 567 | if (sta->plink_retries < mshcfg->dot11MeshMaxRetries) { |
568 | u32 rand; | 568 | u32 rand; |
569 | mpl_dbg(sta->sdata, | 569 | mpl_dbg(sta->sdata, |
570 | "Mesh plink for %pM (retry, timeout): %d %d\n", | 570 | "Mesh plink for %pM (retry, timeout): %d %d\n", |
571 | sta->sta.addr, sta->plink_retries, | 571 | sta->sta.addr, sta->plink_retries, |
572 | sta->plink_timeout); | 572 | sta->plink_timeout); |
573 | get_random_bytes(&rand, sizeof(u32)); | 573 | get_random_bytes(&rand, sizeof(u32)); |
574 | sta->plink_timeout = sta->plink_timeout + | 574 | sta->plink_timeout = sta->plink_timeout + |
575 | rand % sta->plink_timeout; | 575 | rand % sta->plink_timeout; |
576 | ++sta->plink_retries; | 576 | ++sta->plink_retries; |
577 | mod_plink_timer(sta, sta->plink_timeout); | 577 | mod_plink_timer(sta, sta->plink_timeout); |
578 | action = WLAN_SP_MESH_PEERING_OPEN; | 578 | action = WLAN_SP_MESH_PEERING_OPEN; |
579 | break; | 579 | break; |
580 | } | 580 | } |
581 | reason = cpu_to_le16(WLAN_REASON_MESH_MAX_RETRIES); | 581 | reason = cpu_to_le16(WLAN_REASON_MESH_MAX_RETRIES); |
582 | /* fall through on else */ | 582 | /* fall through on else */ |
583 | case NL80211_PLINK_CNF_RCVD: | 583 | case NL80211_PLINK_CNF_RCVD: |
584 | /* confirm timer */ | 584 | /* confirm timer */ |
585 | if (!reason) | 585 | if (!reason) |
586 | reason = cpu_to_le16(WLAN_REASON_MESH_CONFIRM_TIMEOUT); | 586 | reason = cpu_to_le16(WLAN_REASON_MESH_CONFIRM_TIMEOUT); |
587 | sta->plink_state = NL80211_PLINK_HOLDING; | 587 | sta->plink_state = NL80211_PLINK_HOLDING; |
588 | mod_plink_timer(sta, mshcfg->dot11MeshHoldingTimeout); | 588 | mod_plink_timer(sta, mshcfg->dot11MeshHoldingTimeout); |
589 | action = WLAN_SP_MESH_PEERING_CLOSE; | 589 | action = WLAN_SP_MESH_PEERING_CLOSE; |
590 | break; | 590 | break; |
591 | case NL80211_PLINK_HOLDING: | 591 | case NL80211_PLINK_HOLDING: |
592 | /* holding timer */ | 592 | /* holding timer */ |
593 | del_timer(&sta->plink_timer); | 593 | del_timer(&sta->plink_timer); |
594 | mesh_plink_fsm_restart(sta); | 594 | mesh_plink_fsm_restart(sta); |
595 | break; | 595 | break; |
596 | default: | 596 | default: |
597 | break; | 597 | break; |
598 | } | 598 | } |
599 | spin_unlock_bh(&sta->lock); | 599 | spin_unlock_bh(&sta->lock); |
600 | if (action) | 600 | if (action) |
601 | mesh_plink_frame_tx(sdata, action, sta->sta.addr, | 601 | mesh_plink_frame_tx(sdata, action, sta->sta.addr, |
602 | llid, plid, reason); | 602 | llid, plid, reason); |
603 | } | 603 | } |
604 | 604 | ||
605 | static inline void mesh_plink_timer_set(struct sta_info *sta, int timeout) | 605 | static inline void mesh_plink_timer_set(struct sta_info *sta, int timeout) |
606 | { | 606 | { |
607 | sta->plink_timer.expires = jiffies + (HZ * timeout / 1000); | 607 | sta->plink_timer.expires = jiffies + (HZ * timeout / 1000); |
608 | sta->plink_timer.data = (unsigned long) sta; | 608 | sta->plink_timer.data = (unsigned long) sta; |
609 | sta->plink_timer.function = mesh_plink_timer; | 609 | sta->plink_timer.function = mesh_plink_timer; |
610 | sta->plink_timeout = timeout; | 610 | sta->plink_timeout = timeout; |
611 | add_timer(&sta->plink_timer); | 611 | add_timer(&sta->plink_timer); |
612 | } | 612 | } |
613 | 613 | ||
614 | u32 mesh_plink_open(struct sta_info *sta) | 614 | u32 mesh_plink_open(struct sta_info *sta) |
615 | { | 615 | { |
616 | __le16 llid; | 616 | __le16 llid; |
617 | struct ieee80211_sub_if_data *sdata = sta->sdata; | 617 | struct ieee80211_sub_if_data *sdata = sta->sdata; |
618 | u32 changed; | 618 | u32 changed; |
619 | 619 | ||
620 | if (!test_sta_flag(sta, WLAN_STA_AUTH)) | 620 | if (!test_sta_flag(sta, WLAN_STA_AUTH)) |
621 | return 0; | 621 | return 0; |
622 | 622 | ||
623 | spin_lock_bh(&sta->lock); | 623 | spin_lock_bh(&sta->lock); |
624 | get_random_bytes(&llid, 2); | 624 | get_random_bytes(&llid, 2); |
625 | sta->llid = llid; | 625 | sta->llid = llid; |
626 | if (sta->plink_state != NL80211_PLINK_LISTEN && | 626 | if (sta->plink_state != NL80211_PLINK_LISTEN && |
627 | sta->plink_state != NL80211_PLINK_BLOCKED) { | 627 | sta->plink_state != NL80211_PLINK_BLOCKED) { |
628 | spin_unlock_bh(&sta->lock); | 628 | spin_unlock_bh(&sta->lock); |
629 | return 0; | 629 | return 0; |
630 | } | 630 | } |
631 | sta->plink_state = NL80211_PLINK_OPN_SNT; | 631 | sta->plink_state = NL80211_PLINK_OPN_SNT; |
632 | mesh_plink_timer_set(sta, sdata->u.mesh.mshcfg.dot11MeshRetryTimeout); | 632 | mesh_plink_timer_set(sta, sdata->u.mesh.mshcfg.dot11MeshRetryTimeout); |
633 | spin_unlock_bh(&sta->lock); | 633 | spin_unlock_bh(&sta->lock); |
634 | mpl_dbg(sdata, | 634 | mpl_dbg(sdata, |
635 | "Mesh plink: starting establishment with %pM\n", | 635 | "Mesh plink: starting establishment with %pM\n", |
636 | sta->sta.addr); | 636 | sta->sta.addr); |
637 | 637 | ||
638 | /* set the non-peer mode to active during peering */ | 638 | /* set the non-peer mode to active during peering */ |
639 | changed = ieee80211_mps_local_status_update(sdata); | 639 | changed = ieee80211_mps_local_status_update(sdata); |
640 | 640 | ||
641 | mesh_plink_frame_tx(sdata, WLAN_SP_MESH_PEERING_OPEN, | 641 | mesh_plink_frame_tx(sdata, WLAN_SP_MESH_PEERING_OPEN, |
642 | sta->sta.addr, llid, 0, 0); | 642 | sta->sta.addr, llid, 0, 0); |
643 | return changed; | 643 | return changed; |
644 | } | 644 | } |
645 | 645 | ||
646 | u32 mesh_plink_block(struct sta_info *sta) | 646 | u32 mesh_plink_block(struct sta_info *sta) |
647 | { | 647 | { |
648 | u32 changed; | 648 | u32 changed; |
649 | 649 | ||
650 | spin_lock_bh(&sta->lock); | 650 | spin_lock_bh(&sta->lock); |
651 | changed = __mesh_plink_deactivate(sta); | 651 | changed = __mesh_plink_deactivate(sta); |
652 | sta->plink_state = NL80211_PLINK_BLOCKED; | 652 | sta->plink_state = NL80211_PLINK_BLOCKED; |
653 | spin_unlock_bh(&sta->lock); | 653 | spin_unlock_bh(&sta->lock); |
654 | 654 | ||
655 | return changed; | 655 | return changed; |
656 | } | 656 | } |
657 | 657 | ||
658 | static void mesh_plink_close(struct ieee80211_sub_if_data *sdata, | 658 | static void mesh_plink_close(struct ieee80211_sub_if_data *sdata, |
659 | struct sta_info *sta, | 659 | struct sta_info *sta, |
660 | enum plink_event event) | 660 | enum plink_event event) |
661 | { | 661 | { |
662 | struct mesh_config *mshcfg = &sdata->u.mesh.mshcfg; | 662 | struct mesh_config *mshcfg = &sdata->u.mesh.mshcfg; |
663 | 663 | ||
664 | __le16 reason = (event == CLS_ACPT) ? | 664 | __le16 reason = (event == CLS_ACPT) ? |
665 | cpu_to_le16(WLAN_REASON_MESH_CLOSE) : | 665 | cpu_to_le16(WLAN_REASON_MESH_CLOSE) : |
666 | cpu_to_le16(WLAN_REASON_MESH_CONFIG); | 666 | cpu_to_le16(WLAN_REASON_MESH_CONFIG); |
667 | 667 | ||
668 | sta->reason = reason; | 668 | sta->reason = reason; |
669 | sta->plink_state = NL80211_PLINK_HOLDING; | 669 | sta->plink_state = NL80211_PLINK_HOLDING; |
670 | if (!mod_plink_timer(sta, mshcfg->dot11MeshHoldingTimeout)) | 670 | if (!mod_plink_timer(sta, mshcfg->dot11MeshHoldingTimeout)) |
671 | sta->ignore_plink_timer = true; | 671 | sta->ignore_plink_timer = true; |
672 | } | 672 | } |
673 | 673 | ||
674 | static u32 mesh_plink_establish(struct ieee80211_sub_if_data *sdata, | 674 | static u32 mesh_plink_establish(struct ieee80211_sub_if_data *sdata, |
675 | struct sta_info *sta) | 675 | struct sta_info *sta) |
676 | { | 676 | { |
677 | struct mesh_config *mshcfg = &sdata->u.mesh.mshcfg; | 677 | struct mesh_config *mshcfg = &sdata->u.mesh.mshcfg; |
678 | u32 changed = 0; | 678 | u32 changed = 0; |
679 | 679 | ||
680 | del_timer(&sta->plink_timer); | 680 | del_timer(&sta->plink_timer); |
681 | sta->plink_state = NL80211_PLINK_ESTAB; | 681 | sta->plink_state = NL80211_PLINK_ESTAB; |
682 | changed |= mesh_plink_inc_estab_count(sdata); | 682 | changed |= mesh_plink_inc_estab_count(sdata); |
683 | changed |= mesh_set_ht_prot_mode(sdata); | 683 | changed |= mesh_set_ht_prot_mode(sdata); |
684 | changed |= mesh_set_short_slot_time(sdata); | 684 | changed |= mesh_set_short_slot_time(sdata); |
685 | mpl_dbg(sdata, "Mesh plink with %pM ESTABLISHED\n", sta->sta.addr); | 685 | mpl_dbg(sdata, "Mesh plink with %pM ESTABLISHED\n", sta->sta.addr); |
686 | ieee80211_mps_sta_status_update(sta); | 686 | ieee80211_mps_sta_status_update(sta); |
687 | changed |= ieee80211_mps_set_sta_local_pm(sta, mshcfg->power_mode); | 687 | changed |= ieee80211_mps_set_sta_local_pm(sta, mshcfg->power_mode); |
688 | return changed; | 688 | return changed; |
689 | } | 689 | } |
690 | 690 | ||
691 | 691 | ||
692 | void mesh_rx_plink_frame(struct ieee80211_sub_if_data *sdata, | 692 | void mesh_rx_plink_frame(struct ieee80211_sub_if_data *sdata, |
693 | struct ieee80211_mgmt *mgmt, size_t len, | 693 | struct ieee80211_mgmt *mgmt, size_t len, |
694 | struct ieee80211_rx_status *rx_status) | 694 | struct ieee80211_rx_status *rx_status) |
695 | { | 695 | { |
696 | struct mesh_config *mshcfg = &sdata->u.mesh.mshcfg; | 696 | struct mesh_config *mshcfg = &sdata->u.mesh.mshcfg; |
697 | enum ieee80211_self_protected_actioncode action = 0; | 697 | enum ieee80211_self_protected_actioncode action = 0; |
698 | struct ieee802_11_elems elems; | 698 | struct ieee802_11_elems elems; |
699 | struct sta_info *sta; | 699 | struct sta_info *sta; |
700 | enum plink_event event; | 700 | enum plink_event event; |
701 | enum ieee80211_self_protected_actioncode ftype; | 701 | enum ieee80211_self_protected_actioncode ftype; |
702 | size_t baselen; | 702 | size_t baselen; |
703 | bool matches_local = true; | 703 | bool matches_local; |
704 | u8 ie_len; | 704 | u8 ie_len; |
705 | u8 *baseaddr; | 705 | u8 *baseaddr; |
706 | u32 changed = 0; | 706 | u32 changed = 0; |
707 | __le16 plid, llid; | 707 | __le16 plid, llid; |
708 | 708 | ||
709 | /* need action_code, aux */ | 709 | /* need action_code, aux */ |
710 | if (len < IEEE80211_MIN_ACTION_SIZE + 3) | 710 | if (len < IEEE80211_MIN_ACTION_SIZE + 3) |
711 | return; | 711 | return; |
712 | 712 | ||
713 | if (sdata->u.mesh.user_mpm) | 713 | if (sdata->u.mesh.user_mpm) |
714 | /* userspace must register for these */ | 714 | /* userspace must register for these */ |
715 | return; | 715 | return; |
716 | 716 | ||
717 | if (is_multicast_ether_addr(mgmt->da)) { | 717 | if (is_multicast_ether_addr(mgmt->da)) { |
718 | mpl_dbg(sdata, | 718 | mpl_dbg(sdata, |
719 | "Mesh plink: ignore frame from multicast address\n"); | 719 | "Mesh plink: ignore frame from multicast address\n"); |
720 | return; | 720 | return; |
721 | } | 721 | } |
722 | 722 | ||
723 | baseaddr = mgmt->u.action.u.self_prot.variable; | 723 | baseaddr = mgmt->u.action.u.self_prot.variable; |
724 | baselen = (u8 *) mgmt->u.action.u.self_prot.variable - (u8 *) mgmt; | 724 | baselen = (u8 *) mgmt->u.action.u.self_prot.variable - (u8 *) mgmt; |
725 | if (mgmt->u.action.u.self_prot.action_code == | 725 | if (mgmt->u.action.u.self_prot.action_code == |
726 | WLAN_SP_MESH_PEERING_CONFIRM) { | 726 | WLAN_SP_MESH_PEERING_CONFIRM) { |
727 | baseaddr += 4; | 727 | baseaddr += 4; |
728 | baselen += 4; | 728 | baselen += 4; |
729 | } | 729 | } |
730 | ieee802_11_parse_elems(baseaddr, len - baselen, true, &elems); | 730 | ieee802_11_parse_elems(baseaddr, len - baselen, true, &elems); |
731 | 731 | ||
732 | if (!elems.peering) { | 732 | if (!elems.peering) { |
733 | mpl_dbg(sdata, | 733 | mpl_dbg(sdata, |
734 | "Mesh plink: missing necessary peer link ie\n"); | 734 | "Mesh plink: missing necessary peer link ie\n"); |
735 | return; | 735 | return; |
736 | } | 736 | } |
737 | 737 | ||
738 | if (elems.rsn_len && | 738 | if (elems.rsn_len && |
739 | sdata->u.mesh.security == IEEE80211_MESH_SEC_NONE) { | 739 | sdata->u.mesh.security == IEEE80211_MESH_SEC_NONE) { |
740 | mpl_dbg(sdata, | 740 | mpl_dbg(sdata, |
741 | "Mesh plink: can't establish link with secure peer\n"); | 741 | "Mesh plink: can't establish link with secure peer\n"); |
742 | return; | 742 | return; |
743 | } | 743 | } |
744 | 744 | ||
745 | ftype = mgmt->u.action.u.self_prot.action_code; | 745 | ftype = mgmt->u.action.u.self_prot.action_code; |
746 | ie_len = elems.peering_len; | 746 | ie_len = elems.peering_len; |
747 | if ((ftype == WLAN_SP_MESH_PEERING_OPEN && ie_len != 4) || | 747 | if ((ftype == WLAN_SP_MESH_PEERING_OPEN && ie_len != 4) || |
748 | (ftype == WLAN_SP_MESH_PEERING_CONFIRM && ie_len != 6) || | 748 | (ftype == WLAN_SP_MESH_PEERING_CONFIRM && ie_len != 6) || |
749 | (ftype == WLAN_SP_MESH_PEERING_CLOSE && ie_len != 6 | 749 | (ftype == WLAN_SP_MESH_PEERING_CLOSE && ie_len != 6 |
750 | && ie_len != 8)) { | 750 | && ie_len != 8)) { |
751 | mpl_dbg(sdata, | 751 | mpl_dbg(sdata, |
752 | "Mesh plink: incorrect plink ie length %d %d\n", | 752 | "Mesh plink: incorrect plink ie length %d %d\n", |
753 | ftype, ie_len); | 753 | ftype, ie_len); |
754 | return; | 754 | return; |
755 | } | 755 | } |
756 | 756 | ||
757 | if (ftype != WLAN_SP_MESH_PEERING_CLOSE && | 757 | if (ftype != WLAN_SP_MESH_PEERING_CLOSE && |
758 | (!elems.mesh_id || !elems.mesh_config)) { | 758 | (!elems.mesh_id || !elems.mesh_config)) { |
759 | mpl_dbg(sdata, "Mesh plink: missing necessary ie\n"); | 759 | mpl_dbg(sdata, "Mesh plink: missing necessary ie\n"); |
760 | return; | 760 | return; |
761 | } | 761 | } |
762 | /* Note the lines below are correct, the llid in the frame is the plid | 762 | /* Note the lines below are correct, the llid in the frame is the plid |
763 | * from the point of view of this host. | 763 | * from the point of view of this host. |
764 | */ | 764 | */ |
765 | memcpy(&plid, PLINK_GET_LLID(elems.peering), 2); | 765 | memcpy(&plid, PLINK_GET_LLID(elems.peering), 2); |
766 | if (ftype == WLAN_SP_MESH_PEERING_CONFIRM || | 766 | if (ftype == WLAN_SP_MESH_PEERING_CONFIRM || |
767 | (ftype == WLAN_SP_MESH_PEERING_CLOSE && ie_len == 8)) | 767 | (ftype == WLAN_SP_MESH_PEERING_CLOSE && ie_len == 8)) |
768 | memcpy(&llid, PLINK_GET_PLID(elems.peering), 2); | 768 | memcpy(&llid, PLINK_GET_PLID(elems.peering), 2); |
769 | 769 | ||
770 | /* WARNING: Only for sta pointer, is dropped & re-acquired */ | 770 | /* WARNING: Only for sta pointer, is dropped & re-acquired */ |
771 | rcu_read_lock(); | 771 | rcu_read_lock(); |
772 | 772 | ||
773 | sta = sta_info_get(sdata, mgmt->sa); | 773 | sta = sta_info_get(sdata, mgmt->sa); |
774 | if (!sta && ftype != WLAN_SP_MESH_PEERING_OPEN) { | ||
775 | mpl_dbg(sdata, "Mesh plink: cls or cnf from unknown peer\n"); | ||
776 | rcu_read_unlock(); | ||
777 | return; | ||
778 | } | ||
779 | 774 | ||
775 | matches_local = ftype == WLAN_SP_MESH_PEERING_CLOSE || | ||
776 | mesh_matches_local(sdata, &elems); | ||
777 | |||
780 | if (ftype == WLAN_SP_MESH_PEERING_OPEN && | 778 | if (ftype == WLAN_SP_MESH_PEERING_OPEN && |
781 | !rssi_threshold_check(sta, sdata)) { | 779 | !rssi_threshold_check(sta, sdata)) { |
782 | mpl_dbg(sdata, "Mesh plink: %pM does not meet rssi threshold\n", | 780 | mpl_dbg(sdata, "Mesh plink: %pM does not meet rssi threshold\n", |
783 | mgmt->sa); | 781 | mgmt->sa); |
784 | rcu_read_unlock(); | 782 | rcu_read_unlock(); |
785 | return; | 783 | return; |
786 | } | 784 | } |
787 | 785 | ||
788 | if (sta && !test_sta_flag(sta, WLAN_STA_AUTH)) { | 786 | if (!sta) { |
789 | mpl_dbg(sdata, "Mesh plink: Action frame from non-authed peer\n"); | 787 | if (ftype != WLAN_SP_MESH_PEERING_OPEN) { |
790 | rcu_read_unlock(); | 788 | mpl_dbg(sdata, "Mesh plink: cls or cnf from unknown peer\n"); |
791 | return; | 789 | rcu_read_unlock(); |
790 | return; | ||
791 | } | ||
792 | /* ftype == WLAN_SP_MESH_PEERING_OPEN */ | ||
793 | if (!mesh_plink_free_count(sdata)) { | ||
794 | mpl_dbg(sdata, "Mesh plink error: no more free plinks\n"); | ||
795 | rcu_read_unlock(); | ||
796 | return; | ||
797 | } | ||
798 | /* deny open request from non-matching peer */ | ||
799 | if (!matches_local) { | ||
800 | rcu_read_unlock(); | ||
801 | mesh_plink_frame_tx(sdata, WLAN_SP_MESH_PEERING_CLOSE, | ||
802 | mgmt->sa, 0, plid, | ||
803 | cpu_to_le16(WLAN_REASON_MESH_CONFIG)); | ||
804 | return; | ||
805 | } | ||
806 | } else { | ||
807 | if (!test_sta_flag(sta, WLAN_STA_AUTH)) { | ||
808 | mpl_dbg(sdata, "Mesh plink: Action frame from non-authed peer\n"); | ||
809 | rcu_read_unlock(); | ||
810 | return; | ||
811 | } | ||
812 | if (sta->plink_state == NL80211_PLINK_BLOCKED) { | ||
813 | rcu_read_unlock(); | ||
814 | return; | ||
815 | } | ||
792 | } | 816 | } |
793 | 817 | ||
794 | if (sta && sta->plink_state == NL80211_PLINK_BLOCKED) { | ||
795 | rcu_read_unlock(); | ||
796 | return; | ||
797 | } | ||
798 | |||
799 | /* Now we will figure out the appropriate event... */ | 818 | /* Now we will figure out the appropriate event... */ |
800 | event = PLINK_UNDEFINED; | 819 | event = PLINK_UNDEFINED; |
801 | if (ftype != WLAN_SP_MESH_PEERING_CLOSE && | 820 | if (!matches_local) { |
802 | !mesh_matches_local(sdata, &elems)) { | ||
803 | matches_local = false; | ||
804 | switch (ftype) { | 821 | switch (ftype) { |
805 | case WLAN_SP_MESH_PEERING_OPEN: | 822 | case WLAN_SP_MESH_PEERING_OPEN: |
806 | event = OPN_RJCT; | 823 | event = OPN_RJCT; |
807 | break; | 824 | break; |
808 | case WLAN_SP_MESH_PEERING_CONFIRM: | 825 | case WLAN_SP_MESH_PEERING_CONFIRM: |
809 | event = CNF_RJCT; | 826 | event = CNF_RJCT; |
810 | break; | 827 | break; |
811 | default: | 828 | default: |
812 | break; | 829 | break; |
813 | } | 830 | } |
814 | } | 831 | } |
815 | 832 | ||
816 | if (!sta && !matches_local) { | 833 | if (!sta) |
817 | rcu_read_unlock(); | ||
818 | llid = 0; | ||
819 | mesh_plink_frame_tx(sdata, WLAN_SP_MESH_PEERING_CLOSE, | ||
820 | mgmt->sa, llid, plid, | ||
821 | cpu_to_le16(WLAN_REASON_MESH_CONFIG)); | ||
822 | return; | ||
823 | } else if (!sta) { | ||
824 | /* ftype == WLAN_SP_MESH_PEERING_OPEN */ | ||
825 | if (!mesh_plink_free_count(sdata)) { | ||
826 | mpl_dbg(sdata, "Mesh plink error: no more free plinks\n"); | ||
827 | rcu_read_unlock(); | ||
828 | return; | ||
829 | } | ||
830 | event = OPN_ACPT; | 834 | event = OPN_ACPT; |
831 | } else if (matches_local) { | 835 | else if (matches_local) { |
832 | switch (ftype) { | 836 | switch (ftype) { |
833 | case WLAN_SP_MESH_PEERING_OPEN: | 837 | case WLAN_SP_MESH_PEERING_OPEN: |
834 | if (!mesh_plink_free_count(sdata) || | 838 | if (!mesh_plink_free_count(sdata) || |
835 | (sta->plid && sta->plid != plid)) | 839 | (sta->plid && sta->plid != plid)) |
836 | event = OPN_IGNR; | 840 | event = OPN_IGNR; |
837 | else | 841 | else |
838 | event = OPN_ACPT; | 842 | event = OPN_ACPT; |
839 | break; | 843 | break; |
840 | case WLAN_SP_MESH_PEERING_CONFIRM: | 844 | case WLAN_SP_MESH_PEERING_CONFIRM: |
841 | if (!mesh_plink_free_count(sdata) || | 845 | if (!mesh_plink_free_count(sdata) || |
842 | (sta->llid != llid || sta->plid != plid)) | 846 | (sta->llid != llid || sta->plid != plid)) |
843 | event = CNF_IGNR; | 847 | event = CNF_IGNR; |
844 | else | 848 | else |
845 | event = CNF_ACPT; | 849 | event = CNF_ACPT; |
846 | break; | 850 | break; |
847 | case WLAN_SP_MESH_PEERING_CLOSE: | 851 | case WLAN_SP_MESH_PEERING_CLOSE: |
848 | if (sta->plink_state == NL80211_PLINK_ESTAB) | 852 | if (sta->plink_state == NL80211_PLINK_ESTAB) |
849 | /* Do not check for llid or plid. This does not | 853 | /* Do not check for llid or plid. This does not |
850 | * follow the standard but since multiple plinks | 854 | * follow the standard but since multiple plinks |
851 | * per sta are not supported, it is necessary in | 855 | * per sta are not supported, it is necessary in |
852 | * order to avoid a livelock when MP A sees an | 856 | * order to avoid a livelock when MP A sees an |
853 | * establish peer link to MP B but MP B does not | 857 | * establish peer link to MP B but MP B does not |
854 | * see it. This can be caused by a timeout in | 858 | * see it. This can be caused by a timeout in |
855 | * B's peer link establishment or B beign | 859 | * B's peer link establishment or B beign |
856 | * restarted. | 860 | * restarted. |
857 | */ | 861 | */ |
858 | event = CLS_ACPT; | 862 | event = CLS_ACPT; |
859 | else if (sta->plid != plid) | 863 | else if (sta->plid != plid) |
860 | event = CLS_IGNR; | 864 | event = CLS_IGNR; |
861 | else if (ie_len == 8 && sta->llid != llid) | 865 | else if (ie_len == 8 && sta->llid != llid) |
862 | event = CLS_IGNR; | 866 | event = CLS_IGNR; |
863 | else | 867 | else |
864 | event = CLS_ACPT; | 868 | event = CLS_ACPT; |
865 | break; | 869 | break; |
866 | default: | 870 | default: |
867 | mpl_dbg(sdata, "Mesh plink: unknown frame subtype\n"); | 871 | mpl_dbg(sdata, "Mesh plink: unknown frame subtype\n"); |
868 | rcu_read_unlock(); | 872 | rcu_read_unlock(); |
869 | return; | 873 | return; |
870 | } | 874 | } |
871 | } | 875 | } |
872 | 876 | ||
873 | if (event == OPN_ACPT) { | 877 | if (event == OPN_ACPT) { |
874 | rcu_read_unlock(); | 878 | rcu_read_unlock(); |
875 | /* allocate sta entry if necessary and update info */ | 879 | /* allocate sta entry if necessary and update info */ |
876 | sta = mesh_sta_info_get(sdata, mgmt->sa, &elems); | 880 | sta = mesh_sta_info_get(sdata, mgmt->sa, &elems); |
877 | if (!sta) { | 881 | if (!sta) { |
878 | mpl_dbg(sdata, "Mesh plink: failed to init peer!\n"); | 882 | mpl_dbg(sdata, "Mesh plink: failed to init peer!\n"); |
879 | rcu_read_unlock(); | 883 | rcu_read_unlock(); |
880 | return; | 884 | return; |
881 | } | 885 | } |
882 | } | 886 | } |
883 | 887 | ||
884 | mpl_dbg(sdata, "peer %pM in state %s got event %s\n", mgmt->sa, | 888 | mpl_dbg(sdata, "peer %pM in state %s got event %s\n", mgmt->sa, |
885 | mplstates[sta->plink_state], mplevents[event]); | 889 | mplstates[sta->plink_state], mplevents[event]); |
886 | spin_lock_bh(&sta->lock); | 890 | spin_lock_bh(&sta->lock); |
887 | switch (sta->plink_state) { | 891 | switch (sta->plink_state) { |
888 | case NL80211_PLINK_LISTEN: | 892 | case NL80211_PLINK_LISTEN: |
889 | switch (event) { | 893 | switch (event) { |
890 | case CLS_ACPT: | 894 | case CLS_ACPT: |
891 | mesh_plink_fsm_restart(sta); | 895 | mesh_plink_fsm_restart(sta); |
892 | break; | 896 | break; |
893 | case OPN_ACPT: | 897 | case OPN_ACPT: |
894 | sta->plink_state = NL80211_PLINK_OPN_RCVD; | 898 | sta->plink_state = NL80211_PLINK_OPN_RCVD; |
895 | sta->plid = plid; | 899 | sta->plid = plid; |
896 | get_random_bytes(&llid, 2); | 900 | get_random_bytes(&llid, 2); |
897 | sta->llid = llid; | 901 | sta->llid = llid; |
898 | mesh_plink_timer_set(sta, | 902 | mesh_plink_timer_set(sta, |
899 | mshcfg->dot11MeshRetryTimeout); | 903 | mshcfg->dot11MeshRetryTimeout); |
900 | 904 | ||
901 | /* set the non-peer mode to active during peering */ | 905 | /* set the non-peer mode to active during peering */ |
902 | changed |= ieee80211_mps_local_status_update(sdata); | 906 | changed |= ieee80211_mps_local_status_update(sdata); |
903 | 907 | ||
904 | action = WLAN_SP_MESH_PEERING_OPEN; | 908 | action = WLAN_SP_MESH_PEERING_OPEN; |
905 | break; | 909 | break; |
906 | default: | 910 | default: |
907 | break; | 911 | break; |
908 | } | 912 | } |
909 | break; | 913 | break; |
910 | 914 | ||
911 | case NL80211_PLINK_OPN_SNT: | 915 | case NL80211_PLINK_OPN_SNT: |
912 | switch (event) { | 916 | switch (event) { |
913 | case OPN_RJCT: | 917 | case OPN_RJCT: |
914 | case CNF_RJCT: | 918 | case CNF_RJCT: |
915 | case CLS_ACPT: | 919 | case CLS_ACPT: |
916 | mesh_plink_close(sdata, sta, event); | 920 | mesh_plink_close(sdata, sta, event); |
917 | action = WLAN_SP_MESH_PEERING_CLOSE; | 921 | action = WLAN_SP_MESH_PEERING_CLOSE; |
918 | break; | 922 | break; |
919 | 923 | ||
920 | case OPN_ACPT: | 924 | case OPN_ACPT: |
921 | /* retry timer is left untouched */ | 925 | /* retry timer is left untouched */ |
922 | sta->plink_state = NL80211_PLINK_OPN_RCVD; | 926 | sta->plink_state = NL80211_PLINK_OPN_RCVD; |
923 | sta->plid = plid; | 927 | sta->plid = plid; |
924 | action = WLAN_SP_MESH_PEERING_CONFIRM; | 928 | action = WLAN_SP_MESH_PEERING_CONFIRM; |
925 | break; | 929 | break; |
926 | case CNF_ACPT: | 930 | case CNF_ACPT: |
927 | sta->plink_state = NL80211_PLINK_CNF_RCVD; | 931 | sta->plink_state = NL80211_PLINK_CNF_RCVD; |
928 | if (!mod_plink_timer(sta, | 932 | if (!mod_plink_timer(sta, |
929 | mshcfg->dot11MeshConfirmTimeout)) | 933 | mshcfg->dot11MeshConfirmTimeout)) |
930 | sta->ignore_plink_timer = true; | 934 | sta->ignore_plink_timer = true; |
931 | 935 | ||
932 | break; | 936 | break; |
933 | default: | 937 | default: |
934 | break; | 938 | break; |
935 | } | 939 | } |
936 | break; | 940 | break; |
937 | 941 | ||
938 | case NL80211_PLINK_OPN_RCVD: | 942 | case NL80211_PLINK_OPN_RCVD: |
939 | switch (event) { | 943 | switch (event) { |
940 | case OPN_RJCT: | 944 | case OPN_RJCT: |
941 | case CNF_RJCT: | 945 | case CNF_RJCT: |
942 | case CLS_ACPT: | 946 | case CLS_ACPT: |
943 | mesh_plink_close(sdata, sta, event); | 947 | mesh_plink_close(sdata, sta, event); |
944 | action = WLAN_SP_MESH_PEERING_CLOSE; | 948 | action = WLAN_SP_MESH_PEERING_CLOSE; |
945 | break; | 949 | break; |
946 | case OPN_ACPT: | 950 | case OPN_ACPT: |
947 | action = WLAN_SP_MESH_PEERING_CONFIRM; | 951 | action = WLAN_SP_MESH_PEERING_CONFIRM; |
948 | break; | 952 | break; |
949 | case CNF_ACPT: | 953 | case CNF_ACPT: |
950 | changed |= mesh_plink_establish(sdata, sta); | 954 | changed |= mesh_plink_establish(sdata, sta); |
951 | break; | 955 | break; |
952 | default: | 956 | default: |
953 | break; | 957 | break; |
954 | } | 958 | } |
955 | break; | 959 | break; |
956 | 960 | ||
957 | case NL80211_PLINK_CNF_RCVD: | 961 | case NL80211_PLINK_CNF_RCVD: |
958 | switch (event) { | 962 | switch (event) { |
959 | case OPN_RJCT: | 963 | case OPN_RJCT: |
960 | case CNF_RJCT: | 964 | case CNF_RJCT: |
961 | case CLS_ACPT: | 965 | case CLS_ACPT: |
962 | mesh_plink_close(sdata, sta, event); | 966 | mesh_plink_close(sdata, sta, event); |
963 | action = WLAN_SP_MESH_PEERING_CLOSE; | 967 | action = WLAN_SP_MESH_PEERING_CLOSE; |
964 | break; | 968 | break; |
965 | case OPN_ACPT: | 969 | case OPN_ACPT: |
966 | changed |= mesh_plink_establish(sdata, sta); | 970 | changed |= mesh_plink_establish(sdata, sta); |
967 | action = WLAN_SP_MESH_PEERING_CONFIRM; | 971 | action = WLAN_SP_MESH_PEERING_CONFIRM; |
968 | break; | 972 | break; |
969 | default: | 973 | default: |
970 | break; | 974 | break; |
971 | } | 975 | } |
972 | break; | 976 | break; |
973 | 977 | ||
974 | case NL80211_PLINK_ESTAB: | 978 | case NL80211_PLINK_ESTAB: |
975 | switch (event) { | 979 | switch (event) { |
976 | case CLS_ACPT: | 980 | case CLS_ACPT: |
977 | changed |= __mesh_plink_deactivate(sta); | 981 | changed |= __mesh_plink_deactivate(sta); |
978 | changed |= mesh_set_ht_prot_mode(sdata); | 982 | changed |= mesh_set_ht_prot_mode(sdata); |
979 | changed |= mesh_set_short_slot_time(sdata); | 983 | changed |= mesh_set_short_slot_time(sdata); |
980 | mesh_plink_close(sdata, sta, event); | 984 | mesh_plink_close(sdata, sta, event); |
981 | action = WLAN_SP_MESH_PEERING_CLOSE; | 985 | action = WLAN_SP_MESH_PEERING_CLOSE; |
982 | break; | 986 | break; |
983 | case OPN_ACPT: | 987 | case OPN_ACPT: |
984 | action = WLAN_SP_MESH_PEERING_CONFIRM; | 988 | action = WLAN_SP_MESH_PEERING_CONFIRM; |
985 | break; | 989 | break; |
986 | default: | 990 | default: |
987 | break; | 991 | break; |
988 | } | 992 | } |
989 | break; | 993 | break; |
990 | case NL80211_PLINK_HOLDING: | 994 | case NL80211_PLINK_HOLDING: |
991 | switch (event) { | 995 | switch (event) { |
992 | case CLS_ACPT: | 996 | case CLS_ACPT: |
993 | if (del_timer(&sta->plink_timer)) | 997 | if (del_timer(&sta->plink_timer)) |
994 | sta->ignore_plink_timer = 1; | 998 | sta->ignore_plink_timer = 1; |
995 | mesh_plink_fsm_restart(sta); | 999 | mesh_plink_fsm_restart(sta); |
996 | break; | 1000 | break; |
997 | case OPN_ACPT: | 1001 | case OPN_ACPT: |
998 | case CNF_ACPT: | 1002 | case CNF_ACPT: |
999 | case OPN_RJCT: | 1003 | case OPN_RJCT: |
1000 | case CNF_RJCT: | 1004 | case CNF_RJCT: |
1001 | action = WLAN_SP_MESH_PEERING_CLOSE; | 1005 | action = WLAN_SP_MESH_PEERING_CLOSE; |
1002 | break; | 1006 | break; |
1003 | default: | 1007 | default: |
1004 | break; | 1008 | break; |
1005 | } | 1009 | } |
1006 | break; | 1010 | break; |
1007 | default: | 1011 | default: |