diff options
author | Luis Carlos Cobo <luisca@cozybit.com> | 2008-02-23 09:17:13 -0500 |
---|---|---|
committer | John W. Linville <linville@tuxdriver.com> | 2008-03-06 15:30:41 -0500 |
commit | c3896d2ca4dd97be290f000cb1079ed759d28574 (patch) | |
tree | 044b821ea923fd881f2259532163ceab8c05db16 /net/mac80211/mesh_plink.c | |
parent | f709fc696d72d31273a77b82aa32cb6d19857011 (diff) |
mac80211: mesh peer link implementation
This file implements mesh discovery and peer link establishment support using
the mesh peer link table provided in mesh_plinktbl.c.
Secure peer links have not been implemented yet.
Signed-off-by: Luis Carlos Cobo <luisca@cozybit.com>
Signed-off-by: Johannes Berg <johannes@sipsolutions.net>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
Diffstat (limited to 'net/mac80211/mesh_plink.c')
-rw-r--r-- | net/mac80211/mesh_plink.c | 755 |
1 files changed, 755 insertions, 0 deletions
diff --git a/net/mac80211/mesh_plink.c b/net/mac80211/mesh_plink.c new file mode 100644 index 000000000000..5cd97e99be62 --- /dev/null +++ b/net/mac80211/mesh_plink.c | |||
@@ -0,0 +1,755 @@ | |||
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 | |||
10 | #include "ieee80211_i.h" | ||
11 | #include "ieee80211_rate.h" | ||
12 | #include "mesh.h" | ||
13 | #include <linux/random.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->dev); | ||
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->dev); | ||
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 = LISTEN; | ||
88 | sta->llid = sta->plid = sta->reason = sta->plink_retries = 0; | ||
89 | } | ||
90 | |||
91 | /** | ||
92 | * mesh_plink_add - allocate and add a new mesh peer link | ||
93 | * | ||
94 | * @hw_addr: hardware address (ETH_ALEN length) | ||
95 | * @rates: rates the mesh peer supports | ||
96 | * @dev: local mesh interface | ||
97 | * | ||
98 | * The initial state of the new plink is set to LISTEN | ||
99 | * | ||
100 | * Returns: non-NULL on success, ERR_PTR() on error. | ||
101 | */ | ||
102 | struct sta_info *mesh_plink_add(u8 *hw_addr, u64 rates, struct net_device *dev) | ||
103 | { | ||
104 | struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr); | ||
105 | struct sta_info *sta; | ||
106 | |||
107 | if (memcmp(hw_addr, dev->dev_addr, ETH_ALEN) == 0) | ||
108 | /* never add ourselves as neighbours */ | ||
109 | return ERR_PTR(-EINVAL); | ||
110 | |||
111 | if (is_multicast_ether_addr(hw_addr)) | ||
112 | return ERR_PTR(-EINVAL); | ||
113 | |||
114 | if (local->num_sta >= MESH_MAX_PLINKS) | ||
115 | return ERR_PTR(-ENOSPC); | ||
116 | |||
117 | sta = sta_info_add(local, dev, hw_addr, GFP_KERNEL); | ||
118 | if (IS_ERR(sta)) | ||
119 | return sta; | ||
120 | |||
121 | sta->plink_state = LISTEN; | ||
122 | spin_lock_init(&sta->plink_lock); | ||
123 | init_timer(&sta->plink_timer); | ||
124 | sta->flags |= WLAN_STA_AUTHORIZED; | ||
125 | sta->supp_rates[local->hw.conf.channel->band] = rates; | ||
126 | rate_control_rate_init(sta, local); | ||
127 | |||
128 | mesh_accept_plinks_update(dev); | ||
129 | |||
130 | return 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 | * Locking: the caller must hold sta->plink_lock | ||
141 | */ | ||
142 | void mesh_plink_deactivate(struct sta_info *sta) | ||
143 | { | ||
144 | struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(sta->dev); | ||
145 | if (sta->plink_state == ESTAB) | ||
146 | mesh_plink_dec_estab_count(sdata); | ||
147 | sta->plink_state = BLOCKED; | ||
148 | mesh_path_flush_by_nexthop(sta); | ||
149 | } | ||
150 | |||
151 | static int mesh_plink_frame_tx(struct net_device *dev, | ||
152 | enum plink_frame_type action, u8 *da, __le16 llid, __le16 plid, | ||
153 | __le16 reason) { | ||
154 | struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr); | ||
155 | struct sk_buff *skb = dev_alloc_skb(local->hw.extra_tx_headroom + 400); | ||
156 | struct ieee80211_mgmt *mgmt; | ||
157 | bool include_plid = false; | ||
158 | u8 *pos; | ||
159 | int ie_len; | ||
160 | |||
161 | if (!skb) | ||
162 | return -1; | ||
163 | skb_reserve(skb, local->hw.extra_tx_headroom); | ||
164 | /* 25 is the size of the common mgmt part (24) plus the size of the | ||
165 | * common action part (1) | ||
166 | */ | ||
167 | mgmt = (struct ieee80211_mgmt *) | ||
168 | skb_put(skb, 25 + sizeof(mgmt->u.action.u.plink_action)); | ||
169 | memset(mgmt, 0, 25 + sizeof(mgmt->u.action.u.plink_action)); | ||
170 | mgmt->frame_control = IEEE80211_FC(IEEE80211_FTYPE_MGMT, | ||
171 | IEEE80211_STYPE_ACTION); | ||
172 | memcpy(mgmt->da, da, ETH_ALEN); | ||
173 | memcpy(mgmt->sa, dev->dev_addr, ETH_ALEN); | ||
174 | /* BSSID is left zeroed, wildcard value */ | ||
175 | mgmt->u.action.category = PLINK_CATEGORY; | ||
176 | mgmt->u.action.u.plink_action.action_code = action; | ||
177 | |||
178 | if (action == PLINK_CLOSE) | ||
179 | mgmt->u.action.u.plink_action.aux = reason; | ||
180 | else { | ||
181 | mgmt->u.action.u.plink_action.aux = cpu_to_le16(0x0); | ||
182 | if (action == PLINK_CONFIRM) { | ||
183 | pos = skb_put(skb, 4); | ||
184 | /* two-byte status code followed by two-byte AID */ | ||
185 | memset(pos, 0, 4); | ||
186 | } | ||
187 | mesh_mgmt_ies_add(skb, dev); | ||
188 | } | ||
189 | |||
190 | /* Add Peer Link Management element */ | ||
191 | switch (action) { | ||
192 | case PLINK_OPEN: | ||
193 | ie_len = 3; | ||
194 | break; | ||
195 | case PLINK_CONFIRM: | ||
196 | ie_len = 5; | ||
197 | include_plid = true; | ||
198 | break; | ||
199 | case PLINK_CLOSE: | ||
200 | default: | ||
201 | if (!plid) | ||
202 | ie_len = 5; | ||
203 | else { | ||
204 | ie_len = 7; | ||
205 | include_plid = true; | ||
206 | } | ||
207 | break; | ||
208 | } | ||
209 | |||
210 | pos = skb_put(skb, 2 + ie_len); | ||
211 | *pos++ = WLAN_EID_PEER_LINK; | ||
212 | *pos++ = ie_len; | ||
213 | *pos++ = action; | ||
214 | memcpy(pos, &llid, 2); | ||
215 | if (include_plid) { | ||
216 | pos += 2; | ||
217 | memcpy(pos, &plid, 2); | ||
218 | } | ||
219 | if (action == PLINK_CLOSE) { | ||
220 | pos += 2; | ||
221 | memcpy(pos, &reason, 2); | ||
222 | } | ||
223 | |||
224 | ieee80211_sta_tx(dev, skb, 0); | ||
225 | return 0; | ||
226 | } | ||
227 | |||
228 | void mesh_neighbour_update(u8 *hw_addr, u64 rates, struct net_device *dev, | ||
229 | bool peer_accepting_plinks) | ||
230 | { | ||
231 | struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); | ||
232 | struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr); | ||
233 | struct sta_info *sta; | ||
234 | |||
235 | sta = sta_info_get(local, hw_addr); | ||
236 | if (!sta) { | ||
237 | sta = mesh_plink_add(hw_addr, rates, dev); | ||
238 | if (IS_ERR(sta)) | ||
239 | return; | ||
240 | } | ||
241 | |||
242 | sta->last_rx = jiffies; | ||
243 | sta->supp_rates[local->hw.conf.channel->band] = rates; | ||
244 | if (peer_accepting_plinks && sta->plink_state == LISTEN && | ||
245 | sdata->u.sta.accepting_plinks && | ||
246 | sdata->u.sta.mshcfg.auto_open_plinks) | ||
247 | mesh_plink_open(sta); | ||
248 | |||
249 | sta_info_put(sta); | ||
250 | } | ||
251 | |||
252 | static void mesh_plink_timer(unsigned long data) | ||
253 | { | ||
254 | struct sta_info *sta; | ||
255 | __le16 llid, plid, reason; | ||
256 | struct net_device *dev = NULL; | ||
257 | struct ieee80211_sub_if_data *sdata; | ||
258 | #ifdef CONFIG_MAC80211_VERBOSE_MPL_DEBUG | ||
259 | DECLARE_MAC_BUF(mac); | ||
260 | #endif | ||
261 | |||
262 | sta = (struct sta_info *) data; | ||
263 | |||
264 | spin_lock_bh(&sta->plink_lock); | ||
265 | if (sta->ignore_plink_timer) { | ||
266 | sta->ignore_plink_timer = false; | ||
267 | spin_unlock_bh(&sta->plink_lock); | ||
268 | return; | ||
269 | } | ||
270 | mpl_dbg("Mesh plink timer for %s fired on state %d\n", | ||
271 | print_mac(mac, sta->addr), sta->plink_state); | ||
272 | reason = 0; | ||
273 | llid = sta->llid; | ||
274 | plid = sta->plid; | ||
275 | dev = sta->dev; | ||
276 | sdata = IEEE80211_DEV_TO_SUB_IF(dev); | ||
277 | |||
278 | switch (sta->plink_state) { | ||
279 | case OPN_RCVD: | ||
280 | case OPN_SNT: | ||
281 | /* retry timer */ | ||
282 | if (sta->plink_retries < dot11MeshMaxRetries(sdata)) { | ||
283 | u32 rand; | ||
284 | mpl_dbg("Mesh plink for %s (retry, timeout): %d %d\n", | ||
285 | print_mac(mac, sta->addr), | ||
286 | sta->plink_retries, sta->plink_timeout); | ||
287 | get_random_bytes(&rand, sizeof(u32)); | ||
288 | sta->plink_timeout = sta->plink_timeout + | ||
289 | rand % sta->plink_timeout; | ||
290 | ++sta->plink_retries; | ||
291 | if (!mod_plink_timer(sta, sta->plink_timeout)) | ||
292 | __sta_info_get(sta); | ||
293 | spin_unlock_bh(&sta->plink_lock); | ||
294 | mesh_plink_frame_tx(dev, PLINK_OPEN, sta->addr, llid, | ||
295 | 0, 0); | ||
296 | break; | ||
297 | } | ||
298 | reason = cpu_to_le16(MESH_MAX_RETRIES); | ||
299 | /* fall through on else */ | ||
300 | case CNF_RCVD: | ||
301 | /* confirm timer */ | ||
302 | if (!reason) | ||
303 | reason = cpu_to_le16(MESH_CONFIRM_TIMEOUT); | ||
304 | sta->plink_state = HOLDING; | ||
305 | if (!mod_plink_timer(sta, dot11MeshHoldingTimeout(sdata))) | ||
306 | __sta_info_get(sta); | ||
307 | spin_unlock_bh(&sta->plink_lock); | ||
308 | mesh_plink_frame_tx(dev, PLINK_CLOSE, sta->addr, llid, plid, | ||
309 | reason); | ||
310 | break; | ||
311 | case HOLDING: | ||
312 | /* holding timer */ | ||
313 | if (del_timer(&sta->plink_timer)) | ||
314 | sta_info_put(sta); | ||
315 | mesh_plink_fsm_restart(sta); | ||
316 | spin_unlock_bh(&sta->plink_lock); | ||
317 | break; | ||
318 | default: | ||
319 | spin_unlock_bh(&sta->plink_lock); | ||
320 | break; | ||
321 | } | ||
322 | |||
323 | sta_info_put(sta); | ||
324 | } | ||
325 | |||
326 | static inline void mesh_plink_timer_set(struct sta_info *sta, int timeout) | ||
327 | { | ||
328 | sta->plink_timer.expires = jiffies + (HZ * timeout / 1000); | ||
329 | sta->plink_timer.data = (unsigned long) sta; | ||
330 | sta->plink_timer.function = mesh_plink_timer; | ||
331 | sta->plink_timeout = timeout; | ||
332 | __sta_info_get(sta); | ||
333 | add_timer(&sta->plink_timer); | ||
334 | } | ||
335 | |||
336 | int mesh_plink_open(struct sta_info *sta) | ||
337 | { | ||
338 | __le16 llid; | ||
339 | struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(sta->dev); | ||
340 | #ifdef CONFIG_MAC80211_VERBOSE_MPL_DEBUG | ||
341 | DECLARE_MAC_BUF(mac); | ||
342 | #endif | ||
343 | |||
344 | spin_lock_bh(&sta->plink_lock); | ||
345 | get_random_bytes(&llid, 2); | ||
346 | sta->llid = llid; | ||
347 | if (sta->plink_state != LISTEN) { | ||
348 | spin_unlock_bh(&sta->plink_lock); | ||
349 | sta_info_put(sta); | ||
350 | return -EBUSY; | ||
351 | } | ||
352 | sta->plink_state = 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(sta->dev, PLINK_OPEN, sta->addr, llid, 0, 0); | ||
359 | } | ||
360 | |||
361 | void mesh_plink_block(struct sta_info *sta) | ||
362 | { | ||
363 | #ifdef CONFIG_MAC80211_VERBOSE_MPL_DEBUG | ||
364 | DECLARE_MAC_BUF(mac); | ||
365 | #endif | ||
366 | |||
367 | spin_lock_bh(&sta->plink_lock); | ||
368 | mesh_plink_deactivate(sta); | ||
369 | sta->plink_state = BLOCKED; | ||
370 | spin_unlock_bh(&sta->plink_lock); | ||
371 | } | ||
372 | |||
373 | int mesh_plink_close(struct sta_info *sta) | ||
374 | { | ||
375 | struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(sta->dev); | ||
376 | int llid, plid, reason; | ||
377 | #ifdef CONFIG_MAC80211_VERBOSE_MPL_DEBUG | ||
378 | DECLARE_MAC_BUF(mac); | ||
379 | #endif | ||
380 | |||
381 | mpl_dbg("Mesh plink: closing link with %s\n", | ||
382 | print_mac(mac, sta->addr)); | ||
383 | spin_lock_bh(&sta->plink_lock); | ||
384 | sta->reason = cpu_to_le16(MESH_LINK_CANCELLED); | ||
385 | reason = sta->reason; | ||
386 | |||
387 | if (sta->plink_state == LISTEN || sta->plink_state == BLOCKED) { | ||
388 | mesh_plink_fsm_restart(sta); | ||
389 | spin_unlock_bh(&sta->plink_lock); | ||
390 | sta_info_put(sta); | ||
391 | return 0; | ||
392 | } else if (sta->plink_state == ESTAB) { | ||
393 | mesh_plink_deactivate(sta); | ||
394 | /* The timer should not be running */ | ||
395 | if (!mod_plink_timer(sta, dot11MeshHoldingTimeout(sdata))) | ||
396 | __sta_info_get(sta); | ||
397 | } else if (!mod_plink_timer(sta, dot11MeshHoldingTimeout(sdata))) | ||
398 | sta->ignore_plink_timer = true; | ||
399 | |||
400 | sta->plink_state = HOLDING; | ||
401 | llid = sta->llid; | ||
402 | plid = sta->plid; | ||
403 | spin_unlock_bh(&sta->plink_lock); | ||
404 | mesh_plink_frame_tx(sta->dev, PLINK_CLOSE, sta->addr, llid, plid, | ||
405 | 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_local *local = wdev_priv(dev->ieee80211_ptr); | ||
413 | struct ieee802_11_elems elems; | ||
414 | struct sta_info *sta; | ||
415 | enum plink_event event; | ||
416 | enum plink_frame_type ftype; | ||
417 | size_t baselen; | ||
418 | u8 ie_len; | ||
419 | u8 *baseaddr; | ||
420 | __le16 plid, llid, reason; | ||
421 | #ifdef CONFIG_MAC80211_VERBOSE_MPL_DEBUG | ||
422 | DECLARE_MAC_BUF(mac); | ||
423 | #endif | ||
424 | struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); | ||
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 | sta = sta_info_get(local, mgmt->sa); | ||
464 | if (!sta && ftype != PLINK_OPEN) { | ||
465 | mpl_dbg("Mesh plink: cls or cnf from unknown peer\n"); | ||
466 | return; | ||
467 | } | ||
468 | |||
469 | if (sta && sta->plink_state == BLOCKED) { | ||
470 | sta_info_put(sta); | ||
471 | return; | ||
472 | } | ||
473 | |||
474 | /* Now we will figure out the appropriate event... */ | ||
475 | event = PLINK_UNDEFINED; | ||
476 | if (ftype != PLINK_CLOSE && (!mesh_matches_local(&elems, dev))) { | ||
477 | switch (ftype) { | ||
478 | case PLINK_OPEN: | ||
479 | event = OPN_RJCT; | ||
480 | break; | ||
481 | case PLINK_CONFIRM: | ||
482 | event = CNF_RJCT; | ||
483 | break; | ||
484 | case PLINK_CLOSE: | ||
485 | /* avoid warning */ | ||
486 | break; | ||
487 | } | ||
488 | spin_lock_bh(&sta->plink_lock); | ||
489 | } else if (!sta) { | ||
490 | /* ftype == PLINK_OPEN */ | ||
491 | u64 rates; | ||
492 | if (!mesh_plink_free_count(sdata)) { | ||
493 | mpl_dbg("Mesh plink error: no more free plinks\n"); | ||
494 | return; | ||
495 | } | ||
496 | |||
497 | rates = ieee80211_sta_get_rates(local, &elems, rx_status->band); | ||
498 | sta = mesh_plink_add(mgmt->sa, rates, dev); | ||
499 | if (IS_ERR(sta)) { | ||
500 | mpl_dbg("Mesh plink error: plink table full\n"); | ||
501 | return; | ||
502 | } | ||
503 | event = OPN_ACPT; | ||
504 | spin_lock_bh(&sta->plink_lock); | ||
505 | } else { | ||
506 | spin_lock_bh(&sta->plink_lock); | ||
507 | switch (ftype) { | ||
508 | case PLINK_OPEN: | ||
509 | if (!mesh_plink_free_count(sdata) || | ||
510 | (sta->plid && sta->plid != plid)) | ||
511 | event = OPN_IGNR; | ||
512 | else | ||
513 | event = OPN_ACPT; | ||
514 | break; | ||
515 | case PLINK_CONFIRM: | ||
516 | if (!mesh_plink_free_count(sdata) || | ||
517 | (sta->llid != llid || sta->plid != plid)) | ||
518 | event = CNF_IGNR; | ||
519 | else | ||
520 | event = CNF_ACPT; | ||
521 | break; | ||
522 | case PLINK_CLOSE: | ||
523 | if (sta->plink_state == ESTAB) | ||
524 | /* Do not check for llid or plid. This does not | ||
525 | * follow the standard but since multiple plinks | ||
526 | * per sta are not supported, it is necessary in | ||
527 | * order to avoid a livelock when MP A sees an | ||
528 | * establish peer link to MP B but MP B does not | ||
529 | * see it. This can be caused by a timeout in | ||
530 | * B's peer link establishment or B beign | ||
531 | * restarted. | ||
532 | */ | ||
533 | event = CLS_ACPT; | ||
534 | else if (sta->plid != plid) | ||
535 | event = CLS_IGNR; | ||
536 | else if (ie_len == 7 && sta->llid != llid) | ||
537 | event = CLS_IGNR; | ||
538 | else | ||
539 | event = CLS_ACPT; | ||
540 | break; | ||
541 | default: | ||
542 | mpl_dbg("Mesh plink: unknown frame subtype\n"); | ||
543 | spin_unlock_bh(&sta->plink_lock); | ||
544 | sta_info_put(sta); | ||
545 | return; | ||
546 | } | ||
547 | } | ||
548 | |||
549 | mpl_dbg("Mesh plink (peer, state, llid, plid, event): %s %d %d %d %d\n", | ||
550 | print_mac(mac, mgmt->sa), sta->plink_state, | ||
551 | __le16_to_cpu(sta->llid), __le16_to_cpu(sta->plid), | ||
552 | event); | ||
553 | reason = 0; | ||
554 | switch (sta->plink_state) { | ||
555 | /* spin_unlock as soon as state is updated at each case */ | ||
556 | case LISTEN: | ||
557 | switch (event) { | ||
558 | case CLS_ACPT: | ||
559 | mesh_plink_fsm_restart(sta); | ||
560 | spin_unlock_bh(&sta->plink_lock); | ||
561 | break; | ||
562 | case OPN_ACPT: | ||
563 | sta->plink_state = OPN_RCVD; | ||
564 | sta->plid = plid; | ||
565 | get_random_bytes(&llid, 2); | ||
566 | sta->llid = llid; | ||
567 | mesh_plink_timer_set(sta, dot11MeshRetryTimeout(sdata)); | ||
568 | spin_unlock_bh(&sta->plink_lock); | ||
569 | mesh_plink_frame_tx(dev, PLINK_OPEN, sta->addr, llid, | ||
570 | 0, 0); | ||
571 | mesh_plink_frame_tx(dev, PLINK_CONFIRM, sta->addr, | ||
572 | llid, plid, 0); | ||
573 | break; | ||
574 | default: | ||
575 | spin_unlock_bh(&sta->plink_lock); | ||
576 | break; | ||
577 | } | ||
578 | break; | ||
579 | |||
580 | case OPN_SNT: | ||
581 | switch (event) { | ||
582 | case OPN_RJCT: | ||
583 | case CNF_RJCT: | ||
584 | reason = cpu_to_le16(MESH_CAPABILITY_POLICY_VIOLATION); | ||
585 | case CLS_ACPT: | ||
586 | if (!reason) | ||
587 | reason = cpu_to_le16(MESH_CLOSE_RCVD); | ||
588 | sta->reason = reason; | ||
589 | sta->plink_state = HOLDING; | ||
590 | if (!mod_plink_timer(sta, | ||
591 | dot11MeshHoldingTimeout(sdata))) | ||
592 | sta->ignore_plink_timer = true; | ||
593 | |||
594 | llid = sta->llid; | ||
595 | spin_unlock_bh(&sta->plink_lock); | ||
596 | mesh_plink_frame_tx(dev, PLINK_CLOSE, sta->addr, llid, | ||
597 | plid, reason); | ||
598 | break; | ||
599 | case OPN_ACPT: | ||
600 | /* retry timer is left untouched */ | ||
601 | sta->plink_state = OPN_RCVD; | ||
602 | sta->plid = plid; | ||
603 | llid = sta->llid; | ||
604 | spin_unlock_bh(&sta->plink_lock); | ||
605 | mesh_plink_frame_tx(dev, PLINK_CONFIRM, sta->addr, llid, | ||
606 | plid, 0); | ||
607 | break; | ||
608 | case CNF_ACPT: | ||
609 | sta->plink_state = CNF_RCVD; | ||
610 | if (!mod_plink_timer(sta, | ||
611 | dot11MeshConfirmTimeout(sdata))) | ||
612 | sta->ignore_plink_timer = true; | ||
613 | |||
614 | spin_unlock_bh(&sta->plink_lock); | ||
615 | break; | ||
616 | default: | ||
617 | spin_unlock_bh(&sta->plink_lock); | ||
618 | break; | ||
619 | } | ||
620 | break; | ||
621 | |||
622 | case OPN_RCVD: | ||
623 | switch (event) { | ||
624 | case OPN_RJCT: | ||
625 | case CNF_RJCT: | ||
626 | reason = cpu_to_le16(MESH_CAPABILITY_POLICY_VIOLATION); | ||
627 | case CLS_ACPT: | ||
628 | if (!reason) | ||
629 | reason = cpu_to_le16(MESH_CLOSE_RCVD); | ||
630 | sta->reason = reason; | ||
631 | sta->plink_state = HOLDING; | ||
632 | if (!mod_plink_timer(sta, | ||
633 | dot11MeshHoldingTimeout(sdata))) | ||
634 | sta->ignore_plink_timer = true; | ||
635 | |||
636 | llid = sta->llid; | ||
637 | spin_unlock_bh(&sta->plink_lock); | ||
638 | mesh_plink_frame_tx(dev, PLINK_CLOSE, sta->addr, llid, | ||
639 | plid, reason); | ||
640 | break; | ||
641 | case OPN_ACPT: | ||
642 | llid = sta->llid; | ||
643 | spin_unlock_bh(&sta->plink_lock); | ||
644 | mesh_plink_frame_tx(dev, PLINK_CONFIRM, sta->addr, llid, | ||
645 | plid, 0); | ||
646 | break; | ||
647 | case CNF_ACPT: | ||
648 | if (del_timer(&sta->plink_timer)) | ||
649 | sta_info_put(sta); | ||
650 | sta->plink_state = ESTAB; | ||
651 | mesh_plink_inc_estab_count(sdata); | ||
652 | spin_unlock_bh(&sta->plink_lock); | ||
653 | mpl_dbg("Mesh plink with %s ESTABLISHED\n", | ||
654 | print_mac(mac, sta->addr)); | ||
655 | break; | ||
656 | default: | ||
657 | spin_unlock_bh(&sta->plink_lock); | ||
658 | break; | ||
659 | } | ||
660 | break; | ||
661 | |||
662 | case CNF_RCVD: | ||
663 | switch (event) { | ||
664 | case OPN_RJCT: | ||
665 | case CNF_RJCT: | ||
666 | reason = cpu_to_le16(MESH_CAPABILITY_POLICY_VIOLATION); | ||
667 | case CLS_ACPT: | ||
668 | if (!reason) | ||
669 | reason = cpu_to_le16(MESH_CLOSE_RCVD); | ||
670 | sta->reason = reason; | ||
671 | sta->plink_state = HOLDING; | ||
672 | if (!mod_plink_timer(sta, | ||
673 | dot11MeshHoldingTimeout(sdata))) | ||
674 | sta->ignore_plink_timer = true; | ||
675 | |||
676 | llid = sta->llid; | ||
677 | spin_unlock_bh(&sta->plink_lock); | ||
678 | mesh_plink_frame_tx(dev, PLINK_CLOSE, sta->addr, llid, | ||
679 | plid, reason); | ||
680 | case OPN_ACPT: | ||
681 | if (del_timer(&sta->plink_timer)) | ||
682 | sta_info_put(sta); | ||
683 | sta->plink_state = ESTAB; | ||
684 | mesh_plink_inc_estab_count(sdata); | ||
685 | spin_unlock_bh(&sta->plink_lock); | ||
686 | mpl_dbg("Mesh plink with %s ESTABLISHED\n", | ||
687 | print_mac(mac, sta->addr)); | ||
688 | mesh_plink_frame_tx(dev, PLINK_CONFIRM, sta->addr, llid, | ||
689 | plid, 0); | ||
690 | break; | ||
691 | default: | ||
692 | spin_unlock_bh(&sta->plink_lock); | ||
693 | break; | ||
694 | } | ||
695 | break; | ||
696 | |||
697 | case ESTAB: | ||
698 | switch (event) { | ||
699 | case CLS_ACPT: | ||
700 | reason = cpu_to_le16(MESH_CLOSE_RCVD); | ||
701 | sta->reason = reason; | ||
702 | mesh_plink_deactivate(sta); | ||
703 | sta->plink_state = HOLDING; | ||
704 | llid = sta->llid; | ||
705 | if (!mod_plink_timer(sta, | ||
706 | dot11MeshHoldingTimeout(sdata))) | ||
707 | __sta_info_get(sta); | ||
708 | spin_unlock_bh(&sta->plink_lock); | ||
709 | mesh_plink_frame_tx(dev, PLINK_CLOSE, sta->addr, llid, | ||
710 | plid, reason); | ||
711 | break; | ||
712 | case OPN_ACPT: | ||
713 | llid = sta->llid; | ||
714 | spin_unlock_bh(&sta->plink_lock); | ||
715 | mesh_plink_frame_tx(dev, PLINK_CONFIRM, sta->addr, llid, | ||
716 | plid, 0); | ||
717 | break; | ||
718 | default: | ||
719 | spin_unlock_bh(&sta->plink_lock); | ||
720 | break; | ||
721 | } | ||
722 | break; | ||
723 | case HOLDING: | ||
724 | switch (event) { | ||
725 | case CLS_ACPT: | ||
726 | if (del_timer(&sta->plink_timer)) { | ||
727 | sta->ignore_plink_timer = 1; | ||
728 | sta_info_put(sta); | ||
729 | } | ||
730 | mesh_plink_fsm_restart(sta); | ||
731 | spin_unlock_bh(&sta->plink_lock); | ||
732 | break; | ||
733 | case OPN_ACPT: | ||
734 | case CNF_ACPT: | ||
735 | case OPN_RJCT: | ||
736 | case CNF_RJCT: | ||
737 | llid = sta->llid; | ||
738 | reason = sta->reason; | ||
739 | spin_unlock_bh(&sta->plink_lock); | ||
740 | mesh_plink_frame_tx(dev, PLINK_CLOSE, sta->addr, llid, | ||
741 | plid, reason); | ||
742 | break; | ||
743 | default: | ||
744 | spin_unlock_bh(&sta->plink_lock); | ||
745 | } | ||
746 | break; | ||
747 | default: | ||
748 | /* should not get here, BLOCKED is dealt with at the beggining | ||
749 | * of the function | ||
750 | */ | ||
751 | spin_unlock_bh(&sta->plink_lock); | ||
752 | break; | ||
753 | } | ||
754 | sta_info_put(sta); | ||
755 | } | ||