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