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