xref: /OK3568_Linux_fs/kernel/net/sctp/diag.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0-or-later
2*4882a593Smuzhiyun /* SCTP kernel implementation
3*4882a593Smuzhiyun  * (C) Copyright Red Hat Inc. 2017
4*4882a593Smuzhiyun  *
5*4882a593Smuzhiyun  * This file is part of the SCTP kernel implementation
6*4882a593Smuzhiyun  *
7*4882a593Smuzhiyun  * These functions implement sctp diag support.
8*4882a593Smuzhiyun  *
9*4882a593Smuzhiyun  * Please send any bug reports or fixes you make to the
10*4882a593Smuzhiyun  * email addresched(es):
11*4882a593Smuzhiyun  *    lksctp developers <linux-sctp@vger.kernel.org>
12*4882a593Smuzhiyun  *
13*4882a593Smuzhiyun  * Written or modified by:
14*4882a593Smuzhiyun  *    Xin Long <lucien.xin@gmail.com>
15*4882a593Smuzhiyun  */
16*4882a593Smuzhiyun 
17*4882a593Smuzhiyun #include <linux/module.h>
18*4882a593Smuzhiyun #include <linux/inet_diag.h>
19*4882a593Smuzhiyun #include <linux/sock_diag.h>
20*4882a593Smuzhiyun #include <net/sctp/sctp.h>
21*4882a593Smuzhiyun 
22*4882a593Smuzhiyun static void sctp_diag_get_info(struct sock *sk, struct inet_diag_msg *r,
23*4882a593Smuzhiyun 			       void *info);
24*4882a593Smuzhiyun 
25*4882a593Smuzhiyun /* define some functions to make asoc/ep fill look clean */
inet_diag_msg_sctpasoc_fill(struct inet_diag_msg * r,struct sock * sk,struct sctp_association * asoc)26*4882a593Smuzhiyun static void inet_diag_msg_sctpasoc_fill(struct inet_diag_msg *r,
27*4882a593Smuzhiyun 					struct sock *sk,
28*4882a593Smuzhiyun 					struct sctp_association *asoc)
29*4882a593Smuzhiyun {
30*4882a593Smuzhiyun 	union sctp_addr laddr, paddr;
31*4882a593Smuzhiyun 	struct dst_entry *dst;
32*4882a593Smuzhiyun 	struct timer_list *t3_rtx = &asoc->peer.primary_path->T3_rtx_timer;
33*4882a593Smuzhiyun 
34*4882a593Smuzhiyun 	laddr = list_entry(asoc->base.bind_addr.address_list.next,
35*4882a593Smuzhiyun 			   struct sctp_sockaddr_entry, list)->a;
36*4882a593Smuzhiyun 	paddr = asoc->peer.primary_path->ipaddr;
37*4882a593Smuzhiyun 	dst = asoc->peer.primary_path->dst;
38*4882a593Smuzhiyun 
39*4882a593Smuzhiyun 	r->idiag_family = sk->sk_family;
40*4882a593Smuzhiyun 	r->id.idiag_sport = htons(asoc->base.bind_addr.port);
41*4882a593Smuzhiyun 	r->id.idiag_dport = htons(asoc->peer.port);
42*4882a593Smuzhiyun 	r->id.idiag_if = dst ? dst->dev->ifindex : 0;
43*4882a593Smuzhiyun 	sock_diag_save_cookie(sk, r->id.idiag_cookie);
44*4882a593Smuzhiyun 
45*4882a593Smuzhiyun #if IS_ENABLED(CONFIG_IPV6)
46*4882a593Smuzhiyun 	if (sk->sk_family == AF_INET6) {
47*4882a593Smuzhiyun 		*(struct in6_addr *)r->id.idiag_src = laddr.v6.sin6_addr;
48*4882a593Smuzhiyun 		*(struct in6_addr *)r->id.idiag_dst = paddr.v6.sin6_addr;
49*4882a593Smuzhiyun 	} else
50*4882a593Smuzhiyun #endif
51*4882a593Smuzhiyun 	{
52*4882a593Smuzhiyun 		memset(&r->id.idiag_src, 0, sizeof(r->id.idiag_src));
53*4882a593Smuzhiyun 		memset(&r->id.idiag_dst, 0, sizeof(r->id.idiag_dst));
54*4882a593Smuzhiyun 
55*4882a593Smuzhiyun 		r->id.idiag_src[0] = laddr.v4.sin_addr.s_addr;
56*4882a593Smuzhiyun 		r->id.idiag_dst[0] = paddr.v4.sin_addr.s_addr;
57*4882a593Smuzhiyun 	}
58*4882a593Smuzhiyun 
59*4882a593Smuzhiyun 	r->idiag_state = asoc->state;
60*4882a593Smuzhiyun 	if (timer_pending(t3_rtx)) {
61*4882a593Smuzhiyun 		r->idiag_timer = SCTP_EVENT_TIMEOUT_T3_RTX;
62*4882a593Smuzhiyun 		r->idiag_retrans = asoc->rtx_data_chunks;
63*4882a593Smuzhiyun 		r->idiag_expires = jiffies_to_msecs(t3_rtx->expires - jiffies);
64*4882a593Smuzhiyun 	}
65*4882a593Smuzhiyun }
66*4882a593Smuzhiyun 
inet_diag_msg_sctpladdrs_fill(struct sk_buff * skb,struct list_head * address_list)67*4882a593Smuzhiyun static int inet_diag_msg_sctpladdrs_fill(struct sk_buff *skb,
68*4882a593Smuzhiyun 					 struct list_head *address_list)
69*4882a593Smuzhiyun {
70*4882a593Smuzhiyun 	struct sctp_sockaddr_entry *laddr;
71*4882a593Smuzhiyun 	int addrlen = sizeof(struct sockaddr_storage);
72*4882a593Smuzhiyun 	int addrcnt = 0;
73*4882a593Smuzhiyun 	struct nlattr *attr;
74*4882a593Smuzhiyun 	void *info = NULL;
75*4882a593Smuzhiyun 
76*4882a593Smuzhiyun 	list_for_each_entry_rcu(laddr, address_list, list)
77*4882a593Smuzhiyun 		addrcnt++;
78*4882a593Smuzhiyun 
79*4882a593Smuzhiyun 	attr = nla_reserve(skb, INET_DIAG_LOCALS, addrlen * addrcnt);
80*4882a593Smuzhiyun 	if (!attr)
81*4882a593Smuzhiyun 		return -EMSGSIZE;
82*4882a593Smuzhiyun 
83*4882a593Smuzhiyun 	info = nla_data(attr);
84*4882a593Smuzhiyun 	list_for_each_entry_rcu(laddr, address_list, list) {
85*4882a593Smuzhiyun 		memcpy(info, &laddr->a, sizeof(laddr->a));
86*4882a593Smuzhiyun 		memset(info + sizeof(laddr->a), 0, addrlen - sizeof(laddr->a));
87*4882a593Smuzhiyun 		info += addrlen;
88*4882a593Smuzhiyun 	}
89*4882a593Smuzhiyun 
90*4882a593Smuzhiyun 	return 0;
91*4882a593Smuzhiyun }
92*4882a593Smuzhiyun 
inet_diag_msg_sctpaddrs_fill(struct sk_buff * skb,struct sctp_association * asoc)93*4882a593Smuzhiyun static int inet_diag_msg_sctpaddrs_fill(struct sk_buff *skb,
94*4882a593Smuzhiyun 					struct sctp_association *asoc)
95*4882a593Smuzhiyun {
96*4882a593Smuzhiyun 	int addrlen = sizeof(struct sockaddr_storage);
97*4882a593Smuzhiyun 	struct sctp_transport *from;
98*4882a593Smuzhiyun 	struct nlattr *attr;
99*4882a593Smuzhiyun 	void *info = NULL;
100*4882a593Smuzhiyun 
101*4882a593Smuzhiyun 	attr = nla_reserve(skb, INET_DIAG_PEERS,
102*4882a593Smuzhiyun 			   addrlen * asoc->peer.transport_count);
103*4882a593Smuzhiyun 	if (!attr)
104*4882a593Smuzhiyun 		return -EMSGSIZE;
105*4882a593Smuzhiyun 
106*4882a593Smuzhiyun 	info = nla_data(attr);
107*4882a593Smuzhiyun 	list_for_each_entry(from, &asoc->peer.transport_addr_list,
108*4882a593Smuzhiyun 			    transports) {
109*4882a593Smuzhiyun 		memcpy(info, &from->ipaddr, sizeof(from->ipaddr));
110*4882a593Smuzhiyun 		memset(info + sizeof(from->ipaddr), 0,
111*4882a593Smuzhiyun 		       addrlen - sizeof(from->ipaddr));
112*4882a593Smuzhiyun 		info += addrlen;
113*4882a593Smuzhiyun 	}
114*4882a593Smuzhiyun 
115*4882a593Smuzhiyun 	return 0;
116*4882a593Smuzhiyun }
117*4882a593Smuzhiyun 
118*4882a593Smuzhiyun /* sctp asoc/ep fill*/
inet_sctp_diag_fill(struct sock * sk,struct sctp_association * asoc,struct sk_buff * skb,const struct inet_diag_req_v2 * req,struct user_namespace * user_ns,int portid,u32 seq,u16 nlmsg_flags,const struct nlmsghdr * unlh,bool net_admin)119*4882a593Smuzhiyun static int inet_sctp_diag_fill(struct sock *sk, struct sctp_association *asoc,
120*4882a593Smuzhiyun 			       struct sk_buff *skb,
121*4882a593Smuzhiyun 			       const struct inet_diag_req_v2 *req,
122*4882a593Smuzhiyun 			       struct user_namespace *user_ns,
123*4882a593Smuzhiyun 			       int portid, u32 seq, u16 nlmsg_flags,
124*4882a593Smuzhiyun 			       const struct nlmsghdr *unlh,
125*4882a593Smuzhiyun 			       bool net_admin)
126*4882a593Smuzhiyun {
127*4882a593Smuzhiyun 	struct sctp_endpoint *ep = sctp_sk(sk)->ep;
128*4882a593Smuzhiyun 	struct list_head *addr_list;
129*4882a593Smuzhiyun 	struct inet_diag_msg *r;
130*4882a593Smuzhiyun 	struct nlmsghdr  *nlh;
131*4882a593Smuzhiyun 	int ext = req->idiag_ext;
132*4882a593Smuzhiyun 	struct sctp_infox infox;
133*4882a593Smuzhiyun 	void *info = NULL;
134*4882a593Smuzhiyun 
135*4882a593Smuzhiyun 	nlh = nlmsg_put(skb, portid, seq, unlh->nlmsg_type, sizeof(*r),
136*4882a593Smuzhiyun 			nlmsg_flags);
137*4882a593Smuzhiyun 	if (!nlh)
138*4882a593Smuzhiyun 		return -EMSGSIZE;
139*4882a593Smuzhiyun 
140*4882a593Smuzhiyun 	r = nlmsg_data(nlh);
141*4882a593Smuzhiyun 	BUG_ON(!sk_fullsock(sk));
142*4882a593Smuzhiyun 
143*4882a593Smuzhiyun 	r->idiag_timer = 0;
144*4882a593Smuzhiyun 	r->idiag_retrans = 0;
145*4882a593Smuzhiyun 	r->idiag_expires = 0;
146*4882a593Smuzhiyun 	if (asoc) {
147*4882a593Smuzhiyun 		inet_diag_msg_sctpasoc_fill(r, sk, asoc);
148*4882a593Smuzhiyun 	} else {
149*4882a593Smuzhiyun 		inet_diag_msg_common_fill(r, sk);
150*4882a593Smuzhiyun 		r->idiag_state = sk->sk_state;
151*4882a593Smuzhiyun 	}
152*4882a593Smuzhiyun 
153*4882a593Smuzhiyun 	if (inet_diag_msg_attrs_fill(sk, skb, r, ext, user_ns, net_admin))
154*4882a593Smuzhiyun 		goto errout;
155*4882a593Smuzhiyun 
156*4882a593Smuzhiyun 	if (ext & (1 << (INET_DIAG_SKMEMINFO - 1))) {
157*4882a593Smuzhiyun 		u32 mem[SK_MEMINFO_VARS];
158*4882a593Smuzhiyun 		int amt;
159*4882a593Smuzhiyun 
160*4882a593Smuzhiyun 		if (asoc && asoc->ep->sndbuf_policy)
161*4882a593Smuzhiyun 			amt = asoc->sndbuf_used;
162*4882a593Smuzhiyun 		else
163*4882a593Smuzhiyun 			amt = sk_wmem_alloc_get(sk);
164*4882a593Smuzhiyun 		mem[SK_MEMINFO_WMEM_ALLOC] = amt;
165*4882a593Smuzhiyun 		if (asoc && asoc->ep->rcvbuf_policy)
166*4882a593Smuzhiyun 			amt = atomic_read(&asoc->rmem_alloc);
167*4882a593Smuzhiyun 		else
168*4882a593Smuzhiyun 			amt = sk_rmem_alloc_get(sk);
169*4882a593Smuzhiyun 		mem[SK_MEMINFO_RMEM_ALLOC] = amt;
170*4882a593Smuzhiyun 		mem[SK_MEMINFO_RCVBUF] = sk->sk_rcvbuf;
171*4882a593Smuzhiyun 		mem[SK_MEMINFO_SNDBUF] = sk->sk_sndbuf;
172*4882a593Smuzhiyun 		mem[SK_MEMINFO_FWD_ALLOC] = sk->sk_forward_alloc;
173*4882a593Smuzhiyun 		mem[SK_MEMINFO_WMEM_QUEUED] = sk->sk_wmem_queued;
174*4882a593Smuzhiyun 		mem[SK_MEMINFO_OPTMEM] = atomic_read(&sk->sk_omem_alloc);
175*4882a593Smuzhiyun 		mem[SK_MEMINFO_BACKLOG] = READ_ONCE(sk->sk_backlog.len);
176*4882a593Smuzhiyun 		mem[SK_MEMINFO_DROPS] = atomic_read(&sk->sk_drops);
177*4882a593Smuzhiyun 
178*4882a593Smuzhiyun 		if (nla_put(skb, INET_DIAG_SKMEMINFO, sizeof(mem), &mem) < 0)
179*4882a593Smuzhiyun 			goto errout;
180*4882a593Smuzhiyun 	}
181*4882a593Smuzhiyun 
182*4882a593Smuzhiyun 	if (ext & (1 << (INET_DIAG_INFO - 1))) {
183*4882a593Smuzhiyun 		struct nlattr *attr;
184*4882a593Smuzhiyun 
185*4882a593Smuzhiyun 		attr = nla_reserve_64bit(skb, INET_DIAG_INFO,
186*4882a593Smuzhiyun 					 sizeof(struct sctp_info),
187*4882a593Smuzhiyun 					 INET_DIAG_PAD);
188*4882a593Smuzhiyun 		if (!attr)
189*4882a593Smuzhiyun 			goto errout;
190*4882a593Smuzhiyun 
191*4882a593Smuzhiyun 		info = nla_data(attr);
192*4882a593Smuzhiyun 	}
193*4882a593Smuzhiyun 	infox.sctpinfo = (struct sctp_info *)info;
194*4882a593Smuzhiyun 	infox.asoc = asoc;
195*4882a593Smuzhiyun 	sctp_diag_get_info(sk, r, &infox);
196*4882a593Smuzhiyun 
197*4882a593Smuzhiyun 	addr_list = asoc ? &asoc->base.bind_addr.address_list
198*4882a593Smuzhiyun 			 : &ep->base.bind_addr.address_list;
199*4882a593Smuzhiyun 	if (inet_diag_msg_sctpladdrs_fill(skb, addr_list))
200*4882a593Smuzhiyun 		goto errout;
201*4882a593Smuzhiyun 
202*4882a593Smuzhiyun 	if (asoc && (ext & (1 << (INET_DIAG_CONG - 1))))
203*4882a593Smuzhiyun 		if (nla_put_string(skb, INET_DIAG_CONG, "reno") < 0)
204*4882a593Smuzhiyun 			goto errout;
205*4882a593Smuzhiyun 
206*4882a593Smuzhiyun 	if (asoc && inet_diag_msg_sctpaddrs_fill(skb, asoc))
207*4882a593Smuzhiyun 		goto errout;
208*4882a593Smuzhiyun 
209*4882a593Smuzhiyun 	nlmsg_end(skb, nlh);
210*4882a593Smuzhiyun 	return 0;
211*4882a593Smuzhiyun 
212*4882a593Smuzhiyun errout:
213*4882a593Smuzhiyun 	nlmsg_cancel(skb, nlh);
214*4882a593Smuzhiyun 	return -EMSGSIZE;
215*4882a593Smuzhiyun }
216*4882a593Smuzhiyun 
217*4882a593Smuzhiyun /* callback and param */
218*4882a593Smuzhiyun struct sctp_comm_param {
219*4882a593Smuzhiyun 	struct sk_buff *skb;
220*4882a593Smuzhiyun 	struct netlink_callback *cb;
221*4882a593Smuzhiyun 	const struct inet_diag_req_v2 *r;
222*4882a593Smuzhiyun 	const struct nlmsghdr *nlh;
223*4882a593Smuzhiyun 	bool net_admin;
224*4882a593Smuzhiyun };
225*4882a593Smuzhiyun 
inet_assoc_attr_size(struct sctp_association * asoc)226*4882a593Smuzhiyun static size_t inet_assoc_attr_size(struct sctp_association *asoc)
227*4882a593Smuzhiyun {
228*4882a593Smuzhiyun 	int addrlen = sizeof(struct sockaddr_storage);
229*4882a593Smuzhiyun 	int addrcnt = 0;
230*4882a593Smuzhiyun 	struct sctp_sockaddr_entry *laddr;
231*4882a593Smuzhiyun 
232*4882a593Smuzhiyun 	list_for_each_entry_rcu(laddr, &asoc->base.bind_addr.address_list,
233*4882a593Smuzhiyun 				list)
234*4882a593Smuzhiyun 		addrcnt++;
235*4882a593Smuzhiyun 
236*4882a593Smuzhiyun 	return	  nla_total_size(sizeof(struct sctp_info))
237*4882a593Smuzhiyun 		+ nla_total_size(addrlen * asoc->peer.transport_count)
238*4882a593Smuzhiyun 		+ nla_total_size(addrlen * addrcnt)
239*4882a593Smuzhiyun 		+ nla_total_size(sizeof(struct inet_diag_msg))
240*4882a593Smuzhiyun 		+ inet_diag_msg_attrs_size()
241*4882a593Smuzhiyun 		+ nla_total_size(sizeof(struct inet_diag_meminfo))
242*4882a593Smuzhiyun 		+ 64;
243*4882a593Smuzhiyun }
244*4882a593Smuzhiyun 
sctp_tsp_dump_one(struct sctp_transport * tsp,void * p)245*4882a593Smuzhiyun static int sctp_tsp_dump_one(struct sctp_transport *tsp, void *p)
246*4882a593Smuzhiyun {
247*4882a593Smuzhiyun 	struct sctp_association *assoc = tsp->asoc;
248*4882a593Smuzhiyun 	struct sock *sk = tsp->asoc->base.sk;
249*4882a593Smuzhiyun 	struct sctp_comm_param *commp = p;
250*4882a593Smuzhiyun 	struct sk_buff *in_skb = commp->skb;
251*4882a593Smuzhiyun 	const struct inet_diag_req_v2 *req = commp->r;
252*4882a593Smuzhiyun 	const struct nlmsghdr *nlh = commp->nlh;
253*4882a593Smuzhiyun 	struct net *net = sock_net(in_skb->sk);
254*4882a593Smuzhiyun 	struct sk_buff *rep;
255*4882a593Smuzhiyun 	int err;
256*4882a593Smuzhiyun 
257*4882a593Smuzhiyun 	err = sock_diag_check_cookie(sk, req->id.idiag_cookie);
258*4882a593Smuzhiyun 	if (err)
259*4882a593Smuzhiyun 		goto out;
260*4882a593Smuzhiyun 
261*4882a593Smuzhiyun 	err = -ENOMEM;
262*4882a593Smuzhiyun 	rep = nlmsg_new(inet_assoc_attr_size(assoc), GFP_KERNEL);
263*4882a593Smuzhiyun 	if (!rep)
264*4882a593Smuzhiyun 		goto out;
265*4882a593Smuzhiyun 
266*4882a593Smuzhiyun 	lock_sock(sk);
267*4882a593Smuzhiyun 	if (sk != assoc->base.sk) {
268*4882a593Smuzhiyun 		release_sock(sk);
269*4882a593Smuzhiyun 		sk = assoc->base.sk;
270*4882a593Smuzhiyun 		lock_sock(sk);
271*4882a593Smuzhiyun 	}
272*4882a593Smuzhiyun 	err = inet_sctp_diag_fill(sk, assoc, rep, req,
273*4882a593Smuzhiyun 				  sk_user_ns(NETLINK_CB(in_skb).sk),
274*4882a593Smuzhiyun 				  NETLINK_CB(in_skb).portid,
275*4882a593Smuzhiyun 				  nlh->nlmsg_seq, 0, nlh,
276*4882a593Smuzhiyun 				  commp->net_admin);
277*4882a593Smuzhiyun 	release_sock(sk);
278*4882a593Smuzhiyun 	if (err < 0) {
279*4882a593Smuzhiyun 		WARN_ON(err == -EMSGSIZE);
280*4882a593Smuzhiyun 		kfree_skb(rep);
281*4882a593Smuzhiyun 		goto out;
282*4882a593Smuzhiyun 	}
283*4882a593Smuzhiyun 
284*4882a593Smuzhiyun 	err = netlink_unicast(net->diag_nlsk, rep, NETLINK_CB(in_skb).portid,
285*4882a593Smuzhiyun 			      MSG_DONTWAIT);
286*4882a593Smuzhiyun 	if (err > 0)
287*4882a593Smuzhiyun 		err = 0;
288*4882a593Smuzhiyun out:
289*4882a593Smuzhiyun 	return err;
290*4882a593Smuzhiyun }
291*4882a593Smuzhiyun 
sctp_sock_dump(struct sctp_endpoint * ep,struct sctp_transport * tsp,void * p)292*4882a593Smuzhiyun static int sctp_sock_dump(struct sctp_endpoint *ep, struct sctp_transport *tsp, void *p)
293*4882a593Smuzhiyun {
294*4882a593Smuzhiyun 	struct sctp_comm_param *commp = p;
295*4882a593Smuzhiyun 	struct sock *sk = ep->base.sk;
296*4882a593Smuzhiyun 	struct sk_buff *skb = commp->skb;
297*4882a593Smuzhiyun 	struct netlink_callback *cb = commp->cb;
298*4882a593Smuzhiyun 	const struct inet_diag_req_v2 *r = commp->r;
299*4882a593Smuzhiyun 	struct sctp_association *assoc;
300*4882a593Smuzhiyun 	int err = 0;
301*4882a593Smuzhiyun 
302*4882a593Smuzhiyun 	lock_sock(sk);
303*4882a593Smuzhiyun 	if (ep != tsp->asoc->ep)
304*4882a593Smuzhiyun 		goto release;
305*4882a593Smuzhiyun 	list_for_each_entry(assoc, &ep->asocs, asocs) {
306*4882a593Smuzhiyun 		if (cb->args[4] < cb->args[1])
307*4882a593Smuzhiyun 			goto next;
308*4882a593Smuzhiyun 
309*4882a593Smuzhiyun 		if (r->id.idiag_sport != htons(assoc->base.bind_addr.port) &&
310*4882a593Smuzhiyun 		    r->id.idiag_sport)
311*4882a593Smuzhiyun 			goto next;
312*4882a593Smuzhiyun 		if (r->id.idiag_dport != htons(assoc->peer.port) &&
313*4882a593Smuzhiyun 		    r->id.idiag_dport)
314*4882a593Smuzhiyun 			goto next;
315*4882a593Smuzhiyun 
316*4882a593Smuzhiyun 		if (!cb->args[3] &&
317*4882a593Smuzhiyun 		    inet_sctp_diag_fill(sk, NULL, skb, r,
318*4882a593Smuzhiyun 					sk_user_ns(NETLINK_CB(cb->skb).sk),
319*4882a593Smuzhiyun 					NETLINK_CB(cb->skb).portid,
320*4882a593Smuzhiyun 					cb->nlh->nlmsg_seq,
321*4882a593Smuzhiyun 					NLM_F_MULTI, cb->nlh,
322*4882a593Smuzhiyun 					commp->net_admin) < 0) {
323*4882a593Smuzhiyun 			err = 1;
324*4882a593Smuzhiyun 			goto release;
325*4882a593Smuzhiyun 		}
326*4882a593Smuzhiyun 		cb->args[3] = 1;
327*4882a593Smuzhiyun 
328*4882a593Smuzhiyun 		if (inet_sctp_diag_fill(sk, assoc, skb, r,
329*4882a593Smuzhiyun 					sk_user_ns(NETLINK_CB(cb->skb).sk),
330*4882a593Smuzhiyun 					NETLINK_CB(cb->skb).portid,
331*4882a593Smuzhiyun 					cb->nlh->nlmsg_seq, 0, cb->nlh,
332*4882a593Smuzhiyun 					commp->net_admin) < 0) {
333*4882a593Smuzhiyun 			err = 1;
334*4882a593Smuzhiyun 			goto release;
335*4882a593Smuzhiyun 		}
336*4882a593Smuzhiyun next:
337*4882a593Smuzhiyun 		cb->args[4]++;
338*4882a593Smuzhiyun 	}
339*4882a593Smuzhiyun 	cb->args[1] = 0;
340*4882a593Smuzhiyun 	cb->args[3] = 0;
341*4882a593Smuzhiyun 	cb->args[4] = 0;
342*4882a593Smuzhiyun release:
343*4882a593Smuzhiyun 	release_sock(sk);
344*4882a593Smuzhiyun 	return err;
345*4882a593Smuzhiyun }
346*4882a593Smuzhiyun 
sctp_sock_filter(struct sctp_endpoint * ep,struct sctp_transport * tsp,void * p)347*4882a593Smuzhiyun static int sctp_sock_filter(struct sctp_endpoint *ep, struct sctp_transport *tsp, void *p)
348*4882a593Smuzhiyun {
349*4882a593Smuzhiyun 	struct sctp_comm_param *commp = p;
350*4882a593Smuzhiyun 	struct sock *sk = ep->base.sk;
351*4882a593Smuzhiyun 	const struct inet_diag_req_v2 *r = commp->r;
352*4882a593Smuzhiyun 	struct sctp_association *assoc =
353*4882a593Smuzhiyun 		list_entry(ep->asocs.next, struct sctp_association, asocs);
354*4882a593Smuzhiyun 
355*4882a593Smuzhiyun 	/* find the ep only once through the transports by this condition */
356*4882a593Smuzhiyun 	if (tsp->asoc != assoc)
357*4882a593Smuzhiyun 		return 0;
358*4882a593Smuzhiyun 
359*4882a593Smuzhiyun 	if (r->sdiag_family != AF_UNSPEC && sk->sk_family != r->sdiag_family)
360*4882a593Smuzhiyun 		return 0;
361*4882a593Smuzhiyun 
362*4882a593Smuzhiyun 	return 1;
363*4882a593Smuzhiyun }
364*4882a593Smuzhiyun 
sctp_ep_dump(struct sctp_endpoint * ep,void * p)365*4882a593Smuzhiyun static int sctp_ep_dump(struct sctp_endpoint *ep, void *p)
366*4882a593Smuzhiyun {
367*4882a593Smuzhiyun 	struct sctp_comm_param *commp = p;
368*4882a593Smuzhiyun 	struct sock *sk = ep->base.sk;
369*4882a593Smuzhiyun 	struct sk_buff *skb = commp->skb;
370*4882a593Smuzhiyun 	struct netlink_callback *cb = commp->cb;
371*4882a593Smuzhiyun 	const struct inet_diag_req_v2 *r = commp->r;
372*4882a593Smuzhiyun 	struct net *net = sock_net(skb->sk);
373*4882a593Smuzhiyun 	struct inet_sock *inet = inet_sk(sk);
374*4882a593Smuzhiyun 	int err = 0;
375*4882a593Smuzhiyun 
376*4882a593Smuzhiyun 	if (!net_eq(sock_net(sk), net))
377*4882a593Smuzhiyun 		goto out;
378*4882a593Smuzhiyun 
379*4882a593Smuzhiyun 	if (cb->args[4] < cb->args[1])
380*4882a593Smuzhiyun 		goto next;
381*4882a593Smuzhiyun 
382*4882a593Smuzhiyun 	if (!(r->idiag_states & TCPF_LISTEN) && !list_empty(&ep->asocs))
383*4882a593Smuzhiyun 		goto next;
384*4882a593Smuzhiyun 
385*4882a593Smuzhiyun 	if (r->sdiag_family != AF_UNSPEC &&
386*4882a593Smuzhiyun 	    sk->sk_family != r->sdiag_family)
387*4882a593Smuzhiyun 		goto next;
388*4882a593Smuzhiyun 
389*4882a593Smuzhiyun 	if (r->id.idiag_sport != inet->inet_sport &&
390*4882a593Smuzhiyun 	    r->id.idiag_sport)
391*4882a593Smuzhiyun 		goto next;
392*4882a593Smuzhiyun 
393*4882a593Smuzhiyun 	if (r->id.idiag_dport != inet->inet_dport &&
394*4882a593Smuzhiyun 	    r->id.idiag_dport)
395*4882a593Smuzhiyun 		goto next;
396*4882a593Smuzhiyun 
397*4882a593Smuzhiyun 	if (inet_sctp_diag_fill(sk, NULL, skb, r,
398*4882a593Smuzhiyun 				sk_user_ns(NETLINK_CB(cb->skb).sk),
399*4882a593Smuzhiyun 				NETLINK_CB(cb->skb).portid,
400*4882a593Smuzhiyun 				cb->nlh->nlmsg_seq, NLM_F_MULTI,
401*4882a593Smuzhiyun 				cb->nlh, commp->net_admin) < 0) {
402*4882a593Smuzhiyun 		err = 2;
403*4882a593Smuzhiyun 		goto out;
404*4882a593Smuzhiyun 	}
405*4882a593Smuzhiyun next:
406*4882a593Smuzhiyun 	cb->args[4]++;
407*4882a593Smuzhiyun out:
408*4882a593Smuzhiyun 	return err;
409*4882a593Smuzhiyun }
410*4882a593Smuzhiyun 
411*4882a593Smuzhiyun /* define the functions for sctp_diag_handler*/
sctp_diag_get_info(struct sock * sk,struct inet_diag_msg * r,void * info)412*4882a593Smuzhiyun static void sctp_diag_get_info(struct sock *sk, struct inet_diag_msg *r,
413*4882a593Smuzhiyun 			       void *info)
414*4882a593Smuzhiyun {
415*4882a593Smuzhiyun 	struct sctp_infox *infox = (struct sctp_infox *)info;
416*4882a593Smuzhiyun 
417*4882a593Smuzhiyun 	if (infox->asoc) {
418*4882a593Smuzhiyun 		r->idiag_rqueue = atomic_read(&infox->asoc->rmem_alloc);
419*4882a593Smuzhiyun 		r->idiag_wqueue = infox->asoc->sndbuf_used;
420*4882a593Smuzhiyun 	} else {
421*4882a593Smuzhiyun 		r->idiag_rqueue = READ_ONCE(sk->sk_ack_backlog);
422*4882a593Smuzhiyun 		r->idiag_wqueue = READ_ONCE(sk->sk_max_ack_backlog);
423*4882a593Smuzhiyun 	}
424*4882a593Smuzhiyun 	if (infox->sctpinfo)
425*4882a593Smuzhiyun 		sctp_get_sctp_info(sk, infox->asoc, infox->sctpinfo);
426*4882a593Smuzhiyun }
427*4882a593Smuzhiyun 
sctp_diag_dump_one(struct netlink_callback * cb,const struct inet_diag_req_v2 * req)428*4882a593Smuzhiyun static int sctp_diag_dump_one(struct netlink_callback *cb,
429*4882a593Smuzhiyun 			      const struct inet_diag_req_v2 *req)
430*4882a593Smuzhiyun {
431*4882a593Smuzhiyun 	struct sk_buff *in_skb = cb->skb;
432*4882a593Smuzhiyun 	struct net *net = sock_net(in_skb->sk);
433*4882a593Smuzhiyun 	const struct nlmsghdr *nlh = cb->nlh;
434*4882a593Smuzhiyun 	union sctp_addr laddr, paddr;
435*4882a593Smuzhiyun 	struct sctp_comm_param commp = {
436*4882a593Smuzhiyun 		.skb = in_skb,
437*4882a593Smuzhiyun 		.r = req,
438*4882a593Smuzhiyun 		.nlh = nlh,
439*4882a593Smuzhiyun 		.net_admin = netlink_net_capable(in_skb, CAP_NET_ADMIN),
440*4882a593Smuzhiyun 	};
441*4882a593Smuzhiyun 
442*4882a593Smuzhiyun 	if (req->sdiag_family == AF_INET) {
443*4882a593Smuzhiyun 		laddr.v4.sin_port = req->id.idiag_sport;
444*4882a593Smuzhiyun 		laddr.v4.sin_addr.s_addr = req->id.idiag_src[0];
445*4882a593Smuzhiyun 		laddr.v4.sin_family = AF_INET;
446*4882a593Smuzhiyun 
447*4882a593Smuzhiyun 		paddr.v4.sin_port = req->id.idiag_dport;
448*4882a593Smuzhiyun 		paddr.v4.sin_addr.s_addr = req->id.idiag_dst[0];
449*4882a593Smuzhiyun 		paddr.v4.sin_family = AF_INET;
450*4882a593Smuzhiyun 	} else {
451*4882a593Smuzhiyun 		laddr.v6.sin6_port = req->id.idiag_sport;
452*4882a593Smuzhiyun 		memcpy(&laddr.v6.sin6_addr, req->id.idiag_src,
453*4882a593Smuzhiyun 		       sizeof(laddr.v6.sin6_addr));
454*4882a593Smuzhiyun 		laddr.v6.sin6_family = AF_INET6;
455*4882a593Smuzhiyun 
456*4882a593Smuzhiyun 		paddr.v6.sin6_port = req->id.idiag_dport;
457*4882a593Smuzhiyun 		memcpy(&paddr.v6.sin6_addr, req->id.idiag_dst,
458*4882a593Smuzhiyun 		       sizeof(paddr.v6.sin6_addr));
459*4882a593Smuzhiyun 		paddr.v6.sin6_family = AF_INET6;
460*4882a593Smuzhiyun 	}
461*4882a593Smuzhiyun 
462*4882a593Smuzhiyun 	return sctp_transport_lookup_process(sctp_tsp_dump_one,
463*4882a593Smuzhiyun 					     net, &laddr, &paddr, &commp);
464*4882a593Smuzhiyun }
465*4882a593Smuzhiyun 
sctp_diag_dump(struct sk_buff * skb,struct netlink_callback * cb,const struct inet_diag_req_v2 * r)466*4882a593Smuzhiyun static void sctp_diag_dump(struct sk_buff *skb, struct netlink_callback *cb,
467*4882a593Smuzhiyun 			   const struct inet_diag_req_v2 *r)
468*4882a593Smuzhiyun {
469*4882a593Smuzhiyun 	u32 idiag_states = r->idiag_states;
470*4882a593Smuzhiyun 	struct net *net = sock_net(skb->sk);
471*4882a593Smuzhiyun 	struct sctp_comm_param commp = {
472*4882a593Smuzhiyun 		.skb = skb,
473*4882a593Smuzhiyun 		.cb = cb,
474*4882a593Smuzhiyun 		.r = r,
475*4882a593Smuzhiyun 		.net_admin = netlink_net_capable(cb->skb, CAP_NET_ADMIN),
476*4882a593Smuzhiyun 	};
477*4882a593Smuzhiyun 	int pos = cb->args[2];
478*4882a593Smuzhiyun 
479*4882a593Smuzhiyun 	/* eps hashtable dumps
480*4882a593Smuzhiyun 	 * args:
481*4882a593Smuzhiyun 	 * 0 : if it will traversal listen sock
482*4882a593Smuzhiyun 	 * 1 : to record the sock pos of this time's traversal
483*4882a593Smuzhiyun 	 * 4 : to work as a temporary variable to traversal list
484*4882a593Smuzhiyun 	 */
485*4882a593Smuzhiyun 	if (cb->args[0] == 0) {
486*4882a593Smuzhiyun 		if (!(idiag_states & TCPF_LISTEN))
487*4882a593Smuzhiyun 			goto skip;
488*4882a593Smuzhiyun 		if (sctp_for_each_endpoint(sctp_ep_dump, &commp))
489*4882a593Smuzhiyun 			goto done;
490*4882a593Smuzhiyun skip:
491*4882a593Smuzhiyun 		cb->args[0] = 1;
492*4882a593Smuzhiyun 		cb->args[1] = 0;
493*4882a593Smuzhiyun 		cb->args[4] = 0;
494*4882a593Smuzhiyun 	}
495*4882a593Smuzhiyun 
496*4882a593Smuzhiyun 	/* asocs by transport hashtable dump
497*4882a593Smuzhiyun 	 * args:
498*4882a593Smuzhiyun 	 * 1 : to record the assoc pos of this time's traversal
499*4882a593Smuzhiyun 	 * 2 : to record the transport pos of this time's traversal
500*4882a593Smuzhiyun 	 * 3 : to mark if we have dumped the ep info of the current asoc
501*4882a593Smuzhiyun 	 * 4 : to work as a temporary variable to traversal list
502*4882a593Smuzhiyun 	 * 5 : to save the sk we get from travelsing the tsp list.
503*4882a593Smuzhiyun 	 */
504*4882a593Smuzhiyun 	if (!(idiag_states & ~(TCPF_LISTEN | TCPF_CLOSE)))
505*4882a593Smuzhiyun 		goto done;
506*4882a593Smuzhiyun 
507*4882a593Smuzhiyun 	sctp_transport_traverse_process(sctp_sock_filter, sctp_sock_dump,
508*4882a593Smuzhiyun 					net, &pos, &commp);
509*4882a593Smuzhiyun 	cb->args[2] = pos;
510*4882a593Smuzhiyun 
511*4882a593Smuzhiyun done:
512*4882a593Smuzhiyun 	cb->args[1] = cb->args[4];
513*4882a593Smuzhiyun 	cb->args[4] = 0;
514*4882a593Smuzhiyun }
515*4882a593Smuzhiyun 
516*4882a593Smuzhiyun static const struct inet_diag_handler sctp_diag_handler = {
517*4882a593Smuzhiyun 	.dump		 = sctp_diag_dump,
518*4882a593Smuzhiyun 	.dump_one	 = sctp_diag_dump_one,
519*4882a593Smuzhiyun 	.idiag_get_info  = sctp_diag_get_info,
520*4882a593Smuzhiyun 	.idiag_type	 = IPPROTO_SCTP,
521*4882a593Smuzhiyun 	.idiag_info_size = sizeof(struct sctp_info),
522*4882a593Smuzhiyun };
523*4882a593Smuzhiyun 
sctp_diag_init(void)524*4882a593Smuzhiyun static int __init sctp_diag_init(void)
525*4882a593Smuzhiyun {
526*4882a593Smuzhiyun 	return inet_diag_register(&sctp_diag_handler);
527*4882a593Smuzhiyun }
528*4882a593Smuzhiyun 
sctp_diag_exit(void)529*4882a593Smuzhiyun static void __exit sctp_diag_exit(void)
530*4882a593Smuzhiyun {
531*4882a593Smuzhiyun 	inet_diag_unregister(&sctp_diag_handler);
532*4882a593Smuzhiyun }
533*4882a593Smuzhiyun 
534*4882a593Smuzhiyun module_init(sctp_diag_init);
535*4882a593Smuzhiyun module_exit(sctp_diag_exit);
536*4882a593Smuzhiyun MODULE_LICENSE("GPL");
537*4882a593Smuzhiyun MODULE_ALIAS_NET_PF_PROTO_TYPE(PF_NETLINK, NETLINK_SOCK_DIAG, 2-132);
538