xref: /linux-6.15/net/tipc/socket.c (revision e636ba1a)
1b97bf3fdSPer Liden /*
2b97bf3fdSPer Liden  * net/tipc/socket.c: TIPC socket API
3b97bf3fdSPer Liden  *
460c102eeSJon Maloy  * Copyright (c) 2001-2007, 2012-2019, Ericsson AB
5c5fa7b3cSYing Xue  * Copyright (c) 2004-2008, 2010-2013, Wind River Systems
6998d3907SJon Maloy  * Copyright (c) 2020-2021, Red Hat Inc
7b97bf3fdSPer Liden  * All rights reserved.
8b97bf3fdSPer Liden  *
9b97bf3fdSPer Liden  * Redistribution and use in source and binary forms, with or without
10b97bf3fdSPer Liden  * modification, are permitted provided that the following conditions are met:
11b97bf3fdSPer Liden  *
129ea1fd3cSPer Liden  * 1. Redistributions of source code must retain the above copyright
139ea1fd3cSPer Liden  *    notice, this list of conditions and the following disclaimer.
149ea1fd3cSPer Liden  * 2. Redistributions in binary form must reproduce the above copyright
159ea1fd3cSPer Liden  *    notice, this list of conditions and the following disclaimer in the
169ea1fd3cSPer Liden  *    documentation and/or other materials provided with the distribution.
179ea1fd3cSPer Liden  * 3. Neither the names of the copyright holders nor the names of its
189ea1fd3cSPer Liden  *    contributors may be used to endorse or promote products derived from
199ea1fd3cSPer Liden  *    this software without specific prior written permission.
209ea1fd3cSPer Liden  *
219ea1fd3cSPer Liden  * Alternatively, this software may be distributed under the terms of the
229ea1fd3cSPer Liden  * GNU General Public License ("GPL") version 2 as published by the Free
239ea1fd3cSPer Liden  * Software Foundation.
24b97bf3fdSPer Liden  *
25b97bf3fdSPer Liden  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
26b97bf3fdSPer Liden  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
27b97bf3fdSPer Liden  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
28b97bf3fdSPer Liden  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
29b97bf3fdSPer Liden  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
30b97bf3fdSPer Liden  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
31b97bf3fdSPer Liden  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
32b97bf3fdSPer Liden  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
33b97bf3fdSPer Liden  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
34b97bf3fdSPer Liden  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
35b97bf3fdSPer Liden  * POSSIBILITY OF SUCH DAMAGE.
36b97bf3fdSPer Liden  */
37b97bf3fdSPer Liden 
3807f6c4bcSYing Xue #include <linux/rhashtable.h>
39174cd4b1SIngo Molnar #include <linux/sched/signal.h>
4040e0b090SPeilin Ye #include <trace/events/sock.h>
41174cd4b1SIngo Molnar 
42b97bf3fdSPer Liden #include "core.h"
43e2dafe87SJon Paul Maloy #include "name_table.h"
4478acb1f9SErik Hugne #include "node.h"
45e2dafe87SJon Paul Maloy #include "link.h"
46c637c103SJon Paul Maloy #include "name_distr.h"
472e84c60bSJon Paul Maloy #include "socket.h"
48a6bf70f7SJon Paul Maloy #include "bcast.h"
4949cc66eaSRichard Alpe #include "netlink.h"
5075da2163SJon Maloy #include "group.h"
51b4b9771bSTuong Lien #include "trace.h"
522cf8aa19SErik Hugne 
530a3e060fSTuong Lien #define NAGLE_START_INIT	4
540a3e060fSTuong Lien #define NAGLE_START_MAX		1024
553654ea02SAllan Stephens #define CONN_TIMEOUT_DEFAULT    8000    /* default connect timeout = 8s */
560d5fcebfSJon Maloy #define CONN_PROBING_INTV	msecs_to_jiffies(3600000)  /* [ms] => 1 h */
5707f6c4bcSYing Xue #define TIPC_MAX_PORT		0xffffffff
5807f6c4bcSYing Xue #define TIPC_MIN_PORT		1
5960462191SRandy Dunlap #define TIPC_ACK_RATE		4       /* ACK at 1/4 of rcv window size */
60301bae56SJon Paul Maloy 
610c288c86SParthasarathy Bhuvaragan enum {
620c288c86SParthasarathy Bhuvaragan 	TIPC_LISTEN = TCP_LISTEN,
638ea642eeSParthasarathy Bhuvaragan 	TIPC_ESTABLISHED = TCP_ESTABLISHED,
64438adcafSParthasarathy Bhuvaragan 	TIPC_OPEN = TCP_CLOSE,
659fd4b070SParthasarathy Bhuvaragan 	TIPC_DISCONNECTING = TCP_CLOSE_WAIT,
6699a20889SParthasarathy Bhuvaragan 	TIPC_CONNECTING = TCP_SYN_SENT,
670c288c86SParthasarathy Bhuvaragan };
680c288c86SParthasarathy Bhuvaragan 
6931c82a2dSJon Maloy struct sockaddr_pair {
7031c82a2dSJon Maloy 	struct sockaddr_tipc sock;
7131c82a2dSJon Maloy 	struct sockaddr_tipc member;
7231c82a2dSJon Maloy };
7331c82a2dSJon Maloy 
74301bae56SJon Paul Maloy /**
75301bae56SJon Paul Maloy  * struct tipc_sock - TIPC socket structure
76301bae56SJon Paul Maloy  * @sk: socket - interacts with 'port' and with user via the socket API
77301bae56SJon Paul Maloy  * @max_pkt: maximum packet size "hint" used when building messages sent by port
78c0bceb97SJon Maloy  * @maxnagle: maximum size of msg which can be subject to nagle
7907f6c4bcSYing Xue  * @portid: unique port identity in TIPC socket hash table
80301bae56SJon Paul Maloy  * @phdr: preformatted message header used when sending messages
81f172f4b8SRandy Dunlap  * @cong_links: list of congested links
82301bae56SJon Paul Maloy  * @publications: list of publications for port
83301bae56SJon Paul Maloy  * @pub_count: total # of publications port has made during its lifetime
84301bae56SJon Paul Maloy  * @conn_timeout: the time we can wait for an unresponded setup request
85f172f4b8SRandy Dunlap  * @probe_unacked: probe has not received ack yet
86301bae56SJon Paul Maloy  * @dupl_rcvcnt: number of bytes counted twice, in both backlog and rcv queue
87365ad353SJon Paul Maloy  * @cong_link_cnt: number of congested links
8875da2163SJon Maloy  * @snt_unacked: # messages sent by socket, and not yet acked by peer
89f172f4b8SRandy Dunlap  * @snd_win: send window size
90f172f4b8SRandy Dunlap  * @peer_caps: peer capabilities mask
91301bae56SJon Paul Maloy  * @rcv_unacked: # messages read by user, but not yet acked back to peer
92f172f4b8SRandy Dunlap  * @rcv_win: receive window size
93aeda16b6SParthasarathy Bhuvaragan  * @peer: 'connected' peer for dgram/rdm
9407f6c4bcSYing Xue  * @node: hash table node
9501fd12bbSJon Paul Maloy  * @mc_method: cookie for use between socket and broadcast layer
9607f6c4bcSYing Xue  * @rcu: rcu struct for tipc_sock
97f172f4b8SRandy Dunlap  * @group: TIPC communications group
98f172f4b8SRandy Dunlap  * @oneway: message count in one direction (FIXME)
99f172f4b8SRandy Dunlap  * @nagle_start: current nagle value
100f172f4b8SRandy Dunlap  * @snd_backlog: send backlog count
101f172f4b8SRandy Dunlap  * @msg_acc: messages accepted; used in managing backlog and nagle
102f172f4b8SRandy Dunlap  * @pkt_cnt: TIPC socket packet count
103f172f4b8SRandy Dunlap  * @expect_ack: whether this TIPC socket is expecting an ack
104f172f4b8SRandy Dunlap  * @nodelay: setsockopt() TIPC_NODELAY setting
105f172f4b8SRandy Dunlap  * @group_is_open: TIPC socket group is fully open (FIXME)
10614623e00SJon Maloy  * @published: true if port has one or more associated names
10714623e00SJon Maloy  * @conn_addrtype: address type used when establishing connection
108301bae56SJon Paul Maloy  */
109301bae56SJon Paul Maloy struct tipc_sock {
110301bae56SJon Paul Maloy 	struct sock sk;
111301bae56SJon Paul Maloy 	u32 max_pkt;
112c0bceb97SJon Maloy 	u32 maxnagle;
11307f6c4bcSYing Xue 	u32 portid;
114301bae56SJon Paul Maloy 	struct tipc_msg phdr;
115365ad353SJon Paul Maloy 	struct list_head cong_links;
116301bae56SJon Paul Maloy 	struct list_head publications;
117301bae56SJon Paul Maloy 	u32 pub_count;
118301bae56SJon Paul Maloy 	atomic_t dupl_rcvcnt;
11967879274STung Nguyen 	u16 conn_timeout;
1208ea642eeSParthasarathy Bhuvaragan 	bool probe_unacked;
121365ad353SJon Paul Maloy 	u16 cong_link_cnt;
12210724cc7SJon Paul Maloy 	u16 snt_unacked;
12310724cc7SJon Paul Maloy 	u16 snd_win;
12460020e18SJon Paul Maloy 	u16 peer_caps;
12510724cc7SJon Paul Maloy 	u16 rcv_unacked;
12610724cc7SJon Paul Maloy 	u16 rcv_win;
127aeda16b6SParthasarathy Bhuvaragan 	struct sockaddr_tipc peer;
12807f6c4bcSYing Xue 	struct rhash_head node;
12901fd12bbSJon Paul Maloy 	struct tipc_mc_method mc_method;
13007f6c4bcSYing Xue 	struct rcu_head rcu;
13175da2163SJon Maloy 	struct tipc_group *group;
132c0bceb97SJon Maloy 	u32 oneway;
1330a3e060fSTuong Lien 	u32 nagle_start;
134c0bceb97SJon Maloy 	u16 snd_backlog;
1350a3e060fSTuong Lien 	u16 msg_acc;
1360a3e060fSTuong Lien 	u16 pkt_cnt;
137c0bceb97SJon Maloy 	bool expect_ack;
138c0bceb97SJon Maloy 	bool nodelay;
13960c25306SJon Maloy 	bool group_is_open;
14050a3499aSJon Maloy 	bool published;
14114623e00SJon Maloy 	u8 conn_addrtype;
142301bae56SJon Paul Maloy };
143b97bf3fdSPer Liden 
14464ac5f59SJon Maloy static int tipc_sk_backlog_rcv(struct sock *sk, struct sk_buff *skb);
145676d2369SDavid S. Miller static void tipc_data_ready(struct sock *sk);
146f288bef4SYing Xue static void tipc_write_space(struct sock *sk);
147f4195d1eSYing Xue static void tipc_sock_destruct(struct sock *sk);
148247f0f3cSYing Xue static int tipc_release(struct socket *sock);
14931b102bbSKees Cook static void tipc_sk_timeout(struct timer_list *t);
15050a3499aSJon Maloy static int tipc_sk_publish(struct tipc_sock *tsk, struct tipc_uaddr *ua);
1512c98da07SJon Maloy static int tipc_sk_withdraw(struct tipc_sock *tsk, struct tipc_uaddr *ua);
15275da2163SJon Maloy static int tipc_sk_leave(struct tipc_sock *tsk);
153e05b31f4SYing Xue static struct tipc_sock *tipc_sk_lookup(struct net *net, u32 portid);
15407f6c4bcSYing Xue static int tipc_sk_insert(struct tipc_sock *tsk);
15507f6c4bcSYing Xue static void tipc_sk_remove(struct tipc_sock *tsk);
156365ad353SJon Paul Maloy static int __tipc_sendstream(struct socket *sock, struct msghdr *m, size_t dsz);
15739a0295fSYing Xue static int __tipc_sendmsg(struct socket *sock, struct msghdr *m, size_t dsz);
1580a3e060fSTuong Lien static void tipc_sk_push_backlog(struct tipc_sock *tsk, bool nagle_ack);
159f8dd60deSXin Long static int tipc_wait_for_connect(struct socket *sock, long *timeo_p);
160b97bf3fdSPer Liden 
161bca65eaeSFlorian Westphal static const struct proto_ops packet_ops;
162bca65eaeSFlorian Westphal static const struct proto_ops stream_ops;
163bca65eaeSFlorian Westphal static const struct proto_ops msg_ops;
164b97bf3fdSPer Liden static struct proto tipc_proto;
1656cca7289SHerbert Xu static const struct rhashtable_params tsk_rht_params;
1666cca7289SHerbert Xu 
tsk_own_node(struct tipc_sock * tsk)167c5898636SJon Paul Maloy static u32 tsk_own_node(struct tipc_sock *tsk)
168c5898636SJon Paul Maloy {
169c5898636SJon Paul Maloy 	return msg_prevnode(&tsk->phdr);
170c5898636SJon Paul Maloy }
171c5898636SJon Paul Maloy 
tsk_peer_node(struct tipc_sock * tsk)172301bae56SJon Paul Maloy static u32 tsk_peer_node(struct tipc_sock *tsk)
1732e84c60bSJon Paul Maloy {
174301bae56SJon Paul Maloy 	return msg_destnode(&tsk->phdr);
1752e84c60bSJon Paul Maloy }
1762e84c60bSJon Paul Maloy 
tsk_peer_port(struct tipc_sock * tsk)177301bae56SJon Paul Maloy static u32 tsk_peer_port(struct tipc_sock *tsk)
1782e84c60bSJon Paul Maloy {
179301bae56SJon Paul Maloy 	return msg_destport(&tsk->phdr);
1802e84c60bSJon Paul Maloy }
1812e84c60bSJon Paul Maloy 
tsk_unreliable(struct tipc_sock * tsk)182301bae56SJon Paul Maloy static  bool tsk_unreliable(struct tipc_sock *tsk)
1832e84c60bSJon Paul Maloy {
184301bae56SJon Paul Maloy 	return msg_src_droppable(&tsk->phdr) != 0;
1852e84c60bSJon Paul Maloy }
1862e84c60bSJon Paul Maloy 
tsk_set_unreliable(struct tipc_sock * tsk,bool unreliable)187301bae56SJon Paul Maloy static void tsk_set_unreliable(struct tipc_sock *tsk, bool unreliable)
1882e84c60bSJon Paul Maloy {
189301bae56SJon Paul Maloy 	msg_set_src_droppable(&tsk->phdr, unreliable ? 1 : 0);
1902e84c60bSJon Paul Maloy }
1912e84c60bSJon Paul Maloy 
tsk_unreturnable(struct tipc_sock * tsk)192301bae56SJon Paul Maloy static bool tsk_unreturnable(struct tipc_sock *tsk)
1932e84c60bSJon Paul Maloy {
194301bae56SJon Paul Maloy 	return msg_dest_droppable(&tsk->phdr) != 0;
1952e84c60bSJon Paul Maloy }
1962e84c60bSJon Paul Maloy 
tsk_set_unreturnable(struct tipc_sock * tsk,bool unreturnable)197301bae56SJon Paul Maloy static void tsk_set_unreturnable(struct tipc_sock *tsk, bool unreturnable)
1982e84c60bSJon Paul Maloy {
199301bae56SJon Paul Maloy 	msg_set_dest_droppable(&tsk->phdr, unreturnable ? 1 : 0);
2002e84c60bSJon Paul Maloy }
2012e84c60bSJon Paul Maloy 
tsk_importance(struct tipc_sock * tsk)202301bae56SJon Paul Maloy static int tsk_importance(struct tipc_sock *tsk)
2032e84c60bSJon Paul Maloy {
204301bae56SJon Paul Maloy 	return msg_importance(&tsk->phdr);
2052e84c60bSJon Paul Maloy }
2062e84c60bSJon Paul Maloy 
tipc_sk(const struct sock * sk)207301bae56SJon Paul Maloy static struct tipc_sock *tipc_sk(const struct sock *sk)
208301bae56SJon Paul Maloy {
209301bae56SJon Paul Maloy 	return container_of(sk, struct tipc_sock, sk);
210301bae56SJon Paul Maloy }
211301bae56SJon Paul Maloy 
tsk_set_importance(struct sock * sk,int imp)212095ae612SChristoph Hellwig int tsk_set_importance(struct sock *sk, int imp)
213095ae612SChristoph Hellwig {
214095ae612SChristoph Hellwig 	if (imp > TIPC_CRITICAL_IMPORTANCE)
215095ae612SChristoph Hellwig 		return -EINVAL;
216095ae612SChristoph Hellwig 	msg_set_importance(&tipc_sk(sk)->phdr, (u32)imp);
217095ae612SChristoph Hellwig 	return 0;
218095ae612SChristoph Hellwig }
219095ae612SChristoph Hellwig 
tsk_conn_cong(struct tipc_sock * tsk)22010724cc7SJon Paul Maloy static bool tsk_conn_cong(struct tipc_sock *tsk)
221301bae56SJon Paul Maloy {
2226998cc6eSJon Paul Maloy 	return tsk->snt_unacked > tsk->snd_win;
22310724cc7SJon Paul Maloy }
22410724cc7SJon Paul Maloy 
tsk_blocks(int len)225b7d42635SJon Maloy static u16 tsk_blocks(int len)
226b7d42635SJon Maloy {
227b7d42635SJon Maloy 	return ((len / FLOWCTL_BLK_SZ) + 1);
228b7d42635SJon Maloy }
229b7d42635SJon Maloy 
23010724cc7SJon Paul Maloy /* tsk_blocks(): translate a buffer size in bytes to number of
23110724cc7SJon Paul Maloy  * advertisable blocks, taking into account the ratio truesize(len)/len
23210724cc7SJon Paul Maloy  * We can trust that this ratio is always < 4 for len >= FLOWCTL_BLK_SZ
23310724cc7SJon Paul Maloy  */
tsk_adv_blocks(int len)23410724cc7SJon Paul Maloy static u16 tsk_adv_blocks(int len)
23510724cc7SJon Paul Maloy {
23610724cc7SJon Paul Maloy 	return len / FLOWCTL_BLK_SZ / 4;
23710724cc7SJon Paul Maloy }
23810724cc7SJon Paul Maloy 
23910724cc7SJon Paul Maloy /* tsk_inc(): increment counter for sent or received data
24010724cc7SJon Paul Maloy  * - If block based flow control is not supported by peer we
24110724cc7SJon Paul Maloy  *   fall back to message based ditto, incrementing the counter
24210724cc7SJon Paul Maloy  */
tsk_inc(struct tipc_sock * tsk,int msglen)24310724cc7SJon Paul Maloy static u16 tsk_inc(struct tipc_sock *tsk, int msglen)
24410724cc7SJon Paul Maloy {
24510724cc7SJon Paul Maloy 	if (likely(tsk->peer_caps & TIPC_BLOCK_FLOWCTL))
24610724cc7SJon Paul Maloy 		return ((msglen / FLOWCTL_BLK_SZ) + 1);
24710724cc7SJon Paul Maloy 	return 1;
248301bae56SJon Paul Maloy }
249301bae56SJon Paul Maloy 
250c0bceb97SJon Maloy /* tsk_set_nagle - enable/disable nagle property by manipulating maxnagle
251c0bceb97SJon Maloy  */
tsk_set_nagle(struct tipc_sock * tsk)252c0bceb97SJon Maloy static void tsk_set_nagle(struct tipc_sock *tsk)
253c0bceb97SJon Maloy {
254c0bceb97SJon Maloy 	struct sock *sk = &tsk->sk;
255c0bceb97SJon Maloy 
256c0bceb97SJon Maloy 	tsk->maxnagle = 0;
257c0bceb97SJon Maloy 	if (sk->sk_type != SOCK_STREAM)
258c0bceb97SJon Maloy 		return;
259c0bceb97SJon Maloy 	if (tsk->nodelay)
260c0bceb97SJon Maloy 		return;
261c0bceb97SJon Maloy 	if (!(tsk->peer_caps & TIPC_NAGLE))
262c0bceb97SJon Maloy 		return;
263c0bceb97SJon Maloy 	/* Limit node local buffer size to avoid receive queue overflow */
264c0bceb97SJon Maloy 	if (tsk->max_pkt == MAX_MSG_SIZE)
265c0bceb97SJon Maloy 		tsk->maxnagle = 1500;
266c0bceb97SJon Maloy 	else
267c0bceb97SJon Maloy 		tsk->maxnagle = tsk->max_pkt;
268c0bceb97SJon Maloy }
269c0bceb97SJon Maloy 
270b97bf3fdSPer Liden /**
2712e84c60bSJon Paul Maloy  * tsk_advance_rx_queue - discard first buffer in socket receive queue
272f172f4b8SRandy Dunlap  * @sk: network socket
2730c3141e9SAllan Stephens  *
2740c3141e9SAllan Stephens  * Caller must hold socket lock
275b97bf3fdSPer Liden  */
tsk_advance_rx_queue(struct sock * sk)2762e84c60bSJon Paul Maloy static void tsk_advance_rx_queue(struct sock *sk)
277b97bf3fdSPer Liden {
27801e661ebSTuong Lien 	trace_tipc_sk_advance_rx(sk, NULL, TIPC_DUMP_SK_RCVQ, " ");
2795f6d9123SAllan Stephens 	kfree_skb(__skb_dequeue(&sk->sk_receive_queue));
280b97bf3fdSPer Liden }
281b97bf3fdSPer Liden 
282bcd3ffd4SJon Paul Maloy /* tipc_sk_respond() : send response message back to sender
283bcd3ffd4SJon Paul Maloy  */
tipc_sk_respond(struct sock * sk,struct sk_buff * skb,int err)284bcd3ffd4SJon Paul Maloy static void tipc_sk_respond(struct sock *sk, struct sk_buff *skb, int err)
285bcd3ffd4SJon Paul Maloy {
286bcd3ffd4SJon Paul Maloy 	u32 selector;
287bcd3ffd4SJon Paul Maloy 	u32 dnode;
288bcd3ffd4SJon Paul Maloy 	u32 onode = tipc_own_addr(sock_net(sk));
289bcd3ffd4SJon Paul Maloy 
290bcd3ffd4SJon Paul Maloy 	if (!tipc_msg_reverse(onode, &skb, err))
291bcd3ffd4SJon Paul Maloy 		return;
292bcd3ffd4SJon Paul Maloy 
29301e661ebSTuong Lien 	trace_tipc_sk_rej_msg(sk, skb, TIPC_DUMP_NONE, "@sk_respond!");
294bcd3ffd4SJon Paul Maloy 	dnode = msg_destnode(buf_msg(skb));
295bcd3ffd4SJon Paul Maloy 	selector = msg_origport(buf_msg(skb));
296bcd3ffd4SJon Paul Maloy 	tipc_node_xmit_skb(sock_net(sk), skb, dnode, selector);
297bcd3ffd4SJon Paul Maloy }
298bcd3ffd4SJon Paul Maloy 
299b97bf3fdSPer Liden /**
3002e84c60bSJon Paul Maloy  * tsk_rej_rx_queue - reject all buffers in socket receive queue
301f172f4b8SRandy Dunlap  * @sk: network socket
302f172f4b8SRandy Dunlap  * @error: response error code
3030c3141e9SAllan Stephens  *
3040c3141e9SAllan Stephens  * Caller must hold socket lock
3050c3141e9SAllan Stephens  */
tsk_rej_rx_queue(struct sock * sk,int error)30649afb806STuong Lien static void tsk_rej_rx_queue(struct sock *sk, int error)
3070c3141e9SAllan Stephens {
308a6ca1094SYing Xue 	struct sk_buff *skb;
3090c3141e9SAllan Stephens 
310bcd3ffd4SJon Paul Maloy 	while ((skb = __skb_dequeue(&sk->sk_receive_queue)))
31149afb806STuong Lien 		tipc_sk_respond(sk, skb, error);
3120c3141e9SAllan Stephens }
3130c3141e9SAllan Stephens 
tipc_sk_connected(const struct sock * sk)314d0ac89f6SEric Dumazet static bool tipc_sk_connected(const struct sock *sk)
315d6fb7e9cSParthasarathy Bhuvaragan {
316d0ac89f6SEric Dumazet 	return READ_ONCE(sk->sk_state) == TIPC_ESTABLISHED;
317d6fb7e9cSParthasarathy Bhuvaragan }
318d6fb7e9cSParthasarathy Bhuvaragan 
319c752023aSParthasarathy Bhuvaragan /* tipc_sk_type_connectionless - check if the socket is datagram socket
320c752023aSParthasarathy Bhuvaragan  * @sk: socket
321c752023aSParthasarathy Bhuvaragan  *
322c752023aSParthasarathy Bhuvaragan  * Returns true if connection less, false otherwise
323c752023aSParthasarathy Bhuvaragan  */
tipc_sk_type_connectionless(struct sock * sk)324c752023aSParthasarathy Bhuvaragan static bool tipc_sk_type_connectionless(struct sock *sk)
325c752023aSParthasarathy Bhuvaragan {
326c752023aSParthasarathy Bhuvaragan 	return sk->sk_type == SOCK_RDM || sk->sk_type == SOCK_DGRAM;
327c752023aSParthasarathy Bhuvaragan }
328c752023aSParthasarathy Bhuvaragan 
3292e84c60bSJon Paul Maloy /* tsk_peer_msg - verify if message was sent by connected port's peer
3300fc87aaeSJon Paul Maloy  *
3310fc87aaeSJon Paul Maloy  * Handles cases where the node's network address has changed from
3320fc87aaeSJon Paul Maloy  * the default of <0.0.0> to its configured setting.
3330fc87aaeSJon Paul Maloy  */
tsk_peer_msg(struct tipc_sock * tsk,struct tipc_msg * msg)3342e84c60bSJon Paul Maloy static bool tsk_peer_msg(struct tipc_sock *tsk, struct tipc_msg *msg)
3350fc87aaeSJon Paul Maloy {
336d6fb7e9cSParthasarathy Bhuvaragan 	struct sock *sk = &tsk->sk;
33723fd3eacSJon Maloy 	u32 self = tipc_own_addr(sock_net(sk));
338301bae56SJon Paul Maloy 	u32 peer_port = tsk_peer_port(tsk);
33923fd3eacSJon Maloy 	u32 orig_node, peer_node;
3400fc87aaeSJon Paul Maloy 
341d6fb7e9cSParthasarathy Bhuvaragan 	if (unlikely(!tipc_sk_connected(sk)))
3420fc87aaeSJon Paul Maloy 		return false;
3430fc87aaeSJon Paul Maloy 
3440fc87aaeSJon Paul Maloy 	if (unlikely(msg_origport(msg) != peer_port))
3450fc87aaeSJon Paul Maloy 		return false;
3460fc87aaeSJon Paul Maloy 
3470fc87aaeSJon Paul Maloy 	orig_node = msg_orignode(msg);
348301bae56SJon Paul Maloy 	peer_node = tsk_peer_node(tsk);
3490fc87aaeSJon Paul Maloy 
3500fc87aaeSJon Paul Maloy 	if (likely(orig_node == peer_node))
3510fc87aaeSJon Paul Maloy 		return true;
3520fc87aaeSJon Paul Maloy 
35323fd3eacSJon Maloy 	if (!orig_node && peer_node == self)
3540fc87aaeSJon Paul Maloy 		return true;
3550fc87aaeSJon Paul Maloy 
35623fd3eacSJon Maloy 	if (!peer_node && orig_node == self)
3570fc87aaeSJon Paul Maloy 		return true;
3580fc87aaeSJon Paul Maloy 
3590fc87aaeSJon Paul Maloy 	return false;
3600fc87aaeSJon Paul Maloy }
3610fc87aaeSJon Paul Maloy 
3620c288c86SParthasarathy Bhuvaragan /* tipc_set_sk_state - set the sk_state of the socket
3630c288c86SParthasarathy Bhuvaragan  * @sk: socket
3640c288c86SParthasarathy Bhuvaragan  *
3650c288c86SParthasarathy Bhuvaragan  * Caller must hold socket lock
3660c288c86SParthasarathy Bhuvaragan  *
3670c288c86SParthasarathy Bhuvaragan  * Returns 0 on success, errno otherwise
3680c288c86SParthasarathy Bhuvaragan  */
tipc_set_sk_state(struct sock * sk,int state)3690c288c86SParthasarathy Bhuvaragan static int tipc_set_sk_state(struct sock *sk, int state)
3700c288c86SParthasarathy Bhuvaragan {
371438adcafSParthasarathy Bhuvaragan 	int oldsk_state = sk->sk_state;
3720c288c86SParthasarathy Bhuvaragan 	int res = -EINVAL;
3730c288c86SParthasarathy Bhuvaragan 
3740c288c86SParthasarathy Bhuvaragan 	switch (state) {
375438adcafSParthasarathy Bhuvaragan 	case TIPC_OPEN:
376438adcafSParthasarathy Bhuvaragan 		res = 0;
377438adcafSParthasarathy Bhuvaragan 		break;
3780c288c86SParthasarathy Bhuvaragan 	case TIPC_LISTEN:
37999a20889SParthasarathy Bhuvaragan 	case TIPC_CONNECTING:
380438adcafSParthasarathy Bhuvaragan 		if (oldsk_state == TIPC_OPEN)
3810c288c86SParthasarathy Bhuvaragan 			res = 0;
3820c288c86SParthasarathy Bhuvaragan 		break;
3838ea642eeSParthasarathy Bhuvaragan 	case TIPC_ESTABLISHED:
38499a20889SParthasarathy Bhuvaragan 		if (oldsk_state == TIPC_CONNECTING ||
385438adcafSParthasarathy Bhuvaragan 		    oldsk_state == TIPC_OPEN)
3868ea642eeSParthasarathy Bhuvaragan 			res = 0;
3878ea642eeSParthasarathy Bhuvaragan 		break;
3889fd4b070SParthasarathy Bhuvaragan 	case TIPC_DISCONNECTING:
38999a20889SParthasarathy Bhuvaragan 		if (oldsk_state == TIPC_CONNECTING ||
3909fd4b070SParthasarathy Bhuvaragan 		    oldsk_state == TIPC_ESTABLISHED)
3919fd4b070SParthasarathy Bhuvaragan 			res = 0;
3929fd4b070SParthasarathy Bhuvaragan 		break;
3930c288c86SParthasarathy Bhuvaragan 	}
3940c288c86SParthasarathy Bhuvaragan 
3950c288c86SParthasarathy Bhuvaragan 	if (!res)
3960c288c86SParthasarathy Bhuvaragan 		sk->sk_state = state;
3970c288c86SParthasarathy Bhuvaragan 
3980c288c86SParthasarathy Bhuvaragan 	return res;
3990c288c86SParthasarathy Bhuvaragan }
4000c288c86SParthasarathy Bhuvaragan 
tipc_sk_sock_err(struct socket * sock,long * timeout)4018c44e1afSJon Paul Maloy static int tipc_sk_sock_err(struct socket *sock, long *timeout)
4028c44e1afSJon Paul Maloy {
4038c44e1afSJon Paul Maloy 	struct sock *sk = sock->sk;
4048c44e1afSJon Paul Maloy 	int err = sock_error(sk);
4058c44e1afSJon Paul Maloy 	int typ = sock->type;
4068c44e1afSJon Paul Maloy 
4078c44e1afSJon Paul Maloy 	if (err)
4088c44e1afSJon Paul Maloy 		return err;
4098c44e1afSJon Paul Maloy 	if (typ == SOCK_STREAM || typ == SOCK_SEQPACKET) {
4108c44e1afSJon Paul Maloy 		if (sk->sk_state == TIPC_DISCONNECTING)
4118c44e1afSJon Paul Maloy 			return -EPIPE;
4128c44e1afSJon Paul Maloy 		else if (!tipc_sk_connected(sk))
4138c44e1afSJon Paul Maloy 			return -ENOTCONN;
4148c44e1afSJon Paul Maloy 	}
4158c44e1afSJon Paul Maloy 	if (!*timeout)
4168c44e1afSJon Paul Maloy 		return -EAGAIN;
4178c44e1afSJon Paul Maloy 	if (signal_pending(current))
4188c44e1afSJon Paul Maloy 		return sock_intr_errno(*timeout);
4198c44e1afSJon Paul Maloy 
4208c44e1afSJon Paul Maloy 	return 0;
4218c44e1afSJon Paul Maloy }
4228c44e1afSJon Paul Maloy 
423844cf763SJon Paul Maloy #define tipc_wait_for_cond(sock_, timeo_, condition_)			       \
4248c44e1afSJon Paul Maloy ({                                                                             \
425bfd07f3dSTung Nguyen 	DEFINE_WAIT_FUNC(wait_, woken_wake_function);                          \
426844cf763SJon Paul Maloy 	struct sock *sk_;						       \
427844cf763SJon Paul Maloy 	int rc_;							       \
4288c44e1afSJon Paul Maloy 									       \
429844cf763SJon Paul Maloy 	while ((rc_ = !(condition_))) {					       \
430bfd07f3dSTung Nguyen 		/* coupled with smp_wmb() in tipc_sk_proto_rcv() */            \
431bfd07f3dSTung Nguyen 		smp_rmb();                                                     \
432844cf763SJon Paul Maloy 		sk_ = (sock_)->sk;					       \
433844cf763SJon Paul Maloy 		rc_ = tipc_sk_sock_err((sock_), timeo_);		       \
4348c44e1afSJon Paul Maloy 		if (rc_)						       \
4358c44e1afSJon Paul Maloy 			break;						       \
436223b7329STung Nguyen 		add_wait_queue(sk_sleep(sk_), &wait_);                         \
437844cf763SJon Paul Maloy 		release_sock(sk_);					       \
438844cf763SJon Paul Maloy 		*(timeo_) = wait_woken(&wait_, TASK_INTERRUPTIBLE, *(timeo_)); \
439844cf763SJon Paul Maloy 		sched_annotate_sleep();				               \
440844cf763SJon Paul Maloy 		lock_sock(sk_);						       \
4418c44e1afSJon Paul Maloy 		remove_wait_queue(sk_sleep(sk_), &wait_);		       \
4428c44e1afSJon Paul Maloy 	}								       \
4438c44e1afSJon Paul Maloy 	rc_;								       \
4448c44e1afSJon Paul Maloy })
4458c44e1afSJon Paul Maloy 
4460c3141e9SAllan Stephens /**
447c5fa7b3cSYing Xue  * tipc_sk_create - create a TIPC socket
4480c3141e9SAllan Stephens  * @net: network namespace (must be default network)
449b97bf3fdSPer Liden  * @sock: pre-allocated socket structure
450b97bf3fdSPer Liden  * @protocol: protocol indicator (must be 0)
4513f378b68SEric Paris  * @kern: caused by kernel or by userspace?
452b97bf3fdSPer Liden  *
4530c3141e9SAllan Stephens  * This routine creates additional data structures used by the TIPC socket,
4540c3141e9SAllan Stephens  * initializes them, and links them together.
455b97bf3fdSPer Liden  *
456637b77fdSRandy Dunlap  * Return: 0 on success, errno otherwise
457b97bf3fdSPer Liden  */
tipc_sk_create(struct net * net,struct socket * sock,int protocol,int kern)45858ed9442SJon Paul Maloy static int tipc_sk_create(struct net *net, struct socket *sock,
45958ed9442SJon Paul Maloy 			  int protocol, int kern)
460b97bf3fdSPer Liden {
4610c3141e9SAllan Stephens 	const struct proto_ops *ops;
462b97bf3fdSPer Liden 	struct sock *sk;
46358ed9442SJon Paul Maloy 	struct tipc_sock *tsk;
4645b8fa7ceSJon Paul Maloy 	struct tipc_msg *msg;
4650c3141e9SAllan Stephens 
4660c3141e9SAllan Stephens 	/* Validate arguments */
467b97bf3fdSPer Liden 	if (unlikely(protocol != 0))
468b97bf3fdSPer Liden 		return -EPROTONOSUPPORT;
469b97bf3fdSPer Liden 
470b97bf3fdSPer Liden 	switch (sock->type) {
471b97bf3fdSPer Liden 	case SOCK_STREAM:
4720c3141e9SAllan Stephens 		ops = &stream_ops;
473b97bf3fdSPer Liden 		break;
474b97bf3fdSPer Liden 	case SOCK_SEQPACKET:
4750c3141e9SAllan Stephens 		ops = &packet_ops;
476b97bf3fdSPer Liden 		break;
477b97bf3fdSPer Liden 	case SOCK_DGRAM:
478b97bf3fdSPer Liden 	case SOCK_RDM:
4790c3141e9SAllan Stephens 		ops = &msg_ops;
480b97bf3fdSPer Liden 		break;
48149978651SAllan Stephens 	default:
48249978651SAllan Stephens 		return -EPROTOTYPE;
483b97bf3fdSPer Liden 	}
484b97bf3fdSPer Liden 
4850c3141e9SAllan Stephens 	/* Allocate socket's protocol area */
48611aa9c28SEric W. Biederman 	sk = sk_alloc(net, AF_TIPC, GFP_KERNEL, &tipc_proto, kern);
4870c3141e9SAllan Stephens 	if (sk == NULL)
4880c3141e9SAllan Stephens 		return -ENOMEM;
4890c3141e9SAllan Stephens 
49058ed9442SJon Paul Maloy 	tsk = tipc_sk(sk);
491301bae56SJon Paul Maloy 	tsk->max_pkt = MAX_PKT_DEFAULT;
492c0bceb97SJon Maloy 	tsk->maxnagle = 0;
4930a3e060fSTuong Lien 	tsk->nagle_start = NAGLE_START_INIT;
494301bae56SJon Paul Maloy 	INIT_LIST_HEAD(&tsk->publications);
495365ad353SJon Paul Maloy 	INIT_LIST_HEAD(&tsk->cong_links);
496301bae56SJon Paul Maloy 	msg = &tsk->phdr;
497b97bf3fdSPer Liden 
4980c3141e9SAllan Stephens 	/* Finish initializing socket data structures */
4990c3141e9SAllan Stephens 	sock->ops = ops;
500b97bf3fdSPer Liden 	sock_init_data(sock, sk);
501438adcafSParthasarathy Bhuvaragan 	tipc_set_sk_state(sk, TIPC_OPEN);
50207f6c4bcSYing Xue 	if (tipc_sk_insert(tsk)) {
50300aff359SHangyu Hua 		sk_free(sk);
504c19ca6cbSMasanari Iida 		pr_warn("Socket create failed; port number exhausted\n");
50507f6c4bcSYing Xue 		return -EINVAL;
50607f6c4bcSYing Xue 	}
50740f9f439SHerbert Xu 
50840f9f439SHerbert Xu 	/* Ensure tsk is visible before we read own_addr. */
50940f9f439SHerbert Xu 	smp_mb();
51040f9f439SHerbert Xu 
51123fd3eacSJon Maloy 	tipc_msg_init(tipc_own_addr(net), msg, TIPC_LOW_IMPORTANCE,
51223fd3eacSJon Maloy 		      TIPC_NAMED_MSG, NAMED_H_SIZE, 0);
51340f9f439SHerbert Xu 
51407f6c4bcSYing Xue 	msg_set_origport(msg, tsk->portid);
51531b102bbSKees Cook 	timer_setup(&sk->sk_timer, tipc_sk_timeout, 0);
5166f00089cSParthasarathy Bhuvaragan 	sk->sk_shutdown = 0;
51764ac5f59SJon Maloy 	sk->sk_backlog_rcv = tipc_sk_backlog_rcv;
51802739545SKuniyuki Iwashima 	sk->sk_rcvbuf = READ_ONCE(sysctl_tipc_rmem[1]);
519f288bef4SYing Xue 	sk->sk_data_ready = tipc_data_ready;
520f288bef4SYing Xue 	sk->sk_write_space = tipc_write_space;
521f4195d1eSYing Xue 	sk->sk_destruct = tipc_sock_destruct;
5224f4482dcSJon Paul Maloy 	tsk->conn_timeout = CONN_TIMEOUT_DEFAULT;
5231b22bcadSJon Maloy 	tsk->group_is_open = true;
5244f4482dcSJon Paul Maloy 	atomic_set(&tsk->dupl_rcvcnt, 0);
5257ef43ebaSAllan Stephens 
52610724cc7SJon Paul Maloy 	/* Start out with safe limits until we receive an advertised window */
52710724cc7SJon Paul Maloy 	tsk->snd_win = tsk_adv_blocks(RCVBUF_MIN);
52810724cc7SJon Paul Maloy 	tsk->rcv_win = tsk->snd_win;
52910724cc7SJon Paul Maloy 
530c752023aSParthasarathy Bhuvaragan 	if (tipc_sk_type_connectionless(sk)) {
531301bae56SJon Paul Maloy 		tsk_set_unreturnable(tsk, true);
5320c3141e9SAllan Stephens 		if (sock->type == SOCK_DGRAM)
533301bae56SJon Paul Maloy 			tsk_set_unreliable(tsk, true);
5340c3141e9SAllan Stephens 	}
5352948a1fcSJon Maloy 	__skb_queue_head_init(&tsk->mc_method.deferredq);
53601e661ebSTuong Lien 	trace_tipc_sk_create(sk, NULL, TIPC_DUMP_NONE, " ");
537b97bf3fdSPer Liden 	return 0;
538b97bf3fdSPer Liden }
539b97bf3fdSPer Liden 
tipc_sk_callback(struct rcu_head * head)54007f6c4bcSYing Xue static void tipc_sk_callback(struct rcu_head *head)
54107f6c4bcSYing Xue {
54207f6c4bcSYing Xue 	struct tipc_sock *tsk = container_of(head, struct tipc_sock, rcu);
54307f6c4bcSYing Xue 
54407f6c4bcSYing Xue 	sock_put(&tsk->sk);
54507f6c4bcSYing Xue }
54607f6c4bcSYing Xue 
5476f00089cSParthasarathy Bhuvaragan /* Caller should hold socket lock for the socket. */
__tipc_shutdown(struct socket * sock,int error)5486f00089cSParthasarathy Bhuvaragan static void __tipc_shutdown(struct socket *sock, int error)
5496f00089cSParthasarathy Bhuvaragan {
5506f00089cSParthasarathy Bhuvaragan 	struct sock *sk = sock->sk;
5516f00089cSParthasarathy Bhuvaragan 	struct tipc_sock *tsk = tipc_sk(sk);
5526f00089cSParthasarathy Bhuvaragan 	struct net *net = sock_net(sk);
55312db3c80STung Nguyen 	long timeout = msecs_to_jiffies(CONN_TIMEOUT_DEFAULT);
5546f00089cSParthasarathy Bhuvaragan 	u32 dnode = tsk_peer_node(tsk);
5556f00089cSParthasarathy Bhuvaragan 	struct sk_buff *skb;
5566f00089cSParthasarathy Bhuvaragan 
557365ad353SJon Paul Maloy 	/* Avoid that hi-prio shutdown msgs bypass msgs in link wakeup queue */
558365ad353SJon Paul Maloy 	tipc_wait_for_cond(sock, &timeout, (!tsk->cong_link_cnt &&
559365ad353SJon Paul Maloy 					    !tsk_conn_cong(tsk)));
560365ad353SJon Paul Maloy 
561d34910e1STung Nguyen 	/* Push out delayed messages if in Nagle mode */
5620a3e060fSTuong Lien 	tipc_sk_push_backlog(tsk, false);
563d34910e1STung Nguyen 	/* Remove pending SYN */
56467879274STung Nguyen 	__skb_queue_purge(&sk->sk_write_queue);
56567879274STung Nguyen 
56649afb806STuong Lien 	/* Remove partially received buffer if any */
56749afb806STuong Lien 	skb = skb_peek(&sk->sk_receive_queue);
56849afb806STuong Lien 	if (skb && TIPC_SKB_CB(skb)->bytes_read) {
56949afb806STuong Lien 		__skb_unlink(skb, &sk->sk_receive_queue);
5706f00089cSParthasarathy Bhuvaragan 		kfree_skb(skb);
571693c5649SJon Paul Maloy 	}
57249afb806STuong Lien 
57349afb806STuong Lien 	/* Reject all unreceived messages if connectionless */
57449afb806STuong Lien 	if (tipc_sk_type_connectionless(sk)) {
57549afb806STuong Lien 		tsk_rej_rx_queue(sk, error);
57649afb806STuong Lien 		return;
57749afb806STuong Lien 	}
57849afb806STuong Lien 
57949afb806STuong Lien 	switch (sk->sk_state) {
58049afb806STuong Lien 	case TIPC_CONNECTING:
58149afb806STuong Lien 	case TIPC_ESTABLISHED:
5826f00089cSParthasarathy Bhuvaragan 		tipc_set_sk_state(sk, TIPC_DISCONNECTING);
5836f00089cSParthasarathy Bhuvaragan 		tipc_node_remove_conn(net, dnode, tsk->portid);
58449afb806STuong Lien 		/* Send a FIN+/- to its peer */
58549afb806STuong Lien 		skb = __skb_dequeue(&sk->sk_receive_queue);
58649afb806STuong Lien 		if (skb) {
58749afb806STuong Lien 			__skb_queue_purge(&sk->sk_receive_queue);
5886f00089cSParthasarathy Bhuvaragan 			tipc_sk_respond(sk, skb, error);
58949afb806STuong Lien 			break;
5906f00089cSParthasarathy Bhuvaragan 		}
5916f00089cSParthasarathy Bhuvaragan 		skb = tipc_msg_create(TIPC_CRITICAL_IMPORTANCE,
5926f00089cSParthasarathy Bhuvaragan 				      TIPC_CONN_MSG, SHORT_H_SIZE, 0, dnode,
5936f00089cSParthasarathy Bhuvaragan 				      tsk_own_node(tsk), tsk_peer_port(tsk),
5946f00089cSParthasarathy Bhuvaragan 				      tsk->portid, error);
5956f00089cSParthasarathy Bhuvaragan 		if (skb)
5966f00089cSParthasarathy Bhuvaragan 			tipc_node_xmit_skb(net, skb, dnode, tsk->portid);
59749afb806STuong Lien 		break;
59849afb806STuong Lien 	case TIPC_LISTEN:
59949afb806STuong Lien 		/* Reject all SYN messages */
60049afb806STuong Lien 		tsk_rej_rx_queue(sk, error);
60149afb806STuong Lien 		break;
60249afb806STuong Lien 	default:
60349afb806STuong Lien 		__skb_queue_purge(&sk->sk_receive_queue);
60449afb806STuong Lien 		break;
6056f00089cSParthasarathy Bhuvaragan 	}
6066f00089cSParthasarathy Bhuvaragan }
6076f00089cSParthasarathy Bhuvaragan 
608c5fa7b3cSYing Xue /**
609247f0f3cSYing Xue  * tipc_release - destroy a TIPC socket
610b97bf3fdSPer Liden  * @sock: socket to destroy
611b97bf3fdSPer Liden  *
612b97bf3fdSPer Liden  * This routine cleans up any messages that are still queued on the socket.
613b97bf3fdSPer Liden  * For DGRAM and RDM socket types, all queued messages are rejected.
614b97bf3fdSPer Liden  * For SEQPACKET and STREAM socket types, the first message is rejected
615b97bf3fdSPer Liden  * and any others are discarded.  (If the first message on a STREAM socket
616b97bf3fdSPer Liden  * is partially-read, it is discarded and the next one is rejected instead.)
617b97bf3fdSPer Liden  *
618b97bf3fdSPer Liden  * NOTE: Rejected messages are not necessarily returned to the sender!  They
619b97bf3fdSPer Liden  * are returned or discarded according to the "destination droppable" setting
620b97bf3fdSPer Liden  * specified for the message by the sender.
621b97bf3fdSPer Liden  *
622637b77fdSRandy Dunlap  * Return: 0 on success, errno otherwise
623b97bf3fdSPer Liden  */
tipc_release(struct socket * sock)624247f0f3cSYing Xue static int tipc_release(struct socket *sock)
625b97bf3fdSPer Liden {
626b97bf3fdSPer Liden 	struct sock *sk = sock->sk;
62758ed9442SJon Paul Maloy 	struct tipc_sock *tsk;
628b97bf3fdSPer Liden 
6290c3141e9SAllan Stephens 	/*
6300c3141e9SAllan Stephens 	 * Exit if socket isn't fully initialized (occurs when a failed accept()
6310c3141e9SAllan Stephens 	 * releases a pre-allocated child socket that was never used)
6320c3141e9SAllan Stephens 	 */
6330c3141e9SAllan Stephens 	if (sk == NULL)
6340c3141e9SAllan Stephens 		return 0;
6350c3141e9SAllan Stephens 
63658ed9442SJon Paul Maloy 	tsk = tipc_sk(sk);
6370c3141e9SAllan Stephens 	lock_sock(sk);
6380c3141e9SAllan Stephens 
63901e661ebSTuong Lien 	trace_tipc_sk_release(sk, NULL, TIPC_DUMP_ALL, " ");
6406f00089cSParthasarathy Bhuvaragan 	__tipc_shutdown(sock, TIPC_ERR_NO_PORT);
6416f00089cSParthasarathy Bhuvaragan 	sk->sk_shutdown = SHUTDOWN_MASK;
64275da2163SJon Maloy 	tipc_sk_leave(tsk);
6432c98da07SJon Maloy 	tipc_sk_withdraw(tsk, NULL);
644c55c8edaSHoang Le 	__skb_queue_purge(&tsk->mc_method.deferredq);
6451ea23a21SYing Xue 	sk_stop_timer(sk, &sk->sk_timer);
64607f6c4bcSYing Xue 	tipc_sk_remove(tsk);
647b97bf3fdSPer Liden 
6480a3b8b2bSCong Wang 	sock_orphan(sk);
6490c3141e9SAllan Stephens 	/* Reject any messages that accumulated in backlog queue */
6500c3141e9SAllan Stephens 	release_sock(sk);
651a80ae530SJon Maloy 	tipc_dest_list_purge(&tsk->cong_links);
652365ad353SJon Paul Maloy 	tsk->cong_link_cnt = 0;
65307f6c4bcSYing Xue 	call_rcu(&tsk->rcu, tipc_sk_callback);
6540c3141e9SAllan Stephens 	sock->sk = NULL;
655b97bf3fdSPer Liden 
656065d7e39SGeert Uytterhoeven 	return 0;
657b97bf3fdSPer Liden }
658b97bf3fdSPer Liden 
659b97bf3fdSPer Liden /**
660b8c7dd15SJesse Brandeburg  * __tipc_bind - associate or disassociate TIPC name(s) with a socket
661b97bf3fdSPer Liden  * @sock: socket structure
66260c102eeSJon Maloy  * @skaddr: socket address describing name(s) and desired operation
66360c102eeSJon Maloy  * @alen: size of socket address data structure
664b97bf3fdSPer Liden  *
665326af505Sgushengxian  * Name and name sequence binding are indicated using a positive scope value;
666b97bf3fdSPer Liden  * a negative scope value unbinds the specified name.  Specifying no name
667b97bf3fdSPer Liden  * (i.e. a socket address length of 0) unbinds all names from the socket.
668b97bf3fdSPer Liden  *
669637b77fdSRandy Dunlap  * Return: 0 on success, errno otherwise
6700c3141e9SAllan Stephens  *
6710c3141e9SAllan Stephens  * NOTE: This routine doesn't need to take the socket lock since it doesn't
6720c3141e9SAllan Stephens  *       access any non-constant socket information.
673b97bf3fdSPer Liden  */
__tipc_bind(struct socket * sock,struct sockaddr * skaddr,int alen)67460c102eeSJon Maloy static int __tipc_bind(struct socket *sock, struct sockaddr *skaddr, int alen)
675b97bf3fdSPer Liden {
67650a3499aSJon Maloy 	struct tipc_uaddr *ua = (struct tipc_uaddr *)skaddr;
67760c102eeSJon Maloy 	struct tipc_sock *tsk = tipc_sk(sock->sk);
67850a3499aSJon Maloy 	bool unbind = false;
679b97bf3fdSPer Liden 
68060c102eeSJon Maloy 	if (unlikely(!alen))
6812c98da07SJon Maloy 		return tipc_sk_withdraw(tsk, NULL);
682b97bf3fdSPer Liden 
68350a3499aSJon Maloy 	if (ua->addrtype == TIPC_SERVICE_ADDR) {
68450a3499aSJon Maloy 		ua->addrtype = TIPC_SERVICE_RANGE;
68550a3499aSJon Maloy 		ua->sr.upper = ua->sr.lower;
68650a3499aSJon Maloy 	}
68750a3499aSJon Maloy 	if (ua->scope < 0) {
68850a3499aSJon Maloy 		unbind = true;
68950a3499aSJon Maloy 		ua->scope = -ua->scope;
69050a3499aSJon Maloy 	}
69150a3499aSJon Maloy 	/* Users may still use deprecated TIPC_ZONE_SCOPE */
69250a3499aSJon Maloy 	if (ua->scope != TIPC_NODE_SCOPE)
69350a3499aSJon Maloy 		ua->scope = TIPC_CLUSTER_SCOPE;
69460c102eeSJon Maloy 
69560c102eeSJon Maloy 	if (tsk->group)
69660c102eeSJon Maloy 		return -EACCES;
69760c102eeSJon Maloy 
69850a3499aSJon Maloy 	if (unbind)
6992c98da07SJon Maloy 		return tipc_sk_withdraw(tsk, ua);
70050a3499aSJon Maloy 	return tipc_sk_publish(tsk, ua);
70184602761SYing Xue }
702b97bf3fdSPer Liden 
tipc_sk_bind(struct socket * sock,struct sockaddr * skaddr,int alen)70360c102eeSJon Maloy int tipc_sk_bind(struct socket *sock, struct sockaddr *skaddr, int alen)
70460c102eeSJon Maloy {
70560c102eeSJon Maloy 	int res;
70660c102eeSJon Maloy 
70760c102eeSJon Maloy 	lock_sock(sock->sk);
70860c102eeSJon Maloy 	res = __tipc_bind(sock, skaddr, alen);
70960c102eeSJon Maloy 	release_sock(sock->sk);
71084602761SYing Xue 	return res;
711b97bf3fdSPer Liden }
712b97bf3fdSPer Liden 
tipc_bind(struct socket * sock,struct sockaddr * skaddr,int alen)71372671b35SJon Maloy static int tipc_bind(struct socket *sock, struct sockaddr *skaddr, int alen)
71472671b35SJon Maloy {
71550a3499aSJon Maloy 	struct tipc_uaddr *ua = (struct tipc_uaddr *)skaddr;
71650a3499aSJon Maloy 	u32 atype = ua->addrtype;
71772671b35SJon Maloy 
71872671b35SJon Maloy 	if (alen) {
71950a3499aSJon Maloy 		if (!tipc_uaddr_valid(ua, alen))
72072671b35SJon Maloy 			return -EINVAL;
72150a3499aSJon Maloy 		if (atype == TIPC_SOCKET_ADDR)
72260c102eeSJon Maloy 			return -EAFNOSUPPORT;
72350a3499aSJon Maloy 		if (ua->sr.type < TIPC_RESERVED_TYPES) {
72472671b35SJon Maloy 			pr_warn_once("Can't bind to reserved service type %u\n",
72550a3499aSJon Maloy 				     ua->sr.type);
72672671b35SJon Maloy 			return -EACCES;
72772671b35SJon Maloy 		}
72872671b35SJon Maloy 	}
72972671b35SJon Maloy 	return tipc_sk_bind(sock, skaddr, alen);
73072671b35SJon Maloy }
73172671b35SJon Maloy 
732b97bf3fdSPer Liden /**
733247f0f3cSYing Xue  * tipc_getname - get port ID of socket or peer socket
734b97bf3fdSPer Liden  * @sock: socket structure
735b97bf3fdSPer Liden  * @uaddr: area for returned socket address
7362da59918SAllan Stephens  * @peer: 0 = own ID, 1 = current peer ID, 2 = current/former peer ID
737b97bf3fdSPer Liden  *
738637b77fdSRandy Dunlap  * Return: 0 on success, errno otherwise
7390c3141e9SAllan Stephens  *
7402da59918SAllan Stephens  * NOTE: This routine doesn't need to take the socket lock since it only
7412da59918SAllan Stephens  *       accesses socket information that is unchanging (or which changes in
7422da59918SAllan Stephens  *       a completely predictable manner).
743b97bf3fdSPer Liden  */
tipc_getname(struct socket * sock,struct sockaddr * uaddr,int peer)744247f0f3cSYing Xue static int tipc_getname(struct socket *sock, struct sockaddr *uaddr,
7459b2c45d4SDenys Vlasenko 			int peer)
746b97bf3fdSPer Liden {
747b97bf3fdSPer Liden 	struct sockaddr_tipc *addr = (struct sockaddr_tipc *)uaddr;
7489fd4b070SParthasarathy Bhuvaragan 	struct sock *sk = sock->sk;
7499fd4b070SParthasarathy Bhuvaragan 	struct tipc_sock *tsk = tipc_sk(sk);
750b97bf3fdSPer Liden 
75188f8a5e3SKulikov Vasiliy 	memset(addr, 0, sizeof(*addr));
7520c3141e9SAllan Stephens 	if (peer) {
753f40acbafSParthasarathy Bhuvaragan 		if ((!tipc_sk_connected(sk)) &&
7549fd4b070SParthasarathy Bhuvaragan 		    ((peer != 2) || (sk->sk_state != TIPC_DISCONNECTING)))
7552da59918SAllan Stephens 			return -ENOTCONN;
756301bae56SJon Paul Maloy 		addr->addr.id.ref = tsk_peer_port(tsk);
757301bae56SJon Paul Maloy 		addr->addr.id.node = tsk_peer_node(tsk);
7580c3141e9SAllan Stephens 	} else {
75907f6c4bcSYing Xue 		addr->addr.id.ref = tsk->portid;
76023fd3eacSJon Maloy 		addr->addr.id.node = tipc_own_addr(sock_net(sk));
7610c3141e9SAllan Stephens 	}
762b97bf3fdSPer Liden 
763b6f88d9cSJon Maloy 	addr->addrtype = TIPC_SOCKET_ADDR;
764b97bf3fdSPer Liden 	addr->family = AF_TIPC;
765b97bf3fdSPer Liden 	addr->scope = 0;
766b97bf3fdSPer Liden 	addr->addr.name.domain = 0;
767b97bf3fdSPer Liden 
7689b2c45d4SDenys Vlasenko 	return sizeof(*addr);
769b97bf3fdSPer Liden }
770b97bf3fdSPer Liden 
771b97bf3fdSPer Liden /**
772a11e1d43SLinus Torvalds  * tipc_poll - read and possibly block on pollmask
773b97bf3fdSPer Liden  * @file: file structure associated with the socket
774b97bf3fdSPer Liden  * @sock: socket for which to calculate the poll bits
775a11e1d43SLinus Torvalds  * @wait: ???
776b97bf3fdSPer Liden  *
777637b77fdSRandy Dunlap  * Return: pollmask value
7789b674e82SAllan Stephens  *
7799b674e82SAllan Stephens  * COMMENTARY:
7809b674e82SAllan Stephens  * It appears that the usual socket locking mechanisms are not useful here
7819b674e82SAllan Stephens  * since the pollmask info is potentially out-of-date the moment this routine
7829b674e82SAllan Stephens  * exits.  TCP and other protocols seem to rely on higher level poll routines
7839b674e82SAllan Stephens  * to handle any preventable race conditions, so TIPC will do the same ...
7849b674e82SAllan Stephens  *
785f662c070SAllan Stephens  * IMPORTANT: The fact that a read or write operation is indicated does NOT
786f662c070SAllan Stephens  * imply that the operation will succeed, merely that it should be performed
787f662c070SAllan Stephens  * and will not block.
788b97bf3fdSPer Liden  */
tipc_poll(struct file * file,struct socket * sock,poll_table * wait)789a11e1d43SLinus Torvalds static __poll_t tipc_poll(struct file *file, struct socket *sock,
790a11e1d43SLinus Torvalds 			      poll_table *wait)
791b97bf3fdSPer Liden {
7929b674e82SAllan Stephens 	struct sock *sk = sock->sk;
79358ed9442SJon Paul Maloy 	struct tipc_sock *tsk = tipc_sk(sk);
794ade994f4SAl Viro 	__poll_t revents = 0;
7959b674e82SAllan Stephens 
79689ab066dSKarsten Graul 	sock_poll_wait(file, sock, wait);
79701e661ebSTuong Lien 	trace_tipc_sk_poll(sk, NULL, TIPC_DUMP_ALL, " ");
798a11e1d43SLinus Torvalds 
7996f00089cSParthasarathy Bhuvaragan 	if (sk->sk_shutdown & RCV_SHUTDOWN)
800a9a08845SLinus Torvalds 		revents |= EPOLLRDHUP | EPOLLIN | EPOLLRDNORM;
8016f00089cSParthasarathy Bhuvaragan 	if (sk->sk_shutdown == SHUTDOWN_MASK)
802a9a08845SLinus Torvalds 		revents |= EPOLLHUP;
8036f00089cSParthasarathy Bhuvaragan 
804f40acbafSParthasarathy Bhuvaragan 	switch (sk->sk_state) {
805f40acbafSParthasarathy Bhuvaragan 	case TIPC_ESTABLISHED:
806365ad353SJon Paul Maloy 		if (!tsk->cong_link_cnt && !tsk_conn_cong(tsk))
807a9a08845SLinus Torvalds 			revents |= EPOLLOUT;
8087f8901b7SMiaohe Lin 		fallthrough;
809f40acbafSParthasarathy Bhuvaragan 	case TIPC_LISTEN:
810ff946833SParthasarathy Bhuvaragan 	case TIPC_CONNECTING:
8113ef7cf57SEric Dumazet 		if (!skb_queue_empty_lockless(&sk->sk_receive_queue))
812a9a08845SLinus Torvalds 			revents |= EPOLLIN | EPOLLRDNORM;
813f40acbafSParthasarathy Bhuvaragan 		break;
814438adcafSParthasarathy Bhuvaragan 	case TIPC_OPEN:
81560c25306SJon Maloy 		if (tsk->group_is_open && !tsk->cong_link_cnt)
816a9a08845SLinus Torvalds 			revents |= EPOLLOUT;
817ae236fb2SJon Maloy 		if (!tipc_sk_type_connectionless(sk))
818ae236fb2SJon Maloy 			break;
8193ef7cf57SEric Dumazet 		if (skb_queue_empty_lockless(&sk->sk_receive_queue))
820ae236fb2SJon Maloy 			break;
821a9a08845SLinus Torvalds 		revents |= EPOLLIN | EPOLLRDNORM;
822438adcafSParthasarathy Bhuvaragan 		break;
8239fd4b070SParthasarathy Bhuvaragan 	case TIPC_DISCONNECTING:
824a9a08845SLinus Torvalds 		revents = EPOLLIN | EPOLLRDNORM | EPOLLHUP;
8259fd4b070SParthasarathy Bhuvaragan 		break;
826f662c070SAllan Stephens 	}
827ae236fb2SJon Maloy 	return revents;
828b97bf3fdSPer Liden }
829b97bf3fdSPer Liden 
8300abd8ff2SJon Paul Maloy /**
8310abd8ff2SJon Paul Maloy  * tipc_sendmcast - send multicast message
8320abd8ff2SJon Paul Maloy  * @sock: socket structure
833833f8670SJon Maloy  * @ua: destination address struct
834562640f3SAl Viro  * @msg: message to send
835365ad353SJon Paul Maloy  * @dlen: length of data to send
836365ad353SJon Paul Maloy  * @timeout: timeout to wait for wakeup
8370abd8ff2SJon Paul Maloy  *
8380abd8ff2SJon Paul Maloy  * Called from function tipc_sendmsg(), which has done all sanity checks
839637b77fdSRandy Dunlap  * Return: the number of bytes sent on success, or errno
8400abd8ff2SJon Paul Maloy  */
tipc_sendmcast(struct socket * sock,struct tipc_uaddr * ua,struct msghdr * msg,size_t dlen,long timeout)841833f8670SJon Maloy static int tipc_sendmcast(struct  socket *sock, struct tipc_uaddr *ua,
842365ad353SJon Paul Maloy 			  struct msghdr *msg, size_t dlen, long timeout)
8430abd8ff2SJon Paul Maloy {
8440abd8ff2SJon Paul Maloy 	struct sock *sk = sock->sk;
845c5898636SJon Paul Maloy 	struct tipc_sock *tsk = tipc_sk(sk);
846365ad353SJon Paul Maloy 	struct tipc_msg *hdr = &tsk->phdr;
847f2f9800dSYing Xue 	struct net *net = sock_net(sk);
848365ad353SJon Paul Maloy 	int mtu = tipc_bcast_get_mtu(net);
849365ad353SJon Paul Maloy 	struct sk_buff_head pkts;
850a853e4c6SJon Paul Maloy 	struct tipc_nlist dsts;
8510abd8ff2SJon Paul Maloy 	int rc;
8520abd8ff2SJon Paul Maloy 
85375da2163SJon Maloy 	if (tsk->group)
85475da2163SJon Maloy 		return -EACCES;
85575da2163SJon Maloy 
856a853e4c6SJon Paul Maloy 	/* Block or return if any destination link is congested */
857365ad353SJon Paul Maloy 	rc = tipc_wait_for_cond(sock, &timeout, !tsk->cong_link_cnt);
858365ad353SJon Paul Maloy 	if (unlikely(rc))
8590abd8ff2SJon Paul Maloy 		return rc;
8600abd8ff2SJon Paul Maloy 
861a853e4c6SJon Paul Maloy 	/* Lookup destination nodes */
862a853e4c6SJon Paul Maloy 	tipc_nlist_init(&dsts, tipc_own_addr(net));
863833f8670SJon Maloy 	tipc_nametbl_lookup_mcast_nodes(net, ua, &dsts);
864a853e4c6SJon Paul Maloy 	if (!dsts.local && !dsts.remote)
865a853e4c6SJon Paul Maloy 		return -EHOSTUNREACH;
866a853e4c6SJon Paul Maloy 
867a853e4c6SJon Paul Maloy 	/* Build message header */
868365ad353SJon Paul Maloy 	msg_set_type(hdr, TIPC_MCAST_MSG);
869a853e4c6SJon Paul Maloy 	msg_set_hdr_sz(hdr, MCAST_H_SIZE);
870365ad353SJon Paul Maloy 	msg_set_lookup_scope(hdr, TIPC_CLUSTER_SCOPE);
871365ad353SJon Paul Maloy 	msg_set_destport(hdr, 0);
872365ad353SJon Paul Maloy 	msg_set_destnode(hdr, 0);
873833f8670SJon Maloy 	msg_set_nametype(hdr, ua->sr.type);
874833f8670SJon Maloy 	msg_set_namelower(hdr, ua->sr.lower);
875833f8670SJon Maloy 	msg_set_nameupper(hdr, ua->sr.upper);
87622d85c79SJon Paul Maloy 
877a853e4c6SJon Paul Maloy 	/* Build message as chain of buffers */
878e654f9f5SJon Maloy 	__skb_queue_head_init(&pkts);
879365ad353SJon Paul Maloy 	rc = tipc_msg_build(hdr, msg, 0, dlen, mtu, &pkts);
880365ad353SJon Paul Maloy 
881a853e4c6SJon Paul Maloy 	/* Send message if build was successful */
88201e661ebSTuong Lien 	if (unlikely(rc == dlen)) {
88301e661ebSTuong Lien 		trace_tipc_sk_sendmcast(sk, skb_peek(&pkts),
88401e661ebSTuong Lien 					TIPC_DUMP_SK_SNDQ, " ");
885833f8670SJon Maloy 		rc = tipc_mcast_xmit(net, &pkts, &tsk->mc_method, &dsts,
886a853e4c6SJon Paul Maloy 				     &tsk->cong_link_cnt);
88701e661ebSTuong Lien 	}
888a853e4c6SJon Paul Maloy 
889a853e4c6SJon Paul Maloy 	tipc_nlist_purge(&dsts);
890365ad353SJon Paul Maloy 
891365ad353SJon Paul Maloy 	return rc ? rc : dlen;
8920abd8ff2SJon Paul Maloy }
8930abd8ff2SJon Paul Maloy 
894cb1b7280SJon Paul Maloy /**
89527bd9ec0SJon Maloy  * tipc_send_group_msg - send a message to a member in the group
89627bd9ec0SJon Maloy  * @net: network namespace
897f172f4b8SRandy Dunlap  * @tsk: tipc socket
89827bd9ec0SJon Maloy  * @m: message to send
89927bd9ec0SJon Maloy  * @mb: group member
90027bd9ec0SJon Maloy  * @dnode: destination node
90127bd9ec0SJon Maloy  * @dport: destination port
90227bd9ec0SJon Maloy  * @dlen: total length of message data
90327bd9ec0SJon Maloy  */
tipc_send_group_msg(struct net * net,struct tipc_sock * tsk,struct msghdr * m,struct tipc_member * mb,u32 dnode,u32 dport,int dlen)90427bd9ec0SJon Maloy static int tipc_send_group_msg(struct net *net, struct tipc_sock *tsk,
90527bd9ec0SJon Maloy 			       struct msghdr *m, struct tipc_member *mb,
90627bd9ec0SJon Maloy 			       u32 dnode, u32 dport, int dlen)
90727bd9ec0SJon Maloy {
908b87a5ea3SJon Maloy 	u16 bc_snd_nxt = tipc_group_bc_snd_nxt(tsk->group);
9092f487712SJon Maloy 	struct tipc_mc_method *method = &tsk->mc_method;
91027bd9ec0SJon Maloy 	int blks = tsk_blocks(GROUP_H_SIZE + dlen);
91127bd9ec0SJon Maloy 	struct tipc_msg *hdr = &tsk->phdr;
91227bd9ec0SJon Maloy 	struct sk_buff_head pkts;
91327bd9ec0SJon Maloy 	int mtu, rc;
91427bd9ec0SJon Maloy 
91527bd9ec0SJon Maloy 	/* Complete message header */
91627bd9ec0SJon Maloy 	msg_set_type(hdr, TIPC_GRP_UCAST_MSG);
91727bd9ec0SJon Maloy 	msg_set_hdr_sz(hdr, GROUP_H_SIZE);
91827bd9ec0SJon Maloy 	msg_set_destport(hdr, dport);
91927bd9ec0SJon Maloy 	msg_set_destnode(hdr, dnode);
920b87a5ea3SJon Maloy 	msg_set_grp_bc_seqno(hdr, bc_snd_nxt);
92127bd9ec0SJon Maloy 
92227bd9ec0SJon Maloy 	/* Build message as chain of buffers */
923e654f9f5SJon Maloy 	__skb_queue_head_init(&pkts);
924f73b1281SHoang Le 	mtu = tipc_node_get_mtu(net, dnode, tsk->portid, false);
92527bd9ec0SJon Maloy 	rc = tipc_msg_build(hdr, m, 0, dlen, mtu, &pkts);
92627bd9ec0SJon Maloy 	if (unlikely(rc != dlen))
92727bd9ec0SJon Maloy 		return rc;
92827bd9ec0SJon Maloy 
92927bd9ec0SJon Maloy 	/* Send message */
93027bd9ec0SJon Maloy 	rc = tipc_node_xmit(net, &pkts, dnode, tsk->portid);
93127bd9ec0SJon Maloy 	if (unlikely(rc == -ELINKCONG)) {
93227bd9ec0SJon Maloy 		tipc_dest_push(&tsk->cong_links, dnode, 0);
93327bd9ec0SJon Maloy 		tsk->cong_link_cnt++;
93427bd9ec0SJon Maloy 	}
93527bd9ec0SJon Maloy 
9362f487712SJon Maloy 	/* Update send window */
93727bd9ec0SJon Maloy 	tipc_group_update_member(mb, blks);
93827bd9ec0SJon Maloy 
9392f487712SJon Maloy 	/* A broadcast sent within next EXPIRE period must follow same path */
9402f487712SJon Maloy 	method->rcast = true;
9412f487712SJon Maloy 	method->mandatory = true;
94227bd9ec0SJon Maloy 	return dlen;
94327bd9ec0SJon Maloy }
94427bd9ec0SJon Maloy 
94527bd9ec0SJon Maloy /**
94627bd9ec0SJon Maloy  * tipc_send_group_unicast - send message to a member in the group
94727bd9ec0SJon Maloy  * @sock: socket structure
94827bd9ec0SJon Maloy  * @m: message to send
94927bd9ec0SJon Maloy  * @dlen: total length of message data
95027bd9ec0SJon Maloy  * @timeout: timeout to wait for wakeup
95127bd9ec0SJon Maloy  *
95227bd9ec0SJon Maloy  * Called from function tipc_sendmsg(), which has done all sanity checks
953637b77fdSRandy Dunlap  * Return: the number of bytes sent on success, or errno
95427bd9ec0SJon Maloy  */
tipc_send_group_unicast(struct socket * sock,struct msghdr * m,int dlen,long timeout)95527bd9ec0SJon Maloy static int tipc_send_group_unicast(struct socket *sock, struct msghdr *m,
95627bd9ec0SJon Maloy 				   int dlen, long timeout)
95727bd9ec0SJon Maloy {
95827bd9ec0SJon Maloy 	struct sock *sk = sock->sk;
959006ed14eSJon Maloy 	struct tipc_uaddr *ua = (struct tipc_uaddr *)m->msg_name;
96027bd9ec0SJon Maloy 	int blks = tsk_blocks(GROUP_H_SIZE + dlen);
96127bd9ec0SJon Maloy 	struct tipc_sock *tsk = tipc_sk(sk);
96227bd9ec0SJon Maloy 	struct net *net = sock_net(sk);
96327bd9ec0SJon Maloy 	struct tipc_member *mb = NULL;
96427bd9ec0SJon Maloy 	u32 node, port;
96527bd9ec0SJon Maloy 	int rc;
96627bd9ec0SJon Maloy 
967006ed14eSJon Maloy 	node = ua->sk.node;
968006ed14eSJon Maloy 	port = ua->sk.ref;
96927bd9ec0SJon Maloy 	if (!port && !node)
97027bd9ec0SJon Maloy 		return -EHOSTUNREACH;
97127bd9ec0SJon Maloy 
97227bd9ec0SJon Maloy 	/* Block or return if destination link or member is congested */
97327bd9ec0SJon Maloy 	rc = tipc_wait_for_cond(sock, &timeout,
97427bd9ec0SJon Maloy 				!tipc_dest_find(&tsk->cong_links, node, 0) &&
975143ece65SCong Wang 				tsk->group &&
976143ece65SCong Wang 				!tipc_group_cong(tsk->group, node, port, blks,
977143ece65SCong Wang 						 &mb));
97827bd9ec0SJon Maloy 	if (unlikely(rc))
97927bd9ec0SJon Maloy 		return rc;
98027bd9ec0SJon Maloy 
98127bd9ec0SJon Maloy 	if (unlikely(!mb))
98227bd9ec0SJon Maloy 		return -EHOSTUNREACH;
98327bd9ec0SJon Maloy 
98427bd9ec0SJon Maloy 	rc = tipc_send_group_msg(net, tsk, m, mb, node, port, dlen);
98527bd9ec0SJon Maloy 
98627bd9ec0SJon Maloy 	return rc ? rc : dlen;
98727bd9ec0SJon Maloy }
98827bd9ec0SJon Maloy 
98927bd9ec0SJon Maloy /**
990ee106d7fSJon Maloy  * tipc_send_group_anycast - send message to any member with given identity
991ee106d7fSJon Maloy  * @sock: socket structure
992ee106d7fSJon Maloy  * @m: message to send
993ee106d7fSJon Maloy  * @dlen: total length of message data
994ee106d7fSJon Maloy  * @timeout: timeout to wait for wakeup
995ee106d7fSJon Maloy  *
996ee106d7fSJon Maloy  * Called from function tipc_sendmsg(), which has done all sanity checks
997637b77fdSRandy Dunlap  * Return: the number of bytes sent on success, or errno
998ee106d7fSJon Maloy  */
tipc_send_group_anycast(struct socket * sock,struct msghdr * m,int dlen,long timeout)999ee106d7fSJon Maloy static int tipc_send_group_anycast(struct socket *sock, struct msghdr *m,
1000ee106d7fSJon Maloy 				   int dlen, long timeout)
1001ee106d7fSJon Maloy {
1002006ed14eSJon Maloy 	struct tipc_uaddr *ua = (struct tipc_uaddr *)m->msg_name;
1003ee106d7fSJon Maloy 	struct sock *sk = sock->sk;
1004ee106d7fSJon Maloy 	struct tipc_sock *tsk = tipc_sk(sk);
1005ee106d7fSJon Maloy 	struct list_head *cong_links = &tsk->cong_links;
1006ee106d7fSJon Maloy 	int blks = tsk_blocks(GROUP_H_SIZE + dlen);
1007232d07b7SJon Maloy 	struct tipc_msg *hdr = &tsk->phdr;
1008ee106d7fSJon Maloy 	struct tipc_member *first = NULL;
1009ee106d7fSJon Maloy 	struct tipc_member *mbr = NULL;
1010ee106d7fSJon Maloy 	struct net *net = sock_net(sk);
1011ee106d7fSJon Maloy 	u32 node, port, exclude;
1012*e636ba1aSHongbo Li 	LIST_HEAD(dsts);
1013ee106d7fSJon Maloy 	int lookups = 0;
1014ee106d7fSJon Maloy 	int dstcnt, rc;
1015ee106d7fSJon Maloy 	bool cong;
1016ee106d7fSJon Maloy 
1017006ed14eSJon Maloy 	ua->sa.type = msg_nametype(hdr);
1018006ed14eSJon Maloy 	ua->scope = msg_lookup_scope(hdr);
1019ee106d7fSJon Maloy 
1020ee106d7fSJon Maloy 	while (++lookups < 4) {
1021143ece65SCong Wang 		exclude = tipc_group_exclude(tsk->group);
1022143ece65SCong Wang 
1023ee106d7fSJon Maloy 		first = NULL;
1024ee106d7fSJon Maloy 
1025ee106d7fSJon Maloy 		/* Look for a non-congested destination member, if any */
1026ee106d7fSJon Maloy 		while (1) {
1027006ed14eSJon Maloy 			if (!tipc_nametbl_lookup_group(net, ua, &dsts, &dstcnt,
1028006ed14eSJon Maloy 						       exclude, false))
1029ee106d7fSJon Maloy 				return -EHOSTUNREACH;
1030ee106d7fSJon Maloy 			tipc_dest_pop(&dsts, &node, &port);
1031143ece65SCong Wang 			cong = tipc_group_cong(tsk->group, node, port, blks,
1032143ece65SCong Wang 					       &mbr);
1033ee106d7fSJon Maloy 			if (!cong)
1034ee106d7fSJon Maloy 				break;
1035ee106d7fSJon Maloy 			if (mbr == first)
1036ee106d7fSJon Maloy 				break;
1037ee106d7fSJon Maloy 			if (!first)
1038ee106d7fSJon Maloy 				first = mbr;
1039ee106d7fSJon Maloy 		}
1040ee106d7fSJon Maloy 
1041ee106d7fSJon Maloy 		/* Start over if destination was not in member list */
1042ee106d7fSJon Maloy 		if (unlikely(!mbr))
1043ee106d7fSJon Maloy 			continue;
1044ee106d7fSJon Maloy 
1045ee106d7fSJon Maloy 		if (likely(!cong && !tipc_dest_find(cong_links, node, 0)))
1046ee106d7fSJon Maloy 			break;
1047ee106d7fSJon Maloy 
1048ee106d7fSJon Maloy 		/* Block or return if destination link or member is congested */
1049ee106d7fSJon Maloy 		rc = tipc_wait_for_cond(sock, &timeout,
1050ee106d7fSJon Maloy 					!tipc_dest_find(cong_links, node, 0) &&
1051143ece65SCong Wang 					tsk->group &&
1052143ece65SCong Wang 					!tipc_group_cong(tsk->group, node, port,
1053ee106d7fSJon Maloy 							 blks, &mbr));
1054ee106d7fSJon Maloy 		if (unlikely(rc))
1055ee106d7fSJon Maloy 			return rc;
1056ee106d7fSJon Maloy 
1057ee106d7fSJon Maloy 		/* Send, unless destination disappeared while waiting */
1058ee106d7fSJon Maloy 		if (likely(mbr))
1059ee106d7fSJon Maloy 			break;
1060ee106d7fSJon Maloy 	}
1061ee106d7fSJon Maloy 
1062ee106d7fSJon Maloy 	if (unlikely(lookups >= 4))
1063ee106d7fSJon Maloy 		return -EHOSTUNREACH;
1064ee106d7fSJon Maloy 
1065ee106d7fSJon Maloy 	rc = tipc_send_group_msg(net, tsk, m, mbr, node, port, dlen);
1066ee106d7fSJon Maloy 
1067ee106d7fSJon Maloy 	return rc ? rc : dlen;
1068ee106d7fSJon Maloy }
1069ee106d7fSJon Maloy 
1070ee106d7fSJon Maloy /**
107175da2163SJon Maloy  * tipc_send_group_bcast - send message to all members in communication group
1072d8141208SAndrew Lunn  * @sock: socket structure
107375da2163SJon Maloy  * @m: message to send
107475da2163SJon Maloy  * @dlen: total length of message data
107575da2163SJon Maloy  * @timeout: timeout to wait for wakeup
107675da2163SJon Maloy  *
107775da2163SJon Maloy  * Called from function tipc_sendmsg(), which has done all sanity checks
1078637b77fdSRandy Dunlap  * Return: the number of bytes sent on success, or errno
107975da2163SJon Maloy  */
tipc_send_group_bcast(struct socket * sock,struct msghdr * m,int dlen,long timeout)108075da2163SJon Maloy static int tipc_send_group_bcast(struct socket *sock, struct msghdr *m,
108175da2163SJon Maloy 				 int dlen, long timeout)
108275da2163SJon Maloy {
1083006ed14eSJon Maloy 	struct tipc_uaddr *ua = (struct tipc_uaddr *)m->msg_name;
108475da2163SJon Maloy 	struct sock *sk = sock->sk;
108575da2163SJon Maloy 	struct net *net = sock_net(sk);
108675da2163SJon Maloy 	struct tipc_sock *tsk = tipc_sk(sk);
10873c6306d4SCong Wang 	struct tipc_nlist *dsts;
108875da2163SJon Maloy 	struct tipc_mc_method *method = &tsk->mc_method;
10892f487712SJon Maloy 	bool ack = method->mandatory && method->rcast;
1090b7d42635SJon Maloy 	int blks = tsk_blocks(MCAST_H_SIZE + dlen);
109175da2163SJon Maloy 	struct tipc_msg *hdr = &tsk->phdr;
109275da2163SJon Maloy 	int mtu = tipc_bcast_get_mtu(net);
109375da2163SJon Maloy 	struct sk_buff_head pkts;
109475da2163SJon Maloy 	int rc = -EHOSTUNREACH;
109575da2163SJon Maloy 
1096b7d42635SJon Maloy 	/* Block or return if any destination link or member is congested */
1097143ece65SCong Wang 	rc = tipc_wait_for_cond(sock, &timeout,
1098143ece65SCong Wang 				!tsk->cong_link_cnt && tsk->group &&
1099143ece65SCong Wang 				!tipc_group_bc_cong(tsk->group, blks));
110075da2163SJon Maloy 	if (unlikely(rc))
110175da2163SJon Maloy 		return rc;
110275da2163SJon Maloy 
11033c6306d4SCong Wang 	dsts = tipc_group_dests(tsk->group);
11043c6306d4SCong Wang 	if (!dsts->local && !dsts->remote)
11053c6306d4SCong Wang 		return -EHOSTUNREACH;
11063c6306d4SCong Wang 
110775da2163SJon Maloy 	/* Complete message header */
1108006ed14eSJon Maloy 	if (ua) {
11095b8dddb6SJon Maloy 		msg_set_type(hdr, TIPC_GRP_MCAST_MSG);
1110006ed14eSJon Maloy 		msg_set_nameinst(hdr, ua->sa.instance);
11115b8dddb6SJon Maloy 	} else {
111275da2163SJon Maloy 		msg_set_type(hdr, TIPC_GRP_BCAST_MSG);
11135b8dddb6SJon Maloy 		msg_set_nameinst(hdr, 0);
11145b8dddb6SJon Maloy 	}
1115b7d42635SJon Maloy 	msg_set_hdr_sz(hdr, GROUP_H_SIZE);
111675da2163SJon Maloy 	msg_set_destport(hdr, 0);
111775da2163SJon Maloy 	msg_set_destnode(hdr, 0);
1118143ece65SCong Wang 	msg_set_grp_bc_seqno(hdr, tipc_group_bc_snd_nxt(tsk->group));
111975da2163SJon Maloy 
11202f487712SJon Maloy 	/* Avoid getting stuck with repeated forced replicasts */
11212f487712SJon Maloy 	msg_set_grp_bc_ack_req(hdr, ack);
11222f487712SJon Maloy 
112375da2163SJon Maloy 	/* Build message as chain of buffers */
1124e654f9f5SJon Maloy 	__skb_queue_head_init(&pkts);
112575da2163SJon Maloy 	rc = tipc_msg_build(hdr, m, 0, dlen, mtu, &pkts);
112675da2163SJon Maloy 	if (unlikely(rc != dlen))
112775da2163SJon Maloy 		return rc;
112875da2163SJon Maloy 
112975da2163SJon Maloy 	/* Send message */
11302f487712SJon Maloy 	rc = tipc_mcast_xmit(net, &pkts, method, dsts, &tsk->cong_link_cnt);
113175da2163SJon Maloy 	if (unlikely(rc))
113275da2163SJon Maloy 		return rc;
113375da2163SJon Maloy 
1134b7d42635SJon Maloy 	/* Update broadcast sequence number and send windows */
11352f487712SJon Maloy 	tipc_group_update_bc_members(tsk->group, blks, ack);
11362f487712SJon Maloy 
11372f487712SJon Maloy 	/* Broadcast link is now free to choose method for next broadcast */
11382f487712SJon Maloy 	method->mandatory = false;
11392f487712SJon Maloy 	method->expires = jiffies;
11402f487712SJon Maloy 
114175da2163SJon Maloy 	return dlen;
114275da2163SJon Maloy }
114375da2163SJon Maloy 
114475da2163SJon Maloy /**
11455b8dddb6SJon Maloy  * tipc_send_group_mcast - send message to all members with given identity
11465b8dddb6SJon Maloy  * @sock: socket structure
11475b8dddb6SJon Maloy  * @m: message to send
11485b8dddb6SJon Maloy  * @dlen: total length of message data
11495b8dddb6SJon Maloy  * @timeout: timeout to wait for wakeup
11505b8dddb6SJon Maloy  *
11515b8dddb6SJon Maloy  * Called from function tipc_sendmsg(), which has done all sanity checks
1152637b77fdSRandy Dunlap  * Return: the number of bytes sent on success, or errno
11535b8dddb6SJon Maloy  */
tipc_send_group_mcast(struct socket * sock,struct msghdr * m,int dlen,long timeout)11545b8dddb6SJon Maloy static int tipc_send_group_mcast(struct socket *sock, struct msghdr *m,
11555b8dddb6SJon Maloy 				 int dlen, long timeout)
11565b8dddb6SJon Maloy {
1157006ed14eSJon Maloy 	struct tipc_uaddr *ua = (struct tipc_uaddr *)m->msg_name;
11585b8dddb6SJon Maloy 	struct sock *sk = sock->sk;
11595b8dddb6SJon Maloy 	struct tipc_sock *tsk = tipc_sk(sk);
11605b8dddb6SJon Maloy 	struct tipc_group *grp = tsk->group;
1161232d07b7SJon Maloy 	struct tipc_msg *hdr = &tsk->phdr;
11625b8dddb6SJon Maloy 	struct net *net = sock_net(sk);
1163006ed14eSJon Maloy 	u32 dstcnt, exclude;
1164*e636ba1aSHongbo Li 	LIST_HEAD(dsts);
11655b8dddb6SJon Maloy 
1166006ed14eSJon Maloy 	ua->sa.type = msg_nametype(hdr);
1167006ed14eSJon Maloy 	ua->scope = msg_lookup_scope(hdr);
11685b8dddb6SJon Maloy 	exclude = tipc_group_exclude(grp);
1169232d07b7SJon Maloy 
1170006ed14eSJon Maloy 	if (!tipc_nametbl_lookup_group(net, ua, &dsts, &dstcnt, exclude, true))
11715b8dddb6SJon Maloy 		return -EHOSTUNREACH;
11725b8dddb6SJon Maloy 
11735b8dddb6SJon Maloy 	if (dstcnt == 1) {
1174006ed14eSJon Maloy 		tipc_dest_pop(&dsts, &ua->sk.node, &ua->sk.ref);
11755b8dddb6SJon Maloy 		return tipc_send_group_unicast(sock, m, dlen, timeout);
11765b8dddb6SJon Maloy 	}
11775b8dddb6SJon Maloy 
11785b8dddb6SJon Maloy 	tipc_dest_list_purge(&dsts);
11795b8dddb6SJon Maloy 	return tipc_send_group_bcast(sock, m, dlen, timeout);
11805b8dddb6SJon Maloy }
11815b8dddb6SJon Maloy 
11825b8dddb6SJon Maloy /**
1183cb1b7280SJon Paul Maloy  * tipc_sk_mcast_rcv - Deliver multicast messages to all destination sockets
1184f172f4b8SRandy Dunlap  * @net: the associated network namespace
1185cb1b7280SJon Paul Maloy  * @arrvq: queue with arriving messages, to be cloned after destination lookup
1186cb1b7280SJon Paul Maloy  * @inputq: queue with cloned messages, delivered to socket after dest lookup
1187cb1b7280SJon Paul Maloy  *
1188cb1b7280SJon Paul Maloy  * Multi-threaded: parallel calls with reference to same queues may occur
1189078bec82SJon Paul Maloy  */
tipc_sk_mcast_rcv(struct net * net,struct sk_buff_head * arrvq,struct sk_buff_head * inputq)1190cb1b7280SJon Paul Maloy void tipc_sk_mcast_rcv(struct net *net, struct sk_buff_head *arrvq,
1191cb1b7280SJon Paul Maloy 		       struct sk_buff_head *inputq)
1192078bec82SJon Paul Maloy {
119375da2163SJon Maloy 	u32 self = tipc_own_addr(net);
1194cb1b7280SJon Paul Maloy 	struct sk_buff *skb, *_skb;
1195b053fcc4SColin Ian King 	u32 portid, onode;
1196232d07b7SJon Maloy 	struct sk_buff_head tmpq;
119775da2163SJon Maloy 	struct list_head dports;
1198232d07b7SJon Maloy 	struct tipc_msg *hdr;
119945ceea2dSJon Maloy 	struct tipc_uaddr ua;
1200232d07b7SJon Maloy 	int user, mtyp, hlen;
12013c724acdSJon Paul Maloy 
1202cb1b7280SJon Paul Maloy 	__skb_queue_head_init(&tmpq);
12034d8642d8SJon Paul Maloy 	INIT_LIST_HEAD(&dports);
120445ceea2dSJon Maloy 	ua.addrtype = TIPC_SERVICE_RANGE;
1205078bec82SJon Paul Maloy 
12065ef21325SJon Maloy 	/* tipc_skb_peek() increments the head skb's reference counter */
1207cb1b7280SJon Paul Maloy 	skb = tipc_skb_peek(arrvq, &inputq->lock);
1208cb1b7280SJon Paul Maloy 	for (; skb; skb = tipc_skb_peek(arrvq, &inputq->lock)) {
1209232d07b7SJon Maloy 		hdr = buf_msg(skb);
1210232d07b7SJon Maloy 		user = msg_user(hdr);
1211232d07b7SJon Maloy 		mtyp = msg_type(hdr);
1212232d07b7SJon Maloy 		hlen = skb_headroom(skb) + msg_hdr_sz(hdr);
1213232d07b7SJon Maloy 		onode = msg_orignode(hdr);
121445ceea2dSJon Maloy 		ua.sr.type = msg_nametype(hdr);
12155ef21325SJon Maloy 		ua.sr.lower = msg_namelower(hdr);
12165ef21325SJon Maloy 		ua.sr.upper = msg_nameupper(hdr);
12175ef21325SJon Maloy 		if (onode == self)
12185ef21325SJon Maloy 			ua.scope = TIPC_ANY_SCOPE;
12195ef21325SJon Maloy 		else
12205ef21325SJon Maloy 			ua.scope = TIPC_CLUSTER_SCOPE;
1221232d07b7SJon Maloy 
12222f487712SJon Maloy 		if (mtyp == TIPC_GRP_UCAST_MSG || user == GROUP_PROTOCOL) {
12232f487712SJon Maloy 			spin_lock_bh(&inputq->lock);
12242f487712SJon Maloy 			if (skb_peek(arrvq) == skb) {
12252f487712SJon Maloy 				__skb_dequeue(arrvq);
12262f487712SJon Maloy 				__skb_queue_tail(inputq, skb);
12272f487712SJon Maloy 			}
1228c545a945SJon Maloy 			kfree_skb(skb);
12292f487712SJon Maloy 			spin_unlock_bh(&inputq->lock);
12302f487712SJon Maloy 			continue;
12312f487712SJon Maloy 		}
1232232d07b7SJon Maloy 
1233232d07b7SJon Maloy 		/* Group messages require exact scope match */
1234232d07b7SJon Maloy 		if (msg_in_group(hdr)) {
123545ceea2dSJon Maloy 			ua.sr.lower = 0;
123645ceea2dSJon Maloy 			ua.sr.upper = ~0;
123745ceea2dSJon Maloy 			ua.scope = msg_lookup_scope(hdr);
123875da2163SJon Maloy 		}
1239232d07b7SJon Maloy 
1240232d07b7SJon Maloy 		/* Create destination port list: */
12415ef21325SJon Maloy 		tipc_nametbl_lookup_mcast_sockets(net, &ua, &dports);
1242232d07b7SJon Maloy 
1243232d07b7SJon Maloy 		/* Clone message per destination */
1244a80ae530SJon Maloy 		while (tipc_dest_pop(&dports, NULL, &portid)) {
1245232d07b7SJon Maloy 			_skb = __pskb_copy(skb, hlen, GFP_ATOMIC);
1246cb1b7280SJon Paul Maloy 			if (_skb) {
1247cb1b7280SJon Paul Maloy 				msg_set_destport(buf_msg(_skb), portid);
1248cb1b7280SJon Paul Maloy 				__skb_queue_tail(&tmpq, _skb);
1249078bec82SJon Paul Maloy 				continue;
1250078bec82SJon Paul Maloy 			}
1251cb1b7280SJon Paul Maloy 			pr_warn("Failed to clone mcast rcv buffer\n");
1252078bec82SJon Paul Maloy 		}
12535ef21325SJon Maloy 		/* Append clones to inputq only if skb is still head of arrvq */
1254cb1b7280SJon Paul Maloy 		spin_lock_bh(&inputq->lock);
1255cb1b7280SJon Paul Maloy 		if (skb_peek(arrvq) == skb) {
1256cb1b7280SJon Paul Maloy 			skb_queue_splice_tail_init(&tmpq, inputq);
12575ef21325SJon Maloy 			/* Decrement the skb's refcnt */
125875016891SHoang Le 			kfree_skb(__skb_dequeue(arrvq));
1259cb1b7280SJon Paul Maloy 		}
1260cb1b7280SJon Paul Maloy 		spin_unlock_bh(&inputq->lock);
1261cb1b7280SJon Paul Maloy 		__skb_queue_purge(&tmpq);
12623c724acdSJon Paul Maloy 		kfree_skb(skb);
1263078bec82SJon Paul Maloy 	}
1264cb1b7280SJon Paul Maloy 	tipc_sk_rcv(net, inputq);
1265cb1b7280SJon Paul Maloy }
1266078bec82SJon Paul Maloy 
1267c0bceb97SJon Maloy /* tipc_sk_push_backlog(): send accumulated buffers in socket write queue
1268c0bceb97SJon Maloy  *                         when socket is in Nagle mode
1269c0bceb97SJon Maloy  */
tipc_sk_push_backlog(struct tipc_sock * tsk,bool nagle_ack)12700a3e060fSTuong Lien static void tipc_sk_push_backlog(struct tipc_sock *tsk, bool nagle_ack)
1271c0bceb97SJon Maloy {
1272c0bceb97SJon Maloy 	struct sk_buff_head *txq = &tsk->sk.sk_write_queue;
12730a3e060fSTuong Lien 	struct sk_buff *skb = skb_peek_tail(txq);
1274c0bceb97SJon Maloy 	struct net *net = sock_net(&tsk->sk);
1275c0bceb97SJon Maloy 	u32 dnode = tsk_peer_node(tsk);
1276c0bceb97SJon Maloy 	int rc;
1277c0bceb97SJon Maloy 
12780a3e060fSTuong Lien 	if (nagle_ack) {
12790a3e060fSTuong Lien 		tsk->pkt_cnt += skb_queue_len(txq);
12800a3e060fSTuong Lien 		if (!tsk->pkt_cnt || tsk->msg_acc / tsk->pkt_cnt < 2) {
12810a3e060fSTuong Lien 			tsk->oneway = 0;
12820a3e060fSTuong Lien 			if (tsk->nagle_start < NAGLE_START_MAX)
12830a3e060fSTuong Lien 				tsk->nagle_start *= 2;
12840a3e060fSTuong Lien 			tsk->expect_ack = false;
12850a3e060fSTuong Lien 			pr_debug("tsk %10u: bad nagle %u -> %u, next start %u!\n",
12860a3e060fSTuong Lien 				 tsk->portid, tsk->msg_acc, tsk->pkt_cnt,
12870a3e060fSTuong Lien 				 tsk->nagle_start);
12880a3e060fSTuong Lien 		} else {
12890a3e060fSTuong Lien 			tsk->nagle_start = NAGLE_START_INIT;
12900a3e060fSTuong Lien 			if (skb) {
12910a3e060fSTuong Lien 				msg_set_ack_required(buf_msg(skb));
12920a3e060fSTuong Lien 				tsk->expect_ack = true;
12930a3e060fSTuong Lien 			} else {
12940a3e060fSTuong Lien 				tsk->expect_ack = false;
12950a3e060fSTuong Lien 			}
12960a3e060fSTuong Lien 		}
12970a3e060fSTuong Lien 		tsk->msg_acc = 0;
12980a3e060fSTuong Lien 		tsk->pkt_cnt = 0;
12990a3e060fSTuong Lien 	}
13000a3e060fSTuong Lien 
1301d34910e1STung Nguyen 	if (!skb || tsk->cong_link_cnt)
1302d34910e1STung Nguyen 		return;
1303d34910e1STung Nguyen 
1304d34910e1STung Nguyen 	/* Do not send SYN again after congestion */
1305d34910e1STung Nguyen 	if (msg_is_syn(buf_msg(skb)))
1306c0bceb97SJon Maloy 		return;
1307c0bceb97SJon Maloy 
13080a3e060fSTuong Lien 	if (tsk->msg_acc)
13090a3e060fSTuong Lien 		tsk->pkt_cnt += skb_queue_len(txq);
1310c0bceb97SJon Maloy 	tsk->snt_unacked += tsk->snd_backlog;
1311c0bceb97SJon Maloy 	tsk->snd_backlog = 0;
1312c0bceb97SJon Maloy 	rc = tipc_node_xmit(net, txq, dnode, tsk->portid);
1313c0bceb97SJon Maloy 	if (rc == -ELINKCONG)
1314c0bceb97SJon Maloy 		tsk->cong_link_cnt = 1;
1315c0bceb97SJon Maloy }
1316c0bceb97SJon Maloy 
1317b97bf3fdSPer Liden /**
131864ac5f59SJon Maloy  * tipc_sk_conn_proto_rcv - receive a connection mng protocol message
1319ac0074eeSJon Paul Maloy  * @tsk: receiving socket
1320bcd3ffd4SJon Paul Maloy  * @skb: pointer to message buffer.
1321f172f4b8SRandy Dunlap  * @inputq: buffer list containing the buffers
1322f172f4b8SRandy Dunlap  * @xmitq: output message area
1323ac0074eeSJon Paul Maloy  */
tipc_sk_conn_proto_rcv(struct tipc_sock * tsk,struct sk_buff * skb,struct sk_buff_head * inputq,struct sk_buff_head * xmitq)132464ac5f59SJon Maloy static void tipc_sk_conn_proto_rcv(struct tipc_sock *tsk, struct sk_buff *skb,
1325e7eb0582SParthasarathy Bhuvaragan 				   struct sk_buff_head *inputq,
1326f1d048f2SJon Paul Maloy 				   struct sk_buff_head *xmitq)
1327ac0074eeSJon Paul Maloy {
1328bcd3ffd4SJon Paul Maloy 	struct tipc_msg *hdr = buf_msg(skb);
132964ac5f59SJon Maloy 	u32 onode = tsk_own_node(tsk);
133064ac5f59SJon Maloy 	struct sock *sk = &tsk->sk;
1331bcd3ffd4SJon Paul Maloy 	int mtyp = msg_type(hdr);
1332c0bceb97SJon Maloy 	bool was_cong;
1333bcd3ffd4SJon Paul Maloy 
1334ac0074eeSJon Paul Maloy 	/* Ignore if connection cannot be validated: */
133501e661ebSTuong Lien 	if (!tsk_peer_msg(tsk, hdr)) {
133601e661ebSTuong Lien 		trace_tipc_sk_drop_msg(sk, skb, TIPC_DUMP_NONE, "@proto_rcv!");
1337ac0074eeSJon Paul Maloy 		goto exit;
133801e661ebSTuong Lien 	}
1339ac0074eeSJon Paul Maloy 
1340c1be7756SParthasarathy Bhuvaragan 	if (unlikely(msg_errcode(hdr))) {
1341c1be7756SParthasarathy Bhuvaragan 		tipc_set_sk_state(sk, TIPC_DISCONNECTING);
1342c1be7756SParthasarathy Bhuvaragan 		tipc_node_remove_conn(sock_net(sk), tsk_peer_node(tsk),
1343c1be7756SParthasarathy Bhuvaragan 				      tsk_peer_port(tsk));
1344c1be7756SParthasarathy Bhuvaragan 		sk->sk_state_change(sk);
1345e7eb0582SParthasarathy Bhuvaragan 
1346e7eb0582SParthasarathy Bhuvaragan 		/* State change is ignored if socket already awake,
1347e7eb0582SParthasarathy Bhuvaragan 		 * - convert msg to abort msg and add to inqueue
1348e7eb0582SParthasarathy Bhuvaragan 		 */
1349e7eb0582SParthasarathy Bhuvaragan 		msg_set_user(hdr, TIPC_CRITICAL_IMPORTANCE);
1350e7eb0582SParthasarathy Bhuvaragan 		msg_set_type(hdr, TIPC_CONN_MSG);
1351e7eb0582SParthasarathy Bhuvaragan 		msg_set_size(hdr, BASIC_H_SIZE);
1352e7eb0582SParthasarathy Bhuvaragan 		msg_set_hdr_sz(hdr, BASIC_H_SIZE);
1353e7eb0582SParthasarathy Bhuvaragan 		__skb_queue_tail(inputq, skb);
1354e7eb0582SParthasarathy Bhuvaragan 		return;
1355c1be7756SParthasarathy Bhuvaragan 	}
1356c1be7756SParthasarathy Bhuvaragan 
13578ea642eeSParthasarathy Bhuvaragan 	tsk->probe_unacked = false;
1358ac0074eeSJon Paul Maloy 
1359bcd3ffd4SJon Paul Maloy 	if (mtyp == CONN_PROBE) {
1360bcd3ffd4SJon Paul Maloy 		msg_set_type(hdr, CONN_PROBE_REPLY);
1361f1d048f2SJon Paul Maloy 		if (tipc_msg_reverse(onode, &skb, TIPC_OK))
1362f1d048f2SJon Paul Maloy 			__skb_queue_tail(xmitq, skb);
13631186adf7SJon Paul Maloy 		return;
1364bcd3ffd4SJon Paul Maloy 	} else if (mtyp == CONN_ACK) {
1365c0bceb97SJon Maloy 		was_cong = tsk_conn_cong(tsk);
13660a3e060fSTuong Lien 		tipc_sk_push_backlog(tsk, msg_nagle_ack(hdr));
136710724cc7SJon Paul Maloy 		tsk->snt_unacked -= msg_conn_ack(hdr);
136810724cc7SJon Paul Maloy 		if (tsk->peer_caps & TIPC_BLOCK_FLOWCTL)
136910724cc7SJon Paul Maloy 			tsk->snd_win = msg_adv_win(hdr);
1370c0bceb97SJon Maloy 		if (was_cong && !tsk_conn_cong(tsk))
1371bcd3ffd4SJon Paul Maloy 			sk->sk_write_space(sk);
1372bcd3ffd4SJon Paul Maloy 	} else if (mtyp != CONN_PROBE_REPLY) {
1373bcd3ffd4SJon Paul Maloy 		pr_warn("Received unknown CONN_PROTO msg\n");
13741186adf7SJon Paul Maloy 	}
1375ac0074eeSJon Paul Maloy exit:
1376bcd3ffd4SJon Paul Maloy 	kfree_skb(skb);
1377ac0074eeSJon Paul Maloy }
1378ac0074eeSJon Paul Maloy 
1379e2dafe87SJon Paul Maloy /**
1380247f0f3cSYing Xue  * tipc_sendmsg - send message in connectionless manner
1381b97bf3fdSPer Liden  * @sock: socket structure
1382b97bf3fdSPer Liden  * @m: message to send
1383e2dafe87SJon Paul Maloy  * @dsz: amount of user data to be sent
1384b97bf3fdSPer Liden  *
1385b97bf3fdSPer Liden  * Message must have an destination specified explicitly.
1386b97bf3fdSPer Liden  * Used for SOCK_RDM and SOCK_DGRAM messages,
1387b97bf3fdSPer Liden  * and for 'SYN' messages on SOCK_SEQPACKET and SOCK_STREAM connections.
1388b97bf3fdSPer Liden  * (Note: 'SYN+' is prohibited on SOCK_STREAM.)
1389b97bf3fdSPer Liden  *
1390637b77fdSRandy Dunlap  * Return: the number of bytes sent on success, or errno otherwise
1391b97bf3fdSPer Liden  */
tipc_sendmsg(struct socket * sock,struct msghdr * m,size_t dsz)13921b784140SYing Xue static int tipc_sendmsg(struct socket *sock,
1393e2dafe87SJon Paul Maloy 			struct msghdr *m, size_t dsz)
1394b97bf3fdSPer Liden {
139539a0295fSYing Xue 	struct sock *sk = sock->sk;
139639a0295fSYing Xue 	int ret;
139739a0295fSYing Xue 
139839a0295fSYing Xue 	lock_sock(sk);
139939a0295fSYing Xue 	ret = __tipc_sendmsg(sock, m, dsz);
140039a0295fSYing Xue 	release_sock(sk);
140139a0295fSYing Xue 
140239a0295fSYing Xue 	return ret;
140339a0295fSYing Xue }
140439a0295fSYing Xue 
__tipc_sendmsg(struct socket * sock,struct msghdr * m,size_t dlen)1405365ad353SJon Paul Maloy static int __tipc_sendmsg(struct socket *sock, struct msghdr *m, size_t dlen)
140639a0295fSYing Xue {
14070c3141e9SAllan Stephens 	struct sock *sk = sock->sk;
1408f2f9800dSYing Xue 	struct net *net = sock_net(sk);
1409365ad353SJon Paul Maloy 	struct tipc_sock *tsk = tipc_sk(sk);
1410908148bcSJon Maloy 	struct tipc_uaddr *ua = (struct tipc_uaddr *)m->msg_name;
1411365ad353SJon Paul Maloy 	long timeout = sock_sndtimeo(sk, m->msg_flags & MSG_DONTWAIT);
1412365ad353SJon Paul Maloy 	struct list_head *clinks = &tsk->cong_links;
1413365ad353SJon Paul Maloy 	bool syn = !tipc_sk_type_connectionless(sk);
141475da2163SJon Maloy 	struct tipc_group *grp = tsk->group;
1415365ad353SJon Paul Maloy 	struct tipc_msg *hdr = &tsk->phdr;
1416908148bcSJon Maloy 	struct tipc_socket_addr skaddr;
1417365ad353SJon Paul Maloy 	struct sk_buff_head pkts;
1418908148bcSJon Maloy 	int atype, mtu, rc;
1419b97bf3fdSPer Liden 
1420365ad353SJon Paul Maloy 	if (unlikely(dlen > TIPC_MAX_USER_MSG_SIZE))
1421c29c3f70SAllan Stephens 		return -EMSGSIZE;
1422365ad353SJon Paul Maloy 
1423908148bcSJon Maloy 	if (ua) {
1424908148bcSJon Maloy 		if (!tipc_uaddr_valid(ua, m->msg_namelen))
142527bd9ec0SJon Maloy 			return -EINVAL;
1426908148bcSJon Maloy 		atype = ua->addrtype;
142727bd9ec0SJon Maloy 	}
142827bd9ec0SJon Maloy 
1429908148bcSJon Maloy 	/* If socket belongs to a communication group follow other paths */
143027bd9ec0SJon Maloy 	if (grp) {
1431908148bcSJon Maloy 		if (!ua)
143275da2163SJon Maloy 			return tipc_send_group_bcast(sock, m, dlen, timeout);
1433908148bcSJon Maloy 		if (atype == TIPC_SERVICE_ADDR)
1434ee106d7fSJon Maloy 			return tipc_send_group_anycast(sock, m, dlen, timeout);
1435908148bcSJon Maloy 		if (atype == TIPC_SOCKET_ADDR)
143627bd9ec0SJon Maloy 			return tipc_send_group_unicast(sock, m, dlen, timeout);
1437908148bcSJon Maloy 		if (atype == TIPC_SERVICE_RANGE)
14385b8dddb6SJon Maloy 			return tipc_send_group_mcast(sock, m, dlen, timeout);
143927bd9ec0SJon Maloy 		return -EINVAL;
144027bd9ec0SJon Maloy 	}
144175da2163SJon Maloy 
1442908148bcSJon Maloy 	if (!ua) {
1443908148bcSJon Maloy 		ua = (struct tipc_uaddr *)&tsk->peer;
1444908148bcSJon Maloy 		if (!syn && ua->family != AF_TIPC)
1445f2f8036eSErik Hugne 			return -EDESTADDRREQ;
1446908148bcSJon Maloy 		atype = ua->addrtype;
1447f2f8036eSErik Hugne 	}
1448365ad353SJon Paul Maloy 
1449365ad353SJon Paul Maloy 	if (unlikely(syn)) {
14500c288c86SParthasarathy Bhuvaragan 		if (sk->sk_state == TIPC_LISTEN)
145139a0295fSYing Xue 			return -EPIPE;
1452438adcafSParthasarathy Bhuvaragan 		if (sk->sk_state != TIPC_OPEN)
145339a0295fSYing Xue 			return -EISCONN;
145439a0295fSYing Xue 		if (tsk->published)
145539a0295fSYing Xue 			return -EOPNOTSUPP;
145614623e00SJon Maloy 		if (atype == TIPC_SERVICE_ADDR)
145714623e00SJon Maloy 			tsk->conn_addrtype = atype;
145825b9221bSJon Maloy 		msg_set_syn(hdr, 1);
1459b97bf3fdSPer Liden 	}
1460365ad353SJon Paul Maloy 
1461d6d86830SHaimin Zhang 	memset(&skaddr, 0, sizeof(skaddr));
1462d6d86830SHaimin Zhang 
1463908148bcSJon Maloy 	/* Determine destination */
1464908148bcSJon Maloy 	if (atype == TIPC_SERVICE_RANGE) {
1465833f8670SJon Maloy 		return tipc_sendmcast(sock, ua, m, dlen, timeout);
1466908148bcSJon Maloy 	} else if (atype == TIPC_SERVICE_ADDR) {
1467908148bcSJon Maloy 		skaddr.node = ua->lookup_node;
1468908148bcSJon Maloy 		ua->scope = tipc_node2scope(skaddr.node);
1469908148bcSJon Maloy 		if (!tipc_nametbl_lookup_anycast(net, ua, &skaddr))
147039a0295fSYing Xue 			return -EHOSTUNREACH;
1471908148bcSJon Maloy 	} else if (atype == TIPC_SOCKET_ADDR) {
1472908148bcSJon Maloy 		skaddr = ua->sk;
1473335b929bSJon Maloy 	} else {
1474335b929bSJon Maloy 		return -EINVAL;
1475b97bf3fdSPer Liden 	}
1476e2dafe87SJon Paul Maloy 
1477365ad353SJon Paul Maloy 	/* Block or return if destination link is congested */
1478a80ae530SJon Maloy 	rc = tipc_wait_for_cond(sock, &timeout,
1479908148bcSJon Maloy 				!tipc_dest_find(clinks, skaddr.node, 0));
1480365ad353SJon Paul Maloy 	if (unlikely(rc))
1481365ad353SJon Paul Maloy 		return rc;
1482365ad353SJon Paul Maloy 
1483908148bcSJon Maloy 	/* Finally build message header */
1484908148bcSJon Maloy 	msg_set_destnode(hdr, skaddr.node);
1485908148bcSJon Maloy 	msg_set_destport(hdr, skaddr.ref);
1486908148bcSJon Maloy 	if (atype == TIPC_SERVICE_ADDR) {
1487abc9b4e0STuong Lien 		msg_set_type(hdr, TIPC_NAMED_MSG);
1488abc9b4e0STuong Lien 		msg_set_hdr_sz(hdr, NAMED_H_SIZE);
1489908148bcSJon Maloy 		msg_set_nametype(hdr, ua->sa.type);
1490908148bcSJon Maloy 		msg_set_nameinst(hdr, ua->sa.instance);
1491908148bcSJon Maloy 		msg_set_lookup_scope(hdr, ua->scope);
1492b6f88d9cSJon Maloy 	} else { /* TIPC_SOCKET_ADDR */
1493abc9b4e0STuong Lien 		msg_set_type(hdr, TIPC_DIRECT_MSG);
1494abc9b4e0STuong Lien 		msg_set_lookup_scope(hdr, 0);
1495abc9b4e0STuong Lien 		msg_set_hdr_sz(hdr, BASIC_H_SIZE);
1496abc9b4e0STuong Lien 	}
1497abc9b4e0STuong Lien 
1498908148bcSJon Maloy 	/* Add message body */
1499e654f9f5SJon Maloy 	__skb_queue_head_init(&pkts);
1500908148bcSJon Maloy 	mtu = tipc_node_get_mtu(net, skaddr.node, tsk->portid, true);
1501365ad353SJon Paul Maloy 	rc = tipc_msg_build(hdr, m, 0, dlen, mtu, &pkts);
1502365ad353SJon Paul Maloy 	if (unlikely(rc != dlen))
150339a0295fSYing Xue 		return rc;
15042fe97a57STung Nguyen 	if (unlikely(syn && !tipc_msg_skb_clone(&pkts, &sk->sk_write_queue))) {
15052fe97a57STung Nguyen 		__skb_queue_purge(&pkts);
150667879274STung Nguyen 		return -ENOMEM;
15072fe97a57STung Nguyen 	}
1508e2dafe87SJon Paul Maloy 
1509908148bcSJon Maloy 	/* Send message */
151001e661ebSTuong Lien 	trace_tipc_sk_sendmsg(sk, skb_peek(&pkts), TIPC_DUMP_SK_SNDQ, " ");
1511908148bcSJon Maloy 	rc = tipc_node_xmit(net, &pkts, skaddr.node, tsk->portid);
1512365ad353SJon Paul Maloy 	if (unlikely(rc == -ELINKCONG)) {
1513908148bcSJon Maloy 		tipc_dest_push(clinks, skaddr.node, 0);
1514365ad353SJon Paul Maloy 		tsk->cong_link_cnt++;
1515365ad353SJon Paul Maloy 		rc = 0;
1516365ad353SJon Paul Maloy 	}
1517365ad353SJon Paul Maloy 
1518f8dd60deSXin Long 	if (unlikely(syn && !rc)) {
151999a20889SParthasarathy Bhuvaragan 		tipc_set_sk_state(sk, TIPC_CONNECTING);
15207387a72cSXin Long 		if (dlen && timeout) {
1521f8dd60deSXin Long 			timeout = msecs_to_jiffies(timeout);
1522f8dd60deSXin Long 			tipc_wait_for_connect(sock, &timeout);
1523f8dd60deSXin Long 		}
1524f8dd60deSXin Long 	}
1525e2dafe87SJon Paul Maloy 
1526365ad353SJon Paul Maloy 	return rc ? rc : dlen;
1527b97bf3fdSPer Liden }
1528b97bf3fdSPer Liden 
1529b97bf3fdSPer Liden /**
1530365ad353SJon Paul Maloy  * tipc_sendstream - send stream-oriented data
1531b97bf3fdSPer Liden  * @sock: socket structure
1532b97bf3fdSPer Liden  * @m: data to send
15334ccfe5e0SJon Paul Maloy  * @dsz: total length of data to be transmitted
1534b97bf3fdSPer Liden  *
1535b97bf3fdSPer Liden  * Used for SOCK_STREAM data.
1536b97bf3fdSPer Liden  *
1537637b77fdSRandy Dunlap  * Return: the number of bytes sent on success (or partial success),
15381303e8f1SAllan Stephens  * or errno if no data sent
1539b97bf3fdSPer Liden  */
tipc_sendstream(struct socket * sock,struct msghdr * m,size_t dsz)1540365ad353SJon Paul Maloy static int tipc_sendstream(struct socket *sock, struct msghdr *m, size_t dsz)
1541b97bf3fdSPer Liden {
15420c3141e9SAllan Stephens 	struct sock *sk = sock->sk;
154339a0295fSYing Xue 	int ret;
154439a0295fSYing Xue 
154539a0295fSYing Xue 	lock_sock(sk);
1546365ad353SJon Paul Maloy 	ret = __tipc_sendstream(sock, m, dsz);
154739a0295fSYing Xue 	release_sock(sk);
154839a0295fSYing Xue 
154939a0295fSYing Xue 	return ret;
155039a0295fSYing Xue }
155139a0295fSYing Xue 
__tipc_sendstream(struct socket * sock,struct msghdr * m,size_t dlen)1552365ad353SJon Paul Maloy static int __tipc_sendstream(struct socket *sock, struct msghdr *m, size_t dlen)
155339a0295fSYing Xue {
155439a0295fSYing Xue 	struct sock *sk = sock->sk;
15554ccfe5e0SJon Paul Maloy 	DECLARE_SOCKADDR(struct sockaddr_tipc *, dest, m->msg_name);
1556365ad353SJon Paul Maloy 	long timeout = sock_sndtimeo(sk, m->msg_flags & MSG_DONTWAIT);
1557c0bceb97SJon Maloy 	struct sk_buff_head *txq = &sk->sk_write_queue;
1558365ad353SJon Paul Maloy 	struct tipc_sock *tsk = tipc_sk(sk);
1559365ad353SJon Paul Maloy 	struct tipc_msg *hdr = &tsk->phdr;
1560365ad353SJon Paul Maloy 	struct net *net = sock_net(sk);
15610a3e060fSTuong Lien 	struct sk_buff *skb;
1562365ad353SJon Paul Maloy 	u32 dnode = tsk_peer_node(tsk);
1563c0bceb97SJon Maloy 	int maxnagle = tsk->maxnagle;
1564c0bceb97SJon Maloy 	int maxpkt = tsk->max_pkt;
1565365ad353SJon Paul Maloy 	int send, sent = 0;
1566c0bceb97SJon Maloy 	int blocks, rc = 0;
1567365ad353SJon Paul Maloy 
1568365ad353SJon Paul Maloy 	if (unlikely(dlen > INT_MAX))
15694ccfe5e0SJon Paul Maloy 		return -EMSGSIZE;
15704ccfe5e0SJon Paul Maloy 
1571365ad353SJon Paul Maloy 	/* Handle implicit connection setup */
1572f8dd60deSXin Long 	if (unlikely(dest && sk->sk_state == TIPC_OPEN)) {
1573365ad353SJon Paul Maloy 		rc = __tipc_sendmsg(sock, m, dlen);
157492ef12b3SParthasarathy Bhuvaragan 		if (dlen && dlen == rc) {
157592ef12b3SParthasarathy Bhuvaragan 			tsk->peer_caps = tipc_node_get_capabilities(net, dnode);
1576365ad353SJon Paul Maloy 			tsk->snt_unacked = tsk_inc(tsk, dlen + msg_hdr_sz(hdr));
157792ef12b3SParthasarathy Bhuvaragan 		}
157839a0295fSYing Xue 		return rc;
1579365ad353SJon Paul Maloy 	}
1580f214fc40SParthasarathy Bhuvaragan 
15814ccfe5e0SJon Paul Maloy 	do {
1582365ad353SJon Paul Maloy 		rc = tipc_wait_for_cond(sock, &timeout,
1583365ad353SJon Paul Maloy 					(!tsk->cong_link_cnt &&
15848c44e1afSJon Paul Maloy 					 !tsk_conn_cong(tsk) &&
15858c44e1afSJon Paul Maloy 					 tipc_sk_connected(sk)));
1586365ad353SJon Paul Maloy 		if (unlikely(rc))
1587365ad353SJon Paul Maloy 			break;
1588365ad353SJon Paul Maloy 		send = min_t(size_t, dlen - sent, TIPC_MAX_USER_MSG_SIZE);
1589c0bceb97SJon Maloy 		blocks = tsk->snd_backlog;
1590c9aa81faSTuong Lien 		if (tsk->oneway++ >= tsk->nagle_start && maxnagle &&
1591c9aa81faSTuong Lien 		    send <= maxnagle) {
1592c0bceb97SJon Maloy 			rc = tipc_msg_append(hdr, m, send, maxnagle, txq);
1593c0bceb97SJon Maloy 			if (unlikely(rc < 0))
1594c0bceb97SJon Maloy 				break;
1595c0bceb97SJon Maloy 			blocks += rc;
15960a3e060fSTuong Lien 			tsk->msg_acc++;
1597c0bceb97SJon Maloy 			if (blocks <= 64 && tsk->expect_ack) {
1598c0bceb97SJon Maloy 				tsk->snd_backlog = blocks;
1599c0bceb97SJon Maloy 				sent += send;
1600c0bceb97SJon Maloy 				break;
16010a3e060fSTuong Lien 			} else if (blocks > 64) {
16020a3e060fSTuong Lien 				tsk->pkt_cnt += skb_queue_len(txq);
16030a3e060fSTuong Lien 			} else {
16040a3e060fSTuong Lien 				skb = skb_peek_tail(txq);
16054c21daaeSYueHaibing 				if (skb) {
16060a3e060fSTuong Lien 					msg_set_ack_required(buf_msg(skb));
1607c0bceb97SJon Maloy 					tsk->expect_ack = true;
16084c21daaeSYueHaibing 				} else {
16094c21daaeSYueHaibing 					tsk->expect_ack = false;
16104c21daaeSYueHaibing 				}
16110a3e060fSTuong Lien 				tsk->msg_acc = 0;
16120a3e060fSTuong Lien 				tsk->pkt_cnt = 0;
16130a3e060fSTuong Lien 			}
1614c0bceb97SJon Maloy 		} else {
1615c0bceb97SJon Maloy 			rc = tipc_msg_build(hdr, m, sent, send, maxpkt, txq);
1616365ad353SJon Paul Maloy 			if (unlikely(rc != send))
1617365ad353SJon Paul Maloy 				break;
1618c0bceb97SJon Maloy 			blocks += tsk_inc(tsk, send + MIN_H_SIZE);
1619c0bceb97SJon Maloy 		}
1620c0bceb97SJon Maloy 		trace_tipc_sk_sendstream(sk, skb_peek(txq),
162101e661ebSTuong Lien 					 TIPC_DUMP_SK_SNDQ, " ");
1622c0bceb97SJon Maloy 		rc = tipc_node_xmit(net, txq, dnode, tsk->portid);
1623365ad353SJon Paul Maloy 		if (unlikely(rc == -ELINKCONG)) {
1624365ad353SJon Paul Maloy 			tsk->cong_link_cnt = 1;
1625365ad353SJon Paul Maloy 			rc = 0;
1626365ad353SJon Paul Maloy 		}
1627365ad353SJon Paul Maloy 		if (likely(!rc)) {
1628c0bceb97SJon Maloy 			tsk->snt_unacked += blocks;
1629c0bceb97SJon Maloy 			tsk->snd_backlog = 0;
1630365ad353SJon Paul Maloy 			sent += send;
1631365ad353SJon Paul Maloy 		}
1632365ad353SJon Paul Maloy 	} while (sent < dlen && !rc);
1633365ad353SJon Paul Maloy 
16343364d61cSParthasarathy Bhuvaragan 	return sent ? sent : rc;
16354ccfe5e0SJon Paul Maloy }
16364ccfe5e0SJon Paul Maloy 
16374ccfe5e0SJon Paul Maloy /**
16384ccfe5e0SJon Paul Maloy  * tipc_send_packet - send a connection-oriented message
16394ccfe5e0SJon Paul Maloy  * @sock: socket structure
16404ccfe5e0SJon Paul Maloy  * @m: message to send
16414ccfe5e0SJon Paul Maloy  * @dsz: length of data to be transmitted
16424ccfe5e0SJon Paul Maloy  *
16434ccfe5e0SJon Paul Maloy  * Used for SOCK_SEQPACKET messages.
16444ccfe5e0SJon Paul Maloy  *
1645637b77fdSRandy Dunlap  * Return: the number of bytes sent on success, or errno otherwise
16464ccfe5e0SJon Paul Maloy  */
tipc_send_packet(struct socket * sock,struct msghdr * m,size_t dsz)16471b784140SYing Xue static int tipc_send_packet(struct socket *sock, struct msghdr *m, size_t dsz)
16484ccfe5e0SJon Paul Maloy {
16494ccfe5e0SJon Paul Maloy 	if (dsz > TIPC_MAX_USER_MSG_SIZE)
16504ccfe5e0SJon Paul Maloy 		return -EMSGSIZE;
16514ccfe5e0SJon Paul Maloy 
1652365ad353SJon Paul Maloy 	return tipc_sendstream(sock, m, dsz);
1653b97bf3fdSPer Liden }
1654b97bf3fdSPer Liden 
1655dadebc00SJon Paul Maloy /* tipc_sk_finish_conn - complete the setup of a connection
1656b97bf3fdSPer Liden  */
tipc_sk_finish_conn(struct tipc_sock * tsk,u32 peer_port,u32 peer_node)1657301bae56SJon Paul Maloy static void tipc_sk_finish_conn(struct tipc_sock *tsk, u32 peer_port,
1658dadebc00SJon Paul Maloy 				u32 peer_node)
1659b97bf3fdSPer Liden {
16603721e9c7SYing Xue 	struct sock *sk = &tsk->sk;
16613721e9c7SYing Xue 	struct net *net = sock_net(sk);
1662301bae56SJon Paul Maloy 	struct tipc_msg *msg = &tsk->phdr;
1663b97bf3fdSPer Liden 
166425b9221bSJon Maloy 	msg_set_syn(msg, 0);
1665dadebc00SJon Paul Maloy 	msg_set_destnode(msg, peer_node);
1666dadebc00SJon Paul Maloy 	msg_set_destport(msg, peer_port);
1667dadebc00SJon Paul Maloy 	msg_set_type(msg, TIPC_CONN_MSG);
1668dadebc00SJon Paul Maloy 	msg_set_lookup_scope(msg, 0);
1669dadebc00SJon Paul Maloy 	msg_set_hdr_sz(msg, SHORT_H_SIZE);
1670f9fef18cSJon Paul Maloy 
16710d5fcebfSJon Maloy 	sk_reset_timer(sk, &sk->sk_timer, jiffies + CONN_PROBING_INTV);
16728ea642eeSParthasarathy Bhuvaragan 	tipc_set_sk_state(sk, TIPC_ESTABLISHED);
1673f2f9800dSYing Xue 	tipc_node_add_conn(net, peer_node, tsk->portid, peer_port);
1674f73b1281SHoang Le 	tsk->max_pkt = tipc_node_get_mtu(net, peer_node, tsk->portid, true);
167560020e18SJon Paul Maloy 	tsk->peer_caps = tipc_node_get_capabilities(net, peer_node);
1676c0bceb97SJon Maloy 	tsk_set_nagle(tsk);
167767879274STung Nguyen 	__skb_queue_purge(&sk->sk_write_queue);
167810724cc7SJon Paul Maloy 	if (tsk->peer_caps & TIPC_BLOCK_FLOWCTL)
167910724cc7SJon Paul Maloy 		return;
168010724cc7SJon Paul Maloy 
168110724cc7SJon Paul Maloy 	/* Fall back to message based flow control */
168210724cc7SJon Paul Maloy 	tsk->rcv_win = FLOWCTL_MSG_WIN;
168310724cc7SJon Paul Maloy 	tsk->snd_win = FLOWCTL_MSG_WIN;
1684b97bf3fdSPer Liden }
1685b97bf3fdSPer Liden 
1686b97bf3fdSPer Liden /**
168731c82a2dSJon Maloy  * tipc_sk_set_orig_addr - capture sender's address for received message
1688b97bf3fdSPer Liden  * @m: descriptor for message info
1689d8141208SAndrew Lunn  * @skb: received message
1690b97bf3fdSPer Liden  *
1691b97bf3fdSPer Liden  * Note: Address is not captured if not requested by receiver.
1692b97bf3fdSPer Liden  */
tipc_sk_set_orig_addr(struct msghdr * m,struct sk_buff * skb)169331c82a2dSJon Maloy static void tipc_sk_set_orig_addr(struct msghdr *m, struct sk_buff *skb)
1694b97bf3fdSPer Liden {
169531c82a2dSJon Maloy 	DECLARE_SOCKADDR(struct sockaddr_pair *, srcaddr, m->msg_name);
169631c82a2dSJon Maloy 	struct tipc_msg *hdr = buf_msg(skb);
1697b97bf3fdSPer Liden 
169831c82a2dSJon Maloy 	if (!srcaddr)
169931c82a2dSJon Maloy 		return;
170031c82a2dSJon Maloy 
170131c82a2dSJon Maloy 	srcaddr->sock.family = AF_TIPC;
1702b6f88d9cSJon Maloy 	srcaddr->sock.addrtype = TIPC_SOCKET_ADDR;
170309c8b971SEric Dumazet 	srcaddr->sock.scope = 0;
170431c82a2dSJon Maloy 	srcaddr->sock.addr.id.ref = msg_origport(hdr);
170531c82a2dSJon Maloy 	srcaddr->sock.addr.id.node = msg_orignode(hdr);
170631c82a2dSJon Maloy 	srcaddr->sock.addr.name.domain = 0;
1707b97bf3fdSPer Liden 	m->msg_namelen = sizeof(struct sockaddr_tipc);
170831c82a2dSJon Maloy 
170931c82a2dSJon Maloy 	if (!msg_in_group(hdr))
171031c82a2dSJon Maloy 		return;
171131c82a2dSJon Maloy 
171231c82a2dSJon Maloy 	/* Group message users may also want to know sending member's id */
171331c82a2dSJon Maloy 	srcaddr->member.family = AF_TIPC;
1714b6f88d9cSJon Maloy 	srcaddr->member.addrtype = TIPC_SERVICE_ADDR;
171509c8b971SEric Dumazet 	srcaddr->member.scope = 0;
171631c82a2dSJon Maloy 	srcaddr->member.addr.name.name.type = msg_nametype(hdr);
171731c82a2dSJon Maloy 	srcaddr->member.addr.name.name.instance = TIPC_SKB_CB(skb)->orig_member;
171831c82a2dSJon Maloy 	srcaddr->member.addr.name.domain = 0;
171931c82a2dSJon Maloy 	m->msg_namelen = sizeof(*srcaddr);
1720b97bf3fdSPer Liden }
1721b97bf3fdSPer Liden 
1722b97bf3fdSPer Liden /**
1723301bae56SJon Paul Maloy  * tipc_sk_anc_data_recv - optionally capture ancillary data for received message
1724b97bf3fdSPer Liden  * @m: descriptor for message info
17251c1274a5SJon Maloy  * @skb: received message buffer
1726301bae56SJon Paul Maloy  * @tsk: TIPC port associated with message
1727b97bf3fdSPer Liden  *
1728b97bf3fdSPer Liden  * Note: Ancillary data is not captured if not requested by receiver.
1729b97bf3fdSPer Liden  *
1730637b77fdSRandy Dunlap  * Return: 0 if successful, otherwise errno
1731b97bf3fdSPer Liden  */
tipc_sk_anc_data_recv(struct msghdr * m,struct sk_buff * skb,struct tipc_sock * tsk)17321c1274a5SJon Maloy static int tipc_sk_anc_data_recv(struct msghdr *m, struct sk_buff *skb,
1733301bae56SJon Paul Maloy 				 struct tipc_sock *tsk)
1734b97bf3fdSPer Liden {
173562633c2fSJon Maloy 	struct tipc_msg *hdr;
173662633c2fSJon Maloy 	u32 data[3] = {0,};
173762633c2fSJon Maloy 	bool has_addr;
173862633c2fSJon Maloy 	int dlen, rc;
1739b97bf3fdSPer Liden 
1740b97bf3fdSPer Liden 	if (likely(m->msg_controllen == 0))
1741b97bf3fdSPer Liden 		return 0;
1742b97bf3fdSPer Liden 
174362633c2fSJon Maloy 	hdr = buf_msg(skb);
174462633c2fSJon Maloy 	dlen = msg_data_sz(hdr);
174562633c2fSJon Maloy 
174662633c2fSJon Maloy 	/* Capture errored message object, if any */
174762633c2fSJon Maloy 	if (msg_errcode(hdr)) {
17481c1274a5SJon Maloy 		if (skb_linearize(skb))
17491c1274a5SJon Maloy 			return -ENOMEM;
175062633c2fSJon Maloy 		hdr = buf_msg(skb);
175162633c2fSJon Maloy 		data[0] = msg_errcode(hdr);
175262633c2fSJon Maloy 		data[1] = dlen;
175362633c2fSJon Maloy 		rc = put_cmsg(m, SOL_TIPC, TIPC_ERRINFO, 8, data);
175462633c2fSJon Maloy 		if (rc || !dlen)
175562633c2fSJon Maloy 			return rc;
175662633c2fSJon Maloy 		rc = put_cmsg(m, SOL_TIPC, TIPC_RETDATA, dlen, msg_data(hdr));
175762633c2fSJon Maloy 		if (rc)
175862633c2fSJon Maloy 			return rc;
17592db9983aSAllan Stephens 	}
1760b97bf3fdSPer Liden 
176162633c2fSJon Maloy 	/* Capture TIPC_SERVICE_ADDR/RANGE destination address, if any */
176262633c2fSJon Maloy 	switch (msg_type(hdr)) {
1763b97bf3fdSPer Liden 	case TIPC_NAMED_MSG:
176462633c2fSJon Maloy 		has_addr = true;
176562633c2fSJon Maloy 		data[0] = msg_nametype(hdr);
176662633c2fSJon Maloy 		data[1] = msg_namelower(hdr);
176762633c2fSJon Maloy 		data[2] = data[1];
1768b97bf3fdSPer Liden 		break;
1769b97bf3fdSPer Liden 	case TIPC_MCAST_MSG:
177062633c2fSJon Maloy 		has_addr = true;
177162633c2fSJon Maloy 		data[0] = msg_nametype(hdr);
177262633c2fSJon Maloy 		data[1] = msg_namelower(hdr);
177362633c2fSJon Maloy 		data[2] = msg_nameupper(hdr);
1774b97bf3fdSPer Liden 		break;
1775b97bf3fdSPer Liden 	case TIPC_CONN_MSG:
177662633c2fSJon Maloy 		has_addr = !!tsk->conn_addrtype;
177762633c2fSJon Maloy 		data[0] = msg_nametype(&tsk->phdr);
177862633c2fSJon Maloy 		data[1] = msg_nameinst(&tsk->phdr);
177962633c2fSJon Maloy 		data[2] = data[1];
1780b97bf3fdSPer Liden 		break;
1781b97bf3fdSPer Liden 	default:
178262633c2fSJon Maloy 		has_addr = false;
1783b97bf3fdSPer Liden 	}
178462633c2fSJon Maloy 	if (!has_addr)
1785b97bf3fdSPer Liden 		return 0;
178662633c2fSJon Maloy 	return put_cmsg(m, SOL_TIPC, TIPC_DESTNAME, 12, data);
1787b97bf3fdSPer Liden }
1788b97bf3fdSPer Liden 
tipc_sk_build_ack(struct tipc_sock * tsk)1789c7268589STuong Lien static struct sk_buff *tipc_sk_build_ack(struct tipc_sock *tsk)
1790739f5e4eSJon Paul Maloy {
1791d6fb7e9cSParthasarathy Bhuvaragan 	struct sock *sk = &tsk->sk;
1792a6ca1094SYing Xue 	struct sk_buff *skb = NULL;
1793739f5e4eSJon Paul Maloy 	struct tipc_msg *msg;
1794301bae56SJon Paul Maloy 	u32 peer_port = tsk_peer_port(tsk);
1795301bae56SJon Paul Maloy 	u32 dnode = tsk_peer_node(tsk);
1796739f5e4eSJon Paul Maloy 
1797d6fb7e9cSParthasarathy Bhuvaragan 	if (!tipc_sk_connected(sk))
1798c7268589STuong Lien 		return NULL;
1799c5898636SJon Paul Maloy 	skb = tipc_msg_create(CONN_MANAGER, CONN_ACK, INT_H_SIZE, 0,
1800c5898636SJon Paul Maloy 			      dnode, tsk_own_node(tsk), peer_port,
1801c5898636SJon Paul Maloy 			      tsk->portid, TIPC_OK);
1802a6ca1094SYing Xue 	if (!skb)
1803c7268589STuong Lien 		return NULL;
1804a6ca1094SYing Xue 	msg = buf_msg(skb);
180510724cc7SJon Paul Maloy 	msg_set_conn_ack(msg, tsk->rcv_unacked);
180610724cc7SJon Paul Maloy 	tsk->rcv_unacked = 0;
180710724cc7SJon Paul Maloy 
180810724cc7SJon Paul Maloy 	/* Adjust to and advertize the correct window limit */
180910724cc7SJon Paul Maloy 	if (tsk->peer_caps & TIPC_BLOCK_FLOWCTL) {
181010724cc7SJon Paul Maloy 		tsk->rcv_win = tsk_adv_blocks(tsk->sk.sk_rcvbuf);
181110724cc7SJon Paul Maloy 		msg_set_adv_win(msg, tsk->rcv_win);
181210724cc7SJon Paul Maloy 	}
1813c7268589STuong Lien 	return skb;
1814c7268589STuong Lien }
1815c7268589STuong Lien 
tipc_sk_send_ack(struct tipc_sock * tsk)1816c7268589STuong Lien static void tipc_sk_send_ack(struct tipc_sock *tsk)
1817c7268589STuong Lien {
1818c7268589STuong Lien 	struct sk_buff *skb;
1819c7268589STuong Lien 
1820c7268589STuong Lien 	skb = tipc_sk_build_ack(tsk);
1821c7268589STuong Lien 	if (!skb)
1822c7268589STuong Lien 		return;
1823c7268589STuong Lien 
1824c7268589STuong Lien 	tipc_node_xmit_skb(sock_net(&tsk->sk), skb, tsk_peer_node(tsk),
1825c7268589STuong Lien 			   msg_link_selector(buf_msg(skb)));
1826739f5e4eSJon Paul Maloy }
1827739f5e4eSJon Paul Maloy 
tipc_wait_for_rcvmsg(struct socket * sock,long * timeop)182885d3fc94SArnaldo Carvalho de Melo static int tipc_wait_for_rcvmsg(struct socket *sock, long *timeop)
18299bbb4eccSYing Xue {
18309bbb4eccSYing Xue 	struct sock *sk = sock->sk;
183148766a58STung Nguyen 	DEFINE_WAIT_FUNC(wait, woken_wake_function);
183285d3fc94SArnaldo Carvalho de Melo 	long timeo = *timeop;
18334e0df495SParthasarathy Bhuvaragan 	int err = sock_error(sk);
18344e0df495SParthasarathy Bhuvaragan 
18354e0df495SParthasarathy Bhuvaragan 	if (err)
18364e0df495SParthasarathy Bhuvaragan 		return err;
18379bbb4eccSYing Xue 
18389bbb4eccSYing Xue 	for (;;) {
1839fe8e4649SYing Xue 		if (timeo && skb_queue_empty(&sk->sk_receive_queue)) {
18406f00089cSParthasarathy Bhuvaragan 			if (sk->sk_shutdown & RCV_SHUTDOWN) {
18419bbb4eccSYing Xue 				err = -ENOTCONN;
18429bbb4eccSYing Xue 				break;
18439bbb4eccSYing Xue 			}
184448766a58STung Nguyen 			add_wait_queue(sk_sleep(sk), &wait);
18459bbb4eccSYing Xue 			release_sock(sk);
184648766a58STung Nguyen 			timeo = wait_woken(&wait, TASK_INTERRUPTIBLE, timeo);
184748766a58STung Nguyen 			sched_annotate_sleep();
18489bbb4eccSYing Xue 			lock_sock(sk);
184948766a58STung Nguyen 			remove_wait_queue(sk_sleep(sk), &wait);
18509bbb4eccSYing Xue 		}
18519bbb4eccSYing Xue 		err = 0;
18529bbb4eccSYing Xue 		if (!skb_queue_empty(&sk->sk_receive_queue))
18539bbb4eccSYing Xue 			break;
18549bbb4eccSYing Xue 		err = -EAGAIN;
18559bbb4eccSYing Xue 		if (!timeo)
18569bbb4eccSYing Xue 			break;
1857143fe22fSErik Hugne 		err = sock_intr_errno(timeo);
1858143fe22fSErik Hugne 		if (signal_pending(current))
1859143fe22fSErik Hugne 			break;
18604e0df495SParthasarathy Bhuvaragan 
18614e0df495SParthasarathy Bhuvaragan 		err = sock_error(sk);
18624e0df495SParthasarathy Bhuvaragan 		if (err)
18634e0df495SParthasarathy Bhuvaragan 			break;
18649bbb4eccSYing Xue 	}
186585d3fc94SArnaldo Carvalho de Melo 	*timeop = timeo;
18669bbb4eccSYing Xue 	return err;
18679bbb4eccSYing Xue }
18689bbb4eccSYing Xue 
1869b97bf3fdSPer Liden /**
1870247f0f3cSYing Xue  * tipc_recvmsg - receive packet-oriented message
1871f172f4b8SRandy Dunlap  * @sock: network socket
1872b97bf3fdSPer Liden  * @m: descriptor for message info
1873e9f8b101SJon Paul Maloy  * @buflen: length of user buffer area
1874b97bf3fdSPer Liden  * @flags: receive flags
1875b97bf3fdSPer Liden  *
1876b97bf3fdSPer Liden  * Used for SOCK_DGRAM, SOCK_RDM, and SOCK_SEQPACKET messages.
1877b97bf3fdSPer Liden  * If the complete message doesn't fit in user area, truncate it.
1878b97bf3fdSPer Liden  *
1879637b77fdSRandy Dunlap  * Return: size of returned message data, errno otherwise
1880b97bf3fdSPer Liden  */
tipc_recvmsg(struct socket * sock,struct msghdr * m,size_t buflen,int flags)1881e9f8b101SJon Paul Maloy static int tipc_recvmsg(struct socket *sock, struct msghdr *m,
1882e9f8b101SJon Paul Maloy 			size_t buflen,	int flags)
1883b97bf3fdSPer Liden {
18840c3141e9SAllan Stephens 	struct sock *sk = sock->sk;
1885e9f8b101SJon Paul Maloy 	bool connected = !tipc_sk_type_connectionless(sk);
1886ae236fb2SJon Maloy 	struct tipc_sock *tsk = tipc_sk(sk);
1887e9f8b101SJon Paul Maloy 	int rc, err, hlen, dlen, copy;
1888f4919ff5SXin Long 	struct tipc_skb_cb *skb_cb;
1889b7d42635SJon Maloy 	struct sk_buff_head xmitq;
1890ae236fb2SJon Maloy 	struct tipc_msg *hdr;
1891ae236fb2SJon Maloy 	struct sk_buff *skb;
1892ae236fb2SJon Maloy 	bool grp_evt;
1893e9f8b101SJon Paul Maloy 	long timeout;
1894b97bf3fdSPer Liden 
18950c3141e9SAllan Stephens 	/* Catch invalid receive requests */
1896e9f8b101SJon Paul Maloy 	if (unlikely(!buflen))
1897b97bf3fdSPer Liden 		return -EINVAL;
1898b97bf3fdSPer Liden 
18990c3141e9SAllan Stephens 	lock_sock(sk);
1900e9f8b101SJon Paul Maloy 	if (unlikely(connected && sk->sk_state == TIPC_OPEN)) {
1901e9f8b101SJon Paul Maloy 		rc = -ENOTCONN;
1902b97bf3fdSPer Liden 		goto exit;
1903b97bf3fdSPer Liden 	}
1904e9f8b101SJon Paul Maloy 	timeout = sock_rcvtimeo(sk, flags & MSG_DONTWAIT);
1905b97bf3fdSPer Liden 
1906b7d42635SJon Maloy 	/* Step rcv queue to first msg with data or error; wait if necessary */
1907e9f8b101SJon Paul Maloy 	do {
1908e9f8b101SJon Paul Maloy 		rc = tipc_wait_for_rcvmsg(sock, &timeout);
1909e9f8b101SJon Paul Maloy 		if (unlikely(rc))
19100c3141e9SAllan Stephens 			goto exit;
1911e9f8b101SJon Paul Maloy 		skb = skb_peek(&sk->sk_receive_queue);
1912f4919ff5SXin Long 		skb_cb = TIPC_SKB_CB(skb);
1913e9f8b101SJon Paul Maloy 		hdr = buf_msg(skb);
1914e9f8b101SJon Paul Maloy 		dlen = msg_data_sz(hdr);
1915e9f8b101SJon Paul Maloy 		hlen = msg_hdr_sz(hdr);
1916e9f8b101SJon Paul Maloy 		err = msg_errcode(hdr);
1917ae236fb2SJon Maloy 		grp_evt = msg_is_grp_evt(hdr);
1918e9f8b101SJon Paul Maloy 		if (likely(dlen || err))
1919e9f8b101SJon Paul Maloy 			break;
19202e84c60bSJon Paul Maloy 		tsk_advance_rx_queue(sk);
1921e9f8b101SJon Paul Maloy 	} while (1);
1922b97bf3fdSPer Liden 
1923e9f8b101SJon Paul Maloy 	/* Collect msg meta data, including error code and rejected data */
192431c82a2dSJon Maloy 	tipc_sk_set_orig_addr(m, skb);
19251c1274a5SJon Maloy 	rc = tipc_sk_anc_data_recv(m, skb, tsk);
1926e9f8b101SJon Paul Maloy 	if (unlikely(rc))
1927b97bf3fdSPer Liden 		goto exit;
19281c1274a5SJon Maloy 	hdr = buf_msg(skb);
1929b97bf3fdSPer Liden 
1930e9f8b101SJon Paul Maloy 	/* Capture data if non-error msg, otherwise just set return value */
1931e9f8b101SJon Paul Maloy 	if (likely(!err)) {
1932f4919ff5SXin Long 		int offset = skb_cb->bytes_read;
1933f4919ff5SXin Long 
1934f4919ff5SXin Long 		copy = min_t(int, dlen - offset, buflen);
1935f4919ff5SXin Long 		rc = skb_copy_datagram_msg(skb, hlen + offset, m, copy);
1936f4919ff5SXin Long 		if (unlikely(rc))
1937f4919ff5SXin Long 			goto exit;
1938f4919ff5SXin Long 		if (unlikely(offset + copy < dlen)) {
1939f4919ff5SXin Long 			if (flags & MSG_EOR) {
1940f4919ff5SXin Long 				if (!(flags & MSG_PEEK))
1941f4919ff5SXin Long 					skb_cb->bytes_read = offset + copy;
1942f4919ff5SXin Long 			} else {
1943b97bf3fdSPer Liden 				m->msg_flags |= MSG_TRUNC;
1944f4919ff5SXin Long 				skb_cb->bytes_read = 0;
1945f4919ff5SXin Long 			}
1946f4919ff5SXin Long 		} else {
1947f4919ff5SXin Long 			if (flags & MSG_EOR)
1948f4919ff5SXin Long 				m->msg_flags |= MSG_EOR;
1949f4919ff5SXin Long 			skb_cb->bytes_read = 0;
1950f4919ff5SXin Long 		}
1951b97bf3fdSPer Liden 	} else {
1952e9f8b101SJon Paul Maloy 		copy = 0;
1953e9f8b101SJon Paul Maloy 		rc = 0;
1954f4919ff5SXin Long 		if (err != TIPC_CONN_SHUTDOWN && connected && !m->msg_control) {
1955e9f8b101SJon Paul Maloy 			rc = -ECONNRESET;
1956e9f8b101SJon Paul Maloy 			goto exit;
1957f4919ff5SXin Long 		}
1958f4919ff5SXin Long 	}
1959b97bf3fdSPer Liden 
1960ae236fb2SJon Maloy 	/* Mark message as group event if applicable */
1961ae236fb2SJon Maloy 	if (unlikely(grp_evt)) {
1962ae236fb2SJon Maloy 		if (msg_grp_evt(hdr) == TIPC_WITHDRAWN)
1963ae236fb2SJon Maloy 			m->msg_flags |= MSG_EOR;
1964ae236fb2SJon Maloy 		m->msg_flags |= MSG_OOB;
1965ae236fb2SJon Maloy 		copy = 0;
1966ae236fb2SJon Maloy 	}
1967ae236fb2SJon Maloy 
1968e9f8b101SJon Paul Maloy 	/* Caption of data or error code/rejected data was successful */
196910724cc7SJon Paul Maloy 	if (unlikely(flags & MSG_PEEK))
197010724cc7SJon Paul Maloy 		goto exit;
197110724cc7SJon Paul Maloy 
1972b7d42635SJon Maloy 	/* Send group flow control advertisement when applicable */
1973b7d42635SJon Maloy 	if (tsk->group && msg_in_group(hdr) && !grp_evt) {
1974e654f9f5SJon Maloy 		__skb_queue_head_init(&xmitq);
1975b7d42635SJon Maloy 		tipc_group_update_rcv_win(tsk->group, tsk_blocks(hlen + dlen),
1976b7d42635SJon Maloy 					  msg_orignode(hdr), msg_origport(hdr),
1977b7d42635SJon Maloy 					  &xmitq);
1978b7d42635SJon Maloy 		tipc_node_distr_xmit(sock_net(sk), &xmitq);
1979b7d42635SJon Maloy 	}
1980b7d42635SJon Maloy 
1981cc19862fSXin Long 	if (skb_cb->bytes_read)
1982cc19862fSXin Long 		goto exit;
1983cc19862fSXin Long 
19842e84c60bSJon Paul Maloy 	tsk_advance_rx_queue(sk);
1985ae236fb2SJon Maloy 
1986cc19862fSXin Long 	if (likely(!connected))
1987e9f8b101SJon Paul Maloy 		goto exit;
1988e9f8b101SJon Paul Maloy 
1989b7d42635SJon Maloy 	/* Send connection flow control advertisement when applicable */
1990e9f8b101SJon Paul Maloy 	tsk->rcv_unacked += tsk_inc(tsk, hlen + dlen);
1991e9f8b101SJon Paul Maloy 	if (tsk->rcv_unacked >= tsk->rcv_win / TIPC_ACK_RATE)
1992e9f8b101SJon Paul Maloy 		tipc_sk_send_ack(tsk);
1993b97bf3fdSPer Liden exit:
19940c3141e9SAllan Stephens 	release_sock(sk);
1995e9f8b101SJon Paul Maloy 	return rc ? rc : copy;
1996b97bf3fdSPer Liden }
1997b97bf3fdSPer Liden 
1998b97bf3fdSPer Liden /**
1999ec8a09fbSJon Paul Maloy  * tipc_recvstream - receive stream-oriented data
2000f172f4b8SRandy Dunlap  * @sock: network socket
2001b97bf3fdSPer Liden  * @m: descriptor for message info
2002ec8a09fbSJon Paul Maloy  * @buflen: total size of user buffer area
2003b97bf3fdSPer Liden  * @flags: receive flags
2004b97bf3fdSPer Liden  *
2005b97bf3fdSPer Liden  * Used for SOCK_STREAM messages only.  If not enough data is available
2006b97bf3fdSPer Liden  * will optionally wait for more; never truncates data.
2007b97bf3fdSPer Liden  *
2008637b77fdSRandy Dunlap  * Return: size of returned message data, errno otherwise
2009b97bf3fdSPer Liden  */
tipc_recvstream(struct socket * sock,struct msghdr * m,size_t buflen,int flags)2010ec8a09fbSJon Paul Maloy static int tipc_recvstream(struct socket *sock, struct msghdr *m,
2011ec8a09fbSJon Paul Maloy 			   size_t buflen, int flags)
2012b97bf3fdSPer Liden {
20130c3141e9SAllan Stephens 	struct sock *sk = sock->sk;
201458ed9442SJon Paul Maloy 	struct tipc_sock *tsk = tipc_sk(sk);
2015ec8a09fbSJon Paul Maloy 	struct sk_buff *skb;
2016ec8a09fbSJon Paul Maloy 	struct tipc_msg *hdr;
2017ec8a09fbSJon Paul Maloy 	struct tipc_skb_cb *skb_cb;
2018ec8a09fbSJon Paul Maloy 	bool peek = flags & MSG_PEEK;
2019ec8a09fbSJon Paul Maloy 	int offset, required, copy, copied = 0;
2020ec8a09fbSJon Paul Maloy 	int hlen, dlen, err, rc;
2021ec8a09fbSJon Paul Maloy 	long timeout;
2022b97bf3fdSPer Liden 
2023b97bf3fdSPer Liden 	/* Catch invalid receive attempts */
2024ec8a09fbSJon Paul Maloy 	if (unlikely(!buflen))
2025b97bf3fdSPer Liden 		return -EINVAL;
2026b97bf3fdSPer Liden 
20270c3141e9SAllan Stephens 	lock_sock(sk);
2028b97bf3fdSPer Liden 
2029438adcafSParthasarathy Bhuvaragan 	if (unlikely(sk->sk_state == TIPC_OPEN)) {
2030ec8a09fbSJon Paul Maloy 		rc = -ENOTCONN;
2031b97bf3fdSPer Liden 		goto exit;
2032b97bf3fdSPer Liden 	}
2033ec8a09fbSJon Paul Maloy 	required = sock_rcvlowat(sk, flags & MSG_WAITALL, buflen);
2034ec8a09fbSJon Paul Maloy 	timeout = sock_rcvtimeo(sk, flags & MSG_DONTWAIT);
2035b97bf3fdSPer Liden 
2036ec8a09fbSJon Paul Maloy 	do {
2037ec8a09fbSJon Paul Maloy 		/* Look at first msg in receive queue; wait if necessary */
2038ec8a09fbSJon Paul Maloy 		rc = tipc_wait_for_rcvmsg(sock, &timeout);
2039ec8a09fbSJon Paul Maloy 		if (unlikely(rc))
2040ec8a09fbSJon Paul Maloy 			break;
2041ec8a09fbSJon Paul Maloy 		skb = skb_peek(&sk->sk_receive_queue);
2042ec8a09fbSJon Paul Maloy 		skb_cb = TIPC_SKB_CB(skb);
2043ec8a09fbSJon Paul Maloy 		hdr = buf_msg(skb);
2044ec8a09fbSJon Paul Maloy 		dlen = msg_data_sz(hdr);
2045ec8a09fbSJon Paul Maloy 		hlen = msg_hdr_sz(hdr);
2046ec8a09fbSJon Paul Maloy 		err = msg_errcode(hdr);
2047617d3c7aSPaul Gortmaker 
2048ec8a09fbSJon Paul Maloy 		/* Discard any empty non-errored (SYN-) message */
2049ec8a09fbSJon Paul Maloy 		if (unlikely(!dlen && !err)) {
20502e84c60bSJon Paul Maloy 			tsk_advance_rx_queue(sk);
2051ec8a09fbSJon Paul Maloy 			continue;
2052b97bf3fdSPer Liden 		}
2053b97bf3fdSPer Liden 
2054ec8a09fbSJon Paul Maloy 		/* Collect msg meta data, incl. error code and rejected data */
2055ec8a09fbSJon Paul Maloy 		if (!copied) {
205631c82a2dSJon Maloy 			tipc_sk_set_orig_addr(m, skb);
20571c1274a5SJon Maloy 			rc = tipc_sk_anc_data_recv(m, skb, tsk);
2058ec8a09fbSJon Paul Maloy 			if (rc)
2059ec8a09fbSJon Paul Maloy 				break;
20601c1274a5SJon Maloy 			hdr = buf_msg(skb);
2061b97bf3fdSPer Liden 		}
2062b97bf3fdSPer Liden 
2063ec8a09fbSJon Paul Maloy 		/* Copy data if msg ok, otherwise return error/partial data */
2064ec8a09fbSJon Paul Maloy 		if (likely(!err)) {
2065ec8a09fbSJon Paul Maloy 			offset = skb_cb->bytes_read;
2066ec8a09fbSJon Paul Maloy 			copy = min_t(int, dlen - offset, buflen - copied);
2067ec8a09fbSJon Paul Maloy 			rc = skb_copy_datagram_msg(skb, hlen + offset, m, copy);
2068ec8a09fbSJon Paul Maloy 			if (unlikely(rc))
2069ec8a09fbSJon Paul Maloy 				break;
2070ec8a09fbSJon Paul Maloy 			copied += copy;
2071ec8a09fbSJon Paul Maloy 			offset += copy;
2072ec8a09fbSJon Paul Maloy 			if (unlikely(offset < dlen)) {
2073ec8a09fbSJon Paul Maloy 				if (!peek)
2074ec8a09fbSJon Paul Maloy 					skb_cb->bytes_read = offset;
2075ec8a09fbSJon Paul Maloy 				break;
2076b97bf3fdSPer Liden 			}
2077b97bf3fdSPer Liden 		} else {
2078ec8a09fbSJon Paul Maloy 			rc = 0;
2079ec8a09fbSJon Paul Maloy 			if ((err != TIPC_CONN_SHUTDOWN) && !m->msg_control)
2080ec8a09fbSJon Paul Maloy 				rc = -ECONNRESET;
2081ec8a09fbSJon Paul Maloy 			if (copied || rc)
2082ec8a09fbSJon Paul Maloy 				break;
2083b97bf3fdSPer Liden 		}
2084b97bf3fdSPer Liden 
2085ec8a09fbSJon Paul Maloy 		if (unlikely(peek))
2086ec8a09fbSJon Paul Maloy 			break;
208710724cc7SJon Paul Maloy 
20882e84c60bSJon Paul Maloy 		tsk_advance_rx_queue(sk);
2089b97bf3fdSPer Liden 
2090ec8a09fbSJon Paul Maloy 		/* Send connection flow control advertisement when applicable */
2091ec8a09fbSJon Paul Maloy 		tsk->rcv_unacked += tsk_inc(tsk, hlen + dlen);
2092c7268589STuong Lien 		if (tsk->rcv_unacked >= tsk->rcv_win / TIPC_ACK_RATE)
2093ec8a09fbSJon Paul Maloy 			tipc_sk_send_ack(tsk);
2094b97bf3fdSPer Liden 
2095ec8a09fbSJon Paul Maloy 		/* Exit if all requested data or FIN/error received */
2096ec8a09fbSJon Paul Maloy 		if (copied == buflen || err)
2097ec8a09fbSJon Paul Maloy 			break;
2098ec8a09fbSJon Paul Maloy 
2099ec8a09fbSJon Paul Maloy 	} while (!skb_queue_empty(&sk->sk_receive_queue) || copied < required);
2100b97bf3fdSPer Liden exit:
21010c3141e9SAllan Stephens 	release_sock(sk);
2102ec8a09fbSJon Paul Maloy 	return copied ? copied : rc;
2103b97bf3fdSPer Liden }
2104b97bf3fdSPer Liden 
2105b97bf3fdSPer Liden /**
2106f288bef4SYing Xue  * tipc_write_space - wake up thread if port congestion is released
2107f288bef4SYing Xue  * @sk: socket
2108f288bef4SYing Xue  */
tipc_write_space(struct sock * sk)2109f288bef4SYing Xue static void tipc_write_space(struct sock *sk)
2110f288bef4SYing Xue {
2111f288bef4SYing Xue 	struct socket_wq *wq;
2112f288bef4SYing Xue 
2113f288bef4SYing Xue 	rcu_read_lock();
2114f288bef4SYing Xue 	wq = rcu_dereference(sk->sk_wq);
21151ce0bf50SHerbert Xu 	if (skwq_has_sleeper(wq))
2116a9a08845SLinus Torvalds 		wake_up_interruptible_sync_poll(&wq->wait, EPOLLOUT |
2117a9a08845SLinus Torvalds 						EPOLLWRNORM | EPOLLWRBAND);
2118f288bef4SYing Xue 	rcu_read_unlock();
2119f288bef4SYing Xue }
2120f288bef4SYing Xue 
2121f288bef4SYing Xue /**
2122f288bef4SYing Xue  * tipc_data_ready - wake up threads to indicate messages have been received
2123f288bef4SYing Xue  * @sk: socket
2124f288bef4SYing Xue  */
tipc_data_ready(struct sock * sk)2125676d2369SDavid S. Miller static void tipc_data_ready(struct sock *sk)
2126f288bef4SYing Xue {
2127f288bef4SYing Xue 	struct socket_wq *wq;
2128f288bef4SYing Xue 
212940e0b090SPeilin Ye 	trace_sk_data_ready(sk);
213040e0b090SPeilin Ye 
2131f288bef4SYing Xue 	rcu_read_lock();
2132f288bef4SYing Xue 	wq = rcu_dereference(sk->sk_wq);
21331ce0bf50SHerbert Xu 	if (skwq_has_sleeper(wq))
2134a9a08845SLinus Torvalds 		wake_up_interruptible_sync_poll(&wq->wait, EPOLLIN |
2135a9a08845SLinus Torvalds 						EPOLLRDNORM | EPOLLRDBAND);
2136f288bef4SYing Xue 	rcu_read_unlock();
2137f288bef4SYing Xue }
2138f288bef4SYing Xue 
tipc_sock_destruct(struct sock * sk)2139f4195d1eSYing Xue static void tipc_sock_destruct(struct sock *sk)
2140f4195d1eSYing Xue {
2141f4195d1eSYing Xue 	__skb_queue_purge(&sk->sk_receive_queue);
2142f4195d1eSYing Xue }
2143f4195d1eSYing Xue 
tipc_sk_proto_rcv(struct sock * sk,struct sk_buff_head * inputq,struct sk_buff_head * xmitq)214464ac5f59SJon Maloy static void tipc_sk_proto_rcv(struct sock *sk,
214564ac5f59SJon Maloy 			      struct sk_buff_head *inputq,
214664ac5f59SJon Maloy 			      struct sk_buff_head *xmitq)
214764ac5f59SJon Maloy {
214864ac5f59SJon Maloy 	struct sk_buff *skb = __skb_dequeue(inputq);
214964ac5f59SJon Maloy 	struct tipc_sock *tsk = tipc_sk(sk);
215064ac5f59SJon Maloy 	struct tipc_msg *hdr = buf_msg(skb);
215175da2163SJon Maloy 	struct tipc_group *grp = tsk->group;
2152b7d42635SJon Maloy 	bool wakeup = false;
215364ac5f59SJon Maloy 
215464ac5f59SJon Maloy 	switch (msg_user(hdr)) {
215564ac5f59SJon Maloy 	case CONN_MANAGER:
2156e7eb0582SParthasarathy Bhuvaragan 		tipc_sk_conn_proto_rcv(tsk, skb, inputq, xmitq);
215764ac5f59SJon Maloy 		return;
215864ac5f59SJon Maloy 	case SOCK_WAKEUP:
2159a80ae530SJon Maloy 		tipc_dest_del(&tsk->cong_links, msg_orignode(hdr), 0);
2160bfd07f3dSTung Nguyen 		/* coupled with smp_rmb() in tipc_wait_for_cond() */
2161bfd07f3dSTung Nguyen 		smp_wmb();
216264ac5f59SJon Maloy 		tsk->cong_link_cnt--;
2163b7d42635SJon Maloy 		wakeup = true;
21640a3e060fSTuong Lien 		tipc_sk_push_backlog(tsk, false);
216564ac5f59SJon Maloy 		break;
216675da2163SJon Maloy 	case GROUP_PROTOCOL:
2167b7d42635SJon Maloy 		tipc_group_proto_rcv(grp, &wakeup, hdr, inputq, xmitq);
216875da2163SJon Maloy 		break;
216964ac5f59SJon Maloy 	case TOP_SRV:
2170b7d42635SJon Maloy 		tipc_group_member_evt(tsk->group, &wakeup, &sk->sk_rcvbuf,
21717ad32bcbSJon Maloy 				      hdr, inputq, xmitq);
217264ac5f59SJon Maloy 		break;
217364ac5f59SJon Maloy 	default:
217464ac5f59SJon Maloy 		break;
217564ac5f59SJon Maloy 	}
217664ac5f59SJon Maloy 
2177b7d42635SJon Maloy 	if (wakeup)
2178b7d42635SJon Maloy 		sk->sk_write_space(sk);
2179b7d42635SJon Maloy 
218064ac5f59SJon Maloy 	kfree_skb(skb);
218164ac5f59SJon Maloy }
218264ac5f59SJon Maloy 
2183f288bef4SYing Xue /**
218439fdc9c7SJon Maloy  * tipc_sk_filter_connect - check incoming message for a connection-based socket
218558ed9442SJon Paul Maloy  * @tsk: TIPC socket
218639fdc9c7SJon Maloy  * @skb: pointer to message buffer.
2187c7268589STuong Lien  * @xmitq: for Nagle ACK if any
2188637b77fdSRandy Dunlap  * Return: true if message should be added to receive queue, false otherwise
21897e6c131eSYing Xue  */
tipc_sk_filter_connect(struct tipc_sock * tsk,struct sk_buff * skb,struct sk_buff_head * xmitq)2190c7268589STuong Lien static bool tipc_sk_filter_connect(struct tipc_sock *tsk, struct sk_buff *skb,
2191c7268589STuong Lien 				   struct sk_buff_head *xmitq)
21927e6c131eSYing Xue {
219358ed9442SJon Paul Maloy 	struct sock *sk = &tsk->sk;
2194f2f9800dSYing Xue 	struct net *net = sock_net(sk);
2195cda3696dSJon Paul Maloy 	struct tipc_msg *hdr = buf_msg(skb);
219639fdc9c7SJon Maloy 	bool con_msg = msg_connected(hdr);
219739fdc9c7SJon Maloy 	u32 pport = tsk_peer_port(tsk);
219839fdc9c7SJon Maloy 	u32 pnode = tsk_peer_node(tsk);
219939fdc9c7SJon Maloy 	u32 oport = msg_origport(hdr);
220039fdc9c7SJon Maloy 	u32 onode = msg_orignode(hdr);
220139fdc9c7SJon Maloy 	int err = msg_errcode(hdr);
220267879274STung Nguyen 	unsigned long delay;
22037e6c131eSYing Xue 
2204cda3696dSJon Paul Maloy 	if (unlikely(msg_mcast(hdr)))
2205cda3696dSJon Paul Maloy 		return false;
2206c0bceb97SJon Maloy 	tsk->oneway = 0;
22077e6c131eSYing Xue 
220899a20889SParthasarathy Bhuvaragan 	switch (sk->sk_state) {
220999a20889SParthasarathy Bhuvaragan 	case TIPC_CONNECTING:
221039fdc9c7SJon Maloy 		/* Setup ACK */
221139fdc9c7SJon Maloy 		if (likely(con_msg)) {
221239fdc9c7SJon Maloy 			if (err)
221339fdc9c7SJon Maloy 				break;
221439fdc9c7SJon Maloy 			tipc_sk_finish_conn(tsk, oport, onode);
2215cda3696dSJon Paul Maloy 			msg_set_importance(&tsk->phdr, msg_importance(hdr));
221639fdc9c7SJon Maloy 			/* ACK+ message with data is added to receive queue */
2217cda3696dSJon Paul Maloy 			if (msg_data_sz(hdr))
2218cda3696dSJon Paul Maloy 				return true;
221939fdc9c7SJon Maloy 			/* Empty ACK-, - wake up sleeping connect() and drop */
2220ff946833SParthasarathy Bhuvaragan 			sk->sk_state_change(sk);
2221cda3696dSJon Paul Maloy 			msg_set_dest_droppable(hdr, 1);
2222cda3696dSJon Paul Maloy 			return false;
222339fdc9c7SJon Maloy 		}
222439fdc9c7SJon Maloy 		/* Ignore connectionless message if not from listening socket */
222539fdc9c7SJon Maloy 		if (oport != pport || onode != pnode)
2226f40acbafSParthasarathy Bhuvaragan 			return false;
2227f40acbafSParthasarathy Bhuvaragan 
222867879274STung Nguyen 		/* Rejected SYN */
222967879274STung Nguyen 		if (err != TIPC_ERR_OVERLOAD)
223039fdc9c7SJon Maloy 			break;
223167879274STung Nguyen 
223267879274STung Nguyen 		/* Prepare for new setup attempt if we have a SYN clone */
223367879274STung Nguyen 		if (skb_queue_empty(&sk->sk_write_queue))
223467879274STung Nguyen 			break;
223567879274STung Nguyen 		get_random_bytes(&delay, 2);
223667879274STung Nguyen 		delay %= (tsk->conn_timeout / 4);
223767879274STung Nguyen 		delay = msecs_to_jiffies(delay + 100);
223867879274STung Nguyen 		sk_reset_timer(sk, &sk->sk_timer, jiffies + delay);
223967879274STung Nguyen 		return false;
224039fdc9c7SJon Maloy 	case TIPC_OPEN:
224139fdc9c7SJon Maloy 	case TIPC_DISCONNECTING:
224239fdc9c7SJon Maloy 		return false;
224339fdc9c7SJon Maloy 	case TIPC_LISTEN:
224439fdc9c7SJon Maloy 		/* Accept only SYN message */
224525b9221bSJon Maloy 		if (!msg_is_syn(hdr) &&
224625b9221bSJon Maloy 		    tipc_node_get_capabilities(net, onode) & TIPC_SYN_BIT)
224725b9221bSJon Maloy 			return false;
224839fdc9c7SJon Maloy 		if (!con_msg && !err)
224939fdc9c7SJon Maloy 			return true;
225039fdc9c7SJon Maloy 		return false;
225139fdc9c7SJon Maloy 	case TIPC_ESTABLISHED:
2252c0bceb97SJon Maloy 		if (!skb_queue_empty(&sk->sk_write_queue))
22530a3e060fSTuong Lien 			tipc_sk_push_backlog(tsk, false);
225439fdc9c7SJon Maloy 		/* Accept only connection-based messages sent by peer */
2255c7268589STuong Lien 		if (likely(con_msg && !err && pport == oport &&
2256c7268589STuong Lien 			   pnode == onode)) {
2257c7268589STuong Lien 			if (msg_ack_required(hdr)) {
2258c7268589STuong Lien 				struct sk_buff *skb;
2259c7268589STuong Lien 
2260c7268589STuong Lien 				skb = tipc_sk_build_ack(tsk);
22610a3e060fSTuong Lien 				if (skb) {
22620a3e060fSTuong Lien 					msg_set_nagle_ack(buf_msg(skb));
2263c7268589STuong Lien 					__skb_queue_tail(xmitq, skb);
2264c7268589STuong Lien 				}
22650a3e060fSTuong Lien 			}
226639fdc9c7SJon Maloy 			return true;
2267c7268589STuong Lien 		}
226839fdc9c7SJon Maloy 		if (!tsk_peer_msg(tsk, hdr))
226939fdc9c7SJon Maloy 			return false;
227039fdc9c7SJon Maloy 		if (!err)
227139fdc9c7SJon Maloy 			return true;
2272f40acbafSParthasarathy Bhuvaragan 		tipc_set_sk_state(sk, TIPC_DISCONNECTING);
227339fdc9c7SJon Maloy 		tipc_node_remove_conn(net, pnode, tsk->portid);
2274f40acbafSParthasarathy Bhuvaragan 		sk->sk_state_change(sk);
2275f40acbafSParthasarathy Bhuvaragan 		return true;
22767e6c131eSYing Xue 	default:
2277438adcafSParthasarathy Bhuvaragan 		pr_err("Unknown sk_state %u\n", sk->sk_state);
22787e6c131eSYing Xue 	}
227939fdc9c7SJon Maloy 	/* Abort connection setup attempt */
228039fdc9c7SJon Maloy 	tipc_set_sk_state(sk, TIPC_DISCONNECTING);
228139fdc9c7SJon Maloy 	sk->sk_err = ECONNREFUSED;
228239fdc9c7SJon Maloy 	sk->sk_state_change(sk);
228339fdc9c7SJon Maloy 	return true;
22847e6c131eSYing Xue }
22857e6c131eSYing Xue 
22867e6c131eSYing Xue /**
2287aba79f33SYing Xue  * rcvbuf_limit - get proper overload limit of socket receive queue
2288aba79f33SYing Xue  * @sk: socket
228910724cc7SJon Paul Maloy  * @skb: message
2290aba79f33SYing Xue  *
229110724cc7SJon Paul Maloy  * For connection oriented messages, irrespective of importance,
229210724cc7SJon Paul Maloy  * default queue limit is 2 MB.
2293aba79f33SYing Xue  *
229410724cc7SJon Paul Maloy  * For connectionless messages, queue limits are based on message
229510724cc7SJon Paul Maloy  * importance as follows:
2296aba79f33SYing Xue  *
229710724cc7SJon Paul Maloy  * TIPC_LOW_IMPORTANCE       (2 MB)
229810724cc7SJon Paul Maloy  * TIPC_MEDIUM_IMPORTANCE    (4 MB)
229910724cc7SJon Paul Maloy  * TIPC_HIGH_IMPORTANCE      (8 MB)
230010724cc7SJon Paul Maloy  * TIPC_CRITICAL_IMPORTANCE  (16 MB)
2301aba79f33SYing Xue  *
2302637b77fdSRandy Dunlap  * Return: overload limit according to corresponding message importance
2303aba79f33SYing Xue  */
rcvbuf_limit(struct sock * sk,struct sk_buff * skb)230410724cc7SJon Paul Maloy static unsigned int rcvbuf_limit(struct sock *sk, struct sk_buff *skb)
2305aba79f33SYing Xue {
230610724cc7SJon Paul Maloy 	struct tipc_sock *tsk = tipc_sk(sk);
230710724cc7SJon Paul Maloy 	struct tipc_msg *hdr = buf_msg(skb);
2308aba79f33SYing Xue 
2309b7d42635SJon Maloy 	if (unlikely(msg_in_group(hdr)))
23108265792bSEric Dumazet 		return READ_ONCE(sk->sk_rcvbuf);
2311b7d42635SJon Maloy 
231210724cc7SJon Paul Maloy 	if (unlikely(!msg_connected(hdr)))
23138265792bSEric Dumazet 		return READ_ONCE(sk->sk_rcvbuf) << msg_importance(hdr);
23140cee6bbeSwangweidong 
231510724cc7SJon Paul Maloy 	if (likely(tsk->peer_caps & TIPC_BLOCK_FLOWCTL))
23168265792bSEric Dumazet 		return READ_ONCE(sk->sk_rcvbuf);
231710724cc7SJon Paul Maloy 
231810724cc7SJon Paul Maloy 	return FLOWCTL_MSG_LIM;
2319aba79f33SYing Xue }
2320aba79f33SYing Xue 
2321aba79f33SYing Xue /**
232264ac5f59SJon Maloy  * tipc_sk_filter_rcv - validate incoming message
23230c3141e9SAllan Stephens  * @sk: socket
2324cda3696dSJon Paul Maloy  * @skb: pointer to message.
2325f172f4b8SRandy Dunlap  * @xmitq: output message area (FIXME)
2326b97bf3fdSPer Liden  *
23270c3141e9SAllan Stephens  * Enqueues message on receive queue if acceptable; optionally handles
23280c3141e9SAllan Stephens  * disconnect indication for a connected socket.
23290c3141e9SAllan Stephens  *
23301186adf7SJon Paul Maloy  * Called with socket lock already taken
2331b97bf3fdSPer Liden  */
tipc_sk_filter_rcv(struct sock * sk,struct sk_buff * skb,struct sk_buff_head * xmitq)233264ac5f59SJon Maloy static void tipc_sk_filter_rcv(struct sock *sk, struct sk_buff *skb,
2333f1d048f2SJon Paul Maloy 			       struct sk_buff_head *xmitq)
2334b97bf3fdSPer Liden {
233564ac5f59SJon Maloy 	bool sk_conn = !tipc_sk_type_connectionless(sk);
233658ed9442SJon Paul Maloy 	struct tipc_sock *tsk = tipc_sk(sk);
233775da2163SJon Maloy 	struct tipc_group *grp = tsk->group;
2338cda3696dSJon Paul Maloy 	struct tipc_msg *hdr = buf_msg(skb);
233964ac5f59SJon Maloy 	struct net *net = sock_net(sk);
234064ac5f59SJon Maloy 	struct sk_buff_head inputq;
234177d5ad40SHoang Le 	int mtyp = msg_type(hdr);
234264ac5f59SJon Maloy 	int limit, err = TIPC_OK;
2343b97bf3fdSPer Liden 
234401e661ebSTuong Lien 	trace_tipc_sk_filter_rcv(sk, skb, TIPC_DUMP_ALL, " ");
2345ba8aebe9SParthasarathy Bhuvaragan 	TIPC_SKB_CB(skb)->bytes_read = 0;
234664ac5f59SJon Maloy 	__skb_queue_head_init(&inputq);
234764ac5f59SJon Maloy 	__skb_queue_tail(&inputq, skb);
234864ac5f59SJon Maloy 
234964ac5f59SJon Maloy 	if (unlikely(!msg_isdata(hdr)))
235064ac5f59SJon Maloy 		tipc_sk_proto_rcv(sk, &inputq, xmitq);
235164ac5f59SJon Maloy 
235275da2163SJon Maloy 	if (unlikely(grp))
235375da2163SJon Maloy 		tipc_group_filter_msg(grp, &inputq, xmitq);
235475da2163SJon Maloy 
235577d5ad40SHoang Le 	if (unlikely(!grp) && mtyp == TIPC_MCAST_MSG)
235608e046c8SHoang Le 		tipc_mcast_filter_msg(net, &tsk->mc_method.deferredq, &inputq);
2357c55c8edaSHoang Le 
235864ac5f59SJon Maloy 	/* Validate and add to receive buffer if there is space */
235964ac5f59SJon Maloy 	while ((skb = __skb_dequeue(&inputq))) {
236064ac5f59SJon Maloy 		hdr = buf_msg(skb);
236164ac5f59SJon Maloy 		limit = rcvbuf_limit(sk, skb);
2362c7268589STuong Lien 		if ((sk_conn && !tipc_sk_filter_connect(tsk, skb, xmitq)) ||
236375da2163SJon Maloy 		    (!sk_conn && msg_connected(hdr)) ||
236475da2163SJon Maloy 		    (!grp && msg_in_group(hdr)))
236564ac5f59SJon Maloy 			err = TIPC_ERR_NO_PORT;
2366872619d8SGhantaKrishnamurthy MohanKrishna 		else if (sk_rmem_alloc_get(sk) + skb->truesize >= limit) {
236701e661ebSTuong Lien 			trace_tipc_sk_dump(sk, skb, TIPC_DUMP_ALL,
236801e661ebSTuong Lien 					   "err_overload2!");
2369872619d8SGhantaKrishnamurthy MohanKrishna 			atomic_inc(&sk->sk_drops);
237064ac5f59SJon Maloy 			err = TIPC_ERR_OVERLOAD;
2371872619d8SGhantaKrishnamurthy MohanKrishna 		}
237264ac5f59SJon Maloy 
237364ac5f59SJon Maloy 		if (unlikely(err)) {
237401e661ebSTuong Lien 			if (tipc_msg_reverse(tipc_own_addr(net), &skb, err)) {
237501e661ebSTuong Lien 				trace_tipc_sk_rej_msg(sk, skb, TIPC_DUMP_NONE,
237601e661ebSTuong Lien 						      "@filter_rcv!");
237701e661ebSTuong Lien 				__skb_queue_tail(xmitq, skb);
237801e661ebSTuong Lien 			}
237964ac5f59SJon Maloy 			err = TIPC_OK;
238064ac5f59SJon Maloy 			continue;
238164ac5f59SJon Maloy 		}
2382cda3696dSJon Paul Maloy 		__skb_queue_tail(&sk->sk_receive_queue, skb);
2383cda3696dSJon Paul Maloy 		skb_set_owner_r(skb, sk);
238401e661ebSTuong Lien 		trace_tipc_sk_overlimit2(sk, skb, TIPC_DUMP_ALL,
238501e661ebSTuong Lien 					 "rcvq >90% allocated!");
2386676d2369SDavid S. Miller 		sk->sk_data_ready(sk);
238764ac5f59SJon Maloy 	}
2388b97bf3fdSPer Liden }
2389b97bf3fdSPer Liden 
2390b97bf3fdSPer Liden /**
239164ac5f59SJon Maloy  * tipc_sk_backlog_rcv - handle incoming message from backlog queue
23920c3141e9SAllan Stephens  * @sk: socket
2393a6ca1094SYing Xue  * @skb: message
23940c3141e9SAllan Stephens  *
2395e3a77561SJon Paul Maloy  * Caller must hold socket lock
23960c3141e9SAllan Stephens  */
tipc_sk_backlog_rcv(struct sock * sk,struct sk_buff * skb)239764ac5f59SJon Maloy static int tipc_sk_backlog_rcv(struct sock *sk, struct sk_buff *skb)
23980c3141e9SAllan Stephens {
239964ac5f59SJon Maloy 	unsigned int before = sk_rmem_alloc_get(sk);
2400f1d048f2SJon Paul Maloy 	struct sk_buff_head xmitq;
240164ac5f59SJon Maloy 	unsigned int added;
24020c3141e9SAllan Stephens 
2403f1d048f2SJon Paul Maloy 	__skb_queue_head_init(&xmitq);
2404f1d048f2SJon Paul Maloy 
240564ac5f59SJon Maloy 	tipc_sk_filter_rcv(sk, skb, &xmitq);
240664ac5f59SJon Maloy 	added = sk_rmem_alloc_get(sk) - before;
240764ac5f59SJon Maloy 	atomic_add(added, &tipc_sk(sk)->dupl_rcvcnt);
24080c3141e9SAllan Stephens 
240964ac5f59SJon Maloy 	/* Send pending response/rejected messages, if any */
2410f70d37b7SJon Maloy 	tipc_node_distr_xmit(sock_net(sk), &xmitq);
2411f1d048f2SJon Paul Maloy 	return 0;
2412f1d048f2SJon Paul Maloy }
2413f1d048f2SJon Paul Maloy 
24140c3141e9SAllan Stephens /**
2415c637c103SJon Paul Maloy  * tipc_sk_enqueue - extract all buffers with destination 'dport' from
2416c637c103SJon Paul Maloy  *                   inputq and try adding them to socket or backlog queue
2417c637c103SJon Paul Maloy  * @inputq: list of incoming buffers with potentially different destinations
2418c637c103SJon Paul Maloy  * @sk: socket where the buffers should be enqueued
2419c637c103SJon Paul Maloy  * @dport: port number for the socket
2420f172f4b8SRandy Dunlap  * @xmitq: output queue
2421d570d864SJon Paul Maloy  *
2422d570d864SJon Paul Maloy  * Caller must hold socket lock
2423d570d864SJon Paul Maloy  */
tipc_sk_enqueue(struct sk_buff_head * inputq,struct sock * sk,u32 dport,struct sk_buff_head * xmitq)2424cda3696dSJon Paul Maloy static void tipc_sk_enqueue(struct sk_buff_head *inputq, struct sock *sk,
2425f1d048f2SJon Paul Maloy 			    u32 dport, struct sk_buff_head *xmitq)
2426d570d864SJon Paul Maloy {
2427f4bb62e6SHoang Le 	unsigned long time_limit = jiffies + usecs_to_jiffies(20000);
2428f1d048f2SJon Paul Maloy 	struct sk_buff *skb;
2429d570d864SJon Paul Maloy 	unsigned int lim;
2430d570d864SJon Paul Maloy 	atomic_t *dcnt;
2431f1d048f2SJon Paul Maloy 	u32 onode;
2432d570d864SJon Paul Maloy 
2433c637c103SJon Paul Maloy 	while (skb_queue_len(inputq)) {
243451a00dafSJon Paul Maloy 		if (unlikely(time_after_eq(jiffies, time_limit)))
2435cda3696dSJon Paul Maloy 			return;
2436cda3696dSJon Paul Maloy 
2437c637c103SJon Paul Maloy 		skb = tipc_skb_dequeue(inputq, dport);
2438c637c103SJon Paul Maloy 		if (unlikely(!skb))
2439cda3696dSJon Paul Maloy 			return;
2440cda3696dSJon Paul Maloy 
2441cda3696dSJon Paul Maloy 		/* Add message directly to receive queue if possible */
2442c637c103SJon Paul Maloy 		if (!sock_owned_by_user(sk)) {
244364ac5f59SJon Maloy 			tipc_sk_filter_rcv(sk, skb, xmitq);
2444c637c103SJon Paul Maloy 			continue;
2445c637c103SJon Paul Maloy 		}
2446cda3696dSJon Paul Maloy 
2447cda3696dSJon Paul Maloy 		/* Try backlog, compensating for double-counted bytes */
2448d570d864SJon Paul Maloy 		dcnt = &tipc_sk(sk)->dupl_rcvcnt;
24497c8bcfb1SJon Paul Maloy 		if (!sk->sk_backlog.len)
2450d570d864SJon Paul Maloy 			atomic_set(dcnt, 0);
2451c637c103SJon Paul Maloy 		lim = rcvbuf_limit(sk, skb) + atomic_read(dcnt);
245201e661ebSTuong Lien 		if (likely(!sk_add_backlog(sk, skb, lim))) {
245301e661ebSTuong Lien 			trace_tipc_sk_overlimit1(sk, skb, TIPC_DUMP_ALL,
245401e661ebSTuong Lien 						 "bklg & rcvq >90% allocated!");
2455c637c103SJon Paul Maloy 			continue;
245601e661ebSTuong Lien 		}
2457cda3696dSJon Paul Maloy 
245801e661ebSTuong Lien 		trace_tipc_sk_dump(sk, skb, TIPC_DUMP_ALL, "err_overload!");
2459cda3696dSJon Paul Maloy 		/* Overload => reject message back to sender */
2460f1d048f2SJon Paul Maloy 		onode = tipc_own_addr(sock_net(sk));
2461872619d8SGhantaKrishnamurthy MohanKrishna 		atomic_inc(&sk->sk_drops);
246201e661ebSTuong Lien 		if (tipc_msg_reverse(onode, &skb, TIPC_ERR_OVERLOAD)) {
246301e661ebSTuong Lien 			trace_tipc_sk_rej_msg(sk, skb, TIPC_DUMP_ALL,
246401e661ebSTuong Lien 					      "@sk_enqueue!");
2465f1d048f2SJon Paul Maloy 			__skb_queue_tail(xmitq, skb);
246601e661ebSTuong Lien 		}
2467cda3696dSJon Paul Maloy 		break;
2468c637c103SJon Paul Maloy 	}
2469d570d864SJon Paul Maloy }
2470d570d864SJon Paul Maloy 
2471d570d864SJon Paul Maloy /**
2472c637c103SJon Paul Maloy  * tipc_sk_rcv - handle a chain of incoming buffers
2473f172f4b8SRandy Dunlap  * @net: the associated network namespace
2474c637c103SJon Paul Maloy  * @inputq: buffer list containing the buffers
2475c637c103SJon Paul Maloy  * Consumes all buffers in list until inputq is empty
2476c637c103SJon Paul Maloy  * Note: may be called in multiple threads referring to the same queue
24770c3141e9SAllan Stephens  */
tipc_sk_rcv(struct net * net,struct sk_buff_head * inputq)2478cda3696dSJon Paul Maloy void tipc_sk_rcv(struct net *net, struct sk_buff_head *inputq)
24790c3141e9SAllan Stephens {
2480f1d048f2SJon Paul Maloy 	struct sk_buff_head xmitq;
2481c637c103SJon Paul Maloy 	u32 dnode, dport = 0;
24829871b27fSErik Hugne 	int err;
24839816f061SJon Paul Maloy 	struct tipc_sock *tsk;
24849816f061SJon Paul Maloy 	struct sock *sk;
2485cda3696dSJon Paul Maloy 	struct sk_buff *skb;
24869816f061SJon Paul Maloy 
2487f1d048f2SJon Paul Maloy 	__skb_queue_head_init(&xmitq);
2488c637c103SJon Paul Maloy 	while (skb_queue_len(inputq)) {
2489c637c103SJon Paul Maloy 		dport = tipc_skb_peek_port(inputq, dport);
2490e05b31f4SYing Xue 		tsk = tipc_sk_lookup(net, dport);
2491cda3696dSJon Paul Maloy 
2492e3a77561SJon Paul Maloy 		if (likely(tsk)) {
24939816f061SJon Paul Maloy 			sk = &tsk->sk;
2494c637c103SJon Paul Maloy 			if (likely(spin_trylock_bh(&sk->sk_lock.slock))) {
2495f1d048f2SJon Paul Maloy 				tipc_sk_enqueue(inputq, sk, dport, &xmitq);
24961a194c2dSYing Xue 				spin_unlock_bh(&sk->sk_lock.slock);
2497c637c103SJon Paul Maloy 			}
2498f1d048f2SJon Paul Maloy 			/* Send pending response/rejected messages, if any */
2499f70d37b7SJon Maloy 			tipc_node_distr_xmit(sock_net(sk), &xmitq);
250007f6c4bcSYing Xue 			sock_put(sk);
2501c637c103SJon Paul Maloy 			continue;
2502e3a77561SJon Paul Maloy 		}
2503cda3696dSJon Paul Maloy 		/* No destination socket => dequeue skb if still there */
2504cda3696dSJon Paul Maloy 		skb = tipc_skb_dequeue(inputq, dport);
2505cda3696dSJon Paul Maloy 		if (!skb)
2506cda3696dSJon Paul Maloy 			return;
2507cda3696dSJon Paul Maloy 
2508cda3696dSJon Paul Maloy 		/* Try secondary lookup if unresolved named message */
2509cda3696dSJon Paul Maloy 		err = TIPC_ERR_NO_PORT;
2510cda3696dSJon Paul Maloy 		if (tipc_msg_lookup_dest(net, skb, &err))
2511cda3696dSJon Paul Maloy 			goto xmit;
2512cda3696dSJon Paul Maloy 
2513cda3696dSJon Paul Maloy 		/* Prepare for message rejection */
2514cda3696dSJon Paul Maloy 		if (!tipc_msg_reverse(tipc_own_addr(net), &skb, err))
2515c637c103SJon Paul Maloy 			continue;
251601e661ebSTuong Lien 
251701e661ebSTuong Lien 		trace_tipc_sk_rej_msg(NULL, skb, TIPC_DUMP_NONE, "@sk_rcv!");
2518e3a77561SJon Paul Maloy xmit:
2519cda3696dSJon Paul Maloy 		dnode = msg_destnode(buf_msg(skb));
2520af9b028eSJon Paul Maloy 		tipc_node_xmit_skb(net, skb, dnode, dport);
2521c637c103SJon Paul Maloy 	}
25220c3141e9SAllan Stephens }
25230c3141e9SAllan Stephens 
tipc_wait_for_connect(struct socket * sock,long * timeo_p)252478eb3a53SYing Xue static int tipc_wait_for_connect(struct socket *sock, long *timeo_p)
252578eb3a53SYing Xue {
2526d9dc8b0fSWANG Cong 	DEFINE_WAIT_FUNC(wait, woken_wake_function);
252778eb3a53SYing Xue 	struct sock *sk = sock->sk;
252878eb3a53SYing Xue 	int done;
252978eb3a53SYing Xue 
253078eb3a53SYing Xue 	do {
253178eb3a53SYing Xue 		int err = sock_error(sk);
253278eb3a53SYing Xue 		if (err)
253378eb3a53SYing Xue 			return err;
253478eb3a53SYing Xue 		if (!*timeo_p)
253578eb3a53SYing Xue 			return -ETIMEDOUT;
253678eb3a53SYing Xue 		if (signal_pending(current))
253778eb3a53SYing Xue 			return sock_intr_errno(*timeo_p);
25385391a877STuong Lien 		if (sk->sk_state == TIPC_DISCONNECTING)
25395391a877STuong Lien 			break;
254078eb3a53SYing Xue 
2541d9dc8b0fSWANG Cong 		add_wait_queue(sk_sleep(sk), &wait);
25429546a0b7STuong Lien 		done = sk_wait_event(sk, timeo_p, tipc_sk_connected(sk),
25439546a0b7STuong Lien 				     &wait);
2544d9dc8b0fSWANG Cong 		remove_wait_queue(sk_sleep(sk), &wait);
254578eb3a53SYing Xue 	} while (!done);
254678eb3a53SYing Xue 	return 0;
254778eb3a53SYing Xue }
254878eb3a53SYing Xue 
tipc_sockaddr_is_sane(struct sockaddr_tipc * addr)2549ea239314SErik Hugne static bool tipc_sockaddr_is_sane(struct sockaddr_tipc *addr)
2550ea239314SErik Hugne {
2551ea239314SErik Hugne 	if (addr->family != AF_TIPC)
2552ea239314SErik Hugne 		return false;
2553ea239314SErik Hugne 	if (addr->addrtype == TIPC_SERVICE_RANGE)
2554ea239314SErik Hugne 		return (addr->addr.nameseq.lower <= addr->addr.nameseq.upper);
2555ea239314SErik Hugne 	return (addr->addrtype == TIPC_SERVICE_ADDR ||
2556ea239314SErik Hugne 		addr->addrtype == TIPC_SOCKET_ADDR);
2557ea239314SErik Hugne }
2558ea239314SErik Hugne 
2559b97bf3fdSPer Liden /**
2560247f0f3cSYing Xue  * tipc_connect - establish a connection to another TIPC port
2561b97bf3fdSPer Liden  * @sock: socket structure
2562b97bf3fdSPer Liden  * @dest: socket address for destination port
2563b97bf3fdSPer Liden  * @destlen: size of socket address data structure
25640c3141e9SAllan Stephens  * @flags: file-related flags associated with socket
2565b97bf3fdSPer Liden  *
2566637b77fdSRandy Dunlap  * Return: 0 on success, errno otherwise
2567b97bf3fdSPer Liden  */
tipc_connect(struct socket * sock,struct sockaddr * dest,int destlen,int flags)2568247f0f3cSYing Xue static int tipc_connect(struct socket *sock, struct sockaddr *dest,
2569247f0f3cSYing Xue 			int destlen, int flags)
2570b97bf3fdSPer Liden {
25710c3141e9SAllan Stephens 	struct sock *sk = sock->sk;
2572f2f8036eSErik Hugne 	struct tipc_sock *tsk = tipc_sk(sk);
2573b97bf3fdSPer Liden 	struct sockaddr_tipc *dst = (struct sockaddr_tipc *)dest;
25741fc54d8fSSam Ravnborg 	struct msghdr m = {NULL,};
2575f2f8036eSErik Hugne 	long timeout = (flags & O_NONBLOCK) ? 0 : tsk->conn_timeout;
257699a20889SParthasarathy Bhuvaragan 	int previous;
2577f2f8036eSErik Hugne 	int res = 0;
2578b97bf3fdSPer Liden 
257923998835SJon Maloy 	if (destlen != sizeof(struct sockaddr_tipc))
258023998835SJon Maloy 		return -EINVAL;
258123998835SJon Maloy 
25820c3141e9SAllan Stephens 	lock_sock(sk);
25830c3141e9SAllan Stephens 
258475da2163SJon Maloy 	if (tsk->group) {
258575da2163SJon Maloy 		res = -EINVAL;
258675da2163SJon Maloy 		goto exit;
258775da2163SJon Maloy 	}
258875da2163SJon Maloy 
258923998835SJon Maloy 	if (dst->family == AF_UNSPEC) {
259023998835SJon Maloy 		memset(&tsk->peer, 0, sizeof(struct sockaddr_tipc));
259123998835SJon Maloy 		if (!tipc_sk_type_connectionless(sk))
259223998835SJon Maloy 			res = -EINVAL;
259323998835SJon Maloy 		goto exit;
259423998835SJon Maloy 	}
2595ea239314SErik Hugne 	if (!tipc_sockaddr_is_sane(dst)) {
259623998835SJon Maloy 		res = -EINVAL;
259723998835SJon Maloy 		goto exit;
2598ea239314SErik Hugne 	}
2599f2f8036eSErik Hugne 	/* DGRAM/RDM connect(), just save the destaddr */
2600c752023aSParthasarathy Bhuvaragan 	if (tipc_sk_type_connectionless(sk)) {
2601aeda16b6SParthasarathy Bhuvaragan 		memcpy(&tsk->peer, dest, destlen);
26020c3141e9SAllan Stephens 		goto exit;
2603ea239314SErik Hugne 	} else if (dst->addrtype == TIPC_SERVICE_RANGE) {
2604ea239314SErik Hugne 		res = -EINVAL;
2605ea239314SErik Hugne 		goto exit;
26060c3141e9SAllan Stephens 	}
26070c3141e9SAllan Stephens 
260899a20889SParthasarathy Bhuvaragan 	previous = sk->sk_state;
2609438adcafSParthasarathy Bhuvaragan 
2610438adcafSParthasarathy Bhuvaragan 	switch (sk->sk_state) {
2611438adcafSParthasarathy Bhuvaragan 	case TIPC_OPEN:
2612b97bf3fdSPer Liden 		/* Send a 'SYN-' to destination */
2613b97bf3fdSPer Liden 		m.msg_name = dest;
261451f9cc1fSAllan Stephens 		m.msg_namelen = destlen;
261511a4d6f6STung Nguyen 		iov_iter_kvec(&m.msg_iter, ITER_SOURCE, NULL, 0, 0);
2616584d24b3SYing Xue 
2617584d24b3SYing Xue 		/* If connect is in non-blocking case, set MSG_DONTWAIT to
2618584d24b3SYing Xue 		 * indicate send_msg() is never blocked.
2619584d24b3SYing Xue 		 */
2620584d24b3SYing Xue 		if (!timeout)
2621584d24b3SYing Xue 			m.msg_flags = MSG_DONTWAIT;
2622584d24b3SYing Xue 
262339a0295fSYing Xue 		res = __tipc_sendmsg(sock, &m, 0);
2624584d24b3SYing Xue 		if ((res < 0) && (res != -EWOULDBLOCK))
2625584d24b3SYing Xue 			goto exit;
2626584d24b3SYing Xue 
262799a20889SParthasarathy Bhuvaragan 		/* Just entered TIPC_CONNECTING state; the only
2628584d24b3SYing Xue 		 * difference is that return value in non-blocking
2629584d24b3SYing Xue 		 * case is EINPROGRESS, rather than EALREADY.
2630584d24b3SYing Xue 		 */
2631584d24b3SYing Xue 		res = -EINPROGRESS;
26327f8901b7SMiaohe Lin 		fallthrough;
263399a20889SParthasarathy Bhuvaragan 	case TIPC_CONNECTING:
263499a20889SParthasarathy Bhuvaragan 		if (!timeout) {
263599a20889SParthasarathy Bhuvaragan 			if (previous == TIPC_CONNECTING)
2636584d24b3SYing Xue 				res = -EALREADY;
263778eb3a53SYing Xue 			goto exit;
263899a20889SParthasarathy Bhuvaragan 		}
263978eb3a53SYing Xue 		timeout = msecs_to_jiffies(timeout);
264078eb3a53SYing Xue 		/* Wait until an 'ACK' or 'RST' arrives, or a timeout occurs */
264178eb3a53SYing Xue 		res = tipc_wait_for_connect(sock, &timeout);
2642f40acbafSParthasarathy Bhuvaragan 		break;
2643f40acbafSParthasarathy Bhuvaragan 	case TIPC_ESTABLISHED:
264499a20889SParthasarathy Bhuvaragan 		res = -EISCONN;
2645f40acbafSParthasarathy Bhuvaragan 		break;
2646f40acbafSParthasarathy Bhuvaragan 	default:
264799a20889SParthasarathy Bhuvaragan 		res = -EINVAL;
2648f40acbafSParthasarathy Bhuvaragan 	}
264999a20889SParthasarathy Bhuvaragan 
26500c3141e9SAllan Stephens exit:
26510c3141e9SAllan Stephens 	release_sock(sk);
2652b97bf3fdSPer Liden 	return res;
2653b97bf3fdSPer Liden }
2654b97bf3fdSPer Liden 
2655b97bf3fdSPer Liden /**
2656247f0f3cSYing Xue  * tipc_listen - allow socket to listen for incoming connections
2657b97bf3fdSPer Liden  * @sock: socket structure
2658b97bf3fdSPer Liden  * @len: (unused)
2659b97bf3fdSPer Liden  *
2660637b77fdSRandy Dunlap  * Return: 0 on success, errno otherwise
2661b97bf3fdSPer Liden  */
tipc_listen(struct socket * sock,int len)2662247f0f3cSYing Xue static int tipc_listen(struct socket *sock, int len)
2663b97bf3fdSPer Liden {
26640c3141e9SAllan Stephens 	struct sock *sk = sock->sk;
26650c3141e9SAllan Stephens 	int res;
26660c3141e9SAllan Stephens 
26670c3141e9SAllan Stephens 	lock_sock(sk);
26680c288c86SParthasarathy Bhuvaragan 	res = tipc_set_sk_state(sk, TIPC_LISTEN);
26690c3141e9SAllan Stephens 	release_sock(sk);
26700c288c86SParthasarathy Bhuvaragan 
26710c3141e9SAllan Stephens 	return res;
2672b97bf3fdSPer Liden }
2673b97bf3fdSPer Liden 
tipc_wait_for_accept(struct socket * sock,long timeo)26746398e23cSYing Xue static int tipc_wait_for_accept(struct socket *sock, long timeo)
26756398e23cSYing Xue {
26766398e23cSYing Xue 	struct sock *sk = sock->sk;
2677d237a7f1SHoang Le 	DEFINE_WAIT_FUNC(wait, woken_wake_function);
26786398e23cSYing Xue 	int err;
26796398e23cSYing Xue 
26806398e23cSYing Xue 	/* True wake-one mechanism for incoming connections: only
26816398e23cSYing Xue 	 * one process gets woken up, not the 'whole herd'.
26826398e23cSYing Xue 	 * Since we do not 'race & poll' for established sockets
26836398e23cSYing Xue 	 * anymore, the common case will execute the loop only once.
26846398e23cSYing Xue 	*/
26856398e23cSYing Xue 	for (;;) {
2686fe8e4649SYing Xue 		if (timeo && skb_queue_empty(&sk->sk_receive_queue)) {
2687d237a7f1SHoang Le 			add_wait_queue(sk_sleep(sk), &wait);
26886398e23cSYing Xue 			release_sock(sk);
2689d237a7f1SHoang Le 			timeo = wait_woken(&wait, TASK_INTERRUPTIBLE, timeo);
26906398e23cSYing Xue 			lock_sock(sk);
2691d237a7f1SHoang Le 			remove_wait_queue(sk_sleep(sk), &wait);
26926398e23cSYing Xue 		}
26936398e23cSYing Xue 		err = 0;
26946398e23cSYing Xue 		if (!skb_queue_empty(&sk->sk_receive_queue))
26956398e23cSYing Xue 			break;
26966398e23cSYing Xue 		err = -EAGAIN;
26976398e23cSYing Xue 		if (!timeo)
26986398e23cSYing Xue 			break;
2699143fe22fSErik Hugne 		err = sock_intr_errno(timeo);
2700143fe22fSErik Hugne 		if (signal_pending(current))
2701143fe22fSErik Hugne 			break;
27026398e23cSYing Xue 	}
27036398e23cSYing Xue 	return err;
27046398e23cSYing Xue }
27056398e23cSYing Xue 
2706b97bf3fdSPer Liden /**
2707247f0f3cSYing Xue  * tipc_accept - wait for connection request
2708b97bf3fdSPer Liden  * @sock: listening socket
2709d8141208SAndrew Lunn  * @new_sock: new socket that is to be connected
271092ef0fd5SJens Axboe  * @arg: arguments for accept
2711b97bf3fdSPer Liden  *
2712637b77fdSRandy Dunlap  * Return: 0 on success, errno otherwise
2713b97bf3fdSPer Liden  */
tipc_accept(struct socket * sock,struct socket * new_sock,struct proto_accept_arg * arg)271492ef0fd5SJens Axboe static int tipc_accept(struct socket *sock, struct socket *new_sock,
271592ef0fd5SJens Axboe 		       struct proto_accept_arg *arg)
2716b97bf3fdSPer Liden {
27170fef8f20SPaul Gortmaker 	struct sock *new_sk, *sk = sock->sk;
2718301bae56SJon Paul Maloy 	struct tipc_sock *new_tsock;
2719f8dd60deSXin Long 	struct msghdr m = {NULL,};
27200fef8f20SPaul Gortmaker 	struct tipc_msg *msg;
2721f8dd60deSXin Long 	struct sk_buff *buf;
27226398e23cSYing Xue 	long timeo;
27230c3141e9SAllan Stephens 	int res;
2724b97bf3fdSPer Liden 
27250c3141e9SAllan Stephens 	lock_sock(sk);
2726b97bf3fdSPer Liden 
27270c288c86SParthasarathy Bhuvaragan 	if (sk->sk_state != TIPC_LISTEN) {
27280c3141e9SAllan Stephens 		res = -EINVAL;
27290c3141e9SAllan Stephens 		goto exit;
27300c3141e9SAllan Stephens 	}
273192ef0fd5SJens Axboe 	timeo = sock_rcvtimeo(sk, arg->flags & O_NONBLOCK);
27326398e23cSYing Xue 	res = tipc_wait_for_accept(sock, timeo);
27330c3141e9SAllan Stephens 	if (res)
27340c3141e9SAllan Stephens 		goto exit;
27350c3141e9SAllan Stephens 
27360c3141e9SAllan Stephens 	buf = skb_peek(&sk->sk_receive_queue);
27370c3141e9SAllan Stephens 
273892ef0fd5SJens Axboe 	res = tipc_sk_create(sock_net(sock->sk), new_sock, 0, arg->kern);
27390fef8f20SPaul Gortmaker 	if (res)
27400fef8f20SPaul Gortmaker 		goto exit;
2741fdd75ea8SStephen Smalley 	security_sk_clone(sock->sk, new_sock->sk);
27420fef8f20SPaul Gortmaker 
27430fef8f20SPaul Gortmaker 	new_sk = new_sock->sk;
2744301bae56SJon Paul Maloy 	new_tsock = tipc_sk(new_sk);
27450fef8f20SPaul Gortmaker 	msg = buf_msg(buf);
27460c3141e9SAllan Stephens 
2747258f8667SYing Xue 	/* we lock on new_sk; but lockdep sees the lock on sk */
2748258f8667SYing Xue 	lock_sock_nested(new_sk, SINGLE_DEPTH_NESTING);
27490c3141e9SAllan Stephens 
27500c3141e9SAllan Stephens 	/*
27510c3141e9SAllan Stephens 	 * Reject any stray messages received by new socket
27520c3141e9SAllan Stephens 	 * before the socket lock was taken (very, very unlikely)
27530c3141e9SAllan Stephens 	 */
275449afb806STuong Lien 	tsk_rej_rx_queue(new_sk, TIPC_ERR_NO_PORT);
27550c3141e9SAllan Stephens 
27560c3141e9SAllan Stephens 	/* Connect new socket to it's peer */
2757301bae56SJon Paul Maloy 	tipc_sk_finish_conn(new_tsock, msg_origport(msg), msg_orignode(msg));
2758b97bf3fdSPer Liden 
2759095ae612SChristoph Hellwig 	tsk_set_importance(new_sk, msg_importance(msg));
2760b97bf3fdSPer Liden 	if (msg_named(msg)) {
276114623e00SJon Maloy 		new_tsock->conn_addrtype = TIPC_SERVICE_ADDR;
276214623e00SJon Maloy 		msg_set_nametype(&new_tsock->phdr, msg_nametype(msg));
276314623e00SJon Maloy 		msg_set_nameinst(&new_tsock->phdr, msg_nameinst(msg));
2764b97bf3fdSPer Liden 	}
2765b97bf3fdSPer Liden 
2766b97bf3fdSPer Liden 	/*
2767f8dd60deSXin Long 	 * Respond to 'SYN-' by discarding it & returning 'ACK'.
2768f8dd60deSXin Long 	 * Respond to 'SYN+' by queuing it on new socket & returning 'ACK'.
2769b97bf3fdSPer Liden 	 */
2770b97bf3fdSPer Liden 	if (!msg_data_sz(msg)) {
27712e84c60bSJon Paul Maloy 		tsk_advance_rx_queue(sk);
2772b97bf3fdSPer Liden 	} else {
27730c3141e9SAllan Stephens 		__skb_dequeue(&sk->sk_receive_queue);
27740c3141e9SAllan Stephens 		__skb_queue_head(&new_sk->sk_receive_queue, buf);
2775aba79f33SYing Xue 		skb_set_owner_r(buf, new_sk);
2776b97bf3fdSPer Liden 	}
277711a4d6f6STung Nguyen 	iov_iter_kvec(&m.msg_iter, ITER_SOURCE, NULL, 0, 0);
2778f8dd60deSXin Long 	__tipc_sendstream(new_sock, &m, 0);
27790c3141e9SAllan Stephens 	release_sock(new_sk);
2780b97bf3fdSPer Liden exit:
27810c3141e9SAllan Stephens 	release_sock(sk);
2782b97bf3fdSPer Liden 	return res;
2783b97bf3fdSPer Liden }
2784b97bf3fdSPer Liden 
2785b97bf3fdSPer Liden /**
2786247f0f3cSYing Xue  * tipc_shutdown - shutdown socket connection
2787b97bf3fdSPer Liden  * @sock: socket structure
2788e247a8f5SAllan Stephens  * @how: direction to close (must be SHUT_RDWR)
2789b97bf3fdSPer Liden  *
2790b97bf3fdSPer Liden  * Terminates connection (if necessary), then purges socket's receive queue.
2791b97bf3fdSPer Liden  *
2792637b77fdSRandy Dunlap  * Return: 0 on success, errno otherwise
2793b97bf3fdSPer Liden  */
tipc_shutdown(struct socket * sock,int how)2794247f0f3cSYing Xue static int tipc_shutdown(struct socket *sock, int how)
2795b97bf3fdSPer Liden {
27960c3141e9SAllan Stephens 	struct sock *sk = sock->sk;
2797b97bf3fdSPer Liden 	int res;
2798b97bf3fdSPer Liden 
2799e247a8f5SAllan Stephens 	if (how != SHUT_RDWR)
2800e247a8f5SAllan Stephens 		return -EINVAL;
2801b97bf3fdSPer Liden 
28020c3141e9SAllan Stephens 	lock_sock(sk);
2803b97bf3fdSPer Liden 
280401e661ebSTuong Lien 	trace_tipc_sk_shutdown(sk, NULL, TIPC_DUMP_ALL, " ");
28056f00089cSParthasarathy Bhuvaragan 	__tipc_shutdown(sock, TIPC_CONN_SHUTDOWN);
28062a63866cSTetsuo Handa 	sk->sk_shutdown = SHUTDOWN_MASK;
2807b97bf3fdSPer Liden 
28086f00089cSParthasarathy Bhuvaragan 	if (sk->sk_state == TIPC_DISCONNECTING) {
280975031151SYing Xue 		/* Discard any unreceived messages */
281057467e56SYing Xue 		__skb_queue_purge(&sk->sk_receive_queue);
281175031151SYing Xue 
2812b97bf3fdSPer Liden 		res = 0;
28136f00089cSParthasarathy Bhuvaragan 	} else {
2814b97bf3fdSPer Liden 		res = -ENOTCONN;
2815b97bf3fdSPer Liden 	}
28162a63866cSTetsuo Handa 	/* Wake up anyone sleeping in poll. */
28172a63866cSTetsuo Handa 	sk->sk_state_change(sk);
2818b97bf3fdSPer Liden 
28190c3141e9SAllan Stephens 	release_sock(sk);
2820b97bf3fdSPer Liden 	return res;
2821b97bf3fdSPer Liden }
2822b97bf3fdSPer Liden 
tipc_sk_check_probing_state(struct sock * sk,struct sk_buff_head * list)2823afe8792fSJon Maloy static void tipc_sk_check_probing_state(struct sock *sk,
2824afe8792fSJon Maloy 					struct sk_buff_head *list)
2825afe8792fSJon Maloy {
2826afe8792fSJon Maloy 	struct tipc_sock *tsk = tipc_sk(sk);
2827afe8792fSJon Maloy 	u32 pnode = tsk_peer_node(tsk);
2828afe8792fSJon Maloy 	u32 pport = tsk_peer_port(tsk);
2829afe8792fSJon Maloy 	u32 self = tsk_own_node(tsk);
2830afe8792fSJon Maloy 	u32 oport = tsk->portid;
2831afe8792fSJon Maloy 	struct sk_buff *skb;
2832afe8792fSJon Maloy 
2833afe8792fSJon Maloy 	if (tsk->probe_unacked) {
2834afe8792fSJon Maloy 		tipc_set_sk_state(sk, TIPC_DISCONNECTING);
2835afe8792fSJon Maloy 		sk->sk_err = ECONNABORTED;
2836afe8792fSJon Maloy 		tipc_node_remove_conn(sock_net(sk), pnode, pport);
2837afe8792fSJon Maloy 		sk->sk_state_change(sk);
2838afe8792fSJon Maloy 		return;
2839afe8792fSJon Maloy 	}
2840afe8792fSJon Maloy 	/* Prepare new probe */
2841afe8792fSJon Maloy 	skb = tipc_msg_create(CONN_MANAGER, CONN_PROBE, INT_H_SIZE, 0,
2842afe8792fSJon Maloy 			      pnode, self, pport, oport, TIPC_OK);
2843afe8792fSJon Maloy 	if (skb)
2844afe8792fSJon Maloy 		__skb_queue_tail(list, skb);
2845afe8792fSJon Maloy 	tsk->probe_unacked = true;
2846afe8792fSJon Maloy 	sk_reset_timer(sk, &sk->sk_timer, jiffies + CONN_PROBING_INTV);
2847afe8792fSJon Maloy }
2848afe8792fSJon Maloy 
tipc_sk_retry_connect(struct sock * sk,struct sk_buff_head * list)284967879274STung Nguyen static void tipc_sk_retry_connect(struct sock *sk, struct sk_buff_head *list)
285067879274STung Nguyen {
285167879274STung Nguyen 	struct tipc_sock *tsk = tipc_sk(sk);
285267879274STung Nguyen 
285367879274STung Nguyen 	/* Try again later if dest link is congested */
285467879274STung Nguyen 	if (tsk->cong_link_cnt) {
28556a7d8cffSHoang Le 		sk_reset_timer(sk, &sk->sk_timer,
28566a7d8cffSHoang Le 			       jiffies + msecs_to_jiffies(100));
285767879274STung Nguyen 		return;
285867879274STung Nguyen 	}
285967879274STung Nguyen 	/* Prepare SYN for retransmit */
286067879274STung Nguyen 	tipc_msg_skb_clone(&sk->sk_write_queue, list);
286167879274STung Nguyen }
286267879274STung Nguyen 
tipc_sk_timeout(struct timer_list * t)286331b102bbSKees Cook static void tipc_sk_timeout(struct timer_list *t)
286457289015SJon Paul Maloy {
286531b102bbSKees Cook 	struct sock *sk = from_timer(sk, t, sk_timer);
286631b102bbSKees Cook 	struct tipc_sock *tsk = tipc_sk(sk);
2867afe8792fSJon Maloy 	u32 pnode = tsk_peer_node(tsk);
2868afe8792fSJon Maloy 	struct sk_buff_head list;
286967879274STung Nguyen 	int rc = 0;
287057289015SJon Paul Maloy 
2871e654f9f5SJon Maloy 	__skb_queue_head_init(&list);
287257289015SJon Paul Maloy 	bh_lock_sock(sk);
28730d5fcebfSJon Maloy 
28740d5fcebfSJon Maloy 	/* Try again later if socket is busy */
28750d5fcebfSJon Maloy 	if (sock_owned_by_user(sk)) {
28760d5fcebfSJon Maloy 		sk_reset_timer(sk, &sk->sk_timer, jiffies + HZ / 20);
2877afe8792fSJon Maloy 		bh_unlock_sock(sk);
287891a4a3ebSTung Nguyen 		sock_put(sk);
2879afe8792fSJon Maloy 		return;
28806c9808ceSJon Paul Maloy 	}
288157289015SJon Paul Maloy 
2882afe8792fSJon Maloy 	if (sk->sk_state == TIPC_ESTABLISHED)
2883afe8792fSJon Maloy 		tipc_sk_check_probing_state(sk, &list);
288467879274STung Nguyen 	else if (sk->sk_state == TIPC_CONNECTING)
288567879274STung Nguyen 		tipc_sk_retry_connect(sk, &list);
2886afe8792fSJon Maloy 
288757289015SJon Paul Maloy 	bh_unlock_sock(sk);
2888afe8792fSJon Maloy 
2889afe8792fSJon Maloy 	if (!skb_queue_empty(&list))
289067879274STung Nguyen 		rc = tipc_node_xmit(sock_net(sk), &list, pnode, tsk->portid);
2891afe8792fSJon Maloy 
289267879274STung Nguyen 	/* SYN messages may cause link congestion */
289367879274STung Nguyen 	if (rc == -ELINKCONG) {
289467879274STung Nguyen 		tipc_dest_push(&tsk->cong_links, pnode, 0);
289567879274STung Nguyen 		tsk->cong_link_cnt = 1;
289667879274STung Nguyen 	}
289707f6c4bcSYing Xue 	sock_put(sk);
289857289015SJon Paul Maloy }
289957289015SJon Paul Maloy 
tipc_sk_publish(struct tipc_sock * tsk,struct tipc_uaddr * ua)290050a3499aSJon Maloy static int tipc_sk_publish(struct tipc_sock *tsk, struct tipc_uaddr *ua)
29010fc87aaeSJon Paul Maloy {
2902d6fb7e9cSParthasarathy Bhuvaragan 	struct sock *sk = &tsk->sk;
2903d6fb7e9cSParthasarathy Bhuvaragan 	struct net *net = sock_net(sk);
290450a3499aSJon Maloy 	struct tipc_socket_addr skaddr;
290550a3499aSJon Maloy 	struct publication *p;
29060fc87aaeSJon Paul Maloy 	u32 key;
29070fc87aaeSJon Paul Maloy 
2908d6fb7e9cSParthasarathy Bhuvaragan 	if (tipc_sk_connected(sk))
29090fc87aaeSJon Paul Maloy 		return -EINVAL;
291007f6c4bcSYing Xue 	key = tsk->portid + tsk->pub_count + 1;
291107f6c4bcSYing Xue 	if (key == tsk->portid)
29120fc87aaeSJon Paul Maloy 		return -EADDRINUSE;
291350a3499aSJon Maloy 	skaddr.ref = tsk->portid;
291450a3499aSJon Maloy 	skaddr.node = tipc_own_addr(net);
291550a3499aSJon Maloy 	p = tipc_nametbl_publish(net, ua, &skaddr, key);
291650a3499aSJon Maloy 	if (unlikely(!p))
29170fc87aaeSJon Paul Maloy 		return -EINVAL;
29180fc87aaeSJon Paul Maloy 
291950a3499aSJon Maloy 	list_add(&p->binding_sock, &tsk->publications);
2920301bae56SJon Paul Maloy 	tsk->pub_count++;
292150a3499aSJon Maloy 	tsk->published = true;
29220fc87aaeSJon Paul Maloy 	return 0;
29230fc87aaeSJon Paul Maloy }
29240fc87aaeSJon Paul Maloy 
tipc_sk_withdraw(struct tipc_sock * tsk,struct tipc_uaddr * ua)29252c98da07SJon Maloy static int tipc_sk_withdraw(struct tipc_sock *tsk, struct tipc_uaddr *ua)
29260fc87aaeSJon Paul Maloy {
2927f2f9800dSYing Xue 	struct net *net = sock_net(&tsk->sk);
29282c98da07SJon Maloy 	struct publication *safe, *p;
29292c98da07SJon Maloy 	struct tipc_uaddr _ua;
29300fc87aaeSJon Paul Maloy 	int rc = -EINVAL;
29310fc87aaeSJon Paul Maloy 
2932998d3907SJon Maloy 	list_for_each_entry_safe(p, safe, &tsk->publications, binding_sock) {
29332c98da07SJon Maloy 		if (!ua) {
29342c98da07SJon Maloy 			tipc_uaddr(&_ua, TIPC_SERVICE_RANGE, p->scope,
29352c98da07SJon Maloy 				   p->sr.type, p->sr.lower, p->sr.upper);
29362c98da07SJon Maloy 			tipc_nametbl_withdraw(net, &_ua, &p->sk, p->key);
29370fc87aaeSJon Paul Maloy 			continue;
29382c98da07SJon Maloy 		}
29392c98da07SJon Maloy 		/* Unbind specific publication */
29402c98da07SJon Maloy 		if (p->scope != ua->scope)
29410fc87aaeSJon Paul Maloy 			continue;
29422c98da07SJon Maloy 		if (p->sr.type != ua->sr.type)
29430fc87aaeSJon Paul Maloy 			continue;
29442c98da07SJon Maloy 		if (p->sr.lower != ua->sr.lower)
29452c98da07SJon Maloy 			continue;
29462c98da07SJon Maloy 		if (p->sr.upper != ua->sr.upper)
29470fc87aaeSJon Paul Maloy 			break;
29482c98da07SJon Maloy 		tipc_nametbl_withdraw(net, ua, &p->sk, p->key);
29490fc87aaeSJon Paul Maloy 		rc = 0;
29500fc87aaeSJon Paul Maloy 		break;
29510fc87aaeSJon Paul Maloy 	}
29522c98da07SJon Maloy 	if (list_empty(&tsk->publications)) {
2953301bae56SJon Paul Maloy 		tsk->published = 0;
29542c98da07SJon Maloy 		rc = 0;
29552c98da07SJon Maloy 	}
29560fc87aaeSJon Paul Maloy 	return rc;
29570fc87aaeSJon Paul Maloy }
29580fc87aaeSJon Paul Maloy 
29595a9ee0beSJon Paul Maloy /* tipc_sk_reinit: set non-zero address in all existing sockets
29605a9ee0beSJon Paul Maloy  *                 when we go from standalone to network mode.
29615a9ee0beSJon Paul Maloy  */
tipc_sk_reinit(struct net * net)2962e05b31f4SYing Xue void tipc_sk_reinit(struct net *net)
29635a9ee0beSJon Paul Maloy {
2964e05b31f4SYing Xue 	struct tipc_net *tn = net_generic(net, tipc_net_id);
296540f9f439SHerbert Xu 	struct rhashtable_iter iter;
296607f6c4bcSYing Xue 	struct tipc_sock *tsk;
29675a9ee0beSJon Paul Maloy 	struct tipc_msg *msg;
29685a9ee0beSJon Paul Maloy 
296940f9f439SHerbert Xu 	rhashtable_walk_enter(&tn->sk_rht, &iter);
297040f9f439SHerbert Xu 
297140f9f439SHerbert Xu 	do {
297297a6ec4aSTom Herbert 		rhashtable_walk_start(&iter);
297340f9f439SHerbert Xu 
297440f9f439SHerbert Xu 		while ((tsk = rhashtable_walk_next(&iter)) && !IS_ERR(tsk)) {
297515ef70e2SCong Wang 			sock_hold(&tsk->sk);
297615ef70e2SCong Wang 			rhashtable_walk_stop(&iter);
297715ef70e2SCong Wang 			lock_sock(&tsk->sk);
2978301bae56SJon Paul Maloy 			msg = &tsk->phdr;
297923fd3eacSJon Maloy 			msg_set_prevnode(msg, tipc_own_addr(net));
298023fd3eacSJon Maloy 			msg_set_orignode(msg, tipc_own_addr(net));
298115ef70e2SCong Wang 			release_sock(&tsk->sk);
298215ef70e2SCong Wang 			rhashtable_walk_start(&iter);
298315ef70e2SCong Wang 			sock_put(&tsk->sk);
29845a9ee0beSJon Paul Maloy 		}
298597a6ec4aSTom Herbert 
298640f9f439SHerbert Xu 		rhashtable_walk_stop(&iter);
298740f9f439SHerbert Xu 	} while (tsk == ERR_PTR(-EAGAIN));
2988bd583fe3SCong Wang 
2989bd583fe3SCong Wang 	rhashtable_walk_exit(&iter);
299007f6c4bcSYing Xue }
29915a9ee0beSJon Paul Maloy 
tipc_sk_lookup(struct net * net,u32 portid)2992e05b31f4SYing Xue static struct tipc_sock *tipc_sk_lookup(struct net *net, u32 portid)
2993808d90f9SJon Paul Maloy {
2994e05b31f4SYing Xue 	struct tipc_net *tn = net_generic(net, tipc_net_id);
2995808d90f9SJon Paul Maloy 	struct tipc_sock *tsk;
2996808d90f9SJon Paul Maloy 
299707f6c4bcSYing Xue 	rcu_read_lock();
2998ab818362STaehee Yoo 	tsk = rhashtable_lookup(&tn->sk_rht, &portid, tsk_rht_params);
299907f6c4bcSYing Xue 	if (tsk)
3000808d90f9SJon Paul Maloy 		sock_hold(&tsk->sk);
300107f6c4bcSYing Xue 	rcu_read_unlock();
300207f6c4bcSYing Xue 
3003808d90f9SJon Paul Maloy 	return tsk;
3004808d90f9SJon Paul Maloy }
3005808d90f9SJon Paul Maloy 
tipc_sk_insert(struct tipc_sock * tsk)300607f6c4bcSYing Xue static int tipc_sk_insert(struct tipc_sock *tsk)
3007808d90f9SJon Paul Maloy {
3008e05b31f4SYing Xue 	struct sock *sk = &tsk->sk;
3009e05b31f4SYing Xue 	struct net *net = sock_net(sk);
3010e05b31f4SYing Xue 	struct tipc_net *tn = net_generic(net, tipc_net_id);
301107f6c4bcSYing Xue 	u32 remaining = (TIPC_MAX_PORT - TIPC_MIN_PORT) + 1;
30128032bf12SJason A. Donenfeld 	u32 portid = get_random_u32_below(remaining) + TIPC_MIN_PORT;
3013808d90f9SJon Paul Maloy 
301407f6c4bcSYing Xue 	while (remaining--) {
301507f6c4bcSYing Xue 		portid++;
301607f6c4bcSYing Xue 		if ((portid < TIPC_MIN_PORT) || (portid > TIPC_MAX_PORT))
301707f6c4bcSYing Xue 			portid = TIPC_MIN_PORT;
301807f6c4bcSYing Xue 		tsk->portid = portid;
3019808d90f9SJon Paul Maloy 		sock_hold(&tsk->sk);
30206cca7289SHerbert Xu 		if (!rhashtable_lookup_insert_fast(&tn->sk_rht, &tsk->node,
30216cca7289SHerbert Xu 						   tsk_rht_params))
302207f6c4bcSYing Xue 			return 0;
3023808d90f9SJon Paul Maloy 		sock_put(&tsk->sk);
3024808d90f9SJon Paul Maloy 	}
3025808d90f9SJon Paul Maloy 
302607f6c4bcSYing Xue 	return -1;
302707f6c4bcSYing Xue }
302807f6c4bcSYing Xue 
tipc_sk_remove(struct tipc_sock * tsk)302907f6c4bcSYing Xue static void tipc_sk_remove(struct tipc_sock *tsk)
303007f6c4bcSYing Xue {
303107f6c4bcSYing Xue 	struct sock *sk = &tsk->sk;
3032e05b31f4SYing Xue 	struct tipc_net *tn = net_generic(sock_net(sk), tipc_net_id);
303307f6c4bcSYing Xue 
30346cca7289SHerbert Xu 	if (!rhashtable_remove_fast(&tn->sk_rht, &tsk->node, tsk_rht_params)) {
303541c6d650SReshetova, Elena 		WARN_ON(refcount_read(&sk->sk_refcnt) == 1);
303607f6c4bcSYing Xue 		__sock_put(sk);
303707f6c4bcSYing Xue 	}
303807f6c4bcSYing Xue }
303907f6c4bcSYing Xue 
30406cca7289SHerbert Xu static const struct rhashtable_params tsk_rht_params = {
304107f6c4bcSYing Xue 	.nelem_hint = 192,
304207f6c4bcSYing Xue 	.head_offset = offsetof(struct tipc_sock, node),
304307f6c4bcSYing Xue 	.key_offset = offsetof(struct tipc_sock, portid),
304407f6c4bcSYing Xue 	.key_len = sizeof(u32), /* portid */
3045446c89acSHerbert Xu 	.max_size = 1048576,
3046446c89acSHerbert Xu 	.min_size = 256,
3047b5e2c150SThomas Graf 	.automatic_shrinking = true,
304807f6c4bcSYing Xue };
304907f6c4bcSYing Xue 
tipc_sk_rht_init(struct net * net)30506cca7289SHerbert Xu int tipc_sk_rht_init(struct net *net)
30516cca7289SHerbert Xu {
30526cca7289SHerbert Xu 	struct tipc_net *tn = net_generic(net, tipc_net_id);
30536cca7289SHerbert Xu 
30546cca7289SHerbert Xu 	return rhashtable_init(&tn->sk_rht, &tsk_rht_params);
305507f6c4bcSYing Xue }
305607f6c4bcSYing Xue 
tipc_sk_rht_destroy(struct net * net)3057e05b31f4SYing Xue void tipc_sk_rht_destroy(struct net *net)
305807f6c4bcSYing Xue {
3059e05b31f4SYing Xue 	struct tipc_net *tn = net_generic(net, tipc_net_id);
3060e05b31f4SYing Xue 
306107f6c4bcSYing Xue 	/* Wait for socket readers to complete */
306207f6c4bcSYing Xue 	synchronize_net();
306307f6c4bcSYing Xue 
3064e05b31f4SYing Xue 	rhashtable_destroy(&tn->sk_rht);
306507f6c4bcSYing Xue }
306607f6c4bcSYing Xue 
tipc_sk_join(struct tipc_sock * tsk,struct tipc_group_req * mreq)306775da2163SJon Maloy static int tipc_sk_join(struct tipc_sock *tsk, struct tipc_group_req *mreq)
306875da2163SJon Maloy {
306975da2163SJon Maloy 	struct net *net = sock_net(&tsk->sk);
307075da2163SJon Maloy 	struct tipc_group *grp = tsk->group;
307175da2163SJon Maloy 	struct tipc_msg *hdr = &tsk->phdr;
307250a3499aSJon Maloy 	struct tipc_uaddr ua;
307375da2163SJon Maloy 	int rc;
307475da2163SJon Maloy 
307575da2163SJon Maloy 	if (mreq->type < TIPC_RESERVED_TYPES)
307675da2163SJon Maloy 		return -EACCES;
3077232d07b7SJon Maloy 	if (mreq->scope > TIPC_NODE_SCOPE)
3078232d07b7SJon Maloy 		return -EINVAL;
307950a3499aSJon Maloy 	if (mreq->scope != TIPC_NODE_SCOPE)
308050a3499aSJon Maloy 		mreq->scope = TIPC_CLUSTER_SCOPE;
308175da2163SJon Maloy 	if (grp)
308275da2163SJon Maloy 		return -EACCES;
308360c25306SJon Maloy 	grp = tipc_group_create(net, tsk->portid, mreq, &tsk->group_is_open);
308475da2163SJon Maloy 	if (!grp)
308575da2163SJon Maloy 		return -ENOMEM;
308675da2163SJon Maloy 	tsk->group = grp;
308775da2163SJon Maloy 	msg_set_lookup_scope(hdr, mreq->scope);
308875da2163SJon Maloy 	msg_set_nametype(hdr, mreq->type);
308975da2163SJon Maloy 	msg_set_dest_droppable(hdr, true);
309050a3499aSJon Maloy 	tipc_uaddr(&ua, TIPC_SERVICE_RANGE, mreq->scope,
309150a3499aSJon Maloy 		   mreq->type, mreq->instance, mreq->instance);
30926e44867bSJon Maloy 	tipc_nametbl_build_group(net, grp, &ua);
309350a3499aSJon Maloy 	rc = tipc_sk_publish(tsk, &ua);
3094e233df01SCong Wang 	if (rc) {
309575da2163SJon Maloy 		tipc_group_delete(net, grp);
3096e233df01SCong Wang 		tsk->group = NULL;
3097febafc84SJon Maloy 		return rc;
3098e233df01SCong Wang 	}
3099d12d2e12SJon Maloy 	/* Eliminate any risk that a broadcast overtakes sent JOINs */
3100399574d4SJon Maloy 	tsk->mc_method.rcast = true;
3101399574d4SJon Maloy 	tsk->mc_method.mandatory = true;
3102d12d2e12SJon Maloy 	tipc_group_join(net, grp, &tsk->sk.sk_rcvbuf);
310375da2163SJon Maloy 	return rc;
310475da2163SJon Maloy }
310575da2163SJon Maloy 
tipc_sk_leave(struct tipc_sock * tsk)310675da2163SJon Maloy static int tipc_sk_leave(struct tipc_sock *tsk)
310775da2163SJon Maloy {
310875da2163SJon Maloy 	struct net *net = sock_net(&tsk->sk);
310975da2163SJon Maloy 	struct tipc_group *grp = tsk->group;
31102c98da07SJon Maloy 	struct tipc_uaddr ua;
311175da2163SJon Maloy 	int scope;
311275da2163SJon Maloy 
311375da2163SJon Maloy 	if (!grp)
311475da2163SJon Maloy 		return -EINVAL;
31152c98da07SJon Maloy 	ua.addrtype = TIPC_SERVICE_RANGE;
31162c98da07SJon Maloy 	tipc_group_self(grp, &ua.sr, &scope);
31172c98da07SJon Maloy 	ua.scope = scope;
311875da2163SJon Maloy 	tipc_group_delete(net, grp);
311975da2163SJon Maloy 	tsk->group = NULL;
31202c98da07SJon Maloy 	tipc_sk_withdraw(tsk, &ua);
312175da2163SJon Maloy 	return 0;
312275da2163SJon Maloy }
312375da2163SJon Maloy 
3124808d90f9SJon Paul Maloy /**
3125247f0f3cSYing Xue  * tipc_setsockopt - set socket option
3126b97bf3fdSPer Liden  * @sock: socket structure
3127b97bf3fdSPer Liden  * @lvl: option level
3128b97bf3fdSPer Liden  * @opt: option identifier
3129b97bf3fdSPer Liden  * @ov: pointer to new option value
3130b97bf3fdSPer Liden  * @ol: length of option value
3131b97bf3fdSPer Liden  *
3132b97bf3fdSPer Liden  * For stream sockets only, accepts and ignores all IPPROTO_TCP options
3133b97bf3fdSPer Liden  * (to ease compatibility).
3134b97bf3fdSPer Liden  *
3135637b77fdSRandy Dunlap  * Return: 0 on success, errno otherwise
3136b97bf3fdSPer Liden  */
tipc_setsockopt(struct socket * sock,int lvl,int opt,sockptr_t ov,unsigned int ol)3137247f0f3cSYing Xue static int tipc_setsockopt(struct socket *sock, int lvl, int opt,
3138a7b75c5aSChristoph Hellwig 			   sockptr_t ov, unsigned int ol)
3139b97bf3fdSPer Liden {
31400c3141e9SAllan Stephens 	struct sock *sk = sock->sk;
314158ed9442SJon Paul Maloy 	struct tipc_sock *tsk = tipc_sk(sk);
314275da2163SJon Maloy 	struct tipc_group_req mreq;
314301fd12bbSJon Paul Maloy 	u32 value = 0;
3144a08ef476SDan Carpenter 	int res = 0;
3145b97bf3fdSPer Liden 
3146b97bf3fdSPer Liden 	if ((lvl == IPPROTO_TCP) && (sock->type == SOCK_STREAM))
3147b97bf3fdSPer Liden 		return 0;
3148b97bf3fdSPer Liden 	if (lvl != SOL_TIPC)
3149b97bf3fdSPer Liden 		return -ENOPROTOOPT;
315001fd12bbSJon Paul Maloy 
315101fd12bbSJon Paul Maloy 	switch (opt) {
315201fd12bbSJon Paul Maloy 	case TIPC_IMPORTANCE:
315301fd12bbSJon Paul Maloy 	case TIPC_SRC_DROPPABLE:
315401fd12bbSJon Paul Maloy 	case TIPC_DEST_DROPPABLE:
315501fd12bbSJon Paul Maloy 	case TIPC_CONN_TIMEOUT:
3156c0bceb97SJon Maloy 	case TIPC_NODELAY:
3157b97bf3fdSPer Liden 		if (ol < sizeof(value))
3158b97bf3fdSPer Liden 			return -EINVAL;
3159a7b75c5aSChristoph Hellwig 		if (copy_from_sockptr(&value, ov, sizeof(u32)))
316075da2163SJon Maloy 			return -EFAULT;
316175da2163SJon Maloy 		break;
316275da2163SJon Maloy 	case TIPC_GROUP_JOIN:
316375da2163SJon Maloy 		if (ol < sizeof(mreq))
316475da2163SJon Maloy 			return -EINVAL;
3165a7b75c5aSChristoph Hellwig 		if (copy_from_sockptr(&mreq, ov, sizeof(mreq)))
316675da2163SJon Maloy 			return -EFAULT;
316701fd12bbSJon Paul Maloy 		break;
316801fd12bbSJon Paul Maloy 	default:
3169a7b75c5aSChristoph Hellwig 		if (!sockptr_is_null(ov) || ol)
317001fd12bbSJon Paul Maloy 			return -EINVAL;
317101fd12bbSJon Paul Maloy 	}
3172b97bf3fdSPer Liden 
31730c3141e9SAllan Stephens 	lock_sock(sk);
3174b97bf3fdSPer Liden 
3175b97bf3fdSPer Liden 	switch (opt) {
3176b97bf3fdSPer Liden 	case TIPC_IMPORTANCE:
3177095ae612SChristoph Hellwig 		res = tsk_set_importance(sk, value);
3178b97bf3fdSPer Liden 		break;
3179b97bf3fdSPer Liden 	case TIPC_SRC_DROPPABLE:
3180b97bf3fdSPer Liden 		if (sock->type != SOCK_STREAM)
3181301bae56SJon Paul Maloy 			tsk_set_unreliable(tsk, value);
3182b97bf3fdSPer Liden 		else
3183b97bf3fdSPer Liden 			res = -ENOPROTOOPT;
3184b97bf3fdSPer Liden 		break;
3185b97bf3fdSPer Liden 	case TIPC_DEST_DROPPABLE:
3186301bae56SJon Paul Maloy 		tsk_set_unreturnable(tsk, value);
3187b97bf3fdSPer Liden 		break;
3188b97bf3fdSPer Liden 	case TIPC_CONN_TIMEOUT:
3189a0f40f02SAllan Stephens 		tipc_sk(sk)->conn_timeout = value;
3190b97bf3fdSPer Liden 		break;
319101fd12bbSJon Paul Maloy 	case TIPC_MCAST_BROADCAST:
319201fd12bbSJon Paul Maloy 		tsk->mc_method.rcast = false;
319301fd12bbSJon Paul Maloy 		tsk->mc_method.mandatory = true;
319401fd12bbSJon Paul Maloy 		break;
319501fd12bbSJon Paul Maloy 	case TIPC_MCAST_REPLICAST:
319601fd12bbSJon Paul Maloy 		tsk->mc_method.rcast = true;
319701fd12bbSJon Paul Maloy 		tsk->mc_method.mandatory = true;
319801fd12bbSJon Paul Maloy 		break;
319975da2163SJon Maloy 	case TIPC_GROUP_JOIN:
320075da2163SJon Maloy 		res = tipc_sk_join(tsk, &mreq);
320175da2163SJon Maloy 		break;
320275da2163SJon Maloy 	case TIPC_GROUP_LEAVE:
320375da2163SJon Maloy 		res = tipc_sk_leave(tsk);
320475da2163SJon Maloy 		break;
3205c0bceb97SJon Maloy 	case TIPC_NODELAY:
3206c0bceb97SJon Maloy 		tsk->nodelay = !!value;
3207c0bceb97SJon Maloy 		tsk_set_nagle(tsk);
3208c0bceb97SJon Maloy 		break;
3209b97bf3fdSPer Liden 	default:
3210b97bf3fdSPer Liden 		res = -EINVAL;
3211b97bf3fdSPer Liden 	}
3212b97bf3fdSPer Liden 
32130c3141e9SAllan Stephens 	release_sock(sk);
32140c3141e9SAllan Stephens 
3215b97bf3fdSPer Liden 	return res;
3216b97bf3fdSPer Liden }
3217b97bf3fdSPer Liden 
3218b97bf3fdSPer Liden /**
3219247f0f3cSYing Xue  * tipc_getsockopt - get socket option
3220b97bf3fdSPer Liden  * @sock: socket structure
3221b97bf3fdSPer Liden  * @lvl: option level
3222b97bf3fdSPer Liden  * @opt: option identifier
3223b97bf3fdSPer Liden  * @ov: receptacle for option value
3224b97bf3fdSPer Liden  * @ol: receptacle for length of option value
3225b97bf3fdSPer Liden  *
3226b97bf3fdSPer Liden  * For stream sockets only, returns 0 length result for all IPPROTO_TCP options
3227b97bf3fdSPer Liden  * (to ease compatibility).
3228b97bf3fdSPer Liden  *
3229637b77fdSRandy Dunlap  * Return: 0 on success, errno otherwise
3230b97bf3fdSPer Liden  */
tipc_getsockopt(struct socket * sock,int lvl,int opt,char __user * ov,int __user * ol)3231247f0f3cSYing Xue static int tipc_getsockopt(struct socket *sock, int lvl, int opt,
3232247f0f3cSYing Xue 			   char __user *ov, int __user *ol)
3233b97bf3fdSPer Liden {
32340c3141e9SAllan Stephens 	struct sock *sk = sock->sk;
323558ed9442SJon Paul Maloy 	struct tipc_sock *tsk = tipc_sk(sk);
3236b6f88d9cSJon Maloy 	struct tipc_service_range seq;
323775da2163SJon Maloy 	int len, scope;
3238b97bf3fdSPer Liden 	u32 value;
3239b97bf3fdSPer Liden 	int res;
3240b97bf3fdSPer Liden 
3241b97bf3fdSPer Liden 	if ((lvl == IPPROTO_TCP) && (sock->type == SOCK_STREAM))
3242b97bf3fdSPer Liden 		return put_user(0, ol);
3243b97bf3fdSPer Liden 	if (lvl != SOL_TIPC)
3244b97bf3fdSPer Liden 		return -ENOPROTOOPT;
32452db9983aSAllan Stephens 	res = get_user(len, ol);
32462db9983aSAllan Stephens 	if (res)
3247b97bf3fdSPer Liden 		return res;
3248b97bf3fdSPer Liden 
32490c3141e9SAllan Stephens 	lock_sock(sk);
3250b97bf3fdSPer Liden 
3251b97bf3fdSPer Liden 	switch (opt) {
3252b97bf3fdSPer Liden 	case TIPC_IMPORTANCE:
3253301bae56SJon Paul Maloy 		value = tsk_importance(tsk);
3254b97bf3fdSPer Liden 		break;
3255b97bf3fdSPer Liden 	case TIPC_SRC_DROPPABLE:
3256301bae56SJon Paul Maloy 		value = tsk_unreliable(tsk);
3257b97bf3fdSPer Liden 		break;
3258b97bf3fdSPer Liden 	case TIPC_DEST_DROPPABLE:
3259301bae56SJon Paul Maloy 		value = tsk_unreturnable(tsk);
3260b97bf3fdSPer Liden 		break;
3261b97bf3fdSPer Liden 	case TIPC_CONN_TIMEOUT:
3262301bae56SJon Paul Maloy 		value = tsk->conn_timeout;
32630c3141e9SAllan Stephens 		/* no need to set "res", since already 0 at this point */
3264b97bf3fdSPer Liden 		break;
32656650613dS[email protected] 	case TIPC_NODE_RECVQ_DEPTH:
32669da3d475SYing Xue 		value = 0; /* was tipc_queue_size, now obsolete */
32676650613dS[email protected] 		break;
32686650613dS[email protected] 	case TIPC_SOCK_RECVQ_DEPTH:
32696650613dS[email protected] 		value = skb_queue_len(&sk->sk_receive_queue);
32706650613dS[email protected] 		break;
327142e5425aSTung Nguyen 	case TIPC_SOCK_RECVQ_USED:
327242e5425aSTung Nguyen 		value = sk_rmem_alloc_get(sk);
327342e5425aSTung Nguyen 		break;
327475da2163SJon Maloy 	case TIPC_GROUP_JOIN:
327575da2163SJon Maloy 		seq.type = 0;
327675da2163SJon Maloy 		if (tsk->group)
327775da2163SJon Maloy 			tipc_group_self(tsk->group, &seq, &scope);
327875da2163SJon Maloy 		value = seq.type;
327975da2163SJon Maloy 		break;
3280b97bf3fdSPer Liden 	default:
3281b97bf3fdSPer Liden 		res = -EINVAL;
3282b97bf3fdSPer Liden 	}
3283b97bf3fdSPer Liden 
32840c3141e9SAllan Stephens 	release_sock(sk);
32850c3141e9SAllan Stephens 
328625860c3bSPaul Gortmaker 	if (res)
328725860c3bSPaul Gortmaker 		return res;	/* "get" failed */
3288b97bf3fdSPer Liden 
328925860c3bSPaul Gortmaker 	if (len < sizeof(value))
329025860c3bSPaul Gortmaker 		return -EINVAL;
329125860c3bSPaul Gortmaker 
329225860c3bSPaul Gortmaker 	if (copy_to_user(ov, &value, sizeof(value)))
329325860c3bSPaul Gortmaker 		return -EFAULT;
329425860c3bSPaul Gortmaker 
329525860c3bSPaul Gortmaker 	return put_user(sizeof(value), ol);
3296b97bf3fdSPer Liden }
3297b97bf3fdSPer Liden 
tipc_ioctl(struct socket * sock,unsigned int cmd,unsigned long arg)3298f2f9800dSYing Xue static int tipc_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
329978acb1f9SErik Hugne {
33003e5cf362SJon Maloy 	struct net *net = sock_net(sock->sk);
33013e5cf362SJon Maloy 	struct tipc_sioc_nodeid_req nr = {0};
330278acb1f9SErik Hugne 	struct tipc_sioc_ln_req lnr;
330378acb1f9SErik Hugne 	void __user *argp = (void __user *)arg;
330478acb1f9SErik Hugne 
330578acb1f9SErik Hugne 	switch (cmd) {
330678acb1f9SErik Hugne 	case SIOCGETLINKNAME:
330778acb1f9SErik Hugne 		if (copy_from_user(&lnr, argp, sizeof(lnr)))
330878acb1f9SErik Hugne 			return -EFAULT;
33093e5cf362SJon Maloy 		if (!tipc_node_get_linkname(net,
3310f2f9800dSYing Xue 					    lnr.bearer_id & 0xffff, lnr.peer,
331178acb1f9SErik Hugne 					    lnr.linkname, TIPC_MAX_LINK_NAME)) {
331278acb1f9SErik Hugne 			if (copy_to_user(argp, &lnr, sizeof(lnr)))
331378acb1f9SErik Hugne 				return -EFAULT;
331478acb1f9SErik Hugne 			return 0;
331578acb1f9SErik Hugne 		}
331678acb1f9SErik Hugne 		return -EADDRNOTAVAIL;
33173e5cf362SJon Maloy 	case SIOCGETNODEID:
33183e5cf362SJon Maloy 		if (copy_from_user(&nr, argp, sizeof(nr)))
33193e5cf362SJon Maloy 			return -EFAULT;
33203e5cf362SJon Maloy 		if (!tipc_node_get_id(net, nr.peer, nr.node_id))
33213e5cf362SJon Maloy 			return -EADDRNOTAVAIL;
33223e5cf362SJon Maloy 		if (copy_to_user(argp, &nr, sizeof(nr)))
33233e5cf362SJon Maloy 			return -EFAULT;
33243e5cf362SJon Maloy 		return 0;
332578acb1f9SErik Hugne 	default:
332678acb1f9SErik Hugne 		return -ENOIOCTLCMD;
332778acb1f9SErik Hugne 	}
332878acb1f9SErik Hugne }
332978acb1f9SErik Hugne 
tipc_socketpair(struct socket * sock1,struct socket * sock2)333070b03759SErik Hugne static int tipc_socketpair(struct socket *sock1, struct socket *sock2)
333170b03759SErik Hugne {
333270b03759SErik Hugne 	struct tipc_sock *tsk2 = tipc_sk(sock2->sk);
333370b03759SErik Hugne 	struct tipc_sock *tsk1 = tipc_sk(sock1->sk);
333466bc1e8dSErik Hugne 	u32 onode = tipc_own_addr(sock_net(sock1->sk));
333570b03759SErik Hugne 
333666bc1e8dSErik Hugne 	tsk1->peer.family = AF_TIPC;
3337b6f88d9cSJon Maloy 	tsk1->peer.addrtype = TIPC_SOCKET_ADDR;
333866bc1e8dSErik Hugne 	tsk1->peer.scope = TIPC_NODE_SCOPE;
333966bc1e8dSErik Hugne 	tsk1->peer.addr.id.ref = tsk2->portid;
334066bc1e8dSErik Hugne 	tsk1->peer.addr.id.node = onode;
334166bc1e8dSErik Hugne 	tsk2->peer.family = AF_TIPC;
3342b6f88d9cSJon Maloy 	tsk2->peer.addrtype = TIPC_SOCKET_ADDR;
334366bc1e8dSErik Hugne 	tsk2->peer.scope = TIPC_NODE_SCOPE;
334466bc1e8dSErik Hugne 	tsk2->peer.addr.id.ref = tsk1->portid;
334566bc1e8dSErik Hugne 	tsk2->peer.addr.id.node = onode;
334666bc1e8dSErik Hugne 
334766bc1e8dSErik Hugne 	tipc_sk_finish_conn(tsk1, tsk2->portid, onode);
334866bc1e8dSErik Hugne 	tipc_sk_finish_conn(tsk2, tsk1->portid, onode);
334970b03759SErik Hugne 	return 0;
335070b03759SErik Hugne }
335170b03759SErik Hugne 
3352ae86b9e3SBen Hutchings /* Protocol switches for the various types of TIPC sockets */
3353ae86b9e3SBen Hutchings 
3354bca65eaeSFlorian Westphal static const struct proto_ops msg_ops = {
3355b97bf3fdSPer Liden 	.owner		= THIS_MODULE,
3356b97bf3fdSPer Liden 	.family		= AF_TIPC,
3357247f0f3cSYing Xue 	.release	= tipc_release,
3358247f0f3cSYing Xue 	.bind		= tipc_bind,
3359247f0f3cSYing Xue 	.connect	= tipc_connect,
336066bc1e8dSErik Hugne 	.socketpair	= tipc_socketpair,
3361245f3d34SYing Xue 	.accept		= sock_no_accept,
3362247f0f3cSYing Xue 	.getname	= tipc_getname,
3363a11e1d43SLinus Torvalds 	.poll		= tipc_poll,
336478acb1f9SErik Hugne 	.ioctl		= tipc_ioctl,
3365245f3d34SYing Xue 	.listen		= sock_no_listen,
3366247f0f3cSYing Xue 	.shutdown	= tipc_shutdown,
3367247f0f3cSYing Xue 	.setsockopt	= tipc_setsockopt,
3368247f0f3cSYing Xue 	.getsockopt	= tipc_getsockopt,
3369247f0f3cSYing Xue 	.sendmsg	= tipc_sendmsg,
3370247f0f3cSYing Xue 	.recvmsg	= tipc_recvmsg,
33715eee6a6dSAllan Stephens 	.mmap		= sock_no_mmap,
3372b97bf3fdSPer Liden };
3373b97bf3fdSPer Liden 
3374bca65eaeSFlorian Westphal static const struct proto_ops packet_ops = {
3375b97bf3fdSPer Liden 	.owner		= THIS_MODULE,
3376b97bf3fdSPer Liden 	.family		= AF_TIPC,
3377247f0f3cSYing Xue 	.release	= tipc_release,
3378247f0f3cSYing Xue 	.bind		= tipc_bind,
3379247f0f3cSYing Xue 	.connect	= tipc_connect,
338070b03759SErik Hugne 	.socketpair	= tipc_socketpair,
3381247f0f3cSYing Xue 	.accept		= tipc_accept,
3382247f0f3cSYing Xue 	.getname	= tipc_getname,
3383a11e1d43SLinus Torvalds 	.poll		= tipc_poll,
338478acb1f9SErik Hugne 	.ioctl		= tipc_ioctl,
3385247f0f3cSYing Xue 	.listen		= tipc_listen,
3386247f0f3cSYing Xue 	.shutdown	= tipc_shutdown,
3387247f0f3cSYing Xue 	.setsockopt	= tipc_setsockopt,
3388247f0f3cSYing Xue 	.getsockopt	= tipc_getsockopt,
3389247f0f3cSYing Xue 	.sendmsg	= tipc_send_packet,
3390247f0f3cSYing Xue 	.recvmsg	= tipc_recvmsg,
33915eee6a6dSAllan Stephens 	.mmap		= sock_no_mmap,
3392b97bf3fdSPer Liden };
3393b97bf3fdSPer Liden 
3394bca65eaeSFlorian Westphal static const struct proto_ops stream_ops = {
3395b97bf3fdSPer Liden 	.owner		= THIS_MODULE,
3396b97bf3fdSPer Liden 	.family		= AF_TIPC,
3397247f0f3cSYing Xue 	.release	= tipc_release,
3398247f0f3cSYing Xue 	.bind		= tipc_bind,
3399247f0f3cSYing Xue 	.connect	= tipc_connect,
340070b03759SErik Hugne 	.socketpair	= tipc_socketpair,
3401247f0f3cSYing Xue 	.accept		= tipc_accept,
3402247f0f3cSYing Xue 	.getname	= tipc_getname,
3403a11e1d43SLinus Torvalds 	.poll		= tipc_poll,
340478acb1f9SErik Hugne 	.ioctl		= tipc_ioctl,
3405247f0f3cSYing Xue 	.listen		= tipc_listen,
3406247f0f3cSYing Xue 	.shutdown	= tipc_shutdown,
3407247f0f3cSYing Xue 	.setsockopt	= tipc_setsockopt,
3408247f0f3cSYing Xue 	.getsockopt	= tipc_getsockopt,
3409365ad353SJon Paul Maloy 	.sendmsg	= tipc_sendstream,
3410ec8a09fbSJon Paul Maloy 	.recvmsg	= tipc_recvstream,
34115eee6a6dSAllan Stephens 	.mmap		= sock_no_mmap,
3412b97bf3fdSPer Liden };
3413b97bf3fdSPer Liden 
3414bca65eaeSFlorian Westphal static const struct net_proto_family tipc_family_ops = {
3415b97bf3fdSPer Liden 	.owner		= THIS_MODULE,
3416b97bf3fdSPer Liden 	.family		= AF_TIPC,
3417c5fa7b3cSYing Xue 	.create		= tipc_sk_create
3418b97bf3fdSPer Liden };
3419b97bf3fdSPer Liden 
3420b97bf3fdSPer Liden static struct proto tipc_proto = {
3421b97bf3fdSPer Liden 	.name		= "TIPC",
3422b97bf3fdSPer Liden 	.owner		= THIS_MODULE,
3423cc79dd1bSYing Xue 	.obj_size	= sizeof(struct tipc_sock),
3424cc79dd1bSYing Xue 	.sysctl_rmem	= sysctl_tipc_rmem
3425b97bf3fdSPer Liden };
3426b97bf3fdSPer Liden 
3427b97bf3fdSPer Liden /**
34284323add6SPer Liden  * tipc_socket_init - initialize TIPC socket interface
3429b97bf3fdSPer Liden  *
3430637b77fdSRandy Dunlap  * Return: 0 on success, errno otherwise
3431b97bf3fdSPer Liden  */
tipc_socket_init(void)34324323add6SPer Liden int tipc_socket_init(void)
3433b97bf3fdSPer Liden {
3434b97bf3fdSPer Liden 	int res;
3435b97bf3fdSPer Liden 
3436b97bf3fdSPer Liden 	res = proto_register(&tipc_proto, 1);
3437b97bf3fdSPer Liden 	if (res) {
34382cf8aa19SErik Hugne 		pr_err("Failed to register TIPC protocol type\n");
3439b97bf3fdSPer Liden 		goto out;
3440b97bf3fdSPer Liden 	}
3441b97bf3fdSPer Liden 
3442b97bf3fdSPer Liden 	res = sock_register(&tipc_family_ops);
3443b97bf3fdSPer Liden 	if (res) {
34442cf8aa19SErik Hugne 		pr_err("Failed to register TIPC socket type\n");
3445b97bf3fdSPer Liden 		proto_unregister(&tipc_proto);
3446b97bf3fdSPer Liden 		goto out;
3447b97bf3fdSPer Liden 	}
3448b97bf3fdSPer Liden  out:
3449b97bf3fdSPer Liden 	return res;
3450b97bf3fdSPer Liden }
3451b97bf3fdSPer Liden 
3452b97bf3fdSPer Liden /**
34534323add6SPer Liden  * tipc_socket_stop - stop TIPC socket interface
3454b97bf3fdSPer Liden  */
tipc_socket_stop(void)34554323add6SPer Liden void tipc_socket_stop(void)
3456b97bf3fdSPer Liden {
3457b97bf3fdSPer Liden 	sock_unregister(tipc_family_ops.family);
3458b97bf3fdSPer Liden 	proto_unregister(&tipc_proto);
3459b97bf3fdSPer Liden }
346034b78a12SRichard Alpe 
346134b78a12SRichard Alpe /* Caller should hold socket lock for the passed tipc socket. */
__tipc_nl_add_sk_con(struct sk_buff * skb,struct tipc_sock * tsk)3462d8182804SRichard Alpe static int __tipc_nl_add_sk_con(struct sk_buff *skb, struct tipc_sock *tsk)
346334b78a12SRichard Alpe {
346414623e00SJon Maloy 	u32 peer_node, peer_port;
346514623e00SJon Maloy 	u32 conn_type, conn_instance;
346634b78a12SRichard Alpe 	struct nlattr *nest;
346734b78a12SRichard Alpe 
346834b78a12SRichard Alpe 	peer_node = tsk_peer_node(tsk);
346934b78a12SRichard Alpe 	peer_port = tsk_peer_port(tsk);
347014623e00SJon Maloy 	conn_type = msg_nametype(&tsk->phdr);
347114623e00SJon Maloy 	conn_instance = msg_nameinst(&tsk->phdr);
3472ae0be8deSMichal Kubecek 	nest = nla_nest_start_noflag(skb, TIPC_NLA_SOCK_CON);
3473517ccc2aSKangjie Lu 	if (!nest)
3474517ccc2aSKangjie Lu 		return -EMSGSIZE;
347534b78a12SRichard Alpe 
347634b78a12SRichard Alpe 	if (nla_put_u32(skb, TIPC_NLA_CON_NODE, peer_node))
347734b78a12SRichard Alpe 		goto msg_full;
347834b78a12SRichard Alpe 	if (nla_put_u32(skb, TIPC_NLA_CON_SOCK, peer_port))
347934b78a12SRichard Alpe 		goto msg_full;
348034b78a12SRichard Alpe 
348114623e00SJon Maloy 	if (tsk->conn_addrtype != 0) {
348234b78a12SRichard Alpe 		if (nla_put_flag(skb, TIPC_NLA_CON_FLAG))
348334b78a12SRichard Alpe 			goto msg_full;
348414623e00SJon Maloy 		if (nla_put_u32(skb, TIPC_NLA_CON_TYPE, conn_type))
348534b78a12SRichard Alpe 			goto msg_full;
348614623e00SJon Maloy 		if (nla_put_u32(skb, TIPC_NLA_CON_INST, conn_instance))
348734b78a12SRichard Alpe 			goto msg_full;
348834b78a12SRichard Alpe 	}
348934b78a12SRichard Alpe 	nla_nest_end(skb, nest);
349034b78a12SRichard Alpe 
349134b78a12SRichard Alpe 	return 0;
349234b78a12SRichard Alpe 
349334b78a12SRichard Alpe msg_full:
349434b78a12SRichard Alpe 	nla_nest_cancel(skb, nest);
349534b78a12SRichard Alpe 
349634b78a12SRichard Alpe 	return -EMSGSIZE;
349734b78a12SRichard Alpe }
349834b78a12SRichard Alpe 
__tipc_nl_add_sk_info(struct sk_buff * skb,struct tipc_sock * tsk)3499dfde331eSGhantaKrishnamurthy MohanKrishna static int __tipc_nl_add_sk_info(struct sk_buff *skb, struct tipc_sock
3500dfde331eSGhantaKrishnamurthy MohanKrishna 			  *tsk)
3501dfde331eSGhantaKrishnamurthy MohanKrishna {
3502dfde331eSGhantaKrishnamurthy MohanKrishna 	struct net *net = sock_net(skb->sk);
3503dfde331eSGhantaKrishnamurthy MohanKrishna 	struct sock *sk = &tsk->sk;
3504dfde331eSGhantaKrishnamurthy MohanKrishna 
3505dfde331eSGhantaKrishnamurthy MohanKrishna 	if (nla_put_u32(skb, TIPC_NLA_SOCK_REF, tsk->portid) ||
350623fd3eacSJon Maloy 	    nla_put_u32(skb, TIPC_NLA_SOCK_ADDR, tipc_own_addr(net)))
3507dfde331eSGhantaKrishnamurthy MohanKrishna 		return -EMSGSIZE;
3508dfde331eSGhantaKrishnamurthy MohanKrishna 
3509dfde331eSGhantaKrishnamurthy MohanKrishna 	if (tipc_sk_connected(sk)) {
3510dfde331eSGhantaKrishnamurthy MohanKrishna 		if (__tipc_nl_add_sk_con(skb, tsk))
3511dfde331eSGhantaKrishnamurthy MohanKrishna 			return -EMSGSIZE;
3512dfde331eSGhantaKrishnamurthy MohanKrishna 	} else if (!list_empty(&tsk->publications)) {
3513dfde331eSGhantaKrishnamurthy MohanKrishna 		if (nla_put_flag(skb, TIPC_NLA_SOCK_HAS_PUBL))
3514dfde331eSGhantaKrishnamurthy MohanKrishna 			return -EMSGSIZE;
3515dfde331eSGhantaKrishnamurthy MohanKrishna 	}
3516dfde331eSGhantaKrishnamurthy MohanKrishna 	return 0;
3517dfde331eSGhantaKrishnamurthy MohanKrishna }
3518dfde331eSGhantaKrishnamurthy MohanKrishna 
351934b78a12SRichard Alpe /* Caller should hold socket lock for the passed tipc socket. */
__tipc_nl_add_sk(struct sk_buff * skb,struct netlink_callback * cb,struct tipc_sock * tsk)3520d8182804SRichard Alpe static int __tipc_nl_add_sk(struct sk_buff *skb, struct netlink_callback *cb,
352134b78a12SRichard Alpe 			    struct tipc_sock *tsk)
352234b78a12SRichard Alpe {
352334b78a12SRichard Alpe 	struct nlattr *attrs;
3524dfde331eSGhantaKrishnamurthy MohanKrishna 	void *hdr;
352534b78a12SRichard Alpe 
352634b78a12SRichard Alpe 	hdr = genlmsg_put(skb, NETLINK_CB(cb->skb).portid, cb->nlh->nlmsg_seq,
3527bfb3e5ddSRichard Alpe 			  &tipc_genl_family, NLM_F_MULTI, TIPC_NL_SOCK_GET);
352834b78a12SRichard Alpe 	if (!hdr)
352934b78a12SRichard Alpe 		goto msg_cancel;
353034b78a12SRichard Alpe 
3531ae0be8deSMichal Kubecek 	attrs = nla_nest_start_noflag(skb, TIPC_NLA_SOCK);
353234b78a12SRichard Alpe 	if (!attrs)
353334b78a12SRichard Alpe 		goto genlmsg_cancel;
3534dfde331eSGhantaKrishnamurthy MohanKrishna 
3535dfde331eSGhantaKrishnamurthy MohanKrishna 	if (__tipc_nl_add_sk_info(skb, tsk))
353634b78a12SRichard Alpe 		goto attr_msg_cancel;
353734b78a12SRichard Alpe 
353834b78a12SRichard Alpe 	nla_nest_end(skb, attrs);
353934b78a12SRichard Alpe 	genlmsg_end(skb, hdr);
354034b78a12SRichard Alpe 
354134b78a12SRichard Alpe 	return 0;
354234b78a12SRichard Alpe 
354334b78a12SRichard Alpe attr_msg_cancel:
354434b78a12SRichard Alpe 	nla_nest_cancel(skb, attrs);
354534b78a12SRichard Alpe genlmsg_cancel:
354634b78a12SRichard Alpe 	genlmsg_cancel(skb, hdr);
354734b78a12SRichard Alpe msg_cancel:
354834b78a12SRichard Alpe 	return -EMSGSIZE;
354934b78a12SRichard Alpe }
355034b78a12SRichard Alpe 
tipc_nl_sk_walk(struct sk_buff * skb,struct netlink_callback * cb,int (* skb_handler)(struct sk_buff * skb,struct netlink_callback * cb,struct tipc_sock * tsk))3551c30b70deSGhantaKrishnamurthy MohanKrishna int tipc_nl_sk_walk(struct sk_buff *skb, struct netlink_callback *cb,
3552dfde331eSGhantaKrishnamurthy MohanKrishna 		    int (*skb_handler)(struct sk_buff *skb,
3553dfde331eSGhantaKrishnamurthy MohanKrishna 				       struct netlink_callback *cb,
3554dfde331eSGhantaKrishnamurthy MohanKrishna 				       struct tipc_sock *tsk))
355534b78a12SRichard Alpe {
35568f5c5fcfSCong Wang 	struct rhashtable_iter *iter = (void *)cb->args[4];
3557dfde331eSGhantaKrishnamurthy MohanKrishna 	struct tipc_sock *tsk;
3558dfde331eSGhantaKrishnamurthy MohanKrishna 	int err;
355934b78a12SRichard Alpe 
35609a07efa9SCong Wang 	rhashtable_walk_start(iter);
35619a07efa9SCong Wang 	while ((tsk = rhashtable_walk_next(iter)) != NULL) {
35629a07efa9SCong Wang 		if (IS_ERR(tsk)) {
3563195b7fc5SColin Ian King 			if (PTR_ERR(tsk) == -EAGAIN)
3564d6e164e3SRichard Alpe 				continue;
35659a07efa9SCong Wang 			break;
35669a07efa9SCong Wang 		}
356734b78a12SRichard Alpe 
35689a07efa9SCong Wang 		sock_hold(&tsk->sk);
35699a07efa9SCong Wang 		rhashtable_walk_stop(iter);
35709a07efa9SCong Wang 		lock_sock(&tsk->sk);
3571dfde331eSGhantaKrishnamurthy MohanKrishna 		err = skb_handler(skb, cb, tsk);
3572d6e164e3SRichard Alpe 		if (err) {
35739a07efa9SCong Wang 			release_sock(&tsk->sk);
35749a07efa9SCong Wang 			sock_put(&tsk->sk);
3575d6e164e3SRichard Alpe 			goto out;
3576d6e164e3SRichard Alpe 		}
35779a07efa9SCong Wang 		release_sock(&tsk->sk);
35789a07efa9SCong Wang 		rhashtable_walk_start(iter);
35799a07efa9SCong Wang 		sock_put(&tsk->sk);
358034b78a12SRichard Alpe 	}
35819a07efa9SCong Wang 	rhashtable_walk_stop(iter);
3582d6e164e3SRichard Alpe out:
358334b78a12SRichard Alpe 	return skb->len;
358434b78a12SRichard Alpe }
3585c30b70deSGhantaKrishnamurthy MohanKrishna EXPORT_SYMBOL(tipc_nl_sk_walk);
3586c30b70deSGhantaKrishnamurthy MohanKrishna 
tipc_dump_start(struct netlink_callback * cb)35879a07efa9SCong Wang int tipc_dump_start(struct netlink_callback *cb)
35889a07efa9SCong Wang {
35898f5c5fcfSCong Wang 	return __tipc_dump_start(cb, sock_net(cb->skb->sk));
35908f5c5fcfSCong Wang }
35918f5c5fcfSCong Wang EXPORT_SYMBOL(tipc_dump_start);
35928f5c5fcfSCong Wang 
__tipc_dump_start(struct netlink_callback * cb,struct net * net)35938f5c5fcfSCong Wang int __tipc_dump_start(struct netlink_callback *cb, struct net *net)
35948f5c5fcfSCong Wang {
35958f5c5fcfSCong Wang 	/* tipc_nl_name_table_dump() uses cb->args[0...3]. */
35968f5c5fcfSCong Wang 	struct rhashtable_iter *iter = (void *)cb->args[4];
35979a07efa9SCong Wang 	struct tipc_net *tn = tipc_net(net);
35989a07efa9SCong Wang 
35999a07efa9SCong Wang 	if (!iter) {
36009a07efa9SCong Wang 		iter = kmalloc(sizeof(*iter), GFP_KERNEL);
36019a07efa9SCong Wang 		if (!iter)
36029a07efa9SCong Wang 			return -ENOMEM;
36039a07efa9SCong Wang 
36048f5c5fcfSCong Wang 		cb->args[4] = (long)iter;
36059a07efa9SCong Wang 	}
36069a07efa9SCong Wang 
36079a07efa9SCong Wang 	rhashtable_walk_enter(&tn->sk_rht, iter);
36089a07efa9SCong Wang 	return 0;
36099a07efa9SCong Wang }
36109a07efa9SCong Wang 
tipc_dump_done(struct netlink_callback * cb)36119a07efa9SCong Wang int tipc_dump_done(struct netlink_callback *cb)
36129a07efa9SCong Wang {
36138f5c5fcfSCong Wang 	struct rhashtable_iter *hti = (void *)cb->args[4];
36149a07efa9SCong Wang 
36159a07efa9SCong Wang 	rhashtable_walk_exit(hti);
36169a07efa9SCong Wang 	kfree(hti);
36179a07efa9SCong Wang 	return 0;
36189a07efa9SCong Wang }
36199a07efa9SCong Wang EXPORT_SYMBOL(tipc_dump_done);
36209a07efa9SCong Wang 
tipc_sk_fill_sock_diag(struct sk_buff * skb,struct netlink_callback * cb,struct tipc_sock * tsk,u32 sk_filter_state,u64 (* tipc_diag_gen_cookie)(struct sock * sk))3621e41f0548SCong Wang int tipc_sk_fill_sock_diag(struct sk_buff *skb, struct netlink_callback *cb,
3622e41f0548SCong Wang 			   struct tipc_sock *tsk, u32 sk_filter_state,
3623c30b70deSGhantaKrishnamurthy MohanKrishna 			   u64 (*tipc_diag_gen_cookie)(struct sock *sk))
3624c30b70deSGhantaKrishnamurthy MohanKrishna {
3625c30b70deSGhantaKrishnamurthy MohanKrishna 	struct sock *sk = &tsk->sk;
3626c30b70deSGhantaKrishnamurthy MohanKrishna 	struct nlattr *attrs;
3627c30b70deSGhantaKrishnamurthy MohanKrishna 	struct nlattr *stat;
3628c30b70deSGhantaKrishnamurthy MohanKrishna 
3629c30b70deSGhantaKrishnamurthy MohanKrishna 	/*filter response w.r.t sk_state*/
3630c30b70deSGhantaKrishnamurthy MohanKrishna 	if (!(sk_filter_state & (1 << sk->sk_state)))
3631c30b70deSGhantaKrishnamurthy MohanKrishna 		return 0;
3632c30b70deSGhantaKrishnamurthy MohanKrishna 
3633ae0be8deSMichal Kubecek 	attrs = nla_nest_start_noflag(skb, TIPC_NLA_SOCK);
3634c30b70deSGhantaKrishnamurthy MohanKrishna 	if (!attrs)
3635c30b70deSGhantaKrishnamurthy MohanKrishna 		goto msg_cancel;
3636c30b70deSGhantaKrishnamurthy MohanKrishna 
3637c30b70deSGhantaKrishnamurthy MohanKrishna 	if (__tipc_nl_add_sk_info(skb, tsk))
3638c30b70deSGhantaKrishnamurthy MohanKrishna 		goto attr_msg_cancel;
3639c30b70deSGhantaKrishnamurthy MohanKrishna 
3640c30b70deSGhantaKrishnamurthy MohanKrishna 	if (nla_put_u32(skb, TIPC_NLA_SOCK_TYPE, (u32)sk->sk_type) ||
3641c30b70deSGhantaKrishnamurthy MohanKrishna 	    nla_put_u32(skb, TIPC_NLA_SOCK_TIPC_STATE, (u32)sk->sk_state) ||
3642c30b70deSGhantaKrishnamurthy MohanKrishna 	    nla_put_u32(skb, TIPC_NLA_SOCK_INO, sock_i_ino(sk)) ||
3643c30b70deSGhantaKrishnamurthy MohanKrishna 	    nla_put_u32(skb, TIPC_NLA_SOCK_UID,
3644e41f0548SCong Wang 			from_kuid_munged(sk_user_ns(NETLINK_CB(cb->skb).sk),
36454b2e6877SGhantaKrishnamurthy MohanKrishna 					 sock_i_uid(sk))) ||
3646c30b70deSGhantaKrishnamurthy MohanKrishna 	    nla_put_u64_64bit(skb, TIPC_NLA_SOCK_COOKIE,
3647c30b70deSGhantaKrishnamurthy MohanKrishna 			      tipc_diag_gen_cookie(sk),
3648c30b70deSGhantaKrishnamurthy MohanKrishna 			      TIPC_NLA_SOCK_PAD))
3649c30b70deSGhantaKrishnamurthy MohanKrishna 		goto attr_msg_cancel;
3650c30b70deSGhantaKrishnamurthy MohanKrishna 
3651ae0be8deSMichal Kubecek 	stat = nla_nest_start_noflag(skb, TIPC_NLA_SOCK_STAT);
3652c30b70deSGhantaKrishnamurthy MohanKrishna 	if (!stat)
3653c30b70deSGhantaKrishnamurthy MohanKrishna 		goto attr_msg_cancel;
3654c30b70deSGhantaKrishnamurthy MohanKrishna 
3655c30b70deSGhantaKrishnamurthy MohanKrishna 	if (nla_put_u32(skb, TIPC_NLA_SOCK_STAT_RCVQ,
3656c30b70deSGhantaKrishnamurthy MohanKrishna 			skb_queue_len(&sk->sk_receive_queue)) ||
3657c30b70deSGhantaKrishnamurthy MohanKrishna 	    nla_put_u32(skb, TIPC_NLA_SOCK_STAT_SENDQ,
3658872619d8SGhantaKrishnamurthy MohanKrishna 			skb_queue_len(&sk->sk_write_queue)) ||
3659872619d8SGhantaKrishnamurthy MohanKrishna 	    nla_put_u32(skb, TIPC_NLA_SOCK_STAT_DROP,
3660872619d8SGhantaKrishnamurthy MohanKrishna 			atomic_read(&sk->sk_drops)))
3661c30b70deSGhantaKrishnamurthy MohanKrishna 		goto stat_msg_cancel;
3662c30b70deSGhantaKrishnamurthy MohanKrishna 
3663c30b70deSGhantaKrishnamurthy MohanKrishna 	if (tsk->cong_link_cnt &&
3664c30b70deSGhantaKrishnamurthy MohanKrishna 	    nla_put_flag(skb, TIPC_NLA_SOCK_STAT_LINK_CONG))
3665c30b70deSGhantaKrishnamurthy MohanKrishna 		goto stat_msg_cancel;
3666c30b70deSGhantaKrishnamurthy MohanKrishna 
3667c30b70deSGhantaKrishnamurthy MohanKrishna 	if (tsk_conn_cong(tsk) &&
3668c30b70deSGhantaKrishnamurthy MohanKrishna 	    nla_put_flag(skb, TIPC_NLA_SOCK_STAT_CONN_CONG))
3669c30b70deSGhantaKrishnamurthy MohanKrishna 		goto stat_msg_cancel;
3670c30b70deSGhantaKrishnamurthy MohanKrishna 
3671c30b70deSGhantaKrishnamurthy MohanKrishna 	nla_nest_end(skb, stat);
3672a1be5a20SGhantaKrishnamurthy MohanKrishna 
3673a1be5a20SGhantaKrishnamurthy MohanKrishna 	if (tsk->group)
3674a1be5a20SGhantaKrishnamurthy MohanKrishna 		if (tipc_group_fill_sock_diag(tsk->group, skb))
3675a1be5a20SGhantaKrishnamurthy MohanKrishna 			goto stat_msg_cancel;
3676a1be5a20SGhantaKrishnamurthy MohanKrishna 
3677c30b70deSGhantaKrishnamurthy MohanKrishna 	nla_nest_end(skb, attrs);
3678c30b70deSGhantaKrishnamurthy MohanKrishna 
3679c30b70deSGhantaKrishnamurthy MohanKrishna 	return 0;
3680c30b70deSGhantaKrishnamurthy MohanKrishna 
3681c30b70deSGhantaKrishnamurthy MohanKrishna stat_msg_cancel:
3682c30b70deSGhantaKrishnamurthy MohanKrishna 	nla_nest_cancel(skb, stat);
3683c30b70deSGhantaKrishnamurthy MohanKrishna attr_msg_cancel:
3684c30b70deSGhantaKrishnamurthy MohanKrishna 	nla_nest_cancel(skb, attrs);
3685c30b70deSGhantaKrishnamurthy MohanKrishna msg_cancel:
3686c30b70deSGhantaKrishnamurthy MohanKrishna 	return -EMSGSIZE;
3687c30b70deSGhantaKrishnamurthy MohanKrishna }
3688c30b70deSGhantaKrishnamurthy MohanKrishna EXPORT_SYMBOL(tipc_sk_fill_sock_diag);
36891a1a143dSRichard Alpe 
tipc_nl_sk_dump(struct sk_buff * skb,struct netlink_callback * cb)3690dfde331eSGhantaKrishnamurthy MohanKrishna int tipc_nl_sk_dump(struct sk_buff *skb, struct netlink_callback *cb)
3691dfde331eSGhantaKrishnamurthy MohanKrishna {
3692c30b70deSGhantaKrishnamurthy MohanKrishna 	return tipc_nl_sk_walk(skb, cb, __tipc_nl_add_sk);
3693dfde331eSGhantaKrishnamurthy MohanKrishna }
3694dfde331eSGhantaKrishnamurthy MohanKrishna 
36951a1a143dSRichard Alpe /* Caller should hold socket lock for the passed tipc socket. */
__tipc_nl_add_sk_publ(struct sk_buff * skb,struct netlink_callback * cb,struct publication * publ)3696d8182804SRichard Alpe static int __tipc_nl_add_sk_publ(struct sk_buff *skb,
3697d8182804SRichard Alpe 				 struct netlink_callback *cb,
36981a1a143dSRichard Alpe 				 struct publication *publ)
36991a1a143dSRichard Alpe {
37001a1a143dSRichard Alpe 	void *hdr;
37011a1a143dSRichard Alpe 	struct nlattr *attrs;
37021a1a143dSRichard Alpe 
37031a1a143dSRichard Alpe 	hdr = genlmsg_put(skb, NETLINK_CB(cb->skb).portid, cb->nlh->nlmsg_seq,
3704bfb3e5ddSRichard Alpe 			  &tipc_genl_family, NLM_F_MULTI, TIPC_NL_PUBL_GET);
37051a1a143dSRichard Alpe 	if (!hdr)
37061a1a143dSRichard Alpe 		goto msg_cancel;
37071a1a143dSRichard Alpe 
3708ae0be8deSMichal Kubecek 	attrs = nla_nest_start_noflag(skb, TIPC_NLA_PUBL);
37091a1a143dSRichard Alpe 	if (!attrs)
37101a1a143dSRichard Alpe 		goto genlmsg_cancel;
37111a1a143dSRichard Alpe 
37121a1a143dSRichard Alpe 	if (nla_put_u32(skb, TIPC_NLA_PUBL_KEY, publ->key))
37131a1a143dSRichard Alpe 		goto attr_msg_cancel;
3714998d3907SJon Maloy 	if (nla_put_u32(skb, TIPC_NLA_PUBL_TYPE, publ->sr.type))
37151a1a143dSRichard Alpe 		goto attr_msg_cancel;
3716998d3907SJon Maloy 	if (nla_put_u32(skb, TIPC_NLA_PUBL_LOWER, publ->sr.lower))
37171a1a143dSRichard Alpe 		goto attr_msg_cancel;
3718998d3907SJon Maloy 	if (nla_put_u32(skb, TIPC_NLA_PUBL_UPPER, publ->sr.upper))
37191a1a143dSRichard Alpe 		goto attr_msg_cancel;
37201a1a143dSRichard Alpe 
37211a1a143dSRichard Alpe 	nla_nest_end(skb, attrs);
37221a1a143dSRichard Alpe 	genlmsg_end(skb, hdr);
37231a1a143dSRichard Alpe 
37241a1a143dSRichard Alpe 	return 0;
37251a1a143dSRichard Alpe 
37261a1a143dSRichard Alpe attr_msg_cancel:
37271a1a143dSRichard Alpe 	nla_nest_cancel(skb, attrs);
37281a1a143dSRichard Alpe genlmsg_cancel:
37291a1a143dSRichard Alpe 	genlmsg_cancel(skb, hdr);
37301a1a143dSRichard Alpe msg_cancel:
37311a1a143dSRichard Alpe 	return -EMSGSIZE;
37321a1a143dSRichard Alpe }
37331a1a143dSRichard Alpe 
37341a1a143dSRichard Alpe /* Caller should hold socket lock for the passed tipc socket. */
__tipc_nl_list_sk_publ(struct sk_buff * skb,struct netlink_callback * cb,struct tipc_sock * tsk,u32 * last_publ)3735d8182804SRichard Alpe static int __tipc_nl_list_sk_publ(struct sk_buff *skb,
3736d8182804SRichard Alpe 				  struct netlink_callback *cb,
37371a1a143dSRichard Alpe 				  struct tipc_sock *tsk, u32 *last_publ)
37381a1a143dSRichard Alpe {
37391a1a143dSRichard Alpe 	int err;
37401a1a143dSRichard Alpe 	struct publication *p;
37411a1a143dSRichard Alpe 
37421a1a143dSRichard Alpe 	if (*last_publ) {
3743e50e73e1SJon Maloy 		list_for_each_entry(p, &tsk->publications, binding_sock) {
37441a1a143dSRichard Alpe 			if (p->key == *last_publ)
37451a1a143dSRichard Alpe 				break;
37461a1a143dSRichard Alpe 		}
3747a1f8fec4SDan Carpenter 		if (list_entry_is_head(p, &tsk->publications, binding_sock)) {
37481a1a143dSRichard Alpe 			/* We never set seq or call nl_dump_check_consistent()
37491a1a143dSRichard Alpe 			 * this means that setting prev_seq here will cause the
37501a1a143dSRichard Alpe 			 * consistence check to fail in the netlink callback
37511a1a143dSRichard Alpe 			 * handler. Resulting in the last NLMSG_DONE message
37521a1a143dSRichard Alpe 			 * having the NLM_F_DUMP_INTR flag set.
37531a1a143dSRichard Alpe 			 */
37541a1a143dSRichard Alpe 			cb->prev_seq = 1;
37551a1a143dSRichard Alpe 			*last_publ = 0;
37561a1a143dSRichard Alpe 			return -EPIPE;
37571a1a143dSRichard Alpe 		}
37581a1a143dSRichard Alpe 	} else {
37591a1a143dSRichard Alpe 		p = list_first_entry(&tsk->publications, struct publication,
3760e50e73e1SJon Maloy 				     binding_sock);
37611a1a143dSRichard Alpe 	}
37621a1a143dSRichard Alpe 
3763e50e73e1SJon Maloy 	list_for_each_entry_from(p, &tsk->publications, binding_sock) {
37641a1a143dSRichard Alpe 		err = __tipc_nl_add_sk_publ(skb, cb, p);
37651a1a143dSRichard Alpe 		if (err) {
37661a1a143dSRichard Alpe 			*last_publ = p->key;
37671a1a143dSRichard Alpe 			return err;
37681a1a143dSRichard Alpe 		}
37691a1a143dSRichard Alpe 	}
37701a1a143dSRichard Alpe 	*last_publ = 0;
37711a1a143dSRichard Alpe 
37721a1a143dSRichard Alpe 	return 0;
37731a1a143dSRichard Alpe }
37741a1a143dSRichard Alpe 
tipc_nl_publ_dump(struct sk_buff * skb,struct netlink_callback * cb)37751a1a143dSRichard Alpe int tipc_nl_publ_dump(struct sk_buff *skb, struct netlink_callback *cb)
37761a1a143dSRichard Alpe {
37771a1a143dSRichard Alpe 	int err;
377807f6c4bcSYing Xue 	u32 tsk_portid = cb->args[0];
37791a1a143dSRichard Alpe 	u32 last_publ = cb->args[1];
37801a1a143dSRichard Alpe 	u32 done = cb->args[2];
3781e05b31f4SYing Xue 	struct net *net = sock_net(skb->sk);
37821a1a143dSRichard Alpe 	struct tipc_sock *tsk;
37831a1a143dSRichard Alpe 
378407f6c4bcSYing Xue 	if (!tsk_portid) {
37857288dd2fSJakub Kicinski 		struct nlattr **attrs = genl_dumpit_info(cb)->info.attrs;
37861a1a143dSRichard Alpe 		struct nlattr *sock[TIPC_NLA_SOCK_MAX + 1];
37871a1a143dSRichard Alpe 
378845e093aeSRichard Alpe 		if (!attrs[TIPC_NLA_SOCK])
378945e093aeSRichard Alpe 			return -EINVAL;
379045e093aeSRichard Alpe 
37918cb08174SJohannes Berg 		err = nla_parse_nested_deprecated(sock, TIPC_NLA_SOCK_MAX,
37921a1a143dSRichard Alpe 						  attrs[TIPC_NLA_SOCK],
3793fceb6435SJohannes Berg 						  tipc_nl_sock_policy, NULL);
37941a1a143dSRichard Alpe 		if (err)
37951a1a143dSRichard Alpe 			return err;
37961a1a143dSRichard Alpe 
37971a1a143dSRichard Alpe 		if (!sock[TIPC_NLA_SOCK_REF])
37981a1a143dSRichard Alpe 			return -EINVAL;
37991a1a143dSRichard Alpe 
380007f6c4bcSYing Xue 		tsk_portid = nla_get_u32(sock[TIPC_NLA_SOCK_REF]);
38011a1a143dSRichard Alpe 	}
38021a1a143dSRichard Alpe 
38031a1a143dSRichard Alpe 	if (done)
38041a1a143dSRichard Alpe 		return 0;
38051a1a143dSRichard Alpe 
3806e05b31f4SYing Xue 	tsk = tipc_sk_lookup(net, tsk_portid);
38071a1a143dSRichard Alpe 	if (!tsk)
38081a1a143dSRichard Alpe 		return -EINVAL;
38091a1a143dSRichard Alpe 
38101a1a143dSRichard Alpe 	lock_sock(&tsk->sk);
38111a1a143dSRichard Alpe 	err = __tipc_nl_list_sk_publ(skb, cb, tsk, &last_publ);
38121a1a143dSRichard Alpe 	if (!err)
38131a1a143dSRichard Alpe 		done = 1;
38141a1a143dSRichard Alpe 	release_sock(&tsk->sk);
381507f6c4bcSYing Xue 	sock_put(&tsk->sk);
38161a1a143dSRichard Alpe 
381707f6c4bcSYing Xue 	cb->args[0] = tsk_portid;
38181a1a143dSRichard Alpe 	cb->args[1] = last_publ;
38191a1a143dSRichard Alpe 	cb->args[2] = done;
38201a1a143dSRichard Alpe 
38211a1a143dSRichard Alpe 	return skb->len;
38221a1a143dSRichard Alpe }
3823b4b9771bSTuong Lien 
382401e661ebSTuong Lien /**
382501e661ebSTuong Lien  * tipc_sk_filtering - check if a socket should be traced
382601e661ebSTuong Lien  * @sk: the socket to be examined
3827f172f4b8SRandy Dunlap  *
3828f172f4b8SRandy Dunlap  * @sysctl_tipc_sk_filter is used as the socket tuple for filtering:
382901e661ebSTuong Lien  * (portid, sock type, name type, name lower, name upper)
383001e661ebSTuong Lien  *
3831637b77fdSRandy Dunlap  * Return: true if the socket meets the socket tuple data
383201e661ebSTuong Lien  * (value 0 = 'any') or when there is no tuple set (all = 0),
383301e661ebSTuong Lien  * otherwise false
383401e661ebSTuong Lien  */
tipc_sk_filtering(struct sock * sk)383501e661ebSTuong Lien bool tipc_sk_filtering(struct sock *sk)
383601e661ebSTuong Lien {
383701e661ebSTuong Lien 	struct tipc_sock *tsk;
383801e661ebSTuong Lien 	struct publication *p;
383901e661ebSTuong Lien 	u32 _port, _sktype, _type, _lower, _upper;
384001e661ebSTuong Lien 	u32 type = 0, lower = 0, upper = 0;
384101e661ebSTuong Lien 
384201e661ebSTuong Lien 	if (!sk)
384301e661ebSTuong Lien 		return true;
384401e661ebSTuong Lien 
384501e661ebSTuong Lien 	tsk = tipc_sk(sk);
384601e661ebSTuong Lien 
384701e661ebSTuong Lien 	_port = sysctl_tipc_sk_filter[0];
384801e661ebSTuong Lien 	_sktype = sysctl_tipc_sk_filter[1];
384901e661ebSTuong Lien 	_type = sysctl_tipc_sk_filter[2];
385001e661ebSTuong Lien 	_lower = sysctl_tipc_sk_filter[3];
385101e661ebSTuong Lien 	_upper = sysctl_tipc_sk_filter[4];
385201e661ebSTuong Lien 
385301e661ebSTuong Lien 	if (!_port && !_sktype && !_type && !_lower && !_upper)
385401e661ebSTuong Lien 		return true;
385501e661ebSTuong Lien 
385601e661ebSTuong Lien 	if (_port)
385701e661ebSTuong Lien 		return (_port == tsk->portid);
385801e661ebSTuong Lien 
385901e661ebSTuong Lien 	if (_sktype && _sktype != sk->sk_type)
386001e661ebSTuong Lien 		return false;
386101e661ebSTuong Lien 
386201e661ebSTuong Lien 	if (tsk->published) {
386301e661ebSTuong Lien 		p = list_first_entry_or_null(&tsk->publications,
386401e661ebSTuong Lien 					     struct publication, binding_sock);
386501e661ebSTuong Lien 		if (p) {
3866998d3907SJon Maloy 			type = p->sr.type;
3867998d3907SJon Maloy 			lower = p->sr.lower;
3868998d3907SJon Maloy 			upper = p->sr.upper;
386901e661ebSTuong Lien 		}
387001e661ebSTuong Lien 	}
387101e661ebSTuong Lien 
387201e661ebSTuong Lien 	if (!tipc_sk_type_connectionless(sk)) {
387314623e00SJon Maloy 		type = msg_nametype(&tsk->phdr);
387414623e00SJon Maloy 		lower = msg_nameinst(&tsk->phdr);
387514623e00SJon Maloy 		upper = lower;
387601e661ebSTuong Lien 	}
387701e661ebSTuong Lien 
387801e661ebSTuong Lien 	if ((_type && _type != type) || (_lower && _lower != lower) ||
387901e661ebSTuong Lien 	    (_upper && _upper != upper))
388001e661ebSTuong Lien 		return false;
388101e661ebSTuong Lien 
388201e661ebSTuong Lien 	return true;
388301e661ebSTuong Lien }
388401e661ebSTuong Lien 
tipc_sock_get_portid(struct sock * sk)3885b4b9771bSTuong Lien u32 tipc_sock_get_portid(struct sock *sk)
3886b4b9771bSTuong Lien {
3887b4b9771bSTuong Lien 	return (sk) ? (tipc_sk(sk))->portid : 0;
3888b4b9771bSTuong Lien }
3889b4b9771bSTuong Lien 
3890b4b9771bSTuong Lien /**
389101e661ebSTuong Lien  * tipc_sk_overlimit1 - check if socket rx queue is about to be overloaded,
389201e661ebSTuong Lien  *			both the rcv and backlog queues are considered
389301e661ebSTuong Lien  * @sk: tipc sk to be checked
389401e661ebSTuong Lien  * @skb: tipc msg to be checked
389501e661ebSTuong Lien  *
3896637b77fdSRandy Dunlap  * Return: true if the socket rx queue allocation is > 90%, otherwise false
389701e661ebSTuong Lien  */
389801e661ebSTuong Lien 
tipc_sk_overlimit1(struct sock * sk,struct sk_buff * skb)389901e661ebSTuong Lien bool tipc_sk_overlimit1(struct sock *sk, struct sk_buff *skb)
390001e661ebSTuong Lien {
390101e661ebSTuong Lien 	atomic_t *dcnt = &tipc_sk(sk)->dupl_rcvcnt;
390201e661ebSTuong Lien 	unsigned int lim = rcvbuf_limit(sk, skb) + atomic_read(dcnt);
390301e661ebSTuong Lien 	unsigned int qsize = sk->sk_backlog.len + sk_rmem_alloc_get(sk);
390401e661ebSTuong Lien 
390501e661ebSTuong Lien 	return (qsize > lim * 90 / 100);
390601e661ebSTuong Lien }
390701e661ebSTuong Lien 
390801e661ebSTuong Lien /**
390901e661ebSTuong Lien  * tipc_sk_overlimit2 - check if socket rx queue is about to be overloaded,
391001e661ebSTuong Lien  *			only the rcv queue is considered
391101e661ebSTuong Lien  * @sk: tipc sk to be checked
391201e661ebSTuong Lien  * @skb: tipc msg to be checked
391301e661ebSTuong Lien  *
3914637b77fdSRandy Dunlap  * Return: true if the socket rx queue allocation is > 90%, otherwise false
391501e661ebSTuong Lien  */
391601e661ebSTuong Lien 
tipc_sk_overlimit2(struct sock * sk,struct sk_buff * skb)391701e661ebSTuong Lien bool tipc_sk_overlimit2(struct sock *sk, struct sk_buff *skb)
391801e661ebSTuong Lien {
391901e661ebSTuong Lien 	unsigned int lim = rcvbuf_limit(sk, skb);
392001e661ebSTuong Lien 	unsigned int qsize = sk_rmem_alloc_get(sk);
392101e661ebSTuong Lien 
392201e661ebSTuong Lien 	return (qsize > lim * 90 / 100);
392301e661ebSTuong Lien }
392401e661ebSTuong Lien 
392501e661ebSTuong Lien /**
3926b4b9771bSTuong Lien  * tipc_sk_dump - dump TIPC socket
3927b4b9771bSTuong Lien  * @sk: tipc sk to be dumped
3928b4b9771bSTuong Lien  * @dqueues: bitmask to decide if any socket queue to be dumped?
3929b4b9771bSTuong Lien  *           - TIPC_DUMP_NONE: don't dump socket queues
3930b4b9771bSTuong Lien  *           - TIPC_DUMP_SK_SNDQ: dump socket send queue
3931b4b9771bSTuong Lien  *           - TIPC_DUMP_SK_RCVQ: dump socket rcv queue
3932b4b9771bSTuong Lien  *           - TIPC_DUMP_SK_BKLGQ: dump socket backlog queue
3933b4b9771bSTuong Lien  *           - TIPC_DUMP_ALL: dump all the socket queues above
3934b4b9771bSTuong Lien  * @buf: returned buffer of dump data in format
3935b4b9771bSTuong Lien  */
tipc_sk_dump(struct sock * sk,u16 dqueues,char * buf)3936b4b9771bSTuong Lien int tipc_sk_dump(struct sock *sk, u16 dqueues, char *buf)
3937b4b9771bSTuong Lien {
3938b4b9771bSTuong Lien 	int i = 0;
3939b4b9771bSTuong Lien 	size_t sz = (dqueues) ? SK_LMAX : SK_LMIN;
394014623e00SJon Maloy 	u32 conn_type, conn_instance;
3941b4b9771bSTuong Lien 	struct tipc_sock *tsk;
3942b4b9771bSTuong Lien 	struct publication *p;
3943b4b9771bSTuong Lien 	bool tsk_connected;
3944b4b9771bSTuong Lien 
3945b4b9771bSTuong Lien 	if (!sk) {
3946b4b9771bSTuong Lien 		i += scnprintf(buf, sz, "sk data: (null)\n");
3947b4b9771bSTuong Lien 		return i;
3948b4b9771bSTuong Lien 	}
3949b4b9771bSTuong Lien 
3950b4b9771bSTuong Lien 	tsk = tipc_sk(sk);
3951b4b9771bSTuong Lien 	tsk_connected = !tipc_sk_type_connectionless(sk);
3952b4b9771bSTuong Lien 
3953b4b9771bSTuong Lien 	i += scnprintf(buf, sz, "sk data: %u", sk->sk_type);
3954b4b9771bSTuong Lien 	i += scnprintf(buf + i, sz - i, " %d", sk->sk_state);
3955b4b9771bSTuong Lien 	i += scnprintf(buf + i, sz - i, " %x", tsk_own_node(tsk));
3956b4b9771bSTuong Lien 	i += scnprintf(buf + i, sz - i, " %u", tsk->portid);
3957b4b9771bSTuong Lien 	i += scnprintf(buf + i, sz - i, " | %u", tsk_connected);
3958b4b9771bSTuong Lien 	if (tsk_connected) {
3959b4b9771bSTuong Lien 		i += scnprintf(buf + i, sz - i, " %x", tsk_peer_node(tsk));
3960b4b9771bSTuong Lien 		i += scnprintf(buf + i, sz - i, " %u", tsk_peer_port(tsk));
396114623e00SJon Maloy 		conn_type = msg_nametype(&tsk->phdr);
396214623e00SJon Maloy 		conn_instance = msg_nameinst(&tsk->phdr);
396314623e00SJon Maloy 		i += scnprintf(buf + i, sz - i, " %u", conn_type);
396414623e00SJon Maloy 		i += scnprintf(buf + i, sz - i, " %u", conn_instance);
3965b4b9771bSTuong Lien 	}
3966b4b9771bSTuong Lien 	i += scnprintf(buf + i, sz - i, " | %u", tsk->published);
3967b4b9771bSTuong Lien 	if (tsk->published) {
3968b4b9771bSTuong Lien 		p = list_first_entry_or_null(&tsk->publications,
3969b4b9771bSTuong Lien 					     struct publication, binding_sock);
3970998d3907SJon Maloy 		i += scnprintf(buf + i, sz - i, " %u", (p) ? p->sr.type : 0);
3971998d3907SJon Maloy 		i += scnprintf(buf + i, sz - i, " %u", (p) ? p->sr.lower : 0);
3972998d3907SJon Maloy 		i += scnprintf(buf + i, sz - i, " %u", (p) ? p->sr.upper : 0);
3973b4b9771bSTuong Lien 	}
3974b4b9771bSTuong Lien 	i += scnprintf(buf + i, sz - i, " | %u", tsk->snd_win);
3975b4b9771bSTuong Lien 	i += scnprintf(buf + i, sz - i, " %u", tsk->rcv_win);
3976b4b9771bSTuong Lien 	i += scnprintf(buf + i, sz - i, " %u", tsk->max_pkt);
3977b4b9771bSTuong Lien 	i += scnprintf(buf + i, sz - i, " %x", tsk->peer_caps);
3978b4b9771bSTuong Lien 	i += scnprintf(buf + i, sz - i, " %u", tsk->cong_link_cnt);
3979b4b9771bSTuong Lien 	i += scnprintf(buf + i, sz - i, " %u", tsk->snt_unacked);
3980b4b9771bSTuong Lien 	i += scnprintf(buf + i, sz - i, " %u", tsk->rcv_unacked);
3981b4b9771bSTuong Lien 	i += scnprintf(buf + i, sz - i, " %u", atomic_read(&tsk->dupl_rcvcnt));
3982b4b9771bSTuong Lien 	i += scnprintf(buf + i, sz - i, " %u", sk->sk_shutdown);
3983b4b9771bSTuong Lien 	i += scnprintf(buf + i, sz - i, " | %d", sk_wmem_alloc_get(sk));
3984b4b9771bSTuong Lien 	i += scnprintf(buf + i, sz - i, " %d", sk->sk_sndbuf);
3985b4b9771bSTuong Lien 	i += scnprintf(buf + i, sz - i, " | %d", sk_rmem_alloc_get(sk));
3986b4b9771bSTuong Lien 	i += scnprintf(buf + i, sz - i, " %d", sk->sk_rcvbuf);
398770c26558SEric Dumazet 	i += scnprintf(buf + i, sz - i, " | %d\n", READ_ONCE(sk->sk_backlog.len));
3988b4b9771bSTuong Lien 
3989b4b9771bSTuong Lien 	if (dqueues & TIPC_DUMP_SK_SNDQ) {
3990b4b9771bSTuong Lien 		i += scnprintf(buf + i, sz - i, "sk_write_queue: ");
3991b4b9771bSTuong Lien 		i += tipc_list_dump(&sk->sk_write_queue, false, buf + i);
3992b4b9771bSTuong Lien 	}
3993b4b9771bSTuong Lien 
3994b4b9771bSTuong Lien 	if (dqueues & TIPC_DUMP_SK_RCVQ) {
3995b4b9771bSTuong Lien 		i += scnprintf(buf + i, sz - i, "sk_receive_queue: ");
3996b4b9771bSTuong Lien 		i += tipc_list_dump(&sk->sk_receive_queue, false, buf + i);
3997b4b9771bSTuong Lien 	}
3998b4b9771bSTuong Lien 
3999b4b9771bSTuong Lien 	if (dqueues & TIPC_DUMP_SK_BKLGQ) {
4000b4b9771bSTuong Lien 		i += scnprintf(buf + i, sz - i, "sk_backlog:\n  head ");
4001b4b9771bSTuong Lien 		i += tipc_skb_dump(sk->sk_backlog.head, false, buf + i);
4002b4b9771bSTuong Lien 		if (sk->sk_backlog.tail != sk->sk_backlog.head) {
4003b4b9771bSTuong Lien 			i += scnprintf(buf + i, sz - i, "  tail ");
4004b4b9771bSTuong Lien 			i += tipc_skb_dump(sk->sk_backlog.tail, false,
4005b4b9771bSTuong Lien 					   buf + i);
4006b4b9771bSTuong Lien 		}
4007b4b9771bSTuong Lien 	}
4008b4b9771bSTuong Lien 
4009b4b9771bSTuong Lien 	return i;
4010b4b9771bSTuong Lien }
4011