diff options
Diffstat (limited to 'net/mac80211/mesh_plink.c')
-rw-r--r-- | net/mac80211/mesh_plink.c | 762 |
1 files changed, 762 insertions, 0 deletions
diff --git a/net/mac80211/mesh_plink.c b/net/mac80211/mesh_plink.c new file mode 100644 index 000000000000..37f0c2b94ae7 --- /dev/null +++ b/net/mac80211/mesh_plink.c | |||
@@ -0,0 +1,762 @@ | |||
1 | /* | ||
2 | * Copyright (c) 2008 open80211s Ltd. | ||
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 | */ | ||
9 | #include <linux/kernel.h> | ||
10 | #include <linux/random.h> | ||
11 | #include "ieee80211_i.h" | ||
12 | #include "rate.h" | ||
13 | #include "mesh.h" | ||
14 | |||
15 | #ifdef CONFIG_MAC80211_VERBOSE_MPL_DEBUG | ||
16 | #define mpl_dbg(fmt, args...) printk(KERN_DEBUG fmt, ##args) | ||
17 | #else | ||
18 | #define mpl_dbg(fmt, args...) do { (void)(0); } while (0) | ||
19 | #endif | ||
20 | |||
21 | #define PLINK_GET_FRAME_SUBTYPE(p) (p) | ||
22 | #define PLINK_GET_LLID(p) (p + 1) | ||
23 | #define PLINK_GET_PLID(p) (p + 3) | ||
24 | |||
25 | #define mod_plink_timer(s, t) (mod_timer(&s->plink_timer, \ | ||
26 | jiffies + HZ * t / 1000)) | ||
27 | |||
28 | /* Peer link cancel reasons, all subject to ANA approval */ | ||
29 | #define MESH_LINK_CANCELLED 2 | ||
30 | #define MESH_MAX_NEIGHBORS 3 | ||
31 | #define MESH_CAPABILITY_POLICY_VIOLATION 4 | ||
32 | #define MESH_CLOSE_RCVD 5 | ||
33 | #define MESH_MAX_RETRIES 6 | ||
34 | #define MESH_CONFIRM_TIMEOUT 7 | ||
35 | #define MESH_SECURITY_ROLE_NEGOTIATION_DIFFERS 8 | ||
36 | #define MESH_SECURITY_AUTHENTICATION_IMPOSSIBLE 9 | ||
37 | #define MESH_SECURITY_FAILED_VERIFICATION 10 | ||
38 | |||
39 | #define dot11MeshMaxRetries(s) (s->u.sta.mshcfg.dot11MeshMaxRetries) | ||
40 | #define dot11MeshRetryTimeout(s) (s->u.sta.mshcfg.dot11MeshRetryTimeout) | ||
41 | #define dot11MeshConfirmTimeout(s) (s->u.sta.mshcfg.dot11MeshConfirmTimeout) | ||
42 | #define dot11MeshHoldingTimeout(s) (s->u.sta.mshcfg.dot11MeshHoldingTimeout) | ||
43 | #define dot11MeshMaxPeerLinks(s) (s->u.sta.mshcfg.dot11MeshMaxPeerLinks) | ||
44 | |||
45 | enum plink_frame_type { | ||
46 | PLINK_OPEN = 0, | ||
47 | PLINK_CONFIRM, | ||
48 | PLINK_CLOSE | ||
49 | }; | ||
50 | |||
51 | enum plink_event { | ||
52 | PLINK_UNDEFINED, | ||
53 | OPN_ACPT, | ||
54 | OPN_RJCT, | ||
55 | OPN_IGNR, | ||
56 | CNF_ACPT, | ||
57 | CNF_RJCT, | ||
58 | CNF_IGNR, | ||
59 | CLS_ACPT, | ||
60 | CLS_IGNR | ||
61 | }; | ||
62 | |||
63 | static inline | ||
64 | void mesh_plink_inc_estab_count(struct ieee80211_sub_if_data *sdata) | ||
65 | { | ||
66 | atomic_inc(&sdata->u.sta.mshstats.estab_plinks); | ||
67 | mesh_accept_plinks_update(sdata); | ||
68 | } | ||
69 | |||
70 | static inline | ||
71 | void mesh_plink_dec_estab_count(struct ieee80211_sub_if_data *sdata) | ||
72 | { | ||
73 | atomic_dec(&sdata->u.sta.mshstats.estab_plinks); | ||
74 | mesh_accept_plinks_update(sdata); | ||
75 | } | ||
76 | |||
77 | /** | ||
78 | * mesh_plink_fsm_restart - restart a mesh peer link finite state machine | ||
79 | * | ||
80 | * @sta: mes peer link to restart | ||
81 | * | ||
82 | * Locking: this function must be called holding sta->plink_lock | ||
83 | */ | ||
84 | static inline void mesh_plink_fsm_restart(struct sta_info *sta) | ||
85 | { | ||
86 | sta->plink_state = PLINK_LISTEN; | ||
87 | sta->llid = sta->plid = sta->reason = 0; | ||
88 | sta->plink_retries = 0; | ||
89 | } | ||
90 | |||
91 | /* | ||
92 | * NOTE: This is just an alias for sta_info_alloc(), see notes | ||
93 | * on it in the lifecycle management section! | ||
94 | */ | ||
95 | static struct sta_info *mesh_plink_alloc(struct ieee80211_sub_if_data *sdata, | ||
96 | u8 *hw_addr, u64 rates) | ||
97 | { | ||
98 | struct ieee80211_local *local = sdata->local; | ||
99 | struct sta_info *sta; | ||
100 | |||
101 | if (local->num_sta >= MESH_MAX_PLINKS) | ||
102 | return NULL; | ||
103 | |||
104 | sta = sta_info_alloc(sdata, hw_addr, GFP_ATOMIC); | ||
105 | if (!sta) | ||
106 | return NULL; | ||
107 | |||
108 | sta->flags |= WLAN_STA_AUTHORIZED; | ||
109 | sta->supp_rates[local->hw.conf.channel->band] = rates; | ||
110 | |||
111 | return sta; | ||
112 | } | ||
113 | |||
114 | /** | ||
115 | * mesh_plink_deactivate - deactivate mesh peer link | ||
116 | * | ||
117 | * @sta: mesh peer link to deactivate | ||
118 | * | ||
119 | * All mesh paths with this peer as next hop will be flushed | ||
120 | * | ||
121 | * Locking: the caller must hold sta->plink_lock | ||
122 | */ | ||
123 | static void __mesh_plink_deactivate(struct sta_info *sta) | ||
124 | { | ||
125 | struct ieee80211_sub_if_data *sdata = sta->sdata; | ||
126 | |||
127 | if (sta->plink_state == PLINK_ESTAB) | ||
128 | mesh_plink_dec_estab_count(sdata); | ||
129 | sta->plink_state = PLINK_BLOCKED; | ||
130 | mesh_path_flush_by_nexthop(sta); | ||
131 | } | ||
132 | |||
133 | /** | ||
134 | * __mesh_plink_deactivate - deactivate mesh peer link | ||
135 | * | ||
136 | * @sta: mesh peer link to deactivate | ||
137 | * | ||
138 | * All mesh paths with this peer as next hop will be flushed | ||
139 | */ | ||
140 | void mesh_plink_deactivate(struct sta_info *sta) | ||
141 | { | ||
142 | spin_lock_bh(&sta->plink_lock); | ||
143 | __mesh_plink_deactivate(sta); | ||
144 | spin_unlock_bh(&sta->plink_lock); | ||
145 | } | ||
146 | |||
147 | static int mesh_plink_frame_tx(struct net_device *dev, | ||
148 | enum plink_frame_type action, u8 *da, __le16 llid, __le16 plid, | ||
149 | __le16 reason) { | ||
150 | struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr); | ||
151 | struct sk_buff *skb = dev_alloc_skb(local->hw.extra_tx_headroom + 400); | ||
152 | struct ieee80211_mgmt *mgmt; | ||
153 | bool include_plid = false; | ||
154 | u8 *pos; | ||
155 | int ie_len; | ||
156 | |||
157 | if (!skb) | ||
158 | return -1; | ||
159 | skb_reserve(skb, local->hw.extra_tx_headroom); | ||
160 | /* 25 is the size of the common mgmt part (24) plus the size of the | ||
161 | * common action part (1) | ||
162 | */ | ||
163 | mgmt = (struct ieee80211_mgmt *) | ||
164 | skb_put(skb, 25 + sizeof(mgmt->u.action.u.plink_action)); | ||
165 | memset(mgmt, 0, 25 + sizeof(mgmt->u.action.u.plink_action)); | ||
166 | mgmt->frame_control = IEEE80211_FC(IEEE80211_FTYPE_MGMT, | ||
167 | IEEE80211_STYPE_ACTION); | ||
168 | memcpy(mgmt->da, da, ETH_ALEN); | ||
169 | memcpy(mgmt->sa, dev->dev_addr, ETH_ALEN); | ||
170 | /* BSSID is left zeroed, wildcard value */ | ||
171 | mgmt->u.action.category = PLINK_CATEGORY; | ||
172 | mgmt->u.action.u.plink_action.action_code = action; | ||
173 | |||
174 | if (action == PLINK_CLOSE) | ||
175 | mgmt->u.action.u.plink_action.aux = reason; | ||
176 | else { | ||
177 | mgmt->u.action.u.plink_action.aux = cpu_to_le16(0x0); | ||
178 | if (action == PLINK_CONFIRM) { | ||
179 | pos = skb_put(skb, 4); | ||
180 | /* two-byte status code followed by two-byte AID */ | ||
181 | memset(pos, 0, 4); | ||
182 | } | ||
183 | mesh_mgmt_ies_add(skb, dev); | ||
184 | } | ||
185 | |||
186 | /* Add Peer Link Management element */ | ||
187 | switch (action) { | ||
188 | case PLINK_OPEN: | ||
189 | ie_len = 3; | ||
190 | break; | ||
191 | case PLINK_CONFIRM: | ||
192 | ie_len = 5; | ||
193 | include_plid = true; | ||
194 | break; | ||
195 | case PLINK_CLOSE: | ||
196 | default: | ||
197 | if (!plid) | ||
198 | ie_len = 5; | ||
199 | else { | ||
200 | ie_len = 7; | ||
201 | include_plid = true; | ||
202 | } | ||
203 | break; | ||
204 | } | ||
205 | |||
206 | pos = skb_put(skb, 2 + ie_len); | ||
207 | *pos++ = WLAN_EID_PEER_LINK; | ||
208 | *pos++ = ie_len; | ||
209 | *pos++ = action; | ||
210 | memcpy(pos, &llid, 2); | ||
211 | if (include_plid) { | ||
212 | pos += 2; | ||
213 | memcpy(pos, &plid, 2); | ||
214 | } | ||
215 | if (action == PLINK_CLOSE) { | ||
216 | pos += 2; | ||
217 | memcpy(pos, &reason, 2); | ||
218 | } | ||
219 | |||
220 | ieee80211_sta_tx(dev, skb, 0); | ||
221 | return 0; | ||
222 | } | ||
223 | |||
224 | void mesh_neighbour_update(u8 *hw_addr, u64 rates, struct net_device *dev, | ||
225 | bool peer_accepting_plinks) | ||
226 | { | ||
227 | struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); | ||
228 | struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr); | ||
229 | struct sta_info *sta; | ||
230 | |||
231 | rcu_read_lock(); | ||
232 | |||
233 | sta = sta_info_get(local, hw_addr); | ||
234 | if (!sta) { | ||
235 | sta = mesh_plink_alloc(sdata, hw_addr, rates); | ||
236 | if (!sta) { | ||
237 | rcu_read_unlock(); | ||
238 | return; | ||
239 | } | ||
240 | if (sta_info_insert(sta)) { | ||
241 | rcu_read_unlock(); | ||
242 | return; | ||
243 | } | ||
244 | } | ||
245 | |||
246 | sta->last_rx = jiffies; | ||
247 | sta->supp_rates[local->hw.conf.channel->band] = rates; | ||
248 | if (peer_accepting_plinks && sta->plink_state == PLINK_LISTEN && | ||
249 | sdata->u.sta.accepting_plinks && | ||
250 | sdata->u.sta.mshcfg.auto_open_plinks) | ||
251 | mesh_plink_open(sta); | ||
252 | |||
253 | rcu_read_unlock(); | ||
254 | } | ||
255 | |||
256 | static void mesh_plink_timer(unsigned long data) | ||
257 | { | ||
258 | struct sta_info *sta; | ||
259 | __le16 llid, plid, reason; | ||
260 | struct net_device *dev = NULL; | ||
261 | struct ieee80211_sub_if_data *sdata; | ||
262 | #ifdef CONFIG_MAC80211_VERBOSE_MPL_DEBUG | ||
263 | DECLARE_MAC_BUF(mac); | ||
264 | #endif | ||
265 | |||
266 | /* | ||
267 | * This STA is valid because sta_info_destroy() will | ||
268 | * del_timer_sync() this timer after having made sure | ||
269 | * it cannot be readded (by deleting the plink.) | ||
270 | */ | ||
271 | sta = (struct sta_info *) data; | ||
272 | |||
273 | spin_lock_bh(&sta->plink_lock); | ||
274 | if (sta->ignore_plink_timer) { | ||
275 | sta->ignore_plink_timer = false; | ||
276 | spin_unlock_bh(&sta->plink_lock); | ||
277 | return; | ||
278 | } | ||
279 | mpl_dbg("Mesh plink timer for %s fired on state %d\n", | ||
280 | print_mac(mac, sta->addr), sta->plink_state); | ||
281 | reason = 0; | ||
282 | llid = sta->llid; | ||
283 | plid = sta->plid; | ||
284 | sdata = sta->sdata; | ||
285 | dev = sdata->dev; | ||
286 | |||
287 | switch (sta->plink_state) { | ||
288 | case PLINK_OPN_RCVD: | ||
289 | case PLINK_OPN_SNT: | ||
290 | /* retry timer */ | ||
291 | if (sta->plink_retries < dot11MeshMaxRetries(sdata)) { | ||
292 | u32 rand; | ||
293 | mpl_dbg("Mesh plink for %s (retry, timeout): %d %d\n", | ||
294 | print_mac(mac, sta->addr), | ||
295 | sta->plink_retries, sta->plink_timeout); | ||
296 | get_random_bytes(&rand, sizeof(u32)); | ||
297 | sta->plink_timeout = sta->plink_timeout + | ||
298 | rand % sta->plink_timeout; | ||
299 | ++sta->plink_retries; | ||
300 | mod_plink_timer(sta, sta->plink_timeout); | ||
301 | spin_unlock_bh(&sta->plink_lock); | ||
302 | mesh_plink_frame_tx(dev, PLINK_OPEN, sta->addr, llid, | ||
303 | 0, 0); | ||
304 | break; | ||
305 | } | ||
306 | reason = cpu_to_le16(MESH_MAX_RETRIES); | ||
307 | /* fall through on else */ | ||
308 | case PLINK_CNF_RCVD: | ||
309 | /* confirm timer */ | ||
310 | if (!reason) | ||
311 | reason = cpu_to_le16(MESH_CONFIRM_TIMEOUT); | ||
312 | sta->plink_state = PLINK_HOLDING; | ||
313 | mod_plink_timer(sta, dot11MeshHoldingTimeout(sdata)); | ||
314 | spin_unlock_bh(&sta->plink_lock); | ||
315 | mesh_plink_frame_tx(dev, PLINK_CLOSE, sta->addr, llid, plid, | ||
316 | reason); | ||
317 | break; | ||
318 | case PLINK_HOLDING: | ||
319 | /* holding timer */ | ||
320 | del_timer(&sta->plink_timer); | ||
321 | mesh_plink_fsm_restart(sta); | ||
322 | spin_unlock_bh(&sta->plink_lock); | ||
323 | break; | ||
324 | default: | ||
325 | spin_unlock_bh(&sta->plink_lock); | ||
326 | break; | ||
327 | } | ||
328 | } | ||
329 | |||
330 | static inline void mesh_plink_timer_set(struct sta_info *sta, int timeout) | ||
331 | { | ||
332 | sta->plink_timer.expires = jiffies + (HZ * timeout / 1000); | ||
333 | sta->plink_timer.data = (unsigned long) sta; | ||
334 | sta->plink_timer.function = mesh_plink_timer; | ||
335 | sta->plink_timeout = timeout; | ||
336 | add_timer(&sta->plink_timer); | ||
337 | } | ||
338 | |||
339 | int mesh_plink_open(struct sta_info *sta) | ||
340 | { | ||
341 | __le16 llid; | ||
342 | struct ieee80211_sub_if_data *sdata = sta->sdata; | ||
343 | #ifdef CONFIG_MAC80211_VERBOSE_MPL_DEBUG | ||
344 | DECLARE_MAC_BUF(mac); | ||
345 | #endif | ||
346 | |||
347 | spin_lock_bh(&sta->plink_lock); | ||
348 | get_random_bytes(&llid, 2); | ||
349 | sta->llid = llid; | ||
350 | if (sta->plink_state != PLINK_LISTEN) { | ||
351 | spin_unlock_bh(&sta->plink_lock); | ||
352 | return -EBUSY; | ||
353 | } | ||
354 | sta->plink_state = PLINK_OPN_SNT; | ||
355 | mesh_plink_timer_set(sta, dot11MeshRetryTimeout(sdata)); | ||
356 | spin_unlock_bh(&sta->plink_lock); | ||
357 | mpl_dbg("Mesh plink: starting establishment with %s\n", | ||
358 | print_mac(mac, sta->addr)); | ||
359 | |||
360 | return mesh_plink_frame_tx(sdata->dev, PLINK_OPEN, | ||
361 | sta->addr, llid, 0, 0); | ||
362 | } | ||
363 | |||
364 | void mesh_plink_block(struct sta_info *sta) | ||
365 | { | ||
366 | #ifdef CONFIG_MAC80211_VERBOSE_MPL_DEBUG | ||
367 | DECLARE_MAC_BUF(mac); | ||
368 | #endif | ||
369 | |||
370 | spin_lock_bh(&sta->plink_lock); | ||
371 | __mesh_plink_deactivate(sta); | ||
372 | sta->plink_state = PLINK_BLOCKED; | ||
373 | spin_unlock_bh(&sta->plink_lock); | ||
374 | } | ||
375 | |||
376 | int mesh_plink_close(struct sta_info *sta) | ||
377 | { | ||
378 | struct ieee80211_sub_if_data *sdata = sta->sdata; | ||
379 | __le16 llid, plid, reason; | ||
380 | #ifdef CONFIG_MAC80211_VERBOSE_MPL_DEBUG | ||
381 | DECLARE_MAC_BUF(mac); | ||
382 | #endif | ||
383 | |||
384 | mpl_dbg("Mesh plink: closing link with %s\n", | ||
385 | print_mac(mac, sta->addr)); | ||
386 | spin_lock_bh(&sta->plink_lock); | ||
387 | sta->reason = cpu_to_le16(MESH_LINK_CANCELLED); | ||
388 | reason = sta->reason; | ||
389 | |||
390 | if (sta->plink_state == PLINK_LISTEN || | ||
391 | sta->plink_state == PLINK_BLOCKED) { | ||
392 | mesh_plink_fsm_restart(sta); | ||
393 | spin_unlock_bh(&sta->plink_lock); | ||
394 | return 0; | ||
395 | } else if (sta->plink_state == PLINK_ESTAB) { | ||
396 | __mesh_plink_deactivate(sta); | ||
397 | /* The timer should not be running */ | ||
398 | mod_plink_timer(sta, dot11MeshHoldingTimeout(sdata)); | ||
399 | } else if (!mod_plink_timer(sta, dot11MeshHoldingTimeout(sdata))) | ||
400 | sta->ignore_plink_timer = true; | ||
401 | |||
402 | sta->plink_state = PLINK_HOLDING; | ||
403 | llid = sta->llid; | ||
404 | plid = sta->plid; | ||
405 | spin_unlock_bh(&sta->plink_lock); | ||
406 | mesh_plink_frame_tx(sta->sdata->dev, PLINK_CLOSE, sta->addr, llid, | ||
407 | plid, reason); | ||
408 | return 0; | ||
409 | } | ||
410 | |||
411 | void mesh_rx_plink_frame(struct net_device *dev, struct ieee80211_mgmt *mgmt, | ||
412 | size_t len, struct ieee80211_rx_status *rx_status) | ||
413 | { | ||
414 | struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); | ||
415 | struct ieee80211_local *local = sdata->local; | ||
416 | struct ieee802_11_elems elems; | ||
417 | struct sta_info *sta; | ||
418 | enum plink_event event; | ||
419 | enum plink_frame_type ftype; | ||
420 | size_t baselen; | ||
421 | u8 ie_len; | ||
422 | u8 *baseaddr; | ||
423 | __le16 plid, llid, reason; | ||
424 | #ifdef CONFIG_MAC80211_VERBOSE_MPL_DEBUG | ||
425 | DECLARE_MAC_BUF(mac); | ||
426 | #endif | ||
427 | |||
428 | if (is_multicast_ether_addr(mgmt->da)) { | ||
429 | mpl_dbg("Mesh plink: ignore frame from multicast address"); | ||
430 | return; | ||
431 | } | ||
432 | |||
433 | baseaddr = mgmt->u.action.u.plink_action.variable; | ||
434 | baselen = (u8 *) mgmt->u.action.u.plink_action.variable - (u8 *) mgmt; | ||
435 | if (mgmt->u.action.u.plink_action.action_code == PLINK_CONFIRM) { | ||
436 | baseaddr += 4; | ||
437 | baselen -= 4; | ||
438 | } | ||
439 | ieee802_11_parse_elems(baseaddr, len - baselen, &elems); | ||
440 | if (!elems.peer_link) { | ||
441 | mpl_dbg("Mesh plink: missing necessary peer link ie\n"); | ||
442 | return; | ||
443 | } | ||
444 | |||
445 | ftype = *((u8 *)PLINK_GET_FRAME_SUBTYPE(elems.peer_link)); | ||
446 | ie_len = elems.peer_link_len; | ||
447 | if ((ftype == PLINK_OPEN && ie_len != 3) || | ||
448 | (ftype == PLINK_CONFIRM && ie_len != 5) || | ||
449 | (ftype == PLINK_CLOSE && ie_len != 5 && ie_len != 7)) { | ||
450 | mpl_dbg("Mesh plink: incorrect plink ie length\n"); | ||
451 | return; | ||
452 | } | ||
453 | |||
454 | if (ftype != PLINK_CLOSE && (!elems.mesh_id || !elems.mesh_config)) { | ||
455 | mpl_dbg("Mesh plink: missing necessary ie\n"); | ||
456 | return; | ||
457 | } | ||
458 | /* Note the lines below are correct, the llid in the frame is the plid | ||
459 | * from the point of view of this host. | ||
460 | */ | ||
461 | memcpy(&plid, PLINK_GET_LLID(elems.peer_link), 2); | ||
462 | if (ftype == PLINK_CONFIRM || (ftype == PLINK_CLOSE && ie_len == 7)) | ||
463 | memcpy(&llid, PLINK_GET_PLID(elems.peer_link), 2); | ||
464 | |||
465 | rcu_read_lock(); | ||
466 | |||
467 | sta = sta_info_get(local, mgmt->sa); | ||
468 | if (!sta && ftype != PLINK_OPEN) { | ||
469 | mpl_dbg("Mesh plink: cls or cnf from unknown peer\n"); | ||
470 | rcu_read_unlock(); | ||
471 | return; | ||
472 | } | ||
473 | |||
474 | if (sta && sta->plink_state == PLINK_BLOCKED) { | ||
475 | rcu_read_unlock(); | ||
476 | return; | ||
477 | } | ||
478 | |||
479 | /* Now we will figure out the appropriate event... */ | ||
480 | event = PLINK_UNDEFINED; | ||
481 | if (ftype != PLINK_CLOSE && (!mesh_matches_local(&elems, dev))) { | ||
482 | switch (ftype) { | ||
483 | case PLINK_OPEN: | ||
484 | event = OPN_RJCT; | ||
485 | break; | ||
486 | case PLINK_CONFIRM: | ||
487 | event = CNF_RJCT; | ||
488 | break; | ||
489 | case PLINK_CLOSE: | ||
490 | /* avoid warning */ | ||
491 | break; | ||
492 | } | ||
493 | spin_lock_bh(&sta->plink_lock); | ||
494 | } else if (!sta) { | ||
495 | /* ftype == PLINK_OPEN */ | ||
496 | u64 rates; | ||
497 | if (!mesh_plink_free_count(sdata)) { | ||
498 | mpl_dbg("Mesh plink error: no more free plinks\n"); | ||
499 | rcu_read_unlock(); | ||
500 | return; | ||
501 | } | ||
502 | |||
503 | rates = ieee80211_sta_get_rates(local, &elems, rx_status->band); | ||
504 | sta = mesh_plink_alloc(sdata, mgmt->sa, rates); | ||
505 | if (!sta) { | ||
506 | mpl_dbg("Mesh plink error: plink table full\n"); | ||
507 | rcu_read_unlock(); | ||
508 | return; | ||
509 | } | ||
510 | if (sta_info_insert(sta)) { | ||
511 | rcu_read_unlock(); | ||
512 | return; | ||
513 | } | ||
514 | event = OPN_ACPT; | ||
515 | spin_lock_bh(&sta->plink_lock); | ||
516 | } else { | ||
517 | spin_lock_bh(&sta->plink_lock); | ||
518 | switch (ftype) { | ||
519 | case PLINK_OPEN: | ||
520 | if (!mesh_plink_free_count(sdata) || | ||
521 | (sta->plid && sta->plid != plid)) | ||
522 | event = OPN_IGNR; | ||
523 | else | ||
524 | event = OPN_ACPT; | ||
525 | break; | ||
526 | case PLINK_CONFIRM: | ||
527 | if (!mesh_plink_free_count(sdata) || | ||
528 | (sta->llid != llid || sta->plid != plid)) | ||
529 | event = CNF_IGNR; | ||
530 | else | ||
531 | event = CNF_ACPT; | ||
532 | break; | ||
533 | case PLINK_CLOSE: | ||
534 | if (sta->plink_state == PLINK_ESTAB) | ||
535 | /* Do not check for llid or plid. This does not | ||
536 | * follow the standard but since multiple plinks | ||
537 | * per sta are not supported, it is necessary in | ||
538 | * order to avoid a livelock when MP A sees an | ||
539 | * establish peer link to MP B but MP B does not | ||
540 | * see it. This can be caused by a timeout in | ||
541 | * B's peer link establishment or B beign | ||
542 | * restarted. | ||
543 | */ | ||
544 | event = CLS_ACPT; | ||
545 | else if (sta->plid != plid) | ||
546 | event = CLS_IGNR; | ||
547 | else if (ie_len == 7 && sta->llid != llid) | ||
548 | event = CLS_IGNR; | ||
549 | else | ||
550 | event = CLS_ACPT; | ||
551 | break; | ||
552 | default: | ||
553 | mpl_dbg("Mesh plink: unknown frame subtype\n"); | ||
554 | spin_unlock_bh(&sta->plink_lock); | ||
555 | rcu_read_unlock(); | ||
556 | return; | ||
557 | } | ||
558 | } | ||
559 | |||
560 | mpl_dbg("Mesh plink (peer, state, llid, plid, event): %s %d %d %d %d\n", | ||
561 | print_mac(mac, mgmt->sa), sta->plink_state, | ||
562 | le16_to_cpu(sta->llid), le16_to_cpu(sta->plid), | ||
563 | event); | ||
564 | reason = 0; | ||
565 | switch (sta->plink_state) { | ||
566 | /* spin_unlock as soon as state is updated at each case */ | ||
567 | case PLINK_LISTEN: | ||
568 | switch (event) { | ||
569 | case CLS_ACPT: | ||
570 | mesh_plink_fsm_restart(sta); | ||
571 | spin_unlock_bh(&sta->plink_lock); | ||
572 | break; | ||
573 | case OPN_ACPT: | ||
574 | sta->plink_state = PLINK_OPN_RCVD; | ||
575 | sta->plid = plid; | ||
576 | get_random_bytes(&llid, 2); | ||
577 | sta->llid = llid; | ||
578 | mesh_plink_timer_set(sta, dot11MeshRetryTimeout(sdata)); | ||
579 | spin_unlock_bh(&sta->plink_lock); | ||
580 | mesh_plink_frame_tx(dev, PLINK_OPEN, sta->addr, llid, | ||
581 | 0, 0); | ||
582 | mesh_plink_frame_tx(dev, PLINK_CONFIRM, sta->addr, | ||
583 | llid, plid, 0); | ||
584 | break; | ||
585 | default: | ||
586 | spin_unlock_bh(&sta->plink_lock); | ||
587 | break; | ||
588 | } | ||
589 | break; | ||
590 | |||
591 | case PLINK_OPN_SNT: | ||
592 | switch (event) { | ||
593 | case OPN_RJCT: | ||
594 | case CNF_RJCT: | ||
595 | reason = cpu_to_le16(MESH_CAPABILITY_POLICY_VIOLATION); | ||
596 | case CLS_ACPT: | ||
597 | if (!reason) | ||
598 | reason = cpu_to_le16(MESH_CLOSE_RCVD); | ||
599 | sta->reason = reason; | ||
600 | sta->plink_state = PLINK_HOLDING; | ||
601 | if (!mod_plink_timer(sta, | ||
602 | dot11MeshHoldingTimeout(sdata))) | ||
603 | sta->ignore_plink_timer = true; | ||
604 | |||
605 | llid = sta->llid; | ||
606 | spin_unlock_bh(&sta->plink_lock); | ||
607 | mesh_plink_frame_tx(dev, PLINK_CLOSE, sta->addr, llid, | ||
608 | plid, reason); | ||
609 | break; | ||
610 | case OPN_ACPT: | ||
611 | /* retry timer is left untouched */ | ||
612 | sta->plink_state = PLINK_OPN_RCVD; | ||
613 | sta->plid = plid; | ||
614 | llid = sta->llid; | ||
615 | spin_unlock_bh(&sta->plink_lock); | ||
616 | mesh_plink_frame_tx(dev, PLINK_CONFIRM, sta->addr, llid, | ||
617 | plid, 0); | ||
618 | break; | ||
619 | case CNF_ACPT: | ||
620 | sta->plink_state = PLINK_CNF_RCVD; | ||
621 | if (!mod_plink_timer(sta, | ||
622 | dot11MeshConfirmTimeout(sdata))) | ||
623 | sta->ignore_plink_timer = true; | ||
624 | |||
625 | spin_unlock_bh(&sta->plink_lock); | ||
626 | break; | ||
627 | default: | ||
628 | spin_unlock_bh(&sta->plink_lock); | ||
629 | break; | ||
630 | } | ||
631 | break; | ||
632 | |||
633 | case PLINK_OPN_RCVD: | ||
634 | switch (event) { | ||
635 | case OPN_RJCT: | ||
636 | case CNF_RJCT: | ||
637 | reason = cpu_to_le16(MESH_CAPABILITY_POLICY_VIOLATION); | ||
638 | case CLS_ACPT: | ||
639 | if (!reason) | ||
640 | reason = cpu_to_le16(MESH_CLOSE_RCVD); | ||
641 | sta->reason = reason; | ||
642 | sta->plink_state = PLINK_HOLDING; | ||
643 | if (!mod_plink_timer(sta, | ||
644 | dot11MeshHoldingTimeout(sdata))) | ||
645 | sta->ignore_plink_timer = true; | ||
646 | |||
647 | llid = sta->llid; | ||
648 | spin_unlock_bh(&sta->plink_lock); | ||
649 | mesh_plink_frame_tx(dev, PLINK_CLOSE, sta->addr, llid, | ||
650 | plid, reason); | ||
651 | break; | ||
652 | case OPN_ACPT: | ||
653 | llid = sta->llid; | ||
654 | spin_unlock_bh(&sta->plink_lock); | ||
655 | mesh_plink_frame_tx(dev, PLINK_CONFIRM, sta->addr, llid, | ||
656 | plid, 0); | ||
657 | break; | ||
658 | case CNF_ACPT: | ||
659 | del_timer(&sta->plink_timer); | ||
660 | sta->plink_state = PLINK_ESTAB; | ||
661 | mesh_plink_inc_estab_count(sdata); | ||
662 | spin_unlock_bh(&sta->plink_lock); | ||
663 | mpl_dbg("Mesh plink with %s ESTABLISHED\n", | ||
664 | print_mac(mac, sta->addr)); | ||
665 | break; | ||
666 | default: | ||
667 | spin_unlock_bh(&sta->plink_lock); | ||
668 | break; | ||
669 | } | ||
670 | break; | ||
671 | |||
672 | case PLINK_CNF_RCVD: | ||
673 | switch (event) { | ||
674 | case OPN_RJCT: | ||
675 | case CNF_RJCT: | ||
676 | reason = cpu_to_le16(MESH_CAPABILITY_POLICY_VIOLATION); | ||
677 | case CLS_ACPT: | ||
678 | if (!reason) | ||
679 | reason = cpu_to_le16(MESH_CLOSE_RCVD); | ||
680 | sta->reason = reason; | ||
681 | sta->plink_state = PLINK_HOLDING; | ||
682 | if (!mod_plink_timer(sta, | ||
683 | dot11MeshHoldingTimeout(sdata))) | ||
684 | sta->ignore_plink_timer = true; | ||
685 | |||
686 | llid = sta->llid; | ||
687 | spin_unlock_bh(&sta->plink_lock); | ||
688 | mesh_plink_frame_tx(dev, PLINK_CLOSE, sta->addr, llid, | ||
689 | plid, reason); | ||
690 | break; | ||
691 | case OPN_ACPT: | ||
692 | del_timer(&sta->plink_timer); | ||
693 | sta->plink_state = PLINK_ESTAB; | ||
694 | mesh_plink_inc_estab_count(sdata); | ||
695 | spin_unlock_bh(&sta->plink_lock); | ||
696 | mpl_dbg("Mesh plink with %s ESTABLISHED\n", | ||
697 | print_mac(mac, sta->addr)); | ||
698 | mesh_plink_frame_tx(dev, PLINK_CONFIRM, sta->addr, llid, | ||
699 | plid, 0); | ||
700 | break; | ||
701 | default: | ||
702 | spin_unlock_bh(&sta->plink_lock); | ||
703 | break; | ||
704 | } | ||
705 | break; | ||
706 | |||
707 | case PLINK_ESTAB: | ||
708 | switch (event) { | ||
709 | case CLS_ACPT: | ||
710 | reason = cpu_to_le16(MESH_CLOSE_RCVD); | ||
711 | sta->reason = reason; | ||
712 | __mesh_plink_deactivate(sta); | ||
713 | sta->plink_state = PLINK_HOLDING; | ||
714 | llid = sta->llid; | ||
715 | mod_plink_timer(sta, dot11MeshHoldingTimeout(sdata)); | ||
716 | spin_unlock_bh(&sta->plink_lock); | ||
717 | mesh_plink_frame_tx(dev, PLINK_CLOSE, sta->addr, llid, | ||
718 | plid, reason); | ||
719 | break; | ||
720 | case OPN_ACPT: | ||
721 | llid = sta->llid; | ||
722 | spin_unlock_bh(&sta->plink_lock); | ||
723 | mesh_plink_frame_tx(dev, PLINK_CONFIRM, sta->addr, llid, | ||
724 | plid, 0); | ||
725 | break; | ||
726 | default: | ||
727 | spin_unlock_bh(&sta->plink_lock); | ||
728 | break; | ||
729 | } | ||
730 | break; | ||
731 | case PLINK_HOLDING: | ||
732 | switch (event) { | ||
733 | case CLS_ACPT: | ||
734 | if (del_timer(&sta->plink_timer)) | ||
735 | sta->ignore_plink_timer = 1; | ||
736 | mesh_plink_fsm_restart(sta); | ||
737 | spin_unlock_bh(&sta->plink_lock); | ||
738 | break; | ||
739 | case OPN_ACPT: | ||
740 | case CNF_ACPT: | ||
741 | case OPN_RJCT: | ||
742 | case CNF_RJCT: | ||
743 | llid = sta->llid; | ||
744 | reason = sta->reason; | ||
745 | spin_unlock_bh(&sta->plink_lock); | ||
746 | mesh_plink_frame_tx(dev, PLINK_CLOSE, sta->addr, llid, | ||
747 | plid, reason); | ||
748 | break; | ||
749 | default: | ||
750 | spin_unlock_bh(&sta->plink_lock); | ||
751 | } | ||
752 | break; | ||
753 | default: | ||
754 | /* should not get here, PLINK_BLOCKED is dealt with at the | ||
755 | * beggining of the function | ||
756 | */ | ||
757 | spin_unlock_bh(&sta->plink_lock); | ||
758 | break; | ||
759 | } | ||
760 | |||
761 | rcu_read_unlock(); | ||
762 | } | ||