mirror of
https://github.com/edk2-porting/linux-next.git
synced 2025-01-19 02:54:00 +08:00
VSOCK: add sock_diag interface
This patch adds the sock_diag interface for querying sockets from userspace. Tools like ss(8) and netstat(8) can use this interface to list open sockets. The userspace ABI is defined in <linux/vm_sockets_diag.h> and includes netlink request and response structs. The request can query sockets based on their sk_state (e.g. listening sockets only) and the response contains socket information fields including the local/remote addresses, inode number, etc. This patch does not dump VMCI pending sockets because I have only tested the virtio transport, which does not use pending sockets. Support can be added later by extending vsock_diag_dump() if needed by VMCI users. Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com> Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
parent
3b4477d2dc
commit
413a4317ac
@ -14286,6 +14286,8 @@ S: Maintained
|
|||||||
F: include/linux/virtio_vsock.h
|
F: include/linux/virtio_vsock.h
|
||||||
F: include/uapi/linux/virtio_vsock.h
|
F: include/uapi/linux/virtio_vsock.h
|
||||||
F: include/uapi/linux/vsockmon.h
|
F: include/uapi/linux/vsockmon.h
|
||||||
|
F: include/uapi/linux/vm_sockets_diag.h
|
||||||
|
F: net/vmw_vsock/diag.c
|
||||||
F: net/vmw_vsock/af_vsock_tap.c
|
F: net/vmw_vsock/af_vsock_tap.c
|
||||||
F: net/vmw_vsock/virtio_transport_common.c
|
F: net/vmw_vsock/virtio_transport_common.c
|
||||||
F: net/vmw_vsock/virtio_transport.c
|
F: net/vmw_vsock/virtio_transport.c
|
||||||
|
33
include/uapi/linux/vm_sockets_diag.h
Normal file
33
include/uapi/linux/vm_sockets_diag.h
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
/* AF_VSOCK sock_diag(7) interface for querying open sockets */
|
||||||
|
|
||||||
|
#ifndef _UAPI__VM_SOCKETS_DIAG_H__
|
||||||
|
#define _UAPI__VM_SOCKETS_DIAG_H__
|
||||||
|
|
||||||
|
#include <linux/types.h>
|
||||||
|
|
||||||
|
/* Request */
|
||||||
|
struct vsock_diag_req {
|
||||||
|
__u8 sdiag_family; /* must be AF_VSOCK */
|
||||||
|
__u8 sdiag_protocol; /* must be 0 */
|
||||||
|
__u16 pad; /* must be 0 */
|
||||||
|
__u32 vdiag_states; /* query bitmap (e.g. 1 << TCP_LISTEN) */
|
||||||
|
__u32 vdiag_ino; /* must be 0 (reserved) */
|
||||||
|
__u32 vdiag_show; /* must be 0 (reserved) */
|
||||||
|
__u32 vdiag_cookie[2];
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Response */
|
||||||
|
struct vsock_diag_msg {
|
||||||
|
__u8 vdiag_family; /* AF_VSOCK */
|
||||||
|
__u8 vdiag_type; /* SOCK_STREAM or SOCK_DGRAM */
|
||||||
|
__u8 vdiag_state; /* sk_state (e.g. TCP_LISTEN) */
|
||||||
|
__u8 vdiag_shutdown; /* local RCV_SHUTDOWN | SEND_SHUTDOWN */
|
||||||
|
__u32 vdiag_src_cid;
|
||||||
|
__u32 vdiag_src_port;
|
||||||
|
__u32 vdiag_dst_cid;
|
||||||
|
__u32 vdiag_dst_port;
|
||||||
|
__u32 vdiag_ino;
|
||||||
|
__u32 vdiag_cookie[2];
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif /* _UAPI__VM_SOCKETS_DIAG_H__ */
|
@ -15,6 +15,16 @@ config VSOCKETS
|
|||||||
To compile this driver as a module, choose M here: the module
|
To compile this driver as a module, choose M here: the module
|
||||||
will be called vsock. If unsure, say N.
|
will be called vsock. If unsure, say N.
|
||||||
|
|
||||||
|
config VSOCKETS_DIAG
|
||||||
|
tristate "Virtual Sockets monitoring interface"
|
||||||
|
depends on VSOCKETS
|
||||||
|
default y
|
||||||
|
help
|
||||||
|
Support for PF_VSOCK sockets monitoring interface used by the ss tool.
|
||||||
|
If unsure, say Y.
|
||||||
|
|
||||||
|
Enable this module so userspace applications can query open sockets.
|
||||||
|
|
||||||
config VMWARE_VMCI_VSOCKETS
|
config VMWARE_VMCI_VSOCKETS
|
||||||
tristate "VMware VMCI transport for Virtual Sockets"
|
tristate "VMware VMCI transport for Virtual Sockets"
|
||||||
depends on VSOCKETS && VMWARE_VMCI
|
depends on VSOCKETS && VMWARE_VMCI
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
obj-$(CONFIG_VSOCKETS) += vsock.o
|
obj-$(CONFIG_VSOCKETS) += vsock.o
|
||||||
|
obj-$(CONFIG_VSOCKETS_DIAG) += vsock_diag.o
|
||||||
obj-$(CONFIG_VMWARE_VMCI_VSOCKETS) += vmw_vsock_vmci_transport.o
|
obj-$(CONFIG_VMWARE_VMCI_VSOCKETS) += vmw_vsock_vmci_transport.o
|
||||||
obj-$(CONFIG_VIRTIO_VSOCKETS) += vmw_vsock_virtio_transport.o
|
obj-$(CONFIG_VIRTIO_VSOCKETS) += vmw_vsock_virtio_transport.o
|
||||||
obj-$(CONFIG_VIRTIO_VSOCKETS_COMMON) += vmw_vsock_virtio_transport_common.o
|
obj-$(CONFIG_VIRTIO_VSOCKETS_COMMON) += vmw_vsock_virtio_transport_common.o
|
||||||
@ -6,6 +7,8 @@ obj-$(CONFIG_HYPERV_VSOCKETS) += hv_sock.o
|
|||||||
|
|
||||||
vsock-y += af_vsock.o af_vsock_tap.o vsock_addr.o
|
vsock-y += af_vsock.o af_vsock_tap.o vsock_addr.o
|
||||||
|
|
||||||
|
vsock_diag-y += diag.o
|
||||||
|
|
||||||
vmw_vsock_vmci_transport-y += vmci_transport.o vmci_transport_notify.o \
|
vmw_vsock_vmci_transport-y += vmci_transport.o vmci_transport_notify.o \
|
||||||
vmci_transport_notify_qstate.o
|
vmci_transport_notify_qstate.o
|
||||||
|
|
||||||
|
186
net/vmw_vsock/diag.c
Normal file
186
net/vmw_vsock/diag.c
Normal file
@ -0,0 +1,186 @@
|
|||||||
|
/*
|
||||||
|
* vsock sock_diag(7) module
|
||||||
|
*
|
||||||
|
* Copyright (C) 2017 Red Hat, Inc.
|
||||||
|
* Author: Stefan Hajnoczi <stefanha@redhat.com>
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms of the GNU General Public License as published by the Free
|
||||||
|
* Software Foundation version 2 and no later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||||
|
* more details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <linux/module.h>
|
||||||
|
#include <linux/sock_diag.h>
|
||||||
|
#include <linux/vm_sockets_diag.h>
|
||||||
|
#include <net/af_vsock.h>
|
||||||
|
|
||||||
|
static int sk_diag_fill(struct sock *sk, struct sk_buff *skb,
|
||||||
|
u32 portid, u32 seq, u32 flags)
|
||||||
|
{
|
||||||
|
struct vsock_sock *vsk = vsock_sk(sk);
|
||||||
|
struct vsock_diag_msg *rep;
|
||||||
|
struct nlmsghdr *nlh;
|
||||||
|
|
||||||
|
nlh = nlmsg_put(skb, portid, seq, SOCK_DIAG_BY_FAMILY, sizeof(*rep),
|
||||||
|
flags);
|
||||||
|
if (!nlh)
|
||||||
|
return -EMSGSIZE;
|
||||||
|
|
||||||
|
rep = nlmsg_data(nlh);
|
||||||
|
rep->vdiag_family = AF_VSOCK;
|
||||||
|
|
||||||
|
/* Lock order dictates that sk_lock is acquired before
|
||||||
|
* vsock_table_lock, so we cannot lock here. Simply don't take
|
||||||
|
* sk_lock; sk is guaranteed to stay alive since vsock_table_lock is
|
||||||
|
* held.
|
||||||
|
*/
|
||||||
|
rep->vdiag_type = sk->sk_type;
|
||||||
|
rep->vdiag_state = sk->sk_state;
|
||||||
|
rep->vdiag_shutdown = sk->sk_shutdown;
|
||||||
|
rep->vdiag_src_cid = vsk->local_addr.svm_cid;
|
||||||
|
rep->vdiag_src_port = vsk->local_addr.svm_port;
|
||||||
|
rep->vdiag_dst_cid = vsk->remote_addr.svm_cid;
|
||||||
|
rep->vdiag_dst_port = vsk->remote_addr.svm_port;
|
||||||
|
rep->vdiag_ino = sock_i_ino(sk);
|
||||||
|
|
||||||
|
sock_diag_save_cookie(sk, rep->vdiag_cookie);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int vsock_diag_dump(struct sk_buff *skb, struct netlink_callback *cb)
|
||||||
|
{
|
||||||
|
struct vsock_diag_req *req;
|
||||||
|
struct vsock_sock *vsk;
|
||||||
|
unsigned int bucket;
|
||||||
|
unsigned int last_i;
|
||||||
|
unsigned int table;
|
||||||
|
struct net *net;
|
||||||
|
unsigned int i;
|
||||||
|
|
||||||
|
req = nlmsg_data(cb->nlh);
|
||||||
|
net = sock_net(skb->sk);
|
||||||
|
|
||||||
|
/* State saved between calls: */
|
||||||
|
table = cb->args[0];
|
||||||
|
bucket = cb->args[1];
|
||||||
|
i = last_i = cb->args[2];
|
||||||
|
|
||||||
|
/* TODO VMCI pending sockets? */
|
||||||
|
|
||||||
|
spin_lock_bh(&vsock_table_lock);
|
||||||
|
|
||||||
|
/* Bind table (locally created sockets) */
|
||||||
|
if (table == 0) {
|
||||||
|
while (bucket < ARRAY_SIZE(vsock_bind_table)) {
|
||||||
|
struct list_head *head = &vsock_bind_table[bucket];
|
||||||
|
|
||||||
|
i = 0;
|
||||||
|
list_for_each_entry(vsk, head, bound_table) {
|
||||||
|
struct sock *sk = sk_vsock(vsk);
|
||||||
|
|
||||||
|
if (!net_eq(sock_net(sk), net))
|
||||||
|
continue;
|
||||||
|
if (i < last_i)
|
||||||
|
goto next_bind;
|
||||||
|
if (!(req->vdiag_states & (1 << sk->sk_state)))
|
||||||
|
goto next_bind;
|
||||||
|
if (sk_diag_fill(sk, skb,
|
||||||
|
NETLINK_CB(cb->skb).portid,
|
||||||
|
cb->nlh->nlmsg_seq,
|
||||||
|
NLM_F_MULTI) < 0)
|
||||||
|
goto done;
|
||||||
|
next_bind:
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
last_i = 0;
|
||||||
|
bucket++;
|
||||||
|
}
|
||||||
|
|
||||||
|
table++;
|
||||||
|
bucket = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Connected table (accepted connections) */
|
||||||
|
while (bucket < ARRAY_SIZE(vsock_connected_table)) {
|
||||||
|
struct list_head *head = &vsock_connected_table[bucket];
|
||||||
|
|
||||||
|
i = 0;
|
||||||
|
list_for_each_entry(vsk, head, connected_table) {
|
||||||
|
struct sock *sk = sk_vsock(vsk);
|
||||||
|
|
||||||
|
/* Skip sockets we've already seen above */
|
||||||
|
if (__vsock_in_bound_table(vsk))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (!net_eq(sock_net(sk), net))
|
||||||
|
continue;
|
||||||
|
if (i < last_i)
|
||||||
|
goto next_connected;
|
||||||
|
if (!(req->vdiag_states & (1 << sk->sk_state)))
|
||||||
|
goto next_connected;
|
||||||
|
if (sk_diag_fill(sk, skb,
|
||||||
|
NETLINK_CB(cb->skb).portid,
|
||||||
|
cb->nlh->nlmsg_seq,
|
||||||
|
NLM_F_MULTI) < 0)
|
||||||
|
goto done;
|
||||||
|
next_connected:
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
last_i = 0;
|
||||||
|
bucket++;
|
||||||
|
}
|
||||||
|
|
||||||
|
done:
|
||||||
|
spin_unlock_bh(&vsock_table_lock);
|
||||||
|
|
||||||
|
cb->args[0] = table;
|
||||||
|
cb->args[1] = bucket;
|
||||||
|
cb->args[2] = i;
|
||||||
|
|
||||||
|
return skb->len;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int vsock_diag_handler_dump(struct sk_buff *skb, struct nlmsghdr *h)
|
||||||
|
{
|
||||||
|
int hdrlen = sizeof(struct vsock_diag_req);
|
||||||
|
struct net *net = sock_net(skb->sk);
|
||||||
|
|
||||||
|
if (nlmsg_len(h) < hdrlen)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
if (h->nlmsg_flags & NLM_F_DUMP) {
|
||||||
|
struct netlink_dump_control c = {
|
||||||
|
.dump = vsock_diag_dump,
|
||||||
|
};
|
||||||
|
return netlink_dump_start(net->diag_nlsk, skb, h, &c);
|
||||||
|
}
|
||||||
|
|
||||||
|
return -EOPNOTSUPP;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct sock_diag_handler vsock_diag_handler = {
|
||||||
|
.family = AF_VSOCK,
|
||||||
|
.dump = vsock_diag_handler_dump,
|
||||||
|
};
|
||||||
|
|
||||||
|
static int __init vsock_diag_init(void)
|
||||||
|
{
|
||||||
|
return sock_diag_register(&vsock_diag_handler);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void __exit vsock_diag_exit(void)
|
||||||
|
{
|
||||||
|
sock_diag_unregister(&vsock_diag_handler);
|
||||||
|
}
|
||||||
|
|
||||||
|
module_init(vsock_diag_init);
|
||||||
|
module_exit(vsock_diag_exit);
|
||||||
|
MODULE_LICENSE("GPL");
|
||||||
|
MODULE_ALIAS_NET_PF_PROTO_TYPE(PF_NETLINK, NETLINK_SOCK_DIAG,
|
||||||
|
40 /* AF_VSOCK */);
|
Loading…
Reference in New Issue
Block a user