Skip to content

Commit

Permalink
lsfd: support AF_VSOCK sockets
Browse files Browse the repository at this point in the history
Example output:

    # ./lsfd -p 121067 -Q "TYPE == 'AF_VSOCK'"
    COMMAND       PID USER ASSOC  XMODE     TYPE SOURCE MNTID    INODE NAME
    test_mkfds 121067 root     4 rw---- AF_VSOCK sockfs    10 39400798 state=listen type=stream laddr=local:12345
    test_mkfds 121067 root     5 rw---- AF_VSOCK sockfs    10 39400799 state=established type=stream laddr=local:23456 raddr=local:12345
    test_mkfds 121067 root     6 rw---- AF_VSOCK sockfs    10 39400800 state=established type=stream laddr=local:12345 raddr=local:23456

Signed-off-by: Masatake YAMATO <yamato@redhat.com>
  • Loading branch information
masatake committed Dec 29, 2024
1 parent ec55fe9 commit 4153f6b
Show file tree
Hide file tree
Showing 10 changed files with 568 additions and 2 deletions.
4 changes: 4 additions & 0 deletions configure.ac
Original file line number Diff line number Diff line change
Expand Up @@ -592,6 +592,10 @@ AC_CHECK_DECL([SO_PASSCRED],
[#include <sys/types.h>
#include <sys/socket.h>])

AC_CHECK_DECLS([VMADDR_CID_LOCAL], [], [], [
#include <sys/socket.h>
#include <linux/vm_sockets.h>])

AC_CHECK_FUNCS([ \
cachestat \
clearenv \
Expand Down
25 changes: 23 additions & 2 deletions lsfd-cmd/lsfd.1.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -275,6 +275,11 @@ Cooked version of KNAME. It is mostly same as KNAME.
+
Some files have special formats and information sources:
+
AF_VSOCK:::
state=_SOCK.STATE_ type=_SOCK.TYPE_ laddr=_VSOCK.LADDR_[ raddr=_VSOCK.RADDR_]
+
`raddr` is not shown for listening sockets.
+
bpf-map:::
id=_BPF-MAP.ID_ type=_BPF-MAP.TYPE_[ name=_BPF.NAME_]
+
Expand Down Expand Up @@ -348,7 +353,6 @@ state=_SOCK.STATE_[ path=_UNIX.PATH_]
+
UNIX:::
state=_SOCK.STATE_[ path=_UNIX.PATH_] type=_SOCK.TYPE_

____
Note that `(deleted)` markers are removed from this column.
Refer to _KNAME_, _DELETED_, or _XMODE_ to know the
Expand Down Expand Up @@ -546,6 +550,22 @@ Filesystem pathname for UNIX domain socket.
USER <``string``>::
User of the process.

VSOCK.LADDR <``string``>::
VSOCK.RADDR <``string``>::
Local VSOCK address. The format of the element
is _VSOCK.LCID_``:``_VSOCK.LPORT_.
+
Well-known CIDs will be decoded: "`{asterisk}`", "`hypervisor`", "`local`", or "`host`".
Well-known ports will be decoded: "`{asterisk}`".

VSOCK.LCID <``number``>::
VSOCK.RCID <``number``>::
Local and remote VSOCK context identifiers.

VSOCK.LPORT <``number``>::
VSOCK.RPORT <``number``>::
Local and remote VSOCK ports.

XMODE <``string``>::
Extended version of _MODE_. This column may grow; new letters may be
appended to _XMODE_ when *lsfd* supports a new state of file descriptors
Expand Down Expand Up @@ -729,7 +749,8 @@ mailto:kzak@redhat.com[Karel Zak]
*scols-filter*(5),
*socket*(2),
*ss*(8),
*stat*(2)
*stat*(2),
*vsock*(7)

include::man-common/bugreports.adoc[]

Expand Down
18 changes: 18 additions & 0 deletions lsfd-cmd/lsfd.c
Original file line number Diff line number Diff line change
Expand Up @@ -431,6 +431,24 @@ static const struct colinfo infos[] = {
[COL_USER] = { "USER",
0, SCOLS_FL_RIGHT, SCOLS_JSON_STRING,
N_("user of the process") },
[COL_VSOCK_LCID] = { "VSOCK.LCID",
0, SCOLS_FL_RIGHT, SCOLS_JSON_NUMBER,
N_("local VSOCK context identifier") },
[COL_VSOCK_RCID] = { "VSOCK.RCID",
0, SCOLS_FL_RIGHT, SCOLS_JSON_NUMBER,
N_("remote VSOCK context identifier") },
[COL_VSOCK_LPORT] = { "VSOCK.LPORT",
0, SCOLS_FL_RIGHT, SCOLS_JSON_NUMBER,
N_("local VSOCK port") },
[COL_VSOCK_RPORT] = { "VSOCK.RPORT",
0, SCOLS_FL_RIGHT, SCOLS_JSON_NUMBER,
N_("remote VSOCK port") },
[COL_VSOCK_LADDR] = { "VSOCK.LADDR",
0, SCOLS_FL_RIGHT, SCOLS_JSON_STRING,
N_("local VSOCK address (CID:PORT)") },
[COL_VSOCK_RADDR] = { "VSOCK.RADDR",
0, SCOLS_FL_RIGHT, SCOLS_JSON_STRING,
N_("remote VSOCK address (CID:PORT)") },
[COL_XMODE] = { "XMODE",
0, SCOLS_FL_RIGHT, SCOLS_JSON_STRING,
N_("extended version of MODE (rwxD[Ll]m)") },
Expand Down
6 changes: 6 additions & 0 deletions lsfd-cmd/lsfd.h
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,12 @@ enum {
COL_UID, /* process */
COL_UNIX_PATH,
COL_USER, /* process */
COL_VSOCK_LADDR,
COL_VSOCK_RADDR,
COL_VSOCK_LCID,
COL_VSOCK_RCID,
COL_VSOCK_LPORT,
COL_VSOCK_RPORT,
COL_XMODE,
LSFD_N_COLS /* This must be at last. */
};
Expand Down
216 changes: 216 additions & 0 deletions lsfd-cmd/sock-xinfo.c
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@
#include <linux/un.h> /* UNIX_PATH_MAX */
#include <linux/unix_diag.h> /* UNIX_DIAG_*, UDIAG_SHOW_*,
struct unix_diag_req */
#include <linux/vm_sockets.h> /* VMADDR_CID* */
#include <linux/vm_sockets_diag.h> /* vsock_diag_req/vsock_diag_msg */
#include <sched.h> /* for setns(2) */
#include <search.h> /* tfind, tsearch */
#include <stdint.h>
Expand Down Expand Up @@ -62,6 +64,7 @@ static void load_xinfo_from_proc_netlink(ino_t netns_inode);
static void load_xinfo_from_proc_packet(ino_t netns_inode);

static void load_xinfo_from_diag_unix(int diag, ino_t netns_inode);
static void load_xinfo_from_diag_vsock(int diag, ino_t netns_inode);

static int self_netns_fd = -1;
static struct stat self_netns_sb;
Expand Down Expand Up @@ -186,6 +189,7 @@ static void load_sock_xinfo_no_nsswitch(struct netns *nsobj)
(diagsd >= 0)? "successful": strerror(errno)));
if (diagsd >= 0) {
load_xinfo_from_diag_unix(diagsd, netns);
load_xinfo_from_diag_vsock(diagsd, netns);
close(diagsd);
DBG(ENDPOINTS, ul_debug("close the diagnose socket"));
}
Expand Down Expand Up @@ -2388,3 +2392,215 @@ static void load_xinfo_from_proc_packet(ino_t netns_inode)
out:
fclose(packet_fp);
}

/*
* VSOCK
*/
struct vsock_addr {
uint32_t cid;
uint32_t port;
};

struct vsock_xinfo {
struct sock_xinfo sock;
uint8_t type;
uint8_t st;
uint8_t shutdown_mask:3;
struct vsock_addr local;
struct vsock_addr remote;
};

static const char *vsock_decode_cid(uint32_t cid)
{
switch (cid) {
case VMADDR_CID_ANY:
return "*";
case VMADDR_CID_HYPERVISOR:
return "hypervisor";
#if HAVE_DECL_VMADDR_CID_LOCAL
case VMADDR_CID_LOCAL:
return "local";
#endif /* HAVE_DECL_VMADDR_CID_LOCAL */
case VMADDR_CID_HOST:
return "host";
default:
return NULL;
}
}

static const char *vsock_decode_port(uint32_t port)
{
if (port == VMADDR_PORT_ANY)
return "*";
return NULL;
}

static char* vsock_get_addr(struct vsock_addr *addr)
{
const char *tmp_cid = vsock_decode_cid(addr->cid);
const char *tmp_port = vsock_decode_port(addr->port);
char cidstr[BUFSIZ];
char portstr[BUFSIZ];
char *str = NULL;

if (tmp_cid)
snprintf(cidstr, sizeof(cidstr), "%s", tmp_cid);
else
snprintf(cidstr, sizeof(cidstr), "%"PRIu32, addr->cid);

if (tmp_port)
snprintf(portstr, sizeof(portstr), "%s", tmp_port);
else
snprintf(portstr, sizeof(portstr), "%"PRIu32, addr->port);

xasprintf(&str, "%s:%s", cidstr, portstr);
return str;
}

static char *vsock_get_name(struct sock_xinfo *sock_xinfo,
struct sock *sock __attribute__((__unused__)))
{
struct vsock_xinfo *vs = (struct vsock_xinfo *)sock_xinfo;
char *str = NULL;
const char *st_str = l4_decode_state(vs->st);
const char *type_str = sock_decode_type(vs->type);
char *laddr = vsock_get_addr(&vs->local);

if (vs->st == TCP_LISTEN)
xasprintf(&str, "state=%s type=%s laddr=%s",
st_str, type_str, laddr);
else {
char *raddr = vsock_get_addr(&vs->remote);

xasprintf(&str, "state=%s type=%s laddr=%s raddr=%s",
st_str, type_str, laddr, raddr);
free(raddr);
}
free(laddr);

return str;
}

static char *vsock_get_type(struct sock_xinfo *sock_xinfo,
struct sock *sock __attribute__((__unused__)))
{
const char *str;
struct vsock_xinfo *vs = (struct vsock_xinfo *)sock_xinfo;

str = sock_decode_type(vs->type);
return xstrdup(str);
}

static char *vsock_get_state(struct sock_xinfo *sock_xinfo,
struct sock *sock __attribute__((__unused__)))
{
const char *str;
struct vsock_xinfo *vs = (struct vsock_xinfo *)sock_xinfo;

str = l4_decode_state(vs->st);
return xstrdup(str);
}

static bool vsock_get_listening(struct sock_xinfo *sock_xinfo,
struct sock *sock __attribute__((__unused__)))
{
return ((struct vsock_xinfo *)sock_xinfo)->st == TCP_LISTEN;
}

static bool vsock_fill_column(struct proc *proc __attribute__((__unused__)),
struct sock_xinfo *sock_xinfo,
struct sock *sock __attribute__((__unused__)),
struct libscols_line *ln __attribute__((__unused__)),
int column_id,
size_t column_index __attribute__((__unused__)),
char **str)
{
struct vsock_xinfo *vs = (struct vsock_xinfo *)sock_xinfo;

switch (column_id) {
case COL_VSOCK_LCID:
xasprintf(str, "%"PRIu32, vs->local.cid);
return true;
case COL_VSOCK_RCID:
xasprintf(str, "%"PRIu32, vs->remote.cid);
return true;
case COL_VSOCK_LPORT:
xasprintf(str, "%"PRIu32, vs->local.port);
return true;
case COL_VSOCK_RPORT:
xasprintf(str, "%"PRIu32, vs->remote.port);
return true;
case COL_VSOCK_LADDR:
*str = vsock_get_addr(&vs->local);
return true;
case COL_VSOCK_RADDR:
*str = vsock_get_addr(&vs->remote);
return true;
}
return false;
}

static const struct sock_xinfo_class vsock_xinfo_class = {
.get_name = vsock_get_name,
.get_type = vsock_get_type,
.get_state = vsock_get_state,
.get_listening = vsock_get_listening,
.fill_column = vsock_fill_column,
.free = NULL,
};

static bool handle_diag_vsock(ino_t netns __attribute__((__unused__)),
size_t nlmsg_len, void *nlmsg_data)
{
const struct vsock_diag_msg *diag = nlmsg_data;
ino_t inode;
struct sock_xinfo *xinfo;
struct vsock_xinfo *vx;

if (diag->vdiag_family != AF_VSOCK)
return false;
DBG(ENDPOINTS, ul_debug(" VSOCK"));
DBG(ENDPOINTS, ul_debug(" LEN: %zu (>= %zu)", nlmsg_len,
(size_t)(NLMSG_LENGTH(sizeof(*diag)))));

if (nlmsg_len < NLMSG_LENGTH(sizeof(*diag)))
return false;

inode = (ino_t)diag->vdiag_ino;
DBG(ENDPOINTS, ul_debug(" inode: %llu", (unsigned long long)inode));

xinfo = get_sock_xinfo(inode);
if (xinfo != NULL)
/* It seems that the same socket reported twice. */
return true;

vx = xcalloc(1, sizeof(*vx));
xinfo = &vx->sock;
DBG(ENDPOINTS, ul_debug(" xinfo: %p", xinfo));

xinfo->class = &vsock_xinfo_class;
xinfo->inode = (ino_t)inode;
xinfo->netns_inode = (ino_t)netns;

vx->type = diag->vdiag_type;
vx->st = diag->vdiag_state;
vx->shutdown_mask = diag->vdiag_shutdown;
vx->local.cid = diag->vdiag_src_cid;
vx->local.port = diag->vdiag_src_port;
vx->remote.cid = diag->vdiag_dst_cid;
vx->remote.port = diag->vdiag_dst_port;

add_sock_info(xinfo);
return true;
}

static void load_xinfo_from_diag_vsock(int diagsd, ino_t netns)
{
struct vsock_diag_req vdr;

memset(&vdr, 0, sizeof(vdr));
vdr.sdiag_family = AF_VSOCK;
vdr.vdiag_states = ~(uint32_t)0;

send_diag_request(diagsd, &vdr, sizeof(vdr), handle_diag_vsock, netns);
}
4 changes: 4 additions & 0 deletions meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -830,6 +830,10 @@ have_so_passcred = cc.has_header_symbol(
prefix : '#include <sys/types.h>',
required : build_plymouth_support.enabled())

have = cc.has_header_symbol('linux/vm_sockets.h', 'VMADDR_CID_LOCAL',
prefix : '#include <sys/socket.h>')
conf.set('HAVE_DECL_VMADDR_CID_LOCAL', have ? 1 : false)

build_plymouth_support = (not build_plymouth_support.disabled() and
have_tiocglcktrmios and
have_sock_cloexec and
Expand Down
25 changes: 25 additions & 0 deletions tests/expected/lsfd/mkfds-vsock
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
# TYPE: STREAM
3 SOCK state=listen type=stream laddr=local:12345 listen stream 1
4 SOCK state=established type=stream laddr=local:23456 raddr=local:12345 established stream 0
5 SOCK state=established type=stream laddr=local:12345 raddr=local:23456 established stream 0
ASSOC,STTYPE,NAME,SOCK.STATE,SOCK.TYPE,SOCK.LISTENING: 0
local:12345 1 12345 *:* 4294967295 4294967295
local:23456 1 23456 local:12345 1 12345
local:12345 1 12345 local:23456 1 23456
VSOCK.LADDR,VSOCK.LCID,VSOCK.LPORT,VSOCK.RADDR,VSOCK.RCID,VSOCK.RPORT: 0
# TYPE: DGRAM
3 SOCK state=established type=dgram laddr=local:12345 raddr=local:23456 established dgram 0
4 SOCK state=established type=dgram laddr=local:23456 raddr=local:12345 established dgram 0
ASSOC,STTYPE,NAME,SOCK.STATE,SOCK.TYPE,SOCK.LISTENING: 0
local:12345 1 12345 local:23456 1 23456
local:23456 1 23456 local:12345 1 12345
VSOCK.LADDR,VSOCK.LCID,VSOCK.LPORT,VSOCK.RADDR,VSOCK.RCID,VSOCK.RPORT: 0
# TYPE: SEQPACKET
3 SOCK state=listen type=seqpacket laddr=local:12345 listen seqpacket 1
4 SOCK state=established type=seqpacket laddr=local:23456 raddr=local:12345 established seqpacket 0
5 SOCK state=established type=seqpacket laddr=local:12345 raddr=local:23456 established seqpacket 0
ASSOC,STTYPE,NAME,SOCK.STATE,SOCK.TYPE,SOCK.LISTENING: 0
local:12345 1 12345 *:* 4294967295 4294967295
local:23456 1 23456 local:12345 1 12345
local:12345 1 12345 local:23456 1 23456
VSOCK.LADDR,VSOCK.LCID,VSOCK.LPORT,VSOCK.RADDR,VSOCK.RCID,VSOCK.RPORT: 0
Loading

0 comments on commit 4153f6b

Please sign in to comment.