xref: /openbmc/linux/net/vmw_vsock/diag.c (revision 685a6bf8)
1685a6bf8SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
2413a4317SStefan Hajnoczi /*
3413a4317SStefan Hajnoczi  * vsock sock_diag(7) module
4413a4317SStefan Hajnoczi  *
5413a4317SStefan Hajnoczi  * Copyright (C) 2017 Red Hat, Inc.
6413a4317SStefan Hajnoczi  * Author: Stefan Hajnoczi <stefanha@redhat.com>
7413a4317SStefan Hajnoczi  */
8413a4317SStefan Hajnoczi 
9413a4317SStefan Hajnoczi #include <linux/module.h>
10413a4317SStefan Hajnoczi #include <linux/sock_diag.h>
11413a4317SStefan Hajnoczi #include <linux/vm_sockets_diag.h>
12413a4317SStefan Hajnoczi #include <net/af_vsock.h>
13413a4317SStefan Hajnoczi 
sk_diag_fill(struct sock * sk,struct sk_buff * skb,u32 portid,u32 seq,u32 flags)14413a4317SStefan Hajnoczi static int sk_diag_fill(struct sock *sk, struct sk_buff *skb,
15413a4317SStefan Hajnoczi 			u32 portid, u32 seq, u32 flags)
16413a4317SStefan Hajnoczi {
17413a4317SStefan Hajnoczi 	struct vsock_sock *vsk = vsock_sk(sk);
18413a4317SStefan Hajnoczi 	struct vsock_diag_msg *rep;
19413a4317SStefan Hajnoczi 	struct nlmsghdr *nlh;
20413a4317SStefan Hajnoczi 
21413a4317SStefan Hajnoczi 	nlh = nlmsg_put(skb, portid, seq, SOCK_DIAG_BY_FAMILY, sizeof(*rep),
22413a4317SStefan Hajnoczi 			flags);
23413a4317SStefan Hajnoczi 	if (!nlh)
24413a4317SStefan Hajnoczi 		return -EMSGSIZE;
25413a4317SStefan Hajnoczi 
26413a4317SStefan Hajnoczi 	rep = nlmsg_data(nlh);
27413a4317SStefan Hajnoczi 	rep->vdiag_family = AF_VSOCK;
28413a4317SStefan Hajnoczi 
29413a4317SStefan Hajnoczi 	/* Lock order dictates that sk_lock is acquired before
30413a4317SStefan Hajnoczi 	 * vsock_table_lock, so we cannot lock here.  Simply don't take
31413a4317SStefan Hajnoczi 	 * sk_lock; sk is guaranteed to stay alive since vsock_table_lock is
32413a4317SStefan Hajnoczi 	 * held.
33413a4317SStefan Hajnoczi 	 */
34413a4317SStefan Hajnoczi 	rep->vdiag_type = sk->sk_type;
35413a4317SStefan Hajnoczi 	rep->vdiag_state = sk->sk_state;
36413a4317SStefan Hajnoczi 	rep->vdiag_shutdown = sk->sk_shutdown;
37413a4317SStefan Hajnoczi 	rep->vdiag_src_cid = vsk->local_addr.svm_cid;
38413a4317SStefan Hajnoczi 	rep->vdiag_src_port = vsk->local_addr.svm_port;
39413a4317SStefan Hajnoczi 	rep->vdiag_dst_cid = vsk->remote_addr.svm_cid;
40413a4317SStefan Hajnoczi 	rep->vdiag_dst_port = vsk->remote_addr.svm_port;
41413a4317SStefan Hajnoczi 	rep->vdiag_ino = sock_i_ino(sk);
42413a4317SStefan Hajnoczi 
43413a4317SStefan Hajnoczi 	sock_diag_save_cookie(sk, rep->vdiag_cookie);
44413a4317SStefan Hajnoczi 
45413a4317SStefan Hajnoczi 	return 0;
46413a4317SStefan Hajnoczi }
47413a4317SStefan Hajnoczi 
vsock_diag_dump(struct sk_buff * skb,struct netlink_callback * cb)48413a4317SStefan Hajnoczi static int vsock_diag_dump(struct sk_buff *skb, struct netlink_callback *cb)
49413a4317SStefan Hajnoczi {
50413a4317SStefan Hajnoczi 	struct vsock_diag_req *req;
51413a4317SStefan Hajnoczi 	struct vsock_sock *vsk;
52413a4317SStefan Hajnoczi 	unsigned int bucket;
53413a4317SStefan Hajnoczi 	unsigned int last_i;
54413a4317SStefan Hajnoczi 	unsigned int table;
55413a4317SStefan Hajnoczi 	struct net *net;
56413a4317SStefan Hajnoczi 	unsigned int i;
57413a4317SStefan Hajnoczi 
58413a4317SStefan Hajnoczi 	req = nlmsg_data(cb->nlh);
59413a4317SStefan Hajnoczi 	net = sock_net(skb->sk);
60413a4317SStefan Hajnoczi 
61413a4317SStefan Hajnoczi 	/* State saved between calls: */
62413a4317SStefan Hajnoczi 	table = cb->args[0];
63413a4317SStefan Hajnoczi 	bucket = cb->args[1];
64413a4317SStefan Hajnoczi 	i = last_i = cb->args[2];
65413a4317SStefan Hajnoczi 
66413a4317SStefan Hajnoczi 	/* TODO VMCI pending sockets? */
67413a4317SStefan Hajnoczi 
68413a4317SStefan Hajnoczi 	spin_lock_bh(&vsock_table_lock);
69413a4317SStefan Hajnoczi 
70413a4317SStefan Hajnoczi 	/* Bind table (locally created sockets) */
71413a4317SStefan Hajnoczi 	if (table == 0) {
72413a4317SStefan Hajnoczi 		while (bucket < ARRAY_SIZE(vsock_bind_table)) {
73413a4317SStefan Hajnoczi 			struct list_head *head = &vsock_bind_table[bucket];
74413a4317SStefan Hajnoczi 
75413a4317SStefan Hajnoczi 			i = 0;
76413a4317SStefan Hajnoczi 			list_for_each_entry(vsk, head, bound_table) {
77413a4317SStefan Hajnoczi 				struct sock *sk = sk_vsock(vsk);
78413a4317SStefan Hajnoczi 
79413a4317SStefan Hajnoczi 				if (!net_eq(sock_net(sk), net))
80413a4317SStefan Hajnoczi 					continue;
81413a4317SStefan Hajnoczi 				if (i < last_i)
82413a4317SStefan Hajnoczi 					goto next_bind;
83413a4317SStefan Hajnoczi 				if (!(req->vdiag_states & (1 << sk->sk_state)))
84413a4317SStefan Hajnoczi 					goto next_bind;
85413a4317SStefan Hajnoczi 				if (sk_diag_fill(sk, skb,
86413a4317SStefan Hajnoczi 						 NETLINK_CB(cb->skb).portid,
87413a4317SStefan Hajnoczi 						 cb->nlh->nlmsg_seq,
88413a4317SStefan Hajnoczi 						 NLM_F_MULTI) < 0)
89413a4317SStefan Hajnoczi 					goto done;
90413a4317SStefan Hajnoczi next_bind:
91413a4317SStefan Hajnoczi 				i++;
92413a4317SStefan Hajnoczi 			}
93413a4317SStefan Hajnoczi 			last_i = 0;
94413a4317SStefan Hajnoczi 			bucket++;
95413a4317SStefan Hajnoczi 		}
96413a4317SStefan Hajnoczi 
97413a4317SStefan Hajnoczi 		table++;
98413a4317SStefan Hajnoczi 		bucket = 0;
99413a4317SStefan Hajnoczi 	}
100413a4317SStefan Hajnoczi 
101413a4317SStefan Hajnoczi 	/* Connected table (accepted connections) */
102413a4317SStefan Hajnoczi 	while (bucket < ARRAY_SIZE(vsock_connected_table)) {
103413a4317SStefan Hajnoczi 		struct list_head *head = &vsock_connected_table[bucket];
104413a4317SStefan Hajnoczi 
105413a4317SStefan Hajnoczi 		i = 0;
106413a4317SStefan Hajnoczi 		list_for_each_entry(vsk, head, connected_table) {
107413a4317SStefan Hajnoczi 			struct sock *sk = sk_vsock(vsk);
108413a4317SStefan Hajnoczi 
109413a4317SStefan Hajnoczi 			/* Skip sockets we've already seen above */
110413a4317SStefan Hajnoczi 			if (__vsock_in_bound_table(vsk))
111413a4317SStefan Hajnoczi 				continue;
112413a4317SStefan Hajnoczi 
113413a4317SStefan Hajnoczi 			if (!net_eq(sock_net(sk), net))
114413a4317SStefan Hajnoczi 				continue;
115413a4317SStefan Hajnoczi 			if (i < last_i)
116413a4317SStefan Hajnoczi 				goto next_connected;
117413a4317SStefan Hajnoczi 			if (!(req->vdiag_states & (1 << sk->sk_state)))
118413a4317SStefan Hajnoczi 				goto next_connected;
119413a4317SStefan Hajnoczi 			if (sk_diag_fill(sk, skb,
120413a4317SStefan Hajnoczi 					 NETLINK_CB(cb->skb).portid,
121413a4317SStefan Hajnoczi 					 cb->nlh->nlmsg_seq,
122413a4317SStefan Hajnoczi 					 NLM_F_MULTI) < 0)
123413a4317SStefan Hajnoczi 				goto done;
124413a4317SStefan Hajnoczi next_connected:
125413a4317SStefan Hajnoczi 			i++;
126413a4317SStefan Hajnoczi 		}
127413a4317SStefan Hajnoczi 		last_i = 0;
128413a4317SStefan Hajnoczi 		bucket++;
129413a4317SStefan Hajnoczi 	}
130413a4317SStefan Hajnoczi 
131413a4317SStefan Hajnoczi done:
132413a4317SStefan Hajnoczi 	spin_unlock_bh(&vsock_table_lock);
133413a4317SStefan Hajnoczi 
134413a4317SStefan Hajnoczi 	cb->args[0] = table;
135413a4317SStefan Hajnoczi 	cb->args[1] = bucket;
136413a4317SStefan Hajnoczi 	cb->args[2] = i;
137413a4317SStefan Hajnoczi 
138413a4317SStefan Hajnoczi 	return skb->len;
139413a4317SStefan Hajnoczi }
140413a4317SStefan Hajnoczi 
vsock_diag_handler_dump(struct sk_buff * skb,struct nlmsghdr * h)141413a4317SStefan Hajnoczi static int vsock_diag_handler_dump(struct sk_buff *skb, struct nlmsghdr *h)
142413a4317SStefan Hajnoczi {
143413a4317SStefan Hajnoczi 	int hdrlen = sizeof(struct vsock_diag_req);
144413a4317SStefan Hajnoczi 	struct net *net = sock_net(skb->sk);
145413a4317SStefan Hajnoczi 
146413a4317SStefan Hajnoczi 	if (nlmsg_len(h) < hdrlen)
147413a4317SStefan Hajnoczi 		return -EINVAL;
148413a4317SStefan Hajnoczi 
149413a4317SStefan Hajnoczi 	if (h->nlmsg_flags & NLM_F_DUMP) {
150413a4317SStefan Hajnoczi 		struct netlink_dump_control c = {
151413a4317SStefan Hajnoczi 			.dump = vsock_diag_dump,
152413a4317SStefan Hajnoczi 		};
153413a4317SStefan Hajnoczi 		return netlink_dump_start(net->diag_nlsk, skb, h, &c);
154413a4317SStefan Hajnoczi 	}
155413a4317SStefan Hajnoczi 
156413a4317SStefan Hajnoczi 	return -EOPNOTSUPP;
157413a4317SStefan Hajnoczi }
158413a4317SStefan Hajnoczi 
159413a4317SStefan Hajnoczi static const struct sock_diag_handler vsock_diag_handler = {
160413a4317SStefan Hajnoczi 	.family = AF_VSOCK,
161413a4317SStefan Hajnoczi 	.dump = vsock_diag_handler_dump,
162413a4317SStefan Hajnoczi };
163413a4317SStefan Hajnoczi 
vsock_diag_init(void)164413a4317SStefan Hajnoczi static int __init vsock_diag_init(void)
165413a4317SStefan Hajnoczi {
166413a4317SStefan Hajnoczi 	return sock_diag_register(&vsock_diag_handler);
167413a4317SStefan Hajnoczi }
168413a4317SStefan Hajnoczi 
vsock_diag_exit(void)169413a4317SStefan Hajnoczi static void __exit vsock_diag_exit(void)
170413a4317SStefan Hajnoczi {
171413a4317SStefan Hajnoczi 	sock_diag_unregister(&vsock_diag_handler);
172413a4317SStefan Hajnoczi }
173413a4317SStefan Hajnoczi 
174413a4317SStefan Hajnoczi module_init(vsock_diag_init);
175413a4317SStefan Hajnoczi module_exit(vsock_diag_exit);
176413a4317SStefan Hajnoczi MODULE_LICENSE("GPL");
177413a4317SStefan Hajnoczi MODULE_ALIAS_NET_PF_PROTO_TYPE(PF_NETLINK, NETLINK_SOCK_DIAG,
178413a4317SStefan Hajnoczi 			       40 /* AF_VSOCK */);
179