node.c 18.9 KB
Newer Older
P
Per Liden 已提交
1 2
/*
 * net/tipc/node.c: TIPC node management routines
3
 *
4
 * Copyright (c) 2000-2006, 2012-2014, Ericsson AB
5
 * Copyright (c) 2005-2006, 2010-2014, Wind River Systems
P
Per Liden 已提交
6 7
 * All rights reserved.
 *
P
Per Liden 已提交
8
 * Redistribution and use in source and binary forms, with or without
P
Per Liden 已提交
9 10
 * modification, are permitted provided that the following conditions are met:
 *
P
Per Liden 已提交
11 12 13 14 15 16 17 18
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. Neither the names of the copyright holders nor the names of its
 *    contributors may be used to endorse or promote products derived from
 *    this software without specific prior written permission.
P
Per Liden 已提交
19
 *
P
Per Liden 已提交
20 21 22 23 24 25 26 27 28 29 30 31 32 33
 * Alternatively, this software may be distributed under the terms of the
 * GNU General Public License ("GPL") version 2 as published by the Free
 * Software Foundation.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
P
Per Liden 已提交
34 35 36 37 38 39 40
 * POSSIBILITY OF SUCH DAMAGE.
 */

#include "core.h"
#include "config.h"
#include "node.h"
#include "name_distr.h"
41
#include "socket.h"
P
Per Liden 已提交
42

43 44
static void node_lost_contact(struct tipc_node *n_ptr);
static void node_established_contact(struct tipc_node *n_ptr);
P
Per Liden 已提交
45

46 47 48 49 50 51 52
struct tipc_sock_conn {
	u32 port;
	u32 peer_port;
	u32 peer_node;
	struct list_head list;
};

53 54 55 56 57 58
static const struct nla_policy tipc_nl_node_policy[TIPC_NLA_NODE_MAX + 1] = {
	[TIPC_NLA_NODE_UNSPEC]		= { .type = NLA_UNSPEC },
	[TIPC_NLA_NODE_ADDR]		= { .type = NLA_U32 },
	[TIPC_NLA_NODE_UP]		= { .type = NLA_FLAG }
};

59 60 61 62 63 64
/*
 * A trivial power-of-two bitmask technique is used for speed, since this
 * operation is done for every incoming TIPC packet. The number of hash table
 * entries has been chosen so that no hash chain exceeds 8 nodes and will
 * usually be much smaller (typically only a single node).
 */
65
static unsigned int tipc_hashfn(u32 addr)
66 67 68 69
{
	return addr & (NODE_HTABLE_SIZE - 1);
}

70
/*
71 72
 * tipc_node_find - locate specified node object, if it exists
 */
73
struct tipc_node *tipc_node_find(struct net *net, u32 addr)
74
{
75
	struct tipc_net *tn = net_generic(net, tipc_net_id);
76 77
	struct tipc_node *node;

78
	if (unlikely(!in_own_cluster_exact(net, addr)))
79 80
		return NULL;

81
	rcu_read_lock();
82 83
	hlist_for_each_entry_rcu(node, &tn->node_htable[tipc_hashfn(addr)],
				 hash) {
84
		if (node->addr == addr) {
85
			rcu_read_unlock();
86
			return node;
87
		}
88
	}
89
	rcu_read_unlock();
90 91 92
	return NULL;
}

93
struct tipc_node *tipc_node_create(struct net *net, u32 addr)
P
Per Liden 已提交
94
{
95
	struct tipc_net *tn = net_generic(net, tipc_net_id);
96
	struct tipc_node *n_ptr, *temp_node;
P
Per Liden 已提交
97

98
	spin_lock_bh(&tn->node_list_lock);
99 100 101
	n_ptr = tipc_node_find(net, addr);
	if (n_ptr)
		goto exit;
102
	n_ptr = kzalloc(sizeof(*n_ptr), GFP_ATOMIC);
103
	if (!n_ptr) {
104
		pr_warn("Node creation failed, no memory\n");
105
		goto exit;
106 107
	}
	n_ptr->addr = addr;
108
	n_ptr->net = net;
109
	spin_lock_init(&n_ptr->lock);
110 111
	INIT_HLIST_NODE(&n_ptr->hash);
	INIT_LIST_HEAD(&n_ptr->list);
112
	INIT_LIST_HEAD(&n_ptr->publ_list);
113
	INIT_LIST_HEAD(&n_ptr->conn_sks);
114
	skb_queue_head_init(&n_ptr->waiting_sks);
115
	__skb_queue_head_init(&n_ptr->bclink.deferred_queue);
116

117
	hlist_add_head_rcu(&n_ptr->hash, &tn->node_htable[tipc_hashfn(addr)]);
118

119
	list_for_each_entry_rcu(temp_node, &tn->node_list, list) {
120 121 122
		if (n_ptr->addr < temp_node->addr)
			break;
	}
123
	list_add_tail_rcu(&n_ptr->list, &temp_node->list);
Y
Ying Xue 已提交
124
	n_ptr->action_flags = TIPC_WAIT_PEER_LINKS_DOWN;
125
	n_ptr->signature = INVALID_NODE_SIG;
126
	tn->num_nodes++;
127
exit:
128
	spin_unlock_bh(&tn->node_list_lock);
P
Per Liden 已提交
129 130 131
	return n_ptr;
}

132
static void tipc_node_delete(struct tipc_net *tn, struct tipc_node *n_ptr)
P
Per Liden 已提交
133
{
134 135 136
	list_del_rcu(&n_ptr->list);
	hlist_del_rcu(&n_ptr->hash);
	kfree_rcu(n_ptr, rcu);
137

138
	tn->num_nodes--;
P
Per Liden 已提交
139 140
}

141
void tipc_node_stop(struct net *net)
142
{
143
	struct tipc_net *tn = net_generic(net, tipc_net_id);
144 145
	struct tipc_node *node, *t_node;

146 147 148 149
	spin_lock_bh(&tn->node_list_lock);
	list_for_each_entry_safe(node, t_node, &tn->node_list, list)
		tipc_node_delete(tn, node);
	spin_unlock_bh(&tn->node_list_lock);
150 151
}

152
int tipc_node_add_conn(struct net *net, u32 dnode, u32 port, u32 peer_port)
153 154 155 156
{
	struct tipc_node *node;
	struct tipc_sock_conn *conn;

157
	if (in_own_node(net, dnode))
158 159
		return 0;

160
	node = tipc_node_find(net, dnode);
161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177
	if (!node) {
		pr_warn("Connecting sock to node 0x%x failed\n", dnode);
		return -EHOSTUNREACH;
	}
	conn = kmalloc(sizeof(*conn), GFP_ATOMIC);
	if (!conn)
		return -EHOSTUNREACH;
	conn->peer_node = dnode;
	conn->port = port;
	conn->peer_port = peer_port;

	tipc_node_lock(node);
	list_add_tail(&conn->list, &node->conn_sks);
	tipc_node_unlock(node);
	return 0;
}

178
void tipc_node_remove_conn(struct net *net, u32 dnode, u32 port)
179 180 181 182
{
	struct tipc_node *node;
	struct tipc_sock_conn *conn, *safe;

183
	if (in_own_node(net, dnode))
184 185
		return;

186
	node = tipc_node_find(net, dnode);
187 188 189 190 191 192 193 194 195 196 197 198 199
	if (!node)
		return;

	tipc_node_lock(node);
	list_for_each_entry_safe(conn, safe, &node->conn_sks, list) {
		if (port != conn->port)
			continue;
		list_del(&conn->list);
		kfree(conn);
	}
	tipc_node_unlock(node);
}

200
void tipc_node_abort_sock_conns(struct net *net, struct list_head *conns)
201
{
202
	struct tipc_net *tn = net_generic(net, tipc_net_id);
203 204 205 206
	struct tipc_sock_conn *conn, *safe;
	struct sk_buff *buf;

	list_for_each_entry_safe(conn, safe, conns, list) {
207
		buf = tipc_msg_create(TIPC_CRITICAL_IMPORTANCE,
208 209 210 211
				      TIPC_CONN_MSG, SHORT_H_SIZE, 0,
				      tn->own_addr, conn->peer_node,
				      conn->port, conn->peer_port,
				      TIPC_ERR_NO_NODE);
212
		if (likely(buf))
213
			tipc_sk_rcv(net, buf);
214 215 216 217 218
		list_del(&conn->list);
		kfree(conn);
	}
}

P
Per Liden 已提交
219
/**
220
 * tipc_node_link_up - handle addition of link
221
 *
P
Per Liden 已提交
222 223
 * Link becomes active (alone or shared) or standby, depending on its priority.
 */
224
void tipc_node_link_up(struct tipc_node *n_ptr, struct tipc_link *l_ptr)
P
Per Liden 已提交
225
{
226
	struct tipc_link **active = &n_ptr->active_links[0];
P
Per Liden 已提交
227

228
	n_ptr->working_links++;
Y
Ying Xue 已提交
229 230 231
	n_ptr->action_flags |= TIPC_NOTIFY_LINK_UP;
	n_ptr->link_id = l_ptr->peer_bearer_id << 16 | l_ptr->bearer_id;

232 233
	pr_debug("Established link <%s> on network plane %c\n",
		 l_ptr->name, l_ptr->net_plane);
234

P
Per Liden 已提交
235 236 237
	if (!active[0]) {
		active[0] = active[1] = l_ptr;
		node_established_contact(n_ptr);
238
		goto exit;
P
Per Liden 已提交
239
	}
240
	if (l_ptr->priority < active[0]->priority) {
241
		pr_debug("New link <%s> becomes standby\n", l_ptr->name);
242
		goto exit;
P
Per Liden 已提交
243
	}
244
	tipc_link_dup_queue_xmit(active[0], l_ptr);
245
	if (l_ptr->priority == active[0]->priority) {
P
Per Liden 已提交
246
		active[0] = l_ptr;
247
		goto exit;
P
Per Liden 已提交
248
	}
249
	pr_debug("Old link <%s> becomes standby\n", active[0]->name);
250
	if (active[1] != active[0])
251
		pr_debug("Old link <%s> becomes standby\n", active[1]->name);
P
Per Liden 已提交
252
	active[0] = active[1] = l_ptr;
253 254 255 256
exit:
	/* Leave room for changeover header when returning 'mtu' to users: */
	n_ptr->act_mtus[0] = active[0]->max_pkt - INT_H_SIZE;
	n_ptr->act_mtus[1] = active[1]->max_pkt - INT_H_SIZE;
P
Per Liden 已提交
257 258 259 260 261
}

/**
 * node_select_active_links - select active link
 */
262
static void node_select_active_links(struct tipc_node *n_ptr)
P
Per Liden 已提交
263
{
264
	struct tipc_link **active = &n_ptr->active_links[0];
P
Per Liden 已提交
265 266 267
	u32 i;
	u32 highest_prio = 0;

268
	active[0] = active[1] = NULL;
P
Per Liden 已提交
269 270

	for (i = 0; i < MAX_BEARERS; i++) {
271
		struct tipc_link *l_ptr = n_ptr->links[i];
P
Per Liden 已提交
272

273
		if (!l_ptr || !tipc_link_is_up(l_ptr) ||
P
Per Liden 已提交
274 275 276 277
		    (l_ptr->priority < highest_prio))
			continue;

		if (l_ptr->priority > highest_prio) {
278
			highest_prio = l_ptr->priority;
P
Per Liden 已提交
279 280 281 282 283 284 285 286
			active[0] = active[1] = l_ptr;
		} else {
			active[1] = l_ptr;
		}
	}
}

/**
287
 * tipc_node_link_down - handle loss of link
P
Per Liden 已提交
288
 */
289
void tipc_node_link_down(struct tipc_node *n_ptr, struct tipc_link *l_ptr)
P
Per Liden 已提交
290
{
291
	struct tipc_net *tn = net_generic(n_ptr->net, tipc_net_id);
292
	struct tipc_link **active;
P
Per Liden 已提交
293

294
	n_ptr->working_links--;
Y
Ying Xue 已提交
295 296
	n_ptr->action_flags |= TIPC_NOTIFY_LINK_DOWN;
	n_ptr->link_id = l_ptr->peer_bearer_id << 16 | l_ptr->bearer_id;
297

298
	if (!tipc_link_is_active(l_ptr)) {
299 300
		pr_debug("Lost standby link <%s> on network plane %c\n",
			 l_ptr->name, l_ptr->net_plane);
P
Per Liden 已提交
301 302
		return;
	}
303 304
	pr_debug("Lost link <%s> on network plane %c\n",
		 l_ptr->name, l_ptr->net_plane);
P
Per Liden 已提交
305 306 307 308 309 310 311 312

	active = &n_ptr->active_links[0];
	if (active[0] == l_ptr)
		active[0] = active[1];
	if (active[1] == l_ptr)
		active[1] = active[0];
	if (active[0] == l_ptr)
		node_select_active_links(n_ptr);
313
	if (tipc_node_is_up(n_ptr))
314
		tipc_link_failover_send_queue(l_ptr);
315
	else
P
Per Liden 已提交
316
		node_lost_contact(n_ptr);
317 318 319 320 321 322 323 324 325

	/* Leave room for changeover header when returning 'mtu' to users: */
	if (active[0]) {
		n_ptr->act_mtus[0] = active[0]->max_pkt - INT_H_SIZE;
		n_ptr->act_mtus[1] = active[1]->max_pkt - INT_H_SIZE;
		return;
	}

	/* Loopback link went down? No fragmentation needed from now on. */
326
	if (n_ptr->addr == tn->own_addr) {
327 328 329
		n_ptr->act_mtus[0] = MAX_MSG_SIZE;
		n_ptr->act_mtus[1] = MAX_MSG_SIZE;
	}
P
Per Liden 已提交
330 331
}

332
int tipc_node_active_links(struct tipc_node *n_ptr)
P
Per Liden 已提交
333
{
334
	return n_ptr->active_links[0] != NULL;
P
Per Liden 已提交
335 336
}

337
int tipc_node_is_up(struct tipc_node *n_ptr)
P
Per Liden 已提交
338
{
339
	return tipc_node_active_links(n_ptr);
P
Per Liden 已提交
340 341
}

342
void tipc_node_attach_link(struct tipc_node *n_ptr, struct tipc_link *l_ptr)
P
Per Liden 已提交
343
{
344 345
	struct tipc_net *tn = net_generic(n_ptr->net, tipc_net_id);

346
	n_ptr->links[l_ptr->bearer_id] = l_ptr;
347 348 349
	spin_lock_bh(&tn->node_list_lock);
	tn->num_links++;
	spin_unlock_bh(&tn->node_list_lock);
350
	n_ptr->link_cnt++;
P
Per Liden 已提交
351 352
}

353
void tipc_node_detach_link(struct tipc_node *n_ptr, struct tipc_link *l_ptr)
P
Per Liden 已提交
354
{
355
	struct tipc_net *tn = net_generic(n_ptr->net, tipc_net_id);
356 357 358
	int i;

	for (i = 0; i < MAX_BEARERS; i++) {
J
Jon Paul Maloy 已提交
359 360 361
		if (l_ptr != n_ptr->links[i])
			continue;
		n_ptr->links[i] = NULL;
362 363 364
		spin_lock_bh(&tn->node_list_lock);
		tn->num_links--;
		spin_unlock_bh(&tn->node_list_lock);
J
Jon Paul Maloy 已提交
365
		n_ptr->link_cnt--;
366
	}
P
Per Liden 已提交
367 368
}

369
static void node_established_contact(struct tipc_node *n_ptr)
P
Per Liden 已提交
370
{
Y
Ying Xue 已提交
371
	n_ptr->action_flags |= TIPC_NOTIFY_NODE_UP;
372
	n_ptr->bclink.oos_state = 0;
373 374
	n_ptr->bclink.acked = tipc_bclink_get_last_sent(n_ptr->net);
	tipc_bclink_add_node(n_ptr->net, n_ptr->addr);
P
Per Liden 已提交
375 376
}

377
static void node_lost_contact(struct tipc_node *n_ptr)
P
Per Liden 已提交
378 379 380 381
{
	char addr_string[16];
	u32 i;

382 383
	pr_debug("Lost contact with %s\n",
		 tipc_addr_string_fill(addr_string, n_ptr->addr));
384 385

	/* Flush broadcast link info associated with lost node */
386
	if (n_ptr->bclink.recv_permitted) {
387
		__skb_queue_purge(&n_ptr->bclink.deferred_queue);
388

389 390 391
		if (n_ptr->bclink.reasm_buf) {
			kfree_skb(n_ptr->bclink.reasm_buf);
			n_ptr->bclink.reasm_buf = NULL;
392 393
		}

394
		tipc_bclink_remove_node(n_ptr->net, n_ptr->addr);
395
		tipc_bclink_acknowledge(n_ptr, INVALID_LINK_SEQ);
P
Per Liden 已提交
396

397
		n_ptr->bclink.recv_permitted = false;
398
	}
P
Per Liden 已提交
399 400 401

	/* Abort link changeover */
	for (i = 0; i < MAX_BEARERS; i++) {
402
		struct tipc_link *l_ptr = n_ptr->links[i];
403
		if (!l_ptr)
P
Per Liden 已提交
404 405 406
			continue;
		l_ptr->reset_checkpoint = l_ptr->next_in_no;
		l_ptr->exp_msg_count = 0;
407
		tipc_link_reset_fragments(l_ptr);
408 409 410 411

		/* Link marked for deletion after failover? => do it now */
		if (l_ptr->flags & LINK_STOPPED)
			tipc_link_delete(l_ptr);
P
Per Liden 已提交
412 413
	}

414 415
	n_ptr->action_flags &= ~TIPC_WAIT_OWN_LINKS_DOWN;

416 417 418
	/* Notify subscribers and prevent re-contact with node until
	 * cleanup is done.
	 */
419 420
	n_ptr->action_flags |= TIPC_WAIT_PEER_LINKS_DOWN |
			       TIPC_NOTIFY_NODE_DOWN;
P
Per Liden 已提交
421 422
}

423 424
struct sk_buff *tipc_node_get_nodes(struct net *net, const void *req_tlv_area,
				    int req_tlv_space)
P
Per Liden 已提交
425
{
426
	struct tipc_net *tn = net_generic(net, tipc_net_id);
P
Per Liden 已提交
427 428
	u32 domain;
	struct sk_buff *buf;
429
	struct tipc_node *n_ptr;
430
	struct tipc_node_info node_info;
431
	u32 payload_size;
P
Per Liden 已提交
432 433

	if (!TLV_CHECK(req_tlv_area, req_tlv_space, TIPC_TLV_NET_ADDR))
434
		return tipc_cfg_reply_error_string(TIPC_CFG_TLV_ERROR);
P
Per Liden 已提交
435

A
Al Viro 已提交
436
	domain = ntohl(*(__be32 *)TLV_DATA(req_tlv_area));
437 438 439
	if (!tipc_addr_domain_valid(domain))
		return tipc_cfg_reply_error_string(TIPC_CFG_INVALID_VALUE
						   " (network address)");
P
Per Liden 已提交
440

441 442 443
	spin_lock_bh(&tn->node_list_lock);
	if (!tn->num_nodes) {
		spin_unlock_bh(&tn->node_list_lock);
444
		return tipc_cfg_reply_none();
445
	}
P
Per Liden 已提交
446

447
	/* For now, get space for all other nodes */
448
	payload_size = TLV_SPACE(sizeof(node_info)) * tn->num_nodes;
449
	if (payload_size > 32768u) {
450
		spin_unlock_bh(&tn->node_list_lock);
451 452
		return tipc_cfg_reply_error_string(TIPC_CFG_NOT_SUPPORTED
						   " (too many nodes)");
453
	}
454
	spin_unlock_bh(&tn->node_list_lock);
455

456
	buf = tipc_cfg_reply_alloc(payload_size);
457
	if (!buf)
P
Per Liden 已提交
458 459 460
		return NULL;

	/* Add TLVs for all nodes in scope */
461
	rcu_read_lock();
462
	list_for_each_entry_rcu(n_ptr, &tn->node_list, list) {
463
		if (!tipc_in_scope(domain, n_ptr->addr))
P
Per Liden 已提交
464
			continue;
465 466 467
		node_info.addr = htonl(n_ptr->addr);
		node_info.up = htonl(tipc_node_is_up(n_ptr));
		tipc_cfg_append_tlv(buf, TIPC_TLV_NODE_INFO,
468
				    &node_info, sizeof(node_info));
P
Per Liden 已提交
469
	}
470
	rcu_read_unlock();
P
Per Liden 已提交
471 472 473
	return buf;
}

474 475
struct sk_buff *tipc_node_get_links(struct net *net, const void *req_tlv_area,
				    int req_tlv_space)
P
Per Liden 已提交
476
{
477
	struct tipc_net *tn = net_generic(net, tipc_net_id);
P
Per Liden 已提交
478 479
	u32 domain;
	struct sk_buff *buf;
480
	struct tipc_node *n_ptr;
481
	struct tipc_link_info link_info;
482
	u32 payload_size;
P
Per Liden 已提交
483 484

	if (!TLV_CHECK(req_tlv_area, req_tlv_space, TIPC_TLV_NET_ADDR))
485
		return tipc_cfg_reply_error_string(TIPC_CFG_TLV_ERROR);
P
Per Liden 已提交
486

A
Al Viro 已提交
487
	domain = ntohl(*(__be32 *)TLV_DATA(req_tlv_area));
488 489 490
	if (!tipc_addr_domain_valid(domain))
		return tipc_cfg_reply_error_string(TIPC_CFG_INVALID_VALUE
						   " (network address)");
P
Per Liden 已提交
491

492
	if (!tn->own_addr)
493 494
		return tipc_cfg_reply_none();

495
	spin_lock_bh(&tn->node_list_lock);
496
	/* Get space for all unicast links + broadcast link */
497
	payload_size = TLV_SPACE((sizeof(link_info)) * (tn->num_links + 1));
498
	if (payload_size > 32768u) {
499
		spin_unlock_bh(&tn->node_list_lock);
500 501
		return tipc_cfg_reply_error_string(TIPC_CFG_NOT_SUPPORTED
						   " (too many links)");
502
	}
503
	spin_unlock_bh(&tn->node_list_lock);
504

505
	buf = tipc_cfg_reply_alloc(payload_size);
506
	if (!buf)
P
Per Liden 已提交
507 508 509
		return NULL;

	/* Add TLV for broadcast link */
510
	link_info.dest = htonl(tipc_cluster_mask(tn->own_addr));
511
	link_info.up = htonl(1);
512
	strlcpy(link_info.str, tipc_bclink_name, TIPC_MAX_LINK_NAME);
513
	tipc_cfg_append_tlv(buf, TIPC_TLV_LINK_INFO, &link_info, sizeof(link_info));
P
Per Liden 已提交
514 515

	/* Add TLVs for any other links in scope */
516
	rcu_read_lock();
517
	list_for_each_entry_rcu(n_ptr, &tn->node_list, list) {
518
		u32 i;
P
Per Liden 已提交
519

520
		if (!tipc_in_scope(domain, n_ptr->addr))
P
Per Liden 已提交
521
			continue;
522
		tipc_node_lock(n_ptr);
523 524 525 526 527 528 529
		for (i = 0; i < MAX_BEARERS; i++) {
			if (!n_ptr->links[i])
				continue;
			link_info.dest = htonl(n_ptr->addr);
			link_info.up = htonl(tipc_link_is_up(n_ptr->links[i]));
			strcpy(link_info.str, n_ptr->links[i]->name);
			tipc_cfg_append_tlv(buf, TIPC_TLV_LINK_INFO,
530
					    &link_info, sizeof(link_info));
531
		}
532
		tipc_node_unlock(n_ptr);
P
Per Liden 已提交
533
	}
534
	rcu_read_unlock();
P
Per Liden 已提交
535 536
	return buf;
}
E
Erik Hugne 已提交
537 538 539 540 541 542 543 544 545 546

/**
 * tipc_node_get_linkname - get the name of a link
 *
 * @bearer_id: id of the bearer
 * @node: peer node address
 * @linkname: link name output buffer
 *
 * Returns 0 on success
 */
547 548
int tipc_node_get_linkname(struct net *net, u32 bearer_id, u32 addr,
			   char *linkname, size_t len)
E
Erik Hugne 已提交
549 550
{
	struct tipc_link *link;
551
	struct tipc_node *node = tipc_node_find(net, addr);
E
Erik Hugne 已提交
552

E
Erik Hugne 已提交
553
	if ((bearer_id >= MAX_BEARERS) || !node)
E
Erik Hugne 已提交
554 555 556 557 558 559 560 561 562 563 564
		return -EINVAL;
	tipc_node_lock(node);
	link = node->links[bearer_id];
	if (link) {
		strncpy(linkname, link->name, len);
		tipc_node_unlock(node);
		return 0;
	}
	tipc_node_unlock(node);
	return -EINVAL;
}
565 566 567

void tipc_node_unlock(struct tipc_node *node)
{
568
	struct net *net = node->net;
569
	LIST_HEAD(nsub_list);
570
	LIST_HEAD(conn_sks);
571
	struct sk_buff_head waiting_sks;
572
	u32 addr = 0;
Y
Ying Xue 已提交
573 574
	int flags = node->action_flags;
	u32 link_id = 0;
575

Y
Ying Xue 已提交
576
	if (likely(!flags)) {
577 578 579 580
		spin_unlock_bh(&node->lock);
		return;
	}

Y
Ying Xue 已提交
581 582
	addr = node->addr;
	link_id = node->link_id;
583
	__skb_queue_head_init(&waiting_sks);
Y
Ying Xue 已提交
584 585

	if (flags & TIPC_WAKEUP_USERS)
586
		skb_queue_splice_init(&node->waiting_sks, &waiting_sks);
Y
Ying Xue 已提交
587 588

	if (flags & TIPC_NOTIFY_NODE_DOWN) {
589
		list_replace_init(&node->publ_list, &nsub_list);
590
		list_replace_init(&node->conn_sks, &conn_sks);
591
	}
Y
Ying Xue 已提交
592 593 594 595 596
	node->action_flags &= ~(TIPC_WAKEUP_USERS | TIPC_NOTIFY_NODE_DOWN |
				TIPC_NOTIFY_NODE_UP | TIPC_NOTIFY_LINK_UP |
				TIPC_NOTIFY_LINK_DOWN |
				TIPC_WAKEUP_BCAST_USERS);

597 598
	spin_unlock_bh(&node->lock);

599
	while (!skb_queue_empty(&waiting_sks))
600
		tipc_sk_rcv(net, __skb_dequeue(&waiting_sks));
601

602
	if (!list_empty(&conn_sks))
603
		tipc_node_abort_sock_conns(net, &conn_sks);
604

605
	if (!list_empty(&nsub_list))
606
		tipc_publ_notify(net, &nsub_list, addr);
607

608
	if (flags & TIPC_WAKEUP_BCAST_USERS)
609
		tipc_bclink_wakeup_users(net);
610

Y
Ying Xue 已提交
611
	if (flags & TIPC_NOTIFY_NODE_UP)
612
		tipc_named_node_up(net, addr);
Y
Ying Xue 已提交
613 614

	if (flags & TIPC_NOTIFY_LINK_UP)
615
		tipc_nametbl_publish(net, TIPC_LINK_STATE, addr, addr,
Y
Ying Xue 已提交
616 617 618
				     TIPC_NODE_SCOPE, link_id, addr);

	if (flags & TIPC_NOTIFY_LINK_DOWN)
619
		tipc_nametbl_withdraw(net, TIPC_LINK_STATE, addr,
Y
Ying Xue 已提交
620
				      link_id, addr);
621
}
622 623

/* Caller should hold node lock for the passed node */
624
static int __tipc_nl_add_node(struct tipc_nl_msg *msg, struct tipc_node *node)
625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659
{
	void *hdr;
	struct nlattr *attrs;

	hdr = genlmsg_put(msg->skb, msg->portid, msg->seq, &tipc_genl_v2_family,
			  NLM_F_MULTI, TIPC_NL_NODE_GET);
	if (!hdr)
		return -EMSGSIZE;

	attrs = nla_nest_start(msg->skb, TIPC_NLA_NODE);
	if (!attrs)
		goto msg_full;

	if (nla_put_u32(msg->skb, TIPC_NLA_NODE_ADDR, node->addr))
		goto attr_msg_full;
	if (tipc_node_is_up(node))
		if (nla_put_flag(msg->skb, TIPC_NLA_NODE_UP))
			goto attr_msg_full;

	nla_nest_end(msg->skb, attrs);
	genlmsg_end(msg->skb, hdr);

	return 0;

attr_msg_full:
	nla_nest_cancel(msg->skb, attrs);
msg_full:
	genlmsg_cancel(msg->skb, hdr);

	return -EMSGSIZE;
}

int tipc_nl_node_dump(struct sk_buff *skb, struct netlink_callback *cb)
{
	int err;
660 661
	struct net *net = sock_net(skb->sk);
	struct tipc_net *tn = net_generic(net, tipc_net_id);
662 663 664 665 666 667 668 669 670 671 672 673 674 675
	int done = cb->args[0];
	int last_addr = cb->args[1];
	struct tipc_node *node;
	struct tipc_nl_msg msg;

	if (done)
		return 0;

	msg.skb = skb;
	msg.portid = NETLINK_CB(cb->skb).portid;
	msg.seq = cb->nlh->nlmsg_seq;

	rcu_read_lock();

676
	if (last_addr && !tipc_node_find(net, last_addr)) {
677 678 679 680 681 682 683 684 685 686 687
		rcu_read_unlock();
		/* We never set seq or call nl_dump_check_consistent() this
		 * means that setting prev_seq here will cause the consistence
		 * check to fail in the netlink callback handler. Resulting in
		 * the NLMSG_DONE message having the NLM_F_DUMP_INTR flag set if
		 * the node state changed while we released the lock.
		 */
		cb->prev_seq = 1;
		return -EPIPE;
	}

688
	list_for_each_entry_rcu(node, &tn->node_list, list) {
689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713
		if (last_addr) {
			if (node->addr == last_addr)
				last_addr = 0;
			else
				continue;
		}

		tipc_node_lock(node);
		err = __tipc_nl_add_node(&msg, node);
		if (err) {
			last_addr = node->addr;
			tipc_node_unlock(node);
			goto out;
		}

		tipc_node_unlock(node);
	}
	done = 1;
out:
	cb->args[0] = done;
	cb->args[1] = last_addr;
	rcu_read_unlock();

	return skb->len;
}