xref: /OK3568_Linux_fs/kernel/net/core/sock_diag.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
1*4882a593Smuzhiyun /* License: GPL */
2*4882a593Smuzhiyun 
3*4882a593Smuzhiyun #include <linux/mutex.h>
4*4882a593Smuzhiyun #include <linux/socket.h>
5*4882a593Smuzhiyun #include <linux/skbuff.h>
6*4882a593Smuzhiyun #include <net/netlink.h>
7*4882a593Smuzhiyun #include <net/net_namespace.h>
8*4882a593Smuzhiyun #include <linux/module.h>
9*4882a593Smuzhiyun #include <net/sock.h>
10*4882a593Smuzhiyun #include <linux/kernel.h>
11*4882a593Smuzhiyun #include <linux/tcp.h>
12*4882a593Smuzhiyun #include <linux/workqueue.h>
13*4882a593Smuzhiyun #include <linux/nospec.h>
14*4882a593Smuzhiyun #include <linux/cookie.h>
15*4882a593Smuzhiyun #include <linux/inet_diag.h>
16*4882a593Smuzhiyun #include <linux/sock_diag.h>
17*4882a593Smuzhiyun 
18*4882a593Smuzhiyun static const struct sock_diag_handler *sock_diag_handlers[AF_MAX];
19*4882a593Smuzhiyun static int (*inet_rcv_compat)(struct sk_buff *skb, struct nlmsghdr *nlh);
20*4882a593Smuzhiyun static DEFINE_MUTEX(sock_diag_table_mutex);
21*4882a593Smuzhiyun static struct workqueue_struct *broadcast_wq;
22*4882a593Smuzhiyun 
23*4882a593Smuzhiyun DEFINE_COOKIE(sock_cookie);
24*4882a593Smuzhiyun 
__sock_gen_cookie(struct sock * sk)25*4882a593Smuzhiyun u64 __sock_gen_cookie(struct sock *sk)
26*4882a593Smuzhiyun {
27*4882a593Smuzhiyun 	while (1) {
28*4882a593Smuzhiyun 		u64 res = atomic64_read(&sk->sk_cookie);
29*4882a593Smuzhiyun 
30*4882a593Smuzhiyun 		if (res)
31*4882a593Smuzhiyun 			return res;
32*4882a593Smuzhiyun 		res = gen_cookie_next(&sock_cookie);
33*4882a593Smuzhiyun 		atomic64_cmpxchg(&sk->sk_cookie, 0, res);
34*4882a593Smuzhiyun 	}
35*4882a593Smuzhiyun }
36*4882a593Smuzhiyun 
sock_diag_check_cookie(struct sock * sk,const __u32 * cookie)37*4882a593Smuzhiyun int sock_diag_check_cookie(struct sock *sk, const __u32 *cookie)
38*4882a593Smuzhiyun {
39*4882a593Smuzhiyun 	u64 res;
40*4882a593Smuzhiyun 
41*4882a593Smuzhiyun 	if (cookie[0] == INET_DIAG_NOCOOKIE && cookie[1] == INET_DIAG_NOCOOKIE)
42*4882a593Smuzhiyun 		return 0;
43*4882a593Smuzhiyun 
44*4882a593Smuzhiyun 	res = sock_gen_cookie(sk);
45*4882a593Smuzhiyun 	if ((u32)res != cookie[0] || (u32)(res >> 32) != cookie[1])
46*4882a593Smuzhiyun 		return -ESTALE;
47*4882a593Smuzhiyun 
48*4882a593Smuzhiyun 	return 0;
49*4882a593Smuzhiyun }
50*4882a593Smuzhiyun EXPORT_SYMBOL_GPL(sock_diag_check_cookie);
51*4882a593Smuzhiyun 
sock_diag_save_cookie(struct sock * sk,__u32 * cookie)52*4882a593Smuzhiyun void sock_diag_save_cookie(struct sock *sk, __u32 *cookie)
53*4882a593Smuzhiyun {
54*4882a593Smuzhiyun 	u64 res = sock_gen_cookie(sk);
55*4882a593Smuzhiyun 
56*4882a593Smuzhiyun 	cookie[0] = (u32)res;
57*4882a593Smuzhiyun 	cookie[1] = (u32)(res >> 32);
58*4882a593Smuzhiyun }
59*4882a593Smuzhiyun EXPORT_SYMBOL_GPL(sock_diag_save_cookie);
60*4882a593Smuzhiyun 
sock_diag_put_meminfo(struct sock * sk,struct sk_buff * skb,int attrtype)61*4882a593Smuzhiyun int sock_diag_put_meminfo(struct sock *sk, struct sk_buff *skb, int attrtype)
62*4882a593Smuzhiyun {
63*4882a593Smuzhiyun 	u32 mem[SK_MEMINFO_VARS];
64*4882a593Smuzhiyun 
65*4882a593Smuzhiyun 	sk_get_meminfo(sk, mem);
66*4882a593Smuzhiyun 
67*4882a593Smuzhiyun 	return nla_put(skb, attrtype, sizeof(mem), &mem);
68*4882a593Smuzhiyun }
69*4882a593Smuzhiyun EXPORT_SYMBOL_GPL(sock_diag_put_meminfo);
70*4882a593Smuzhiyun 
sock_diag_put_filterinfo(bool may_report_filterinfo,struct sock * sk,struct sk_buff * skb,int attrtype)71*4882a593Smuzhiyun int sock_diag_put_filterinfo(bool may_report_filterinfo, struct sock *sk,
72*4882a593Smuzhiyun 			     struct sk_buff *skb, int attrtype)
73*4882a593Smuzhiyun {
74*4882a593Smuzhiyun 	struct sock_fprog_kern *fprog;
75*4882a593Smuzhiyun 	struct sk_filter *filter;
76*4882a593Smuzhiyun 	struct nlattr *attr;
77*4882a593Smuzhiyun 	unsigned int flen;
78*4882a593Smuzhiyun 	int err = 0;
79*4882a593Smuzhiyun 
80*4882a593Smuzhiyun 	if (!may_report_filterinfo) {
81*4882a593Smuzhiyun 		nla_reserve(skb, attrtype, 0);
82*4882a593Smuzhiyun 		return 0;
83*4882a593Smuzhiyun 	}
84*4882a593Smuzhiyun 
85*4882a593Smuzhiyun 	rcu_read_lock();
86*4882a593Smuzhiyun 	filter = rcu_dereference(sk->sk_filter);
87*4882a593Smuzhiyun 	if (!filter)
88*4882a593Smuzhiyun 		goto out;
89*4882a593Smuzhiyun 
90*4882a593Smuzhiyun 	fprog = filter->prog->orig_prog;
91*4882a593Smuzhiyun 	if (!fprog)
92*4882a593Smuzhiyun 		goto out;
93*4882a593Smuzhiyun 
94*4882a593Smuzhiyun 	flen = bpf_classic_proglen(fprog);
95*4882a593Smuzhiyun 
96*4882a593Smuzhiyun 	attr = nla_reserve(skb, attrtype, flen);
97*4882a593Smuzhiyun 	if (attr == NULL) {
98*4882a593Smuzhiyun 		err = -EMSGSIZE;
99*4882a593Smuzhiyun 		goto out;
100*4882a593Smuzhiyun 	}
101*4882a593Smuzhiyun 
102*4882a593Smuzhiyun 	memcpy(nla_data(attr), fprog->filter, flen);
103*4882a593Smuzhiyun out:
104*4882a593Smuzhiyun 	rcu_read_unlock();
105*4882a593Smuzhiyun 	return err;
106*4882a593Smuzhiyun }
107*4882a593Smuzhiyun EXPORT_SYMBOL(sock_diag_put_filterinfo);
108*4882a593Smuzhiyun 
109*4882a593Smuzhiyun struct broadcast_sk {
110*4882a593Smuzhiyun 	struct sock *sk;
111*4882a593Smuzhiyun 	struct work_struct work;
112*4882a593Smuzhiyun };
113*4882a593Smuzhiyun 
sock_diag_nlmsg_size(void)114*4882a593Smuzhiyun static size_t sock_diag_nlmsg_size(void)
115*4882a593Smuzhiyun {
116*4882a593Smuzhiyun 	return NLMSG_ALIGN(sizeof(struct inet_diag_msg)
117*4882a593Smuzhiyun 	       + nla_total_size(sizeof(u8)) /* INET_DIAG_PROTOCOL */
118*4882a593Smuzhiyun 	       + nla_total_size_64bit(sizeof(struct tcp_info))); /* INET_DIAG_INFO */
119*4882a593Smuzhiyun }
120*4882a593Smuzhiyun 
sock_diag_broadcast_destroy_work(struct work_struct * work)121*4882a593Smuzhiyun static void sock_diag_broadcast_destroy_work(struct work_struct *work)
122*4882a593Smuzhiyun {
123*4882a593Smuzhiyun 	struct broadcast_sk *bsk =
124*4882a593Smuzhiyun 		container_of(work, struct broadcast_sk, work);
125*4882a593Smuzhiyun 	struct sock *sk = bsk->sk;
126*4882a593Smuzhiyun 	const struct sock_diag_handler *hndl;
127*4882a593Smuzhiyun 	struct sk_buff *skb;
128*4882a593Smuzhiyun 	const enum sknetlink_groups group = sock_diag_destroy_group(sk);
129*4882a593Smuzhiyun 	int err = -1;
130*4882a593Smuzhiyun 
131*4882a593Smuzhiyun 	WARN_ON(group == SKNLGRP_NONE);
132*4882a593Smuzhiyun 
133*4882a593Smuzhiyun 	skb = nlmsg_new(sock_diag_nlmsg_size(), GFP_KERNEL);
134*4882a593Smuzhiyun 	if (!skb)
135*4882a593Smuzhiyun 		goto out;
136*4882a593Smuzhiyun 
137*4882a593Smuzhiyun 	mutex_lock(&sock_diag_table_mutex);
138*4882a593Smuzhiyun 	hndl = sock_diag_handlers[sk->sk_family];
139*4882a593Smuzhiyun 	if (hndl && hndl->get_info)
140*4882a593Smuzhiyun 		err = hndl->get_info(skb, sk);
141*4882a593Smuzhiyun 	mutex_unlock(&sock_diag_table_mutex);
142*4882a593Smuzhiyun 
143*4882a593Smuzhiyun 	if (!err)
144*4882a593Smuzhiyun 		nlmsg_multicast(sock_net(sk)->diag_nlsk, skb, 0, group,
145*4882a593Smuzhiyun 				GFP_KERNEL);
146*4882a593Smuzhiyun 	else
147*4882a593Smuzhiyun 		kfree_skb(skb);
148*4882a593Smuzhiyun out:
149*4882a593Smuzhiyun 	sk_destruct(sk);
150*4882a593Smuzhiyun 	kfree(bsk);
151*4882a593Smuzhiyun }
152*4882a593Smuzhiyun 
sock_diag_broadcast_destroy(struct sock * sk)153*4882a593Smuzhiyun void sock_diag_broadcast_destroy(struct sock *sk)
154*4882a593Smuzhiyun {
155*4882a593Smuzhiyun 	/* Note, this function is often called from an interrupt context. */
156*4882a593Smuzhiyun 	struct broadcast_sk *bsk =
157*4882a593Smuzhiyun 		kmalloc(sizeof(struct broadcast_sk), GFP_ATOMIC);
158*4882a593Smuzhiyun 	if (!bsk)
159*4882a593Smuzhiyun 		return sk_destruct(sk);
160*4882a593Smuzhiyun 	bsk->sk = sk;
161*4882a593Smuzhiyun 	INIT_WORK(&bsk->work, sock_diag_broadcast_destroy_work);
162*4882a593Smuzhiyun 	queue_work(broadcast_wq, &bsk->work);
163*4882a593Smuzhiyun }
164*4882a593Smuzhiyun 
sock_diag_register_inet_compat(int (* fn)(struct sk_buff * skb,struct nlmsghdr * nlh))165*4882a593Smuzhiyun void sock_diag_register_inet_compat(int (*fn)(struct sk_buff *skb, struct nlmsghdr *nlh))
166*4882a593Smuzhiyun {
167*4882a593Smuzhiyun 	mutex_lock(&sock_diag_table_mutex);
168*4882a593Smuzhiyun 	inet_rcv_compat = fn;
169*4882a593Smuzhiyun 	mutex_unlock(&sock_diag_table_mutex);
170*4882a593Smuzhiyun }
171*4882a593Smuzhiyun EXPORT_SYMBOL_GPL(sock_diag_register_inet_compat);
172*4882a593Smuzhiyun 
sock_diag_unregister_inet_compat(int (* fn)(struct sk_buff * skb,struct nlmsghdr * nlh))173*4882a593Smuzhiyun void sock_diag_unregister_inet_compat(int (*fn)(struct sk_buff *skb, struct nlmsghdr *nlh))
174*4882a593Smuzhiyun {
175*4882a593Smuzhiyun 	mutex_lock(&sock_diag_table_mutex);
176*4882a593Smuzhiyun 	inet_rcv_compat = NULL;
177*4882a593Smuzhiyun 	mutex_unlock(&sock_diag_table_mutex);
178*4882a593Smuzhiyun }
179*4882a593Smuzhiyun EXPORT_SYMBOL_GPL(sock_diag_unregister_inet_compat);
180*4882a593Smuzhiyun 
sock_diag_register(const struct sock_diag_handler * hndl)181*4882a593Smuzhiyun int sock_diag_register(const struct sock_diag_handler *hndl)
182*4882a593Smuzhiyun {
183*4882a593Smuzhiyun 	int err = 0;
184*4882a593Smuzhiyun 
185*4882a593Smuzhiyun 	if (hndl->family >= AF_MAX)
186*4882a593Smuzhiyun 		return -EINVAL;
187*4882a593Smuzhiyun 
188*4882a593Smuzhiyun 	mutex_lock(&sock_diag_table_mutex);
189*4882a593Smuzhiyun 	if (sock_diag_handlers[hndl->family])
190*4882a593Smuzhiyun 		err = -EBUSY;
191*4882a593Smuzhiyun 	else
192*4882a593Smuzhiyun 		sock_diag_handlers[hndl->family] = hndl;
193*4882a593Smuzhiyun 	mutex_unlock(&sock_diag_table_mutex);
194*4882a593Smuzhiyun 
195*4882a593Smuzhiyun 	return err;
196*4882a593Smuzhiyun }
197*4882a593Smuzhiyun EXPORT_SYMBOL_GPL(sock_diag_register);
198*4882a593Smuzhiyun 
sock_diag_unregister(const struct sock_diag_handler * hnld)199*4882a593Smuzhiyun void sock_diag_unregister(const struct sock_diag_handler *hnld)
200*4882a593Smuzhiyun {
201*4882a593Smuzhiyun 	int family = hnld->family;
202*4882a593Smuzhiyun 
203*4882a593Smuzhiyun 	if (family >= AF_MAX)
204*4882a593Smuzhiyun 		return;
205*4882a593Smuzhiyun 
206*4882a593Smuzhiyun 	mutex_lock(&sock_diag_table_mutex);
207*4882a593Smuzhiyun 	BUG_ON(sock_diag_handlers[family] != hnld);
208*4882a593Smuzhiyun 	sock_diag_handlers[family] = NULL;
209*4882a593Smuzhiyun 	mutex_unlock(&sock_diag_table_mutex);
210*4882a593Smuzhiyun }
211*4882a593Smuzhiyun EXPORT_SYMBOL_GPL(sock_diag_unregister);
212*4882a593Smuzhiyun 
__sock_diag_cmd(struct sk_buff * skb,struct nlmsghdr * nlh)213*4882a593Smuzhiyun static int __sock_diag_cmd(struct sk_buff *skb, struct nlmsghdr *nlh)
214*4882a593Smuzhiyun {
215*4882a593Smuzhiyun 	int err;
216*4882a593Smuzhiyun 	struct sock_diag_req *req = nlmsg_data(nlh);
217*4882a593Smuzhiyun 	const struct sock_diag_handler *hndl;
218*4882a593Smuzhiyun 
219*4882a593Smuzhiyun 	if (nlmsg_len(nlh) < sizeof(*req))
220*4882a593Smuzhiyun 		return -EINVAL;
221*4882a593Smuzhiyun 
222*4882a593Smuzhiyun 	if (req->sdiag_family >= AF_MAX)
223*4882a593Smuzhiyun 		return -EINVAL;
224*4882a593Smuzhiyun 	req->sdiag_family = array_index_nospec(req->sdiag_family, AF_MAX);
225*4882a593Smuzhiyun 
226*4882a593Smuzhiyun 	if (sock_diag_handlers[req->sdiag_family] == NULL)
227*4882a593Smuzhiyun 		sock_load_diag_module(req->sdiag_family, 0);
228*4882a593Smuzhiyun 
229*4882a593Smuzhiyun 	mutex_lock(&sock_diag_table_mutex);
230*4882a593Smuzhiyun 	hndl = sock_diag_handlers[req->sdiag_family];
231*4882a593Smuzhiyun 	if (hndl == NULL)
232*4882a593Smuzhiyun 		err = -ENOENT;
233*4882a593Smuzhiyun 	else if (nlh->nlmsg_type == SOCK_DIAG_BY_FAMILY)
234*4882a593Smuzhiyun 		err = hndl->dump(skb, nlh);
235*4882a593Smuzhiyun 	else if (nlh->nlmsg_type == SOCK_DESTROY && hndl->destroy)
236*4882a593Smuzhiyun 		err = hndl->destroy(skb, nlh);
237*4882a593Smuzhiyun 	else
238*4882a593Smuzhiyun 		err = -EOPNOTSUPP;
239*4882a593Smuzhiyun 	mutex_unlock(&sock_diag_table_mutex);
240*4882a593Smuzhiyun 
241*4882a593Smuzhiyun 	return err;
242*4882a593Smuzhiyun }
243*4882a593Smuzhiyun 
sock_diag_rcv_msg(struct sk_buff * skb,struct nlmsghdr * nlh,struct netlink_ext_ack * extack)244*4882a593Smuzhiyun static int sock_diag_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh,
245*4882a593Smuzhiyun 			     struct netlink_ext_ack *extack)
246*4882a593Smuzhiyun {
247*4882a593Smuzhiyun 	int ret;
248*4882a593Smuzhiyun 
249*4882a593Smuzhiyun 	switch (nlh->nlmsg_type) {
250*4882a593Smuzhiyun 	case TCPDIAG_GETSOCK:
251*4882a593Smuzhiyun 	case DCCPDIAG_GETSOCK:
252*4882a593Smuzhiyun 		if (inet_rcv_compat == NULL)
253*4882a593Smuzhiyun 			sock_load_diag_module(AF_INET, 0);
254*4882a593Smuzhiyun 
255*4882a593Smuzhiyun 		mutex_lock(&sock_diag_table_mutex);
256*4882a593Smuzhiyun 		if (inet_rcv_compat != NULL)
257*4882a593Smuzhiyun 			ret = inet_rcv_compat(skb, nlh);
258*4882a593Smuzhiyun 		else
259*4882a593Smuzhiyun 			ret = -EOPNOTSUPP;
260*4882a593Smuzhiyun 		mutex_unlock(&sock_diag_table_mutex);
261*4882a593Smuzhiyun 
262*4882a593Smuzhiyun 		return ret;
263*4882a593Smuzhiyun 	case SOCK_DIAG_BY_FAMILY:
264*4882a593Smuzhiyun 	case SOCK_DESTROY:
265*4882a593Smuzhiyun 		return __sock_diag_cmd(skb, nlh);
266*4882a593Smuzhiyun 	default:
267*4882a593Smuzhiyun 		return -EINVAL;
268*4882a593Smuzhiyun 	}
269*4882a593Smuzhiyun }
270*4882a593Smuzhiyun 
271*4882a593Smuzhiyun static DEFINE_MUTEX(sock_diag_mutex);
272*4882a593Smuzhiyun 
sock_diag_rcv(struct sk_buff * skb)273*4882a593Smuzhiyun static void sock_diag_rcv(struct sk_buff *skb)
274*4882a593Smuzhiyun {
275*4882a593Smuzhiyun 	mutex_lock(&sock_diag_mutex);
276*4882a593Smuzhiyun 	netlink_rcv_skb(skb, &sock_diag_rcv_msg);
277*4882a593Smuzhiyun 	mutex_unlock(&sock_diag_mutex);
278*4882a593Smuzhiyun }
279*4882a593Smuzhiyun 
sock_diag_bind(struct net * net,int group)280*4882a593Smuzhiyun static int sock_diag_bind(struct net *net, int group)
281*4882a593Smuzhiyun {
282*4882a593Smuzhiyun 	switch (group) {
283*4882a593Smuzhiyun 	case SKNLGRP_INET_TCP_DESTROY:
284*4882a593Smuzhiyun 	case SKNLGRP_INET_UDP_DESTROY:
285*4882a593Smuzhiyun 		if (!sock_diag_handlers[AF_INET])
286*4882a593Smuzhiyun 			sock_load_diag_module(AF_INET, 0);
287*4882a593Smuzhiyun 		break;
288*4882a593Smuzhiyun 	case SKNLGRP_INET6_TCP_DESTROY:
289*4882a593Smuzhiyun 	case SKNLGRP_INET6_UDP_DESTROY:
290*4882a593Smuzhiyun 		if (!sock_diag_handlers[AF_INET6])
291*4882a593Smuzhiyun 			sock_load_diag_module(AF_INET6, 0);
292*4882a593Smuzhiyun 		break;
293*4882a593Smuzhiyun 	}
294*4882a593Smuzhiyun 	return 0;
295*4882a593Smuzhiyun }
296*4882a593Smuzhiyun 
sock_diag_destroy(struct sock * sk,int err)297*4882a593Smuzhiyun int sock_diag_destroy(struct sock *sk, int err)
298*4882a593Smuzhiyun {
299*4882a593Smuzhiyun 	if (!ns_capable(sock_net(sk)->user_ns, CAP_NET_ADMIN))
300*4882a593Smuzhiyun 		return -EPERM;
301*4882a593Smuzhiyun 
302*4882a593Smuzhiyun 	if (!sk->sk_prot->diag_destroy)
303*4882a593Smuzhiyun 		return -EOPNOTSUPP;
304*4882a593Smuzhiyun 
305*4882a593Smuzhiyun 	return sk->sk_prot->diag_destroy(sk, err);
306*4882a593Smuzhiyun }
307*4882a593Smuzhiyun EXPORT_SYMBOL_GPL(sock_diag_destroy);
308*4882a593Smuzhiyun 
diag_net_init(struct net * net)309*4882a593Smuzhiyun static int __net_init diag_net_init(struct net *net)
310*4882a593Smuzhiyun {
311*4882a593Smuzhiyun 	struct netlink_kernel_cfg cfg = {
312*4882a593Smuzhiyun 		.groups	= SKNLGRP_MAX,
313*4882a593Smuzhiyun 		.input	= sock_diag_rcv,
314*4882a593Smuzhiyun 		.bind	= sock_diag_bind,
315*4882a593Smuzhiyun 		.flags	= NL_CFG_F_NONROOT_RECV,
316*4882a593Smuzhiyun 	};
317*4882a593Smuzhiyun 
318*4882a593Smuzhiyun 	net->diag_nlsk = netlink_kernel_create(net, NETLINK_SOCK_DIAG, &cfg);
319*4882a593Smuzhiyun 	return net->diag_nlsk == NULL ? -ENOMEM : 0;
320*4882a593Smuzhiyun }
321*4882a593Smuzhiyun 
diag_net_exit(struct net * net)322*4882a593Smuzhiyun static void __net_exit diag_net_exit(struct net *net)
323*4882a593Smuzhiyun {
324*4882a593Smuzhiyun 	netlink_kernel_release(net->diag_nlsk);
325*4882a593Smuzhiyun 	net->diag_nlsk = NULL;
326*4882a593Smuzhiyun }
327*4882a593Smuzhiyun 
328*4882a593Smuzhiyun static struct pernet_operations diag_net_ops = {
329*4882a593Smuzhiyun 	.init = diag_net_init,
330*4882a593Smuzhiyun 	.exit = diag_net_exit,
331*4882a593Smuzhiyun };
332*4882a593Smuzhiyun 
sock_diag_init(void)333*4882a593Smuzhiyun static int __init sock_diag_init(void)
334*4882a593Smuzhiyun {
335*4882a593Smuzhiyun 	broadcast_wq = alloc_workqueue("sock_diag_events", 0, 0);
336*4882a593Smuzhiyun 	BUG_ON(!broadcast_wq);
337*4882a593Smuzhiyun 	return register_pernet_subsys(&diag_net_ops);
338*4882a593Smuzhiyun }
339*4882a593Smuzhiyun device_initcall(sock_diag_init);
340