node.c 18.5 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(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
	n_ptr = kzalloc(sizeof(*n_ptr), GFP_ATOMIC);
101
	if (!n_ptr) {
102
		spin_unlock_bh(&tn->node_list_lock);
103
		pr_warn("Node creation failed, no memory\n");
104 105 106 107
		return NULL;
	}

	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

127
	tn->num_nodes++;
128

129
	spin_unlock_bh(&tn->node_list_lock);
P
Per Liden 已提交
130 131 132
	return n_ptr;
}

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

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

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

147 148 149 150
	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);
151 152
}

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

	if (in_own_node(dnode))
		return 0;

161
	node = tipc_node_find(net, dnode);
162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178
	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;
}

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

	if (in_own_node(dnode))
		return;

187
	node = tipc_node_find(net, dnode);
188 189 190 191 192 193 194 195 196 197 198 199 200
	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);
}

201
void tipc_node_abort_sock_conns(struct net *net, struct list_head *conns)
202 203 204 205 206 207 208 209 210 211
{
	struct tipc_sock_conn *conn, *safe;
	struct sk_buff *buf;

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

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

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

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

P
Per Liden 已提交
234 235 236
	if (!active[0]) {
		active[0] = active[1] = l_ptr;
		node_established_contact(n_ptr);
237
		goto exit;
P
Per Liden 已提交
238
	}
239
	if (l_ptr->priority < active[0]->priority) {
240
		pr_info("New link <%s> becomes standby\n", l_ptr->name);
241
		goto exit;
P
Per Liden 已提交
242
	}
243
	tipc_link_dup_queue_xmit(active[0], l_ptr);
244
	if (l_ptr->priority == active[0]->priority) {
P
Per Liden 已提交
245
		active[0] = l_ptr;
246
		goto exit;
P
Per Liden 已提交
247
	}
248
	pr_info("Old link <%s> becomes standby\n", active[0]->name);
249
	if (active[1] != active[0])
250
		pr_info("Old link <%s> becomes standby\n", active[1]->name);
P
Per Liden 已提交
251
	active[0] = active[1] = l_ptr;
252 253 254 255
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 已提交
256 257 258 259 260
}

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

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

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

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

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

/**
286
 * tipc_node_link_down - handle loss of link
P
Per Liden 已提交
287
 */
288
void tipc_node_link_down(struct tipc_node *n_ptr, struct tipc_link *l_ptr)
P
Per Liden 已提交
289
{
290
	struct tipc_link **active;
P
Per Liden 已提交
291

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

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

	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);
311
	if (tipc_node_is_up(n_ptr))
312
		tipc_link_failover_send_queue(l_ptr);
313
	else
P
Per Liden 已提交
314
		node_lost_contact(n_ptr);
315 316 317 318 319 320 321 322 323 324 325 326 327

	/* 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. */
	if (n_ptr->addr == tipc_own_addr) {
		n_ptr->act_mtus[0] = MAX_MSG_SIZE;
		n_ptr->act_mtus[1] = MAX_MSG_SIZE;
	}
P
Per Liden 已提交
328 329
}

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

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

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

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

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

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

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

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

380 381
	pr_info("Lost contact with %s\n",
		tipc_addr_string_fill(addr_string, n_ptr->addr));
382 383

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

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

392
		tipc_bclink_remove_node(n_ptr->addr);
393
		tipc_bclink_acknowledge(n_ptr, INVALID_LINK_SEQ);
P
Per Liden 已提交
394

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

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

408 409
	n_ptr->action_flags &= ~TIPC_WAIT_OWN_LINKS_DOWN;

410 411 412
	/* Notify subscribers and prevent re-contact with node until
	 * cleanup is done.
	 */
413 414
	n_ptr->action_flags |= TIPC_WAIT_PEER_LINKS_DOWN |
			       TIPC_NOTIFY_NODE_DOWN;
P
Per Liden 已提交
415 416
}

417 418
struct sk_buff *tipc_node_get_nodes(struct net *net, const void *req_tlv_area,
				    int req_tlv_space)
P
Per Liden 已提交
419
{
420
	struct tipc_net *tn = net_generic(net, tipc_net_id);
P
Per Liden 已提交
421 422
	u32 domain;
	struct sk_buff *buf;
423
	struct tipc_node *n_ptr;
424
	struct tipc_node_info node_info;
425
	u32 payload_size;
P
Per Liden 已提交
426 427

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

A
Al Viro 已提交
430
	domain = ntohl(*(__be32 *)TLV_DATA(req_tlv_area));
431 432 433
	if (!tipc_addr_domain_valid(domain))
		return tipc_cfg_reply_error_string(TIPC_CFG_INVALID_VALUE
						   " (network address)");
P
Per Liden 已提交
434

435 436 437
	spin_lock_bh(&tn->node_list_lock);
	if (!tn->num_nodes) {
		spin_unlock_bh(&tn->node_list_lock);
438
		return tipc_cfg_reply_none();
439
	}
P
Per Liden 已提交
440

441
	/* For now, get space for all other nodes */
442
	payload_size = TLV_SPACE(sizeof(node_info)) * tn->num_nodes;
443
	if (payload_size > 32768u) {
444
		spin_unlock_bh(&tn->node_list_lock);
445 446
		return tipc_cfg_reply_error_string(TIPC_CFG_NOT_SUPPORTED
						   " (too many nodes)");
447
	}
448
	spin_unlock_bh(&tn->node_list_lock);
449

450
	buf = tipc_cfg_reply_alloc(payload_size);
451
	if (!buf)
P
Per Liden 已提交
452 453 454
		return NULL;

	/* Add TLVs for all nodes in scope */
455
	rcu_read_lock();
456
	list_for_each_entry_rcu(n_ptr, &tn->node_list, list) {
457
		if (!tipc_in_scope(domain, n_ptr->addr))
P
Per Liden 已提交
458
			continue;
459 460 461
		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,
462
				    &node_info, sizeof(node_info));
P
Per Liden 已提交
463
	}
464
	rcu_read_unlock();
P
Per Liden 已提交
465 466 467
	return buf;
}

468 469
struct sk_buff *tipc_node_get_links(struct net *net, const void *req_tlv_area,
				    int req_tlv_space)
P
Per Liden 已提交
470
{
471
	struct tipc_net *tn = net_generic(net, tipc_net_id);
P
Per Liden 已提交
472 473
	u32 domain;
	struct sk_buff *buf;
474
	struct tipc_node *n_ptr;
475
	struct tipc_link_info link_info;
476
	u32 payload_size;
P
Per Liden 已提交
477 478

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

A
Al Viro 已提交
481
	domain = ntohl(*(__be32 *)TLV_DATA(req_tlv_area));
482 483 484
	if (!tipc_addr_domain_valid(domain))
		return tipc_cfg_reply_error_string(TIPC_CFG_INVALID_VALUE
						   " (network address)");
P
Per Liden 已提交
485

486
	if (!tipc_own_addr)
487 488
		return tipc_cfg_reply_none();

489
	spin_lock_bh(&tn->node_list_lock);
490
	/* Get space for all unicast links + broadcast link */
491
	payload_size = TLV_SPACE((sizeof(link_info)) * (tn->num_links + 1));
492
	if (payload_size > 32768u) {
493
		spin_unlock_bh(&tn->node_list_lock);
494 495
		return tipc_cfg_reply_error_string(TIPC_CFG_NOT_SUPPORTED
						   " (too many links)");
496
	}
497
	spin_unlock_bh(&tn->node_list_lock);
498

499
	buf = tipc_cfg_reply_alloc(payload_size);
500
	if (!buf)
P
Per Liden 已提交
501 502 503
		return NULL;

	/* Add TLV for broadcast link */
504
	link_info.dest = htonl(tipc_cluster_mask(tipc_own_addr));
505
	link_info.up = htonl(1);
506
	strlcpy(link_info.str, tipc_bclink_name, TIPC_MAX_LINK_NAME);
507
	tipc_cfg_append_tlv(buf, TIPC_TLV_LINK_INFO, &link_info, sizeof(link_info));
P
Per Liden 已提交
508 509

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

514
		if (!tipc_in_scope(domain, n_ptr->addr))
P
Per Liden 已提交
515
			continue;
516
		tipc_node_lock(n_ptr);
517 518 519 520 521 522 523
		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,
524
					    &link_info, sizeof(link_info));
525
		}
526
		tipc_node_unlock(n_ptr);
P
Per Liden 已提交
527
	}
528
	rcu_read_unlock();
P
Per Liden 已提交
529 530
	return buf;
}
E
Erik Hugne 已提交
531 532 533 534 535 536 537 538 539 540

/**
 * 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
 */
541 542
int tipc_node_get_linkname(struct net *net, u32 bearer_id, u32 addr,
			   char *linkname, size_t len)
E
Erik Hugne 已提交
543 544
{
	struct tipc_link *link;
545
	struct tipc_node *node = tipc_node_find(net, addr);
E
Erik Hugne 已提交
546

E
Erik Hugne 已提交
547
	if ((bearer_id >= MAX_BEARERS) || !node)
E
Erik Hugne 已提交
548 549 550 551 552 553 554 555 556 557 558
		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;
}
559 560 561

void tipc_node_unlock(struct tipc_node *node)
{
562
	struct net *net = node->net;
563
	LIST_HEAD(nsub_list);
564
	LIST_HEAD(conn_sks);
565
	struct sk_buff_head waiting_sks;
566
	u32 addr = 0;
Y
Ying Xue 已提交
567 568
	int flags = node->action_flags;
	u32 link_id = 0;
569

Y
Ying Xue 已提交
570
	if (likely(!flags)) {
571 572 573 574
		spin_unlock_bh(&node->lock);
		return;
	}

Y
Ying Xue 已提交
575 576
	addr = node->addr;
	link_id = node->link_id;
577
	__skb_queue_head_init(&waiting_sks);
Y
Ying Xue 已提交
578 579

	if (flags & TIPC_WAKEUP_USERS)
580
		skb_queue_splice_init(&node->waiting_sks, &waiting_sks);
Y
Ying Xue 已提交
581 582

	if (flags & TIPC_NOTIFY_NODE_DOWN) {
583
		list_replace_init(&node->publ_list, &nsub_list);
584
		list_replace_init(&node->conn_sks, &conn_sks);
585
	}
Y
Ying Xue 已提交
586 587 588 589 590
	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);

591 592
	spin_unlock_bh(&node->lock);

593
	while (!skb_queue_empty(&waiting_sks))
594
		tipc_sk_rcv(net, __skb_dequeue(&waiting_sks));
595

596
	if (!list_empty(&conn_sks))
597
		tipc_node_abort_sock_conns(net, &conn_sks);
598

599
	if (!list_empty(&nsub_list))
600
		tipc_publ_notify(net, &nsub_list, addr);
601

602
	if (flags & TIPC_WAKEUP_BCAST_USERS)
603
		tipc_bclink_wakeup_users(net);
604

Y
Ying Xue 已提交
605
	if (flags & TIPC_NOTIFY_NODE_UP)
606
		tipc_named_node_up(net, addr);
Y
Ying Xue 已提交
607 608

	if (flags & TIPC_NOTIFY_LINK_UP)
609
		tipc_nametbl_publish(net, TIPC_LINK_STATE, addr, addr,
Y
Ying Xue 已提交
610 611 612
				     TIPC_NODE_SCOPE, link_id, addr);

	if (flags & TIPC_NOTIFY_LINK_DOWN)
613
		tipc_nametbl_withdraw(net, TIPC_LINK_STATE, addr,
Y
Ying Xue 已提交
614
				      link_id, addr);
615
}
616 617

/* Caller should hold node lock for the passed node */
618
static int __tipc_nl_add_node(struct tipc_nl_msg *msg, struct tipc_node *node)
619 620 621 622 623 624 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
{
	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;
654 655
	struct net *net = sock_net(skb->sk);
	struct tipc_net *tn = net_generic(net, tipc_net_id);
656 657 658 659 660 661 662 663 664 665 666 667 668 669
	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();

670
	if (last_addr && !tipc_node_find(net, last_addr)) {
671 672 673 674 675 676 677 678 679 680 681
		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;
	}

682
	list_for_each_entry_rcu(node, &tn->node_list, list) {
683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707
		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;
}