]>
Commit | Line | Data |
---|---|---|
b97bf3fd PL |
1 | /* |
2 | * net/tipc/node.c: TIPC node management routines | |
c4307285 | 3 | * |
dd3f9e70 | 4 | * Copyright (c) 2000-2006, 2012-2015, Ericsson AB |
46651c59 | 5 | * Copyright (c) 2005-2006, 2010-2014, Wind River Systems |
b97bf3fd PL |
6 | * All rights reserved. |
7 | * | |
9ea1fd3c | 8 | * Redistribution and use in source and binary forms, with or without |
b97bf3fd PL |
9 | * modification, are permitted provided that the following conditions are met: |
10 | * | |
9ea1fd3c PL |
11 | * 1. Redistributions of source code must retain the above copyright |
12 | * notice, this list of conditions and the following disclaimer. | |
13 | * 2. Redistributions in binary form must reproduce the above copyright | |
14 | * notice, this list of conditions and the following disclaimer in the | |
15 | * documentation and/or other materials provided with the distribution. | |
16 | * 3. Neither the names of the copyright holders nor the names of its | |
17 | * contributors may be used to endorse or promote products derived from | |
18 | * this software without specific prior written permission. | |
b97bf3fd | 19 | * |
9ea1fd3c PL |
20 | * Alternatively, this software may be distributed under the terms of the |
21 | * GNU General Public License ("GPL") version 2 as published by the Free | |
22 | * Software Foundation. | |
23 | * | |
24 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" | |
25 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | |
26 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | |
27 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE | |
28 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR | |
29 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF | |
30 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS | |
31 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN | |
32 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) | |
33 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE | |
b97bf3fd PL |
34 | * POSSIBILITY OF SUCH DAMAGE. |
35 | */ | |
36 | ||
37 | #include "core.h" | |
22ae7cff | 38 | #include "link.h" |
b97bf3fd | 39 | #include "node.h" |
b97bf3fd | 40 | #include "name_distr.h" |
50100a5e | 41 | #include "socket.h" |
a6bf70f7 | 42 | #include "bcast.h" |
b97bf3fd | 43 | |
6c00055a DM |
44 | static void node_lost_contact(struct tipc_node *n_ptr); |
45 | static void node_established_contact(struct tipc_node *n_ptr); | |
8a0f6ebe | 46 | static void tipc_node_delete(struct tipc_node *node); |
8a1577c9 | 47 | static void tipc_node_timeout(unsigned long data); |
b97bf3fd | 48 | |
02be61a9 JPM |
49 | struct tipc_sock_conn { |
50 | u32 port; | |
51 | u32 peer_port; | |
52 | u32 peer_node; | |
53 | struct list_head list; | |
54 | }; | |
55 | ||
3e4b6ab5 RA |
56 | static const struct nla_policy tipc_nl_node_policy[TIPC_NLA_NODE_MAX + 1] = { |
57 | [TIPC_NLA_NODE_UNSPEC] = { .type = NLA_UNSPEC }, | |
58 | [TIPC_NLA_NODE_ADDR] = { .type = NLA_U32 }, | |
59 | [TIPC_NLA_NODE_UP] = { .type = NLA_FLAG } | |
60 | }; | |
61 | ||
a635b46b AS |
62 | /* |
63 | * A trivial power-of-two bitmask technique is used for speed, since this | |
64 | * operation is done for every incoming TIPC packet. The number of hash table | |
65 | * entries has been chosen so that no hash chain exceeds 8 nodes and will | |
66 | * usually be much smaller (typically only a single node). | |
67 | */ | |
872f24db | 68 | static unsigned int tipc_hashfn(u32 addr) |
a635b46b AS |
69 | { |
70 | return addr & (NODE_HTABLE_SIZE - 1); | |
71 | } | |
72 | ||
8a0f6ebe YX |
73 | static void tipc_node_kref_release(struct kref *kref) |
74 | { | |
75 | struct tipc_node *node = container_of(kref, struct tipc_node, kref); | |
76 | ||
77 | tipc_node_delete(node); | |
78 | } | |
79 | ||
80 | void tipc_node_put(struct tipc_node *node) | |
81 | { | |
82 | kref_put(&node->kref, tipc_node_kref_release); | |
83 | } | |
84 | ||
85 | static void tipc_node_get(struct tipc_node *node) | |
86 | { | |
87 | kref_get(&node->kref); | |
88 | } | |
89 | ||
1ec2bb08 | 90 | /* |
672d99e1 AS |
91 | * tipc_node_find - locate specified node object, if it exists |
92 | */ | |
f2f9800d | 93 | struct tipc_node *tipc_node_find(struct net *net, u32 addr) |
672d99e1 | 94 | { |
f2f9800d | 95 | struct tipc_net *tn = net_generic(net, tipc_net_id); |
672d99e1 | 96 | struct tipc_node *node; |
672d99e1 | 97 | |
34747539 | 98 | if (unlikely(!in_own_cluster_exact(net, addr))) |
672d99e1 AS |
99 | return NULL; |
100 | ||
6c7a762e | 101 | rcu_read_lock(); |
f2f9800d YX |
102 | hlist_for_each_entry_rcu(node, &tn->node_htable[tipc_hashfn(addr)], |
103 | hash) { | |
46651c59 | 104 | if (node->addr == addr) { |
8a0f6ebe | 105 | tipc_node_get(node); |
6c7a762e | 106 | rcu_read_unlock(); |
672d99e1 | 107 | return node; |
46651c59 | 108 | } |
672d99e1 | 109 | } |
6c7a762e | 110 | rcu_read_unlock(); |
672d99e1 AS |
111 | return NULL; |
112 | } | |
113 | ||
f2f9800d | 114 | struct tipc_node *tipc_node_create(struct net *net, u32 addr) |
b97bf3fd | 115 | { |
f2f9800d | 116 | struct tipc_net *tn = net_generic(net, tipc_net_id); |
672d99e1 | 117 | struct tipc_node *n_ptr, *temp_node; |
b97bf3fd | 118 | |
f2f9800d | 119 | spin_lock_bh(&tn->node_list_lock); |
b45db71b JPM |
120 | n_ptr = tipc_node_find(net, addr); |
121 | if (n_ptr) | |
122 | goto exit; | |
5af54792 | 123 | n_ptr = kzalloc(sizeof(*n_ptr), GFP_ATOMIC); |
a10bd924 | 124 | if (!n_ptr) { |
2cf8aa19 | 125 | pr_warn("Node creation failed, no memory\n"); |
b45db71b | 126 | goto exit; |
a10bd924 | 127 | } |
a10bd924 | 128 | n_ptr->addr = addr; |
f2f9800d | 129 | n_ptr->net = net; |
8a0f6ebe | 130 | kref_init(&n_ptr->kref); |
51a8e4de | 131 | spin_lock_init(&n_ptr->lock); |
672d99e1 AS |
132 | INIT_HLIST_NODE(&n_ptr->hash); |
133 | INIT_LIST_HEAD(&n_ptr->list); | |
a8f48af5 | 134 | INIT_LIST_HEAD(&n_ptr->publ_list); |
02be61a9 | 135 | INIT_LIST_HEAD(&n_ptr->conn_sks); |
d39bbd44 | 136 | skb_queue_head_init(&n_ptr->bclink.namedq); |
05dcc5aa | 137 | __skb_queue_head_init(&n_ptr->bclink.deferdq); |
f2f9800d | 138 | hlist_add_head_rcu(&n_ptr->hash, &tn->node_htable[tipc_hashfn(addr)]); |
f2f9800d | 139 | list_for_each_entry_rcu(temp_node, &tn->node_list, list) { |
672d99e1 AS |
140 | if (n_ptr->addr < temp_node->addr) |
141 | break; | |
142 | } | |
6c7a762e | 143 | list_add_tail_rcu(&n_ptr->list, &temp_node->list); |
aecb9bb8 | 144 | n_ptr->action_flags = TIPC_WAIT_PEER_LINKS_DOWN; |
fc0eea69 | 145 | n_ptr->signature = INVALID_NODE_SIG; |
36e78a46 JPM |
146 | n_ptr->active_links[0] = INVALID_BEARER_ID; |
147 | n_ptr->active_links[1] = INVALID_BEARER_ID; | |
8a0f6ebe | 148 | tipc_node_get(n_ptr); |
8a1577c9 JPM |
149 | setup_timer(&n_ptr->timer, tipc_node_timeout, (unsigned long)n_ptr); |
150 | n_ptr->keepalive_intv = U32_MAX; | |
b45db71b | 151 | exit: |
f2f9800d | 152 | spin_unlock_bh(&tn->node_list_lock); |
b97bf3fd PL |
153 | return n_ptr; |
154 | } | |
155 | ||
8a1577c9 JPM |
156 | static void tipc_node_calculate_timer(struct tipc_node *n, struct tipc_link *l) |
157 | { | |
158 | unsigned long tol = l->tolerance; | |
159 | unsigned long intv = ((tol / 4) > 500) ? 500 : tol / 4; | |
160 | unsigned long keepalive_intv = msecs_to_jiffies(intv); | |
161 | ||
162 | /* Link with lowest tolerance determines timer interval */ | |
163 | if (keepalive_intv < n->keepalive_intv) | |
164 | n->keepalive_intv = keepalive_intv; | |
165 | ||
166 | /* Ensure link's abort limit corresponds to current interval */ | |
167 | l->abort_limit = l->tolerance / jiffies_to_msecs(n->keepalive_intv); | |
168 | } | |
169 | ||
8a0f6ebe | 170 | static void tipc_node_delete(struct tipc_node *node) |
b97bf3fd | 171 | { |
8a0f6ebe YX |
172 | list_del_rcu(&node->list); |
173 | hlist_del_rcu(&node->hash); | |
174 | kfree_rcu(node, rcu); | |
b97bf3fd PL |
175 | } |
176 | ||
f2f9800d | 177 | void tipc_node_stop(struct net *net) |
46651c59 | 178 | { |
f2f9800d | 179 | struct tipc_net *tn = net_generic(net, tipc_net_id); |
46651c59 YX |
180 | struct tipc_node *node, *t_node; |
181 | ||
f2f9800d | 182 | spin_lock_bh(&tn->node_list_lock); |
8a1577c9 JPM |
183 | list_for_each_entry_safe(node, t_node, &tn->node_list, list) { |
184 | if (del_timer(&node->timer)) | |
185 | tipc_node_put(node); | |
8a0f6ebe | 186 | tipc_node_put(node); |
8a1577c9 | 187 | } |
f2f9800d | 188 | spin_unlock_bh(&tn->node_list_lock); |
46651c59 YX |
189 | } |
190 | ||
f2f9800d | 191 | int tipc_node_add_conn(struct net *net, u32 dnode, u32 port, u32 peer_port) |
02be61a9 JPM |
192 | { |
193 | struct tipc_node *node; | |
194 | struct tipc_sock_conn *conn; | |
8a0f6ebe | 195 | int err = 0; |
02be61a9 | 196 | |
34747539 | 197 | if (in_own_node(net, dnode)) |
02be61a9 JPM |
198 | return 0; |
199 | ||
f2f9800d | 200 | node = tipc_node_find(net, dnode); |
02be61a9 JPM |
201 | if (!node) { |
202 | pr_warn("Connecting sock to node 0x%x failed\n", dnode); | |
203 | return -EHOSTUNREACH; | |
204 | } | |
205 | conn = kmalloc(sizeof(*conn), GFP_ATOMIC); | |
8a0f6ebe YX |
206 | if (!conn) { |
207 | err = -EHOSTUNREACH; | |
208 | goto exit; | |
209 | } | |
02be61a9 JPM |
210 | conn->peer_node = dnode; |
211 | conn->port = port; | |
212 | conn->peer_port = peer_port; | |
213 | ||
214 | tipc_node_lock(node); | |
215 | list_add_tail(&conn->list, &node->conn_sks); | |
216 | tipc_node_unlock(node); | |
8a0f6ebe YX |
217 | exit: |
218 | tipc_node_put(node); | |
219 | return err; | |
02be61a9 JPM |
220 | } |
221 | ||
f2f9800d | 222 | void tipc_node_remove_conn(struct net *net, u32 dnode, u32 port) |
02be61a9 JPM |
223 | { |
224 | struct tipc_node *node; | |
225 | struct tipc_sock_conn *conn, *safe; | |
226 | ||
34747539 | 227 | if (in_own_node(net, dnode)) |
02be61a9 JPM |
228 | return; |
229 | ||
f2f9800d | 230 | node = tipc_node_find(net, dnode); |
02be61a9 JPM |
231 | if (!node) |
232 | return; | |
233 | ||
234 | tipc_node_lock(node); | |
235 | list_for_each_entry_safe(conn, safe, &node->conn_sks, list) { | |
236 | if (port != conn->port) | |
237 | continue; | |
238 | list_del(&conn->list); | |
239 | kfree(conn); | |
240 | } | |
241 | tipc_node_unlock(node); | |
8a0f6ebe | 242 | tipc_node_put(node); |
02be61a9 JPM |
243 | } |
244 | ||
8a1577c9 JPM |
245 | /* tipc_node_timeout - handle expiration of node timer |
246 | */ | |
247 | static void tipc_node_timeout(unsigned long data) | |
248 | { | |
249 | struct tipc_node *n = (struct tipc_node *)data; | |
250 | struct sk_buff_head xmitq; | |
251 | struct tipc_link *l; | |
252 | struct tipc_media_addr *maddr; | |
253 | int bearer_id; | |
254 | int rc = 0; | |
255 | ||
256 | __skb_queue_head_init(&xmitq); | |
257 | ||
258 | for (bearer_id = 0; bearer_id < MAX_BEARERS; bearer_id++) { | |
259 | tipc_node_lock(n); | |
260 | l = n->links[bearer_id].link; | |
261 | if (l) { | |
262 | /* Link tolerance may change asynchronously: */ | |
263 | tipc_node_calculate_timer(n, l); | |
264 | rc = tipc_link_timeout(l, &xmitq); | |
265 | if (rc & TIPC_LINK_DOWN_EVT) | |
266 | tipc_link_reset(l); | |
267 | } | |
268 | tipc_node_unlock(n); | |
269 | maddr = &n->links[bearer_id].maddr; | |
270 | tipc_bearer_xmit(n->net, bearer_id, &xmitq, maddr); | |
271 | } | |
272 | if (!mod_timer(&n->timer, jiffies + n->keepalive_intv)) | |
273 | tipc_node_get(n); | |
274 | tipc_node_put(n); | |
275 | } | |
276 | ||
b97bf3fd | 277 | /** |
4323add6 | 278 | * tipc_node_link_up - handle addition of link |
c4307285 | 279 | * |
b97bf3fd PL |
280 | * Link becomes active (alone or shared) or standby, depending on its priority. |
281 | */ | |
9d13ec65 | 282 | void tipc_node_link_up(struct tipc_node *n, int bearer_id) |
b97bf3fd | 283 | { |
36e78a46 JPM |
284 | int *slot0 = &n->active_links[0]; |
285 | int *slot1 = &n->active_links[1]; | |
286 | struct tipc_link_entry *links = n->links; | |
287 | struct tipc_link *l = n->links[bearer_id].link; | |
b97bf3fd | 288 | |
9d13ec65 | 289 | /* Leave room for tunnel header when returning 'mtu' to users: */ |
36e78a46 | 290 | links[bearer_id].mtu = l->mtu - INT_H_SIZE; |
9d13ec65 JPM |
291 | |
292 | n->working_links++; | |
293 | n->action_flags |= TIPC_NOTIFY_LINK_UP; | |
294 | n->link_id = l->peer_bearer_id << 16 | l->bearer_id; | |
7b8613e0 | 295 | |
3fa9cacd | 296 | pr_debug("Established link <%s> on network plane %c\n", |
9d13ec65 | 297 | l->name, l->net_plane); |
c4307285 | 298 | |
9d13ec65 | 299 | /* No active links ? => take both active slots */ |
36e78a46 JPM |
300 | if (*slot0 < 0) { |
301 | *slot0 = bearer_id; | |
302 | *slot1 = bearer_id; | |
9d13ec65 JPM |
303 | node_established_contact(n); |
304 | return; | |
b97bf3fd | 305 | } |
36e78a46 JPM |
306 | |
307 | /* Lower prio than current active ? => no slot */ | |
308 | if (l->priority < links[*slot0].link->priority) { | |
9d13ec65 JPM |
309 | pr_debug("New link <%s> becomes standby\n", l->name); |
310 | return; | |
b97bf3fd | 311 | } |
36e78a46 | 312 | tipc_link_dup_queue_xmit(links[*slot0].link, l); |
9d13ec65 | 313 | |
36e78a46 JPM |
314 | /* Same prio as current active ? => take one slot */ |
315 | if (l->priority == links[*slot0].link->priority) { | |
316 | *slot0 = bearer_id; | |
9d13ec65 | 317 | return; |
b97bf3fd | 318 | } |
b97bf3fd | 319 | |
36e78a46 JPM |
320 | /* Higher prio than current active => take both active slots */ |
321 | pr_debug("Old link <%s> now standby\n", links[*slot0].link->name); | |
322 | *slot0 = bearer_id; | |
323 | *slot1 = bearer_id; | |
b97bf3fd PL |
324 | } |
325 | ||
326 | /** | |
4323add6 | 327 | * tipc_node_link_down - handle loss of link |
b97bf3fd | 328 | */ |
9d13ec65 | 329 | void tipc_node_link_down(struct tipc_node *n, int bearer_id) |
b97bf3fd | 330 | { |
36e78a46 JPM |
331 | int *slot0 = &n->active_links[0]; |
332 | int *slot1 = &n->active_links[1]; | |
333 | int i, highest = 0; | |
334 | struct tipc_link *l, *_l; | |
b97bf3fd | 335 | |
36e78a46 | 336 | l = n->links[bearer_id].link; |
9d13ec65 JPM |
337 | n->working_links--; |
338 | n->action_flags |= TIPC_NOTIFY_LINK_DOWN; | |
339 | n->link_id = l->peer_bearer_id << 16 | l->bearer_id; | |
5392d646 | 340 | |
3fa9cacd | 341 | pr_debug("Lost link <%s> on network plane %c\n", |
9d13ec65 | 342 | l->name, l->net_plane); |
16e166b8 | 343 | |
36e78a46 JPM |
344 | /* Select new active link if any available */ |
345 | *slot0 = INVALID_BEARER_ID; | |
346 | *slot1 = INVALID_BEARER_ID; | |
347 | for (i = 0; i < MAX_BEARERS; i++) { | |
348 | _l = n->links[i].link; | |
349 | if (!_l || !tipc_link_is_up(_l)) | |
350 | continue; | |
351 | if (_l->priority < highest) | |
352 | continue; | |
353 | if (_l->priority > highest) { | |
354 | highest = _l->priority; | |
355 | *slot0 = i; | |
356 | *slot1 = i; | |
357 | continue; | |
358 | } | |
359 | *slot1 = i; | |
360 | } | |
9d13ec65 JPM |
361 | if (tipc_node_is_up(n)) |
362 | tipc_link_failover_send_queue(l); | |
363 | else | |
364 | node_lost_contact(n); | |
b97bf3fd PL |
365 | } |
366 | ||
9d13ec65 | 367 | bool tipc_node_is_up(struct tipc_node *n) |
b97bf3fd | 368 | { |
36e78a46 | 369 | return n->active_links[0] != INVALID_BEARER_ID; |
b97bf3fd PL |
370 | } |
371 | ||
d3a43b90 JPM |
372 | void tipc_node_check_dest(struct tipc_node *n, struct tipc_bearer *b, |
373 | bool *link_up, bool *addr_match, | |
374 | struct tipc_media_addr *maddr) | |
375 | { | |
376 | struct tipc_link *l = n->links[b->identity].link; | |
377 | struct tipc_media_addr *curr = &n->links[b->identity].maddr; | |
378 | ||
379 | *link_up = l && tipc_link_is_up(l); | |
380 | *addr_match = l && !memcmp(curr, maddr, sizeof(*maddr)); | |
381 | } | |
382 | ||
383 | bool tipc_node_update_dest(struct tipc_node *n, struct tipc_bearer *b, | |
384 | struct tipc_media_addr *maddr) | |
385 | { | |
386 | struct tipc_link *l = n->links[b->identity].link; | |
387 | struct tipc_media_addr *curr = &n->links[b->identity].maddr; | |
d39bbd44 | 388 | struct sk_buff_head *inputq = &n->links[b->identity].inputq; |
d3a43b90 | 389 | |
8a1577c9 | 390 | if (!l) { |
d39bbd44 | 391 | l = tipc_link_create(n, b, maddr, inputq, &n->bclink.namedq); |
8a1577c9 JPM |
392 | if (!l) |
393 | return false; | |
394 | tipc_node_calculate_timer(n, l); | |
395 | if (n->link_cnt == 1) { | |
396 | if (!mod_timer(&n->timer, jiffies + n->keepalive_intv)) | |
397 | tipc_node_get(n); | |
398 | } | |
399 | } | |
d3a43b90 JPM |
400 | memcpy(&l->media_addr, maddr, sizeof(*maddr)); |
401 | memcpy(curr, maddr, sizeof(*maddr)); | |
402 | tipc_link_reset(l); | |
403 | return true; | |
404 | } | |
405 | ||
a18c4bc3 | 406 | void tipc_node_attach_link(struct tipc_node *n_ptr, struct tipc_link *l_ptr) |
b97bf3fd | 407 | { |
9d13ec65 | 408 | n_ptr->links[l_ptr->bearer_id].link = l_ptr; |
37b9c08a | 409 | n_ptr->link_cnt++; |
b97bf3fd PL |
410 | } |
411 | ||
a18c4bc3 | 412 | void tipc_node_detach_link(struct tipc_node *n_ptr, struct tipc_link *l_ptr) |
b97bf3fd | 413 | { |
7d33939f JPM |
414 | int i; |
415 | ||
416 | for (i = 0; i < MAX_BEARERS; i++) { | |
9d13ec65 | 417 | if (l_ptr != n_ptr->links[i].link) |
074bb43e | 418 | continue; |
9d13ec65 | 419 | n_ptr->links[i].link = NULL; |
074bb43e | 420 | n_ptr->link_cnt--; |
7d33939f | 421 | } |
b97bf3fd PL |
422 | } |
423 | ||
6c00055a | 424 | static void node_established_contact(struct tipc_node *n_ptr) |
b97bf3fd | 425 | { |
aecb9bb8 | 426 | n_ptr->action_flags |= TIPC_NOTIFY_NODE_UP; |
c64f7a6a | 427 | n_ptr->bclink.oos_state = 0; |
1da46568 YX |
428 | n_ptr->bclink.acked = tipc_bclink_get_last_sent(n_ptr->net); |
429 | tipc_bclink_add_node(n_ptr->net, n_ptr->addr); | |
b97bf3fd PL |
430 | } |
431 | ||
6c00055a | 432 | static void node_lost_contact(struct tipc_node *n_ptr) |
b97bf3fd | 433 | { |
b97bf3fd | 434 | char addr_string[16]; |
708ac32c JPM |
435 | struct tipc_sock_conn *conn, *safe; |
436 | struct list_head *conns = &n_ptr->conn_sks; | |
437 | struct sk_buff *skb; | |
438 | struct tipc_net *tn = net_generic(n_ptr->net, tipc_net_id); | |
439 | uint i; | |
b97bf3fd | 440 | |
3fa9cacd EH |
441 | pr_debug("Lost contact with %s\n", |
442 | tipc_addr_string_fill(addr_string, n_ptr->addr)); | |
c5bd4d85 AS |
443 | |
444 | /* Flush broadcast link info associated with lost node */ | |
389dd9bc | 445 | if (n_ptr->bclink.recv_permitted) { |
05dcc5aa | 446 | __skb_queue_purge(&n_ptr->bclink.deferdq); |
c5bd4d85 | 447 | |
37e22164 JPM |
448 | if (n_ptr->bclink.reasm_buf) { |
449 | kfree_skb(n_ptr->bclink.reasm_buf); | |
450 | n_ptr->bclink.reasm_buf = NULL; | |
c5bd4d85 AS |
451 | } |
452 | ||
1da46568 | 453 | tipc_bclink_remove_node(n_ptr->net, n_ptr->addr); |
36559591 | 454 | tipc_bclink_acknowledge(n_ptr, INVALID_LINK_SEQ); |
b97bf3fd | 455 | |
389dd9bc | 456 | n_ptr->bclink.recv_permitted = false; |
c5bd4d85 | 457 | } |
b97bf3fd | 458 | |
dff29b1a | 459 | /* Abort any ongoing link failover */ |
b97bf3fd | 460 | for (i = 0; i < MAX_BEARERS; i++) { |
9d13ec65 | 461 | struct tipc_link *l_ptr = n_ptr->links[i].link; |
c4307285 | 462 | if (!l_ptr) |
b97bf3fd | 463 | continue; |
d3504c34 | 464 | l_ptr->exec_mode = TIPC_LINK_OPEN; |
dff29b1a JPM |
465 | l_ptr->failover_checkpt = 0; |
466 | l_ptr->failover_pkts = 0; | |
467 | kfree_skb(l_ptr->failover_skb); | |
468 | l_ptr->failover_skb = NULL; | |
4323add6 | 469 | tipc_link_reset_fragments(l_ptr); |
b97bf3fd PL |
470 | } |
471 | ||
ca9cf06a YX |
472 | n_ptr->action_flags &= ~TIPC_WAIT_OWN_LINKS_DOWN; |
473 | ||
708ac32c JPM |
474 | /* Prevent re-contact with node until cleanup is done */ |
475 | n_ptr->action_flags |= TIPC_WAIT_PEER_LINKS_DOWN; | |
476 | ||
477 | /* Notify publications from this node */ | |
478 | n_ptr->action_flags |= TIPC_NOTIFY_NODE_DOWN; | |
479 | ||
480 | /* Notify sockets connected to node */ | |
481 | list_for_each_entry_safe(conn, safe, conns, list) { | |
482 | skb = tipc_msg_create(TIPC_CRITICAL_IMPORTANCE, TIPC_CONN_MSG, | |
483 | SHORT_H_SIZE, 0, tn->own_addr, | |
484 | conn->peer_node, conn->port, | |
485 | conn->peer_port, TIPC_ERR_NO_NODE); | |
486 | if (likely(skb)) { | |
487 | skb_queue_tail(n_ptr->inputq, skb); | |
488 | n_ptr->action_flags |= TIPC_MSG_EVT; | |
489 | } | |
490 | list_del(&conn->list); | |
491 | kfree(conn); | |
492 | } | |
b97bf3fd PL |
493 | } |
494 | ||
78acb1f9 EH |
495 | /** |
496 | * tipc_node_get_linkname - get the name of a link | |
497 | * | |
498 | * @bearer_id: id of the bearer | |
499 | * @node: peer node address | |
500 | * @linkname: link name output buffer | |
501 | * | |
502 | * Returns 0 on success | |
503 | */ | |
f2f9800d YX |
504 | int tipc_node_get_linkname(struct net *net, u32 bearer_id, u32 addr, |
505 | char *linkname, size_t len) | |
78acb1f9 EH |
506 | { |
507 | struct tipc_link *link; | |
8a0f6ebe | 508 | int err = -EINVAL; |
f2f9800d | 509 | struct tipc_node *node = tipc_node_find(net, addr); |
78acb1f9 | 510 | |
8a0f6ebe YX |
511 | if (!node) |
512 | return err; | |
513 | ||
514 | if (bearer_id >= MAX_BEARERS) | |
515 | goto exit; | |
516 | ||
78acb1f9 | 517 | tipc_node_lock(node); |
9d13ec65 | 518 | link = node->links[bearer_id].link; |
78acb1f9 EH |
519 | if (link) { |
520 | strncpy(linkname, link->name, len); | |
8a0f6ebe | 521 | err = 0; |
78acb1f9 | 522 | } |
8a0f6ebe | 523 | exit: |
78acb1f9 | 524 | tipc_node_unlock(node); |
8a0f6ebe YX |
525 | tipc_node_put(node); |
526 | return err; | |
78acb1f9 | 527 | } |
9db9fdd1 YX |
528 | |
529 | void tipc_node_unlock(struct tipc_node *node) | |
530 | { | |
f2f9800d | 531 | struct net *net = node->net; |
ca0c4273 | 532 | u32 addr = 0; |
c637c103 | 533 | u32 flags = node->action_flags; |
7b8613e0 | 534 | u32 link_id = 0; |
708ac32c | 535 | struct list_head *publ_list; |
c637c103 | 536 | struct sk_buff_head *inputq = node->inputq; |
708ac32c | 537 | struct sk_buff_head *namedq; |
9db9fdd1 | 538 | |
c637c103 JPM |
539 | if (likely(!flags || (flags == TIPC_MSG_EVT))) { |
540 | node->action_flags = 0; | |
9db9fdd1 | 541 | spin_unlock_bh(&node->lock); |
c637c103 JPM |
542 | if (flags == TIPC_MSG_EVT) |
543 | tipc_sk_rcv(net, inputq); | |
9db9fdd1 YX |
544 | return; |
545 | } | |
546 | ||
7b8613e0 YX |
547 | addr = node->addr; |
548 | link_id = node->link_id; | |
c637c103 | 549 | namedq = node->namedq; |
708ac32c | 550 | publ_list = &node->publ_list; |
7b8613e0 | 551 | |
cb1b7280 JPM |
552 | node->action_flags &= ~(TIPC_MSG_EVT | |
553 | TIPC_NOTIFY_NODE_DOWN | TIPC_NOTIFY_NODE_UP | | |
554 | TIPC_NOTIFY_LINK_DOWN | TIPC_NOTIFY_LINK_UP | | |
555 | TIPC_WAKEUP_BCAST_USERS | TIPC_BCAST_MSG_EVT | | |
b952b2be | 556 | TIPC_NAMED_MSG_EVT | TIPC_BCAST_RESET); |
7b8613e0 | 557 | |
9db9fdd1 YX |
558 | spin_unlock_bh(&node->lock); |
559 | ||
708ac32c JPM |
560 | if (flags & TIPC_NOTIFY_NODE_DOWN) |
561 | tipc_publ_notify(net, publ_list, addr); | |
50100a5e | 562 | |
908344cd | 563 | if (flags & TIPC_WAKEUP_BCAST_USERS) |
f2f9800d | 564 | tipc_bclink_wakeup_users(net); |
908344cd | 565 | |
7b8613e0 | 566 | if (flags & TIPC_NOTIFY_NODE_UP) |
f2f9800d | 567 | tipc_named_node_up(net, addr); |
7b8613e0 YX |
568 | |
569 | if (flags & TIPC_NOTIFY_LINK_UP) | |
f2f9800d | 570 | tipc_nametbl_publish(net, TIPC_LINK_STATE, addr, addr, |
7b8613e0 YX |
571 | TIPC_NODE_SCOPE, link_id, addr); |
572 | ||
573 | if (flags & TIPC_NOTIFY_LINK_DOWN) | |
f2f9800d | 574 | tipc_nametbl_withdraw(net, TIPC_LINK_STATE, addr, |
7b8613e0 | 575 | link_id, addr); |
c637c103 JPM |
576 | |
577 | if (flags & TIPC_MSG_EVT) | |
578 | tipc_sk_rcv(net, inputq); | |
579 | ||
580 | if (flags & TIPC_NAMED_MSG_EVT) | |
581 | tipc_named_rcv(net, namedq); | |
cb1b7280 JPM |
582 | |
583 | if (flags & TIPC_BCAST_MSG_EVT) | |
584 | tipc_bclink_input(net); | |
b952b2be YX |
585 | |
586 | if (flags & TIPC_BCAST_RESET) | |
587 | tipc_link_reset_all(node); | |
9db9fdd1 | 588 | } |
3e4b6ab5 RA |
589 | |
590 | /* Caller should hold node lock for the passed node */ | |
d8182804 | 591 | static int __tipc_nl_add_node(struct tipc_nl_msg *msg, struct tipc_node *node) |
3e4b6ab5 RA |
592 | { |
593 | void *hdr; | |
594 | struct nlattr *attrs; | |
595 | ||
bfb3e5dd | 596 | hdr = genlmsg_put(msg->skb, msg->portid, msg->seq, &tipc_genl_family, |
3e4b6ab5 RA |
597 | NLM_F_MULTI, TIPC_NL_NODE_GET); |
598 | if (!hdr) | |
599 | return -EMSGSIZE; | |
600 | ||
601 | attrs = nla_nest_start(msg->skb, TIPC_NLA_NODE); | |
602 | if (!attrs) | |
603 | goto msg_full; | |
604 | ||
605 | if (nla_put_u32(msg->skb, TIPC_NLA_NODE_ADDR, node->addr)) | |
606 | goto attr_msg_full; | |
607 | if (tipc_node_is_up(node)) | |
608 | if (nla_put_flag(msg->skb, TIPC_NLA_NODE_UP)) | |
609 | goto attr_msg_full; | |
610 | ||
611 | nla_nest_end(msg->skb, attrs); | |
612 | genlmsg_end(msg->skb, hdr); | |
613 | ||
614 | return 0; | |
615 | ||
616 | attr_msg_full: | |
617 | nla_nest_cancel(msg->skb, attrs); | |
618 | msg_full: | |
619 | genlmsg_cancel(msg->skb, hdr); | |
620 | ||
621 | return -EMSGSIZE; | |
622 | } | |
623 | ||
af9b028e JPM |
624 | static struct tipc_link *tipc_node_select_link(struct tipc_node *n, int sel, |
625 | int *bearer_id, | |
626 | struct tipc_media_addr **maddr) | |
627 | { | |
628 | int id = n->active_links[sel & 1]; | |
629 | ||
630 | if (unlikely(id < 0)) | |
631 | return NULL; | |
632 | ||
633 | *bearer_id = id; | |
634 | *maddr = &n->links[id].maddr; | |
635 | return n->links[id].link; | |
636 | } | |
637 | ||
638 | /** | |
639 | * tipc_node_xmit() is the general link level function for message sending | |
640 | * @net: the applicable net namespace | |
641 | * @list: chain of buffers containing message | |
642 | * @dnode: address of destination node | |
643 | * @selector: a number used for deterministic link selection | |
644 | * Consumes the buffer chain, except when returning -ELINKCONG | |
645 | * Returns 0 if success, otherwise errno: -ELINKCONG,-EHOSTUNREACH,-EMSGSIZE | |
646 | */ | |
647 | int tipc_node_xmit(struct net *net, struct sk_buff_head *list, | |
648 | u32 dnode, int selector) | |
649 | { | |
650 | struct tipc_link *l = NULL; | |
651 | struct tipc_node *n; | |
652 | struct sk_buff_head xmitq; | |
653 | struct tipc_media_addr *maddr; | |
654 | int bearer_id; | |
655 | int rc = -EHOSTUNREACH; | |
656 | ||
657 | __skb_queue_head_init(&xmitq); | |
658 | n = tipc_node_find(net, dnode); | |
659 | if (likely(n)) { | |
660 | tipc_node_lock(n); | |
661 | l = tipc_node_select_link(n, selector, &bearer_id, &maddr); | |
662 | if (likely(l)) | |
663 | rc = tipc_link_xmit(l, list, &xmitq); | |
664 | if (unlikely(rc == -ENOBUFS)) | |
665 | tipc_link_reset(l); | |
666 | tipc_node_unlock(n); | |
667 | tipc_node_put(n); | |
668 | } | |
669 | if (likely(!rc)) { | |
670 | tipc_bearer_xmit(net, bearer_id, &xmitq, maddr); | |
671 | return 0; | |
672 | } | |
673 | if (likely(in_own_node(net, dnode))) { | |
674 | tipc_sk_rcv(net, list); | |
675 | return 0; | |
676 | } | |
677 | return rc; | |
678 | } | |
679 | ||
680 | /* tipc_node_xmit_skb(): send single buffer to destination | |
681 | * Buffers sent via this functon are generally TIPC_SYSTEM_IMPORTANCE | |
682 | * messages, which will not be rejected | |
683 | * The only exception is datagram messages rerouted after secondary | |
684 | * lookup, which are rare and safe to dispose of anyway. | |
685 | * TODO: Return real return value, and let callers use | |
686 | * tipc_wait_for_sendpkt() where applicable | |
687 | */ | |
688 | int tipc_node_xmit_skb(struct net *net, struct sk_buff *skb, u32 dnode, | |
689 | u32 selector) | |
690 | { | |
691 | struct sk_buff_head head; | |
692 | int rc; | |
693 | ||
694 | skb_queue_head_init(&head); | |
695 | __skb_queue_tail(&head, skb); | |
696 | rc = tipc_node_xmit(net, &head, dnode, selector); | |
697 | if (rc == -ELINKCONG) | |
698 | kfree_skb(skb); | |
699 | return 0; | |
700 | } | |
701 | ||
3e4b6ab5 RA |
702 | int tipc_nl_node_dump(struct sk_buff *skb, struct netlink_callback *cb) |
703 | { | |
704 | int err; | |
f2f9800d YX |
705 | struct net *net = sock_net(skb->sk); |
706 | struct tipc_net *tn = net_generic(net, tipc_net_id); | |
3e4b6ab5 RA |
707 | int done = cb->args[0]; |
708 | int last_addr = cb->args[1]; | |
709 | struct tipc_node *node; | |
710 | struct tipc_nl_msg msg; | |
711 | ||
712 | if (done) | |
713 | return 0; | |
714 | ||
715 | msg.skb = skb; | |
716 | msg.portid = NETLINK_CB(cb->skb).portid; | |
717 | msg.seq = cb->nlh->nlmsg_seq; | |
718 | ||
719 | rcu_read_lock(); | |
8a0f6ebe YX |
720 | if (last_addr) { |
721 | node = tipc_node_find(net, last_addr); | |
722 | if (!node) { | |
723 | rcu_read_unlock(); | |
724 | /* We never set seq or call nl_dump_check_consistent() | |
725 | * this means that setting prev_seq here will cause the | |
726 | * consistence check to fail in the netlink callback | |
727 | * handler. Resulting in the NLMSG_DONE message having | |
728 | * the NLM_F_DUMP_INTR flag set if the node state | |
729 | * changed while we released the lock. | |
730 | */ | |
731 | cb->prev_seq = 1; | |
732 | return -EPIPE; | |
733 | } | |
734 | tipc_node_put(node); | |
3e4b6ab5 RA |
735 | } |
736 | ||
f2f9800d | 737 | list_for_each_entry_rcu(node, &tn->node_list, list) { |
3e4b6ab5 RA |
738 | if (last_addr) { |
739 | if (node->addr == last_addr) | |
740 | last_addr = 0; | |
741 | else | |
742 | continue; | |
743 | } | |
744 | ||
745 | tipc_node_lock(node); | |
746 | err = __tipc_nl_add_node(&msg, node); | |
747 | if (err) { | |
748 | last_addr = node->addr; | |
749 | tipc_node_unlock(node); | |
750 | goto out; | |
751 | } | |
752 | ||
753 | tipc_node_unlock(node); | |
754 | } | |
755 | done = 1; | |
756 | out: | |
757 | cb->args[0] = done; | |
758 | cb->args[1] = last_addr; | |
759 | rcu_read_unlock(); | |
760 | ||
761 | return skb->len; | |
762 | } |