1
0
Fork 0
util-linux/lsfd-cmd/sock-xinfo.c
Daniel Baumann c36e531662
Adding upstream version 2.41.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
2025-06-21 11:26:35 +02:00

2606 lines
63 KiB
C

/*
* lsfd-sock-xinfo.c - read various information from files under /proc/net/ and NETLINK_SOCK_DIAG
*
* Copyright (C) 2022 Red Hat, Inc. All rights reserved.
* Written by Masatake YAMATO <yamato@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; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it would 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.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <arpa/inet.h> /* inet_ntop */
#include <netinet/in.h> /* in6_addr */
#include <fcntl.h> /* open(2) */
#include <ifaddrs.h> /* getifaddrs */
#include <inttypes.h> /* SCNu16 */
#include <net/if.h> /* if_nametoindex */
#include <linux/if_ether.h> /* ETH_P_* */
#include <linux/net.h> /* SS_* */
#include <linux/netlink.h> /* NETLINK_*, NLMSG_* */
#include <linux/rtnetlink.h> /* RTA_*, struct rtattr, */
#include <linux/sock_diag.h> /* SOCK_DIAG_BY_FAMILY */
#include <linux/sockios.h> /* SIOCGSKNS */
#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>
#include <string.h>
#include <sys/ioctl.h>
#include <sys/socket.h> /* SOCK_* */
#include "sysfs.h"
#include "bitops.h"
#include "lsfd.h"
#include "pidfd-utils.h"
#include "sock.h"
static void load_xinfo_from_proc_icmp(ino_t netns_inode, enum sysfs_byteorder byteorder);
static void load_xinfo_from_proc_icmp6(ino_t netns_inode, enum sysfs_byteorder byteorder);
static void load_xinfo_from_proc_unix(ino_t netns_inode);
static void load_xinfo_from_proc_raw(ino_t netns_inode, enum sysfs_byteorder byteorder);
static void load_xinfo_from_proc_tcp(ino_t netns_inode, enum sysfs_byteorder byteorder);
static void load_xinfo_from_proc_udp(ino_t netns_inode, enum sysfs_byteorder byteorder);
static void load_xinfo_from_proc_udplite(ino_t netns_inode, enum sysfs_byteorder byteorder);
static void load_xinfo_from_proc_tcp6(ino_t netns_inode, enum sysfs_byteorder byteorder);
static void load_xinfo_from_proc_udp6(ino_t netns_inode, enum sysfs_byteorder byteorder);
static void load_xinfo_from_proc_udplite6(ino_t netns_inode, enum sysfs_byteorder byteorder);
static void load_xinfo_from_proc_raw6(ino_t netns_inode, enum sysfs_byteorder byteorder);
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;
static void *xinfo_tree; /* for tsearch/tfind */
static void *netns_tree;
struct iface {
unsigned int index;
char name[IF_NAMESIZE];
};
static const char *get_iface_name(ino_t netns, unsigned int iface_index);
struct netns {
ino_t inode;
struct iface *ifaces;
};
static int netns_compare(const void *a, const void *b)
{
const struct netns *netns_a = a;
const struct netns *netns_b = b;
return netns_a->inode - netns_b->inode;
}
static void netns_free(void *netns)
{
struct netns *nsobj = netns;
free(nsobj->ifaces);
free(netns);
}
/*
* iface index -> iface name mappings
*/
static void load_ifaces_from_getifaddrs(struct netns *nsobj)
{
struct ifaddrs *ifa_list;
struct ifaddrs *ifa;
size_t i, count = 0;
if (getifaddrs(&ifa_list) < 0)
return;
for (ifa = ifa_list; ifa != NULL; ifa = ifa->ifa_next)
count++;
nsobj->ifaces = xcalloc(count + 1, sizeof(*nsobj->ifaces));
for (ifa = ifa_list, i = 0; ifa != NULL; ifa = ifa->ifa_next, i++) {
unsigned int if_index = if_nametoindex(ifa->ifa_name);
nsobj->ifaces[i].index = if_index;
strncpy(nsobj->ifaces[i].name, ifa->ifa_name, IF_NAMESIZE - 1);
/* The slot for the last byte is already filled by calloc. */
}
/* nsobj->ifaces[count] is the sentinel value. */
freeifaddrs(ifa_list);
return;
}
static const char *get_iface_name(ino_t netns, unsigned int iface_index)
{
struct netns key = { .inode = netns };
struct netns **nsobj = tfind(&key, &netns_tree, netns_compare);
if (!nsobj)
return NULL;
for (size_t i = 0; (*nsobj)->ifaces[i].index; i++) {
if ((*nsobj)->ifaces[i].index == iface_index)
return (*nsobj)->ifaces[i].name;
}
return NULL;
}
static bool is_sock_xinfo_loaded(ino_t netns)
{
struct netns key = { .inode = netns };
return tfind(&key, &netns_tree, netns_compare)? true: false;
}
static struct netns *mark_sock_xinfo_loaded(ino_t ino)
{
struct netns *netns = xcalloc(1, sizeof(*netns));
ino_t **tmp;
netns->inode = ino;
tmp = tsearch(netns, &netns_tree, netns_compare);
if (tmp == NULL)
errx(EXIT_FAILURE, _("failed to allocate memory"));
return *(struct netns **)tmp;
}
static void load_sock_xinfo_no_nsswitch(struct netns *nsobj)
{
ino_t netns = nsobj? nsobj->inode: 0;
int diagsd;
enum sysfs_byteorder byteorder = sysfs_get_byteorder(NULL);
load_xinfo_from_proc_unix(netns);
load_xinfo_from_proc_tcp(netns, byteorder);
load_xinfo_from_proc_udp(netns, byteorder);
load_xinfo_from_proc_udplite(netns, byteorder);
load_xinfo_from_proc_raw(netns, byteorder);
load_xinfo_from_proc_tcp6(netns, byteorder);
load_xinfo_from_proc_udp6(netns, byteorder);
load_xinfo_from_proc_udplite6(netns, byteorder);
load_xinfo_from_proc_raw6(netns, byteorder);
load_xinfo_from_proc_icmp(netns, byteorder);
load_xinfo_from_proc_icmp6(netns, byteorder);
load_xinfo_from_proc_netlink(netns);
load_xinfo_from_proc_packet(netns);
diagsd = socket(AF_NETLINK, SOCK_DGRAM, NETLINK_SOCK_DIAG);
DBG(ENDPOINTS, ul_debug("made a diagnose socket [fd=%d; %s]", diagsd,
(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"));
}
if (nsobj)
load_ifaces_from_getifaddrs(nsobj);
}
static void load_sock_xinfo_with_fd(int fd, struct netns *nsobj)
{
if (setns(fd, CLONE_NEWNET) == 0) {
load_sock_xinfo_no_nsswitch(nsobj);
setns(self_netns_fd, CLONE_NEWNET);
}
}
void load_sock_xinfo(struct path_cxt *pc, const char *name, ino_t netns)
{
if (self_netns_fd == -1)
return;
if (!is_sock_xinfo_loaded(netns)) {
int fd;
struct netns *nsobj = mark_sock_xinfo_loaded(netns);
fd = ul_path_open(pc, O_RDONLY, name);
if (fd < 0)
return;
load_sock_xinfo_with_fd(fd, nsobj);
close(fd);
}
}
void load_fdsk_xinfo(struct proc *proc, int fd)
{
int pidfd, sk, nsfd;
struct netns *nsobj;
struct stat sb;
/* This is additional/extra information, ignoring failures. */
pidfd = pidfd_open(proc->pid, 0);
if (pidfd < 0)
return;
sk = pidfd_getfd(pidfd, fd, 0);
if (sk < 0)
goto out_pidfd;
nsfd = ioctl(sk, SIOCGSKNS);
if (nsfd < 0)
goto out_sk;
if (fstat(nsfd, &sb) < 0)
goto out_nsfd;
if (is_sock_xinfo_loaded(sb.st_ino))
goto out_nsfd;
nsobj = mark_sock_xinfo_loaded(sb.st_ino);
load_sock_xinfo_with_fd(nsfd, nsobj);
out_nsfd:
close(nsfd);
out_sk:
close(sk);
out_pidfd:
close(pidfd);
}
void initialize_sock_xinfos(void)
{
struct path_cxt *pc;
DIR *dir;
struct dirent *d;
self_netns_fd = open("/proc/self/ns/net", O_RDONLY);
if (self_netns_fd < 0)
load_sock_xinfo_no_nsswitch(NULL);
else {
if (fstat(self_netns_fd, &self_netns_sb) == 0) {
unsigned long m;
struct netns *nsobj = mark_sock_xinfo_loaded(self_netns_sb.st_ino);
load_sock_xinfo_no_nsswitch(nsobj);
m = minor(self_netns_sb.st_dev);
add_nodev(m, "nsfs");
}
}
/* Load /proc/net/{unix,...} of the network namespace
* specified with netns files under /var/run/netns/.
*
* `ip netns' command pins a network namespace on
* /var/run/netns.
*/
pc = ul_new_path("/var/run/netns");
if (!pc)
err(EXIT_FAILURE, _("failed to alloc path context for /var/run/netns"));
dir = ul_path_opendir(pc, NULL);
if (dir == NULL) {
ul_unref_path(pc);
return;
}
while ((d = readdir(dir))) {
struct stat sb;
int fd;
struct netns *nsobj;
if (ul_path_stat(pc, &sb, 0, d->d_name) < 0)
continue;
if (is_sock_xinfo_loaded(sb.st_ino))
continue;
nsobj = mark_sock_xinfo_loaded(sb.st_ino);
fd = ul_path_open(pc, O_RDONLY, d->d_name);
if (fd < 0)
continue;
load_sock_xinfo_with_fd(fd, nsobj);
close(fd);
}
closedir(dir);
ul_unref_path(pc);
}
static void free_sock_xinfo(void *node)
{
struct sock_xinfo *xinfo = node;
if (xinfo->class->free)
xinfo->class->free(xinfo);
free(node);
}
void finalize_sock_xinfos(void)
{
if (self_netns_fd != -1)
close(self_netns_fd);
tdestroy(netns_tree, netns_free);
tdestroy(xinfo_tree, free_sock_xinfo);
}
static int xinfo_compare(const void *a, const void *b)
{
return ((struct sock_xinfo *)a)->inode - ((struct sock_xinfo *)b)->inode;
}
static void add_sock_info(struct sock_xinfo *xinfo)
{
struct sock_xinfo **tmp = tsearch(xinfo, &xinfo_tree, xinfo_compare);
if (tmp == NULL)
errx(EXIT_FAILURE, _("failed to allocate memory"));
}
struct sock_xinfo *get_sock_xinfo(ino_t inode)
{
struct sock_xinfo key = { .inode = inode };
struct sock_xinfo **xinfo = tfind(&key, &xinfo_tree, xinfo_compare);
if (xinfo)
return *xinfo;
return NULL;
}
bool is_nsfs_dev(dev_t dev)
{
return dev == self_netns_sb.st_dev;
}
static const char *sock_decode_type(uint16_t type)
{
switch (type) {
case SOCK_STREAM:
return "stream";
case SOCK_DGRAM:
return "dgram";
case SOCK_RAW:
return "raw";
case SOCK_RDM:
return "rdm";
case SOCK_SEQPACKET:
return "seqpacket";
case SOCK_DCCP:
return "dccp";
case SOCK_PACKET:
return "packet";
default:
return "unknown";
}
}
static void send_diag_request(int diagsd, void *req, size_t req_size,
bool (*cb)(ino_t, size_t, void *),
ino_t netns)
{
int r;
struct sockaddr_nl nladdr = {
.nl_family = AF_NETLINK,
};
struct nlmsghdr nlh = {
.nlmsg_len = sizeof(nlh) + req_size,
.nlmsg_type = SOCK_DIAG_BY_FAMILY,
.nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP,
};
struct iovec iovecs[] = {
{ &nlh, sizeof(nlh) },
{ req, req_size },
};
const struct msghdr mhd = {
.msg_namelen = sizeof(nladdr),
.msg_name = &nladdr,
.msg_iovlen = ARRAY_SIZE(iovecs),
.msg_iov = iovecs,
};
__attribute__((aligned(sizeof(void *)))) uint8_t buf[8192];
r = sendmsg(diagsd, &mhd, 0);
DBG(ENDPOINTS, ul_debug("sendmsg [rc=%d; %s]",
r, (r >= 0)? "successful": strerror(errno)));
if (r < 0)
return;
for (;;) {
const struct nlmsghdr *h;
r = recvfrom(diagsd, buf, sizeof(buf), 0, NULL, NULL);
DBG(ENDPOINTS, ul_debug("recvfrom [rc=%d; %s]",
r, (r >= 0)? "successful": strerror(errno)));
if (r < 0)
return;
h = (void *) buf;
DBG(ENDPOINTS, ul_debug(" OK: %d", NLMSG_OK(h, (size_t)r)));
if (!NLMSG_OK(h, (size_t)r))
return;
for (; NLMSG_OK(h, (size_t)r); h = NLMSG_NEXT(h, r)) {
if (h->nlmsg_type == NLMSG_DONE) {
DBG(ENDPOINTS, ul_debug(" DONE"));
return;
}
if (h->nlmsg_type == NLMSG_ERROR) {
struct nlmsgerr *e = (struct nlmsgerr *)NLMSG_DATA(h);
DBG(ENDPOINTS, ul_debug(" ERROR: %s",
strerror(- e->error)));
return;
}
if (h->nlmsg_type == SOCK_DIAG_BY_FAMILY) {
DBG(ENDPOINTS, ul_debug(" FAMILY"));
if (!cb(netns, h->nlmsg_len, NLMSG_DATA(h)))
return;
}
DBG(ENDPOINTS, ul_debug(" NEXT"));
}
DBG(ENDPOINTS, ul_debug(" OK: 0"));
}
}
/*
* Protocol specific code
*/
/*
* UNIX
*/
struct unix_ipc {
struct ipc ipc;
ino_t inode;
ino_t ipeer;
};
struct unix_xinfo {
struct sock_xinfo sock;
int acceptcon; /* flags */
uint16_t type;
uint8_t st;
#define is_shutdown_mask_set(mask) ((mask) & (1 << 2))
#define set_shutdown_mask(mask) ((mask) |= (1 << 2))
uint8_t shutdown_mask:3;
struct unix_ipc *unix_ipc;
char path[
UNIX_PATH_MAX
+ 1 /* for @ */
+ 1 /* \0? */
];
};
static const char *unix_decode_state(uint8_t st)
{
switch (st) {
case SS_FREE:
return "free";
case SS_UNCONNECTED:
return "unconnected";
case SS_CONNECTING:
return "connecting";
case SS_CONNECTED:
return "connected";
case SS_DISCONNECTING:
return "disconnecting";
default:
return "unknown";
}
}
static char *unix_get_name(struct sock_xinfo *sock_xinfo,
struct sock *sock)
{
struct unix_xinfo *ux = (struct unix_xinfo *)sock_xinfo;
const char *state = unix_decode_state(ux->st);
char *str = NULL;
if (sock->protoname && (strcmp(sock->protoname, "UNIX-STREAM") == 0))
xasprintf(&str, "state=%s%s%s",
(ux->acceptcon)? "listen": state,
*(ux->path)? " path=": "",
*(ux->path)? ux->path: "");
else
xasprintf(&str, "state=%s%s%s type=%s",
(ux->acceptcon)? "listen": state,
*(ux->path)? " path=": "",
*(ux->path)? ux->path: "",
sock_decode_type(ux->type));
return str;
}
static char *unix_get_type(struct sock_xinfo *sock_xinfo,
struct sock *sock __attribute__((__unused__)))
{
const char *str;
struct unix_xinfo *ux = (struct unix_xinfo *)sock_xinfo;
str = sock_decode_type(ux->type);
return xstrdup(str);
}
static char *unix_get_state(struct sock_xinfo *sock_xinfo,
struct sock *sock __attribute__((__unused__)))
{
const char *str;
struct unix_xinfo *ux = (struct unix_xinfo *)sock_xinfo;
if (ux->acceptcon)
return xstrdup("listen");
str = unix_decode_state(ux->st);
return xstrdup(str);
}
static bool unix_get_listening(struct sock_xinfo *sock_xinfo,
struct sock *sock __attribute__((__unused__)))
{
struct unix_xinfo *ux = (struct unix_xinfo *)sock_xinfo;
return ux->acceptcon;
}
static unsigned int unix_get_hash(struct file *file)
{
return (unsigned int)(file->stat.st_ino % UINT_MAX);
}
static bool unix_is_suitable_ipc(struct ipc *ipc, struct file *file)
{
return ((struct unix_ipc *)ipc)->inode == file->stat.st_ino;
}
/* For looking up an ipc struct for a sock inode, we need a sock strcuct
* for the inode. See the signature o get_ipc().
*
* However, there is a case that we have no sock strcuct for the inode;
* in the context we know only the sock inode.
* For the case, unix_make_dummy_sock() provides the way to make a
* dummy sock struct for the inode.
*/
static void unix_make_dummy_sock(struct sock *original, ino_t ino, struct sock *dummy)
{
*dummy = *original;
dummy->file.stat.st_ino = ino;
}
static struct ipc_class unix_ipc_class = {
.size = sizeof(struct unix_ipc),
.get_hash = unix_get_hash,
.is_suitable_ipc = unix_is_suitable_ipc,
.free = NULL,
};
static struct ipc_class *unix_get_ipc_class(struct sock_xinfo *sock_xinfo __attribute__((__unused__)),
struct sock *sock __attribute__((__unused__)))
{
return &unix_ipc_class;
}
static bool unix_shutdown_chars(struct unix_xinfo *ux, char rw[2])
{
uint8_t mask = ux->shutdown_mask;
if (is_shutdown_mask_set(mask)) {
rw[0] = ((mask & (1 << 0))? '-': 'r');
rw[1] = ((mask & (1 << 1))? '-': 'w');
return true;
}
return false;
}
static inline char *unix_xstrendpoint(struct sock *sock)
{
char *str = NULL;
char shutdown_chars[3] = { 0 };
if (!unix_shutdown_chars(((struct unix_xinfo *)sock->xinfo), shutdown_chars)) {
shutdown_chars[0] = '?';
shutdown_chars[1] = '?';
}
xasprintf(&str, "%d,%s,%d%c%c",
sock->file.proc->pid, sock->file.proc->command, sock->file.association,
shutdown_chars[0], shutdown_chars[1]);
return str;
}
static struct ipc *unix_get_peer_ipc(struct unix_xinfo *ux,
struct sock *sock)
{
struct unix_ipc *unix_ipc;
struct sock dummy_peer_sock;
unix_ipc = ux->unix_ipc;
if (!unix_ipc)
return NULL;
unix_make_dummy_sock(sock, unix_ipc->ipeer, &dummy_peer_sock);
return get_ipc(&dummy_peer_sock.file);
}
static bool unix_fill_column(struct proc *proc __attribute__((__unused__)),
struct sock_xinfo *sock_xinfo,
struct sock *sock,
struct libscols_line *ln __attribute__((__unused__)),
int column_id,
size_t column_index __attribute__((__unused__)),
char **str)
{
struct unix_xinfo *ux = (struct unix_xinfo *)sock_xinfo;
struct ipc *peer_ipc;
struct list_head *e;
char shutdown_chars[3] = { 0 };
switch (column_id) {
case COL_UNIX_PATH:
if (*ux->path) {
*str = xstrdup(ux->path);
return true;
}
break;
case COL_ENDPOINTS:
peer_ipc = unix_get_peer_ipc(ux, sock);
if (!peer_ipc)
break;
list_for_each_backwardly(e, &peer_ipc->endpoints) {
struct sock *peer_sock = list_entry(e, struct sock, endpoint.endpoints);
char *estr;
if (*str)
xstrputc(str, '\n');
estr = unix_xstrendpoint(peer_sock);
xstrappend(str, estr);
free(estr);
}
if (*str)
return true;
break;
case COL_SOCK_SHUTDOWN:
if (unix_shutdown_chars(ux, shutdown_chars)) {
*str = xstrdup(shutdown_chars);
return true;
}
break;
}
return false;
}
static const struct sock_xinfo_class unix_xinfo_class = {
.get_name = unix_get_name,
.get_type = unix_get_type,
.get_state = unix_get_state,
.get_listening = unix_get_listening,
.fill_column = unix_fill_column,
.get_ipc_class = unix_get_ipc_class,
.free = NULL,
};
/* UNIX_LINE_LEN need at least 54 + 21 + UNIX_PATH_MAX + 1.
*
* An actual number must be used in this definition
* since UNIX_LINE_LEN is specified as an argument for
* stringify_value().
*/
#define UNIX_LINE_LEN 256
static void load_xinfo_from_proc_unix(ino_t netns_inode)
{
char line[UNIX_LINE_LEN];
FILE *unix_fp;
unix_fp = fopen("/proc/net/unix", "r");
DBG(ENDPOINTS, ul_debug("open /proc/net/unix [fp=%p; %s]", unix_fp,
unix_fp? "successful": strerror(errno)));
if (!unix_fp)
return;
if (fgets(line, sizeof(line), unix_fp) == NULL)
goto out;
if (!(line[0] == 'N' && line[1] == 'u' && line[2] == 'm'))
/* Unexpected line */
goto out;
while (fgets(line, sizeof(line), unix_fp)) {
uint64_t flags;
uint32_t type;
unsigned int st;
unsigned long inode;
struct unix_xinfo *ux;
char path[UNIX_LINE_LEN + 1] = { 0 };
int r;
DBG(ENDPOINTS, ul_debug(" line: %s", line));
r = sscanf(line, "%*x: %*x %*x %" SCNx64 " %x %x %lu %"
stringify_value(UNIX_LINE_LEN) "[^\n]",
&flags, &type, &st, &inode, path);
DBG(ENDPOINTS, ul_debug(" scanf: %d", r));
if (r < 4)
continue;
DBG(ENDPOINTS, ul_debug(" inode: %lu", inode));
if (inode == 0)
continue;
ux = xcalloc(1, sizeof(*ux));
ux->sock.class = &unix_xinfo_class;
ux->sock.inode = (ino_t)inode;
ux->sock.netns_inode = netns_inode;
ux->acceptcon = !!flags;
ux->type = type;
ux->st = st;
xstrncpy(ux->path, path, sizeof(ux->path));
DBG(ENDPOINTS, ul_debug(" path: %s", ux->path));
add_sock_info(&ux->sock);
}
out:
DBG(ENDPOINTS, ul_debug("close /proc/net/unix"));
fclose(unix_fp);
}
/* The path name extracted from /proc/net/unix is unreliable; the line oriented interface cannot
* represent a file name including newlines. With unix_refill_name(), we patch the path
* member of unix_xinfos with information received via netlink diag interface. */
static void unix_refill_name(struct sock_xinfo *xinfo, const char *name, size_t len)
{
struct unix_xinfo *ux = (struct unix_xinfo *)xinfo;
size_t min_len;
if (len == 0)
return;
min_len = min(sizeof(ux->path) - 1, len);
memcpy(ux->path, name, min_len);
if (ux->path[0] == '\0') {
ux->path[0] = '@';
}
ux->path[min_len] = '\0';
}
static bool handle_diag_unix(ino_t netns __attribute__((__unused__)),
size_t nlmsg_len, void *nlmsg_data)
{
const struct unix_diag_msg *diag = nlmsg_data;
size_t rta_len;
ino_t inode;
struct sock_xinfo *xinfo;
struct unix_xinfo *unix_xinfo;
if (diag->udiag_family != AF_UNIX)
return false;
DBG(ENDPOINTS, ul_debug(" UNIX"));
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->udiag_ino;
xinfo = get_sock_xinfo(inode);
DBG(ENDPOINTS, ul_debug(" inode: %llu", (unsigned long long)inode));
DBG(ENDPOINTS, ul_debug(" xinfo: %p", xinfo));
if (xinfo == NULL)
/* The socket is found in the diag response
but not in the proc fs. */
return true;
DBG(ENDPOINTS, ul_debug(" xinfo->class == &unix_xinfo_class: %d",
xinfo->class == &unix_xinfo_class));
if (xinfo->class != &unix_xinfo_class)
return true;
unix_xinfo = (struct unix_xinfo *)xinfo;
rta_len = nlmsg_len - NLMSG_LENGTH(sizeof(*diag));
DBG(ENDPOINTS, ul_debug(" rta_len: %zu", rta_len));
for (struct rtattr *attr = (struct rtattr *)(diag + 1);
RTA_OK(attr, rta_len);
attr = RTA_NEXT(attr, rta_len)) {
size_t len = RTA_PAYLOAD(attr);
DBG(ENDPOINTS, ul_debug(" len = %2zu, type: %d",
rta_len, attr->rta_type));
switch (attr->rta_type) {
case UNIX_DIAG_NAME:
unix_refill_name(xinfo, RTA_DATA(attr), len);
break;
case UNIX_DIAG_SHUTDOWN:
if (len < 1)
break;
unix_xinfo->shutdown_mask = *(uint8_t *)RTA_DATA(attr);
set_shutdown_mask(unix_xinfo->shutdown_mask);
break;
case UNIX_DIAG_PEER:
if (len < 4)
break;
unix_xinfo->unix_ipc = (struct unix_ipc *)new_ipc(&unix_ipc_class);
unix_xinfo->unix_ipc->inode = inode;
unix_xinfo->unix_ipc->ipeer = (ino_t)(*(uint32_t *)RTA_DATA(attr));
add_ipc(&unix_xinfo->unix_ipc->ipc, inode % UINT_MAX);
break;
}
}
return true;
}
static void load_xinfo_from_diag_unix(int diagsd, ino_t netns)
{
struct unix_diag_req udr = {
.sdiag_family = AF_UNIX,
.udiag_states = -1, /* set the all bits. */
.udiag_show = UDIAG_SHOW_NAME | UDIAG_SHOW_PEER | UNIX_DIAG_SHUTDOWN,
};
send_diag_request(diagsd, &udr, sizeof(udr), handle_diag_unix, netns);
}
/*
* AF_INET
*/
struct inet_xinfo {
struct sock_xinfo sock;
struct in_addr local_addr;
struct in_addr remote_addr;
};
static uint32_t kernel32_to_cpu(enum sysfs_byteorder byteorder, uint32_t v)
{
if (byteorder == SYSFS_BYTEORDER_LITTLE)
return le32_to_cpu(v);
else
return be32_to_cpu(v);
}
/*
* AF_INET6
*/
struct inet6_xinfo {
struct sock_xinfo sock;
struct in6_addr local_addr;
struct in6_addr remote_addr;
};
/*
* L4 abstract-layer for protocols stacked on IP and IP6.
*/
enum l4_state {
/*
* Taken from linux/include/net/tcp_states.h.
* (GPL-2.0-or-later)
*
* UDP and RAW sockets also uses the contents in Linux.
*/
TCP_ESTABLISHED = 1,
TCP_SYN_SENT,
TCP_SYN_RECV,
TCP_FIN_WAIT1,
TCP_FIN_WAIT2,
TCP_TIME_WAIT,
TCP_CLOSE,
TCP_CLOSE_WAIT,
TCP_LAST_ACK,
TCP_LISTEN,
TCP_CLOSING,
TCP_NEW_SYN_RECV,
TCP_MAX_STATES /* Leave at the end! */
};
static const char *l4_decode_state(enum l4_state st)
{
const char * const table [] = {
[TCP_ESTABLISHED] = "established",
[TCP_SYN_SENT] = "syn-sent",
[TCP_SYN_RECV] = "syn-recv",
[TCP_FIN_WAIT1] = "fin-wait1",
[TCP_FIN_WAIT2] = "fin-wait2",
[TCP_TIME_WAIT] = "time-wait",
[TCP_CLOSE] = "close",
[TCP_CLOSE_WAIT] = "close-wait",
[TCP_LAST_ACK] = "last-ack",
[TCP_LISTEN] = "listen",
[TCP_CLOSING] = "closing",
[TCP_NEW_SYN_RECV] = "new-syn-recv",
};
if (st < TCP_MAX_STATES)
return table[st];
return "unknown";
}
struct l4_xinfo {
union {
struct inet_xinfo inet;
struct inet6_xinfo inet6;
};
enum l4_state st;
};
enum l4_side { L4_LOCAL, L4_REMOTE };
enum l3_decorator { L3_DECO_START, L3_DECO_END };
struct l4_xinfo_class {
struct sock_xinfo_class sock;
struct sock_xinfo *(*scan_line)(const struct sock_xinfo_class *,
char *,
ino_t,
enum sysfs_byteorder);
void * (*get_addr)(struct l4_xinfo *, enum l4_side);
bool (*is_any_addr)(void *);
int family;
const char *l3_decorator[2];
};
#define l3_fill_column_handler(L3, SOCK_XINFO, COLUMN_ID, STR) __extension__ \
({ \
struct l4_xinfo_class *class = (struct l4_xinfo_class *)SOCK_XINFO->class; \
struct l4_xinfo *l4 = (struct l4_xinfo *)SOCK_XINFO; \
void *n = NULL; \
char s[BUFSIZ]; \
bool r = false; \
\
switch (COLUMN_ID) { \
case COL_##L3##_LADDR: \
n = class->get_addr(l4, L4_LOCAL); \
break; \
case COL_##L3##_RADDR: \
n = class->get_addr(l4, L4_REMOTE); \
break; \
default: \
break; \
} \
\
if (n && inet_ntop(class->family, n, s, sizeof(s))) { \
*STR = xstrdup(s); \
r = true; \
} \
r; \
})
/*
* TCP
*/
struct tcp_xinfo {
struct l4_xinfo l4;
uint16_t local_port;
uint16_t remote_port;
};
static char *tcp_get_name(struct sock_xinfo *sock_xinfo,
struct sock *sock __attribute__((__unused__)))
{
char *str = NULL;
struct tcp_xinfo *tcp = ((struct tcp_xinfo *)sock_xinfo);
struct l4_xinfo *l4 = &tcp->l4;
const char *st_str = l4_decode_state(l4->st);
struct l4_xinfo_class *class = (struct l4_xinfo_class *)sock_xinfo->class;
void *laddr = class->get_addr(l4, L4_LOCAL);
void *raddr = class->get_addr(l4, L4_REMOTE);
char local_s[BUFSIZ];
char remote_s[BUFSIZ];
const char *start = class->l3_decorator[L3_DECO_START];
const char *end = class->l3_decorator[L3_DECO_END];
if (!inet_ntop(class->family, laddr, local_s, sizeof(local_s)))
xasprintf(&str, "state=%s", st_str);
else if (l4->st == TCP_LISTEN
|| !inet_ntop(class->family, raddr, remote_s, sizeof(remote_s)))
xasprintf(&str, "state=%s laddr=%s%s%s:%"PRIu16,
st_str,
start, local_s, end, tcp->local_port);
else
xasprintf(&str, "state=%s laddr=%s%s%s:%"PRIu16" raddr=%s%s%s:%"PRIu16,
st_str,
start, local_s, end, tcp->local_port,
start, remote_s, end, tcp->remote_port);
return str;
}
static char *tcp_get_type(struct sock_xinfo *sock_xinfo __attribute__((__unused__)),
struct sock *sock __attribute__((__unused__)))
{
return xstrdup("stream");
}
static char *tcp_get_state(struct sock_xinfo *sock_xinfo,
struct sock *sock __attribute__((__unused__)))
{
return xstrdup(l4_decode_state(((struct l4_xinfo *)sock_xinfo)->st));
}
static bool tcp_get_listening(struct sock_xinfo *sock_xinfo,
struct sock *sock __attribute__((__unused__)))
{
return ((struct l4_xinfo *)sock_xinfo)->st == TCP_LISTEN;
}
#define l4_fill_column_handler(L4, SOCK_XINFO, COLUMN_ID, STR) __extension__ \
({ \
struct l4_xinfo_class *class = (struct l4_xinfo_class *)SOCK_XINFO->class; \
struct tcp_xinfo *tcp = (struct tcp_xinfo *)SOCK_XINFO; \
struct l4_xinfo *l4 = &tcp->l4; \
void *n = NULL; \
bool has_laddr = false; \
unsigned short p; \
bool has_lport = false; \
char s[BUFSIZ]; \
bool r = true; \
\
switch (COLUMN_ID) { \
case COL_##L4##_LADDR: \
n = class->get_addr(l4, L4_LOCAL); \
has_laddr = true; \
p = tcp->local_port; \
/* FALL THROUGH */ \
case COL_##L4##_RADDR: \
if (!has_laddr) { \
n = class->get_addr(l4, L4_REMOTE); \
p = tcp->remote_port; \
} \
if (n && inet_ntop(class->family, n, s, sizeof(s))) \
xasprintf(STR, "%s%s%s:%"PRIu16, \
class->l3_decorator[L3_DECO_START], \
s, \
class->l3_decorator[L3_DECO_END], \
p); \
break; \
case COL_##L4##_LPORT: \
p = tcp->local_port; \
has_lport = true; \
/* FALL THROUGH */ \
case COL_##L4##_RPORT: \
if (!has_lport) \
p = tcp->remote_port; \
xasprintf(STR, "%"PRIu16, p); \
break; \
default: \
r = false; \
break; \
} \
r; \
})
static struct sock_xinfo *tcp_xinfo_scan_line(const struct sock_xinfo_class *class,
char * line,
ino_t netns_inode,
enum sysfs_byteorder byteorder)
{
unsigned long local_addr;
unsigned long local_port;
unsigned long remote_addr;
unsigned long remote_port;
unsigned long st;
unsigned long long inode;
struct tcp_xinfo *tcp;
struct inet_xinfo *inet;
struct sock_xinfo *sock;
if (sscanf(line, "%*d: %lx:%lx %lx:%lx %lx %*x:%*x %*x:%*x %*x %*u %*u %lld",
&local_addr, &local_port, &remote_addr, &remote_port,
&st, &inode) != 6)
return NULL;
if (inode == 0)
return NULL;
tcp = xcalloc(1, sizeof(*tcp));
inet = &tcp->l4.inet;
sock = &inet->sock;
sock->class = class;
sock->inode = (ino_t)inode;
sock->netns_inode = netns_inode;
inet->local_addr.s_addr = kernel32_to_cpu(byteorder, local_addr);
tcp->local_port = local_port;
inet->remote_addr.s_addr = kernel32_to_cpu(byteorder, remote_addr);
tcp->remote_port = remote_port;
tcp->l4.st = st;
return sock;
}
static void *tcp_xinfo_get_addr(struct l4_xinfo *l4, enum l4_side side)
{
return (side == L4_LOCAL)
? &l4->inet.local_addr
: &l4->inet.remote_addr;
}
static bool tcp_xinfo_is_any_addr(void *addr)
{
return ((struct in_addr *)addr)->s_addr == INADDR_ANY;
}
static bool tcp_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)
{
return l3_fill_column_handler(INET, sock_xinfo, column_id, str)
|| l4_fill_column_handler(TCP, sock_xinfo, column_id, str);
}
static const struct l4_xinfo_class tcp_xinfo_class = {
.sock = {
.get_name = tcp_get_name,
.get_type = tcp_get_type,
.get_state = tcp_get_state,
.get_listening = tcp_get_listening,
.fill_column = tcp_fill_column,
.free = NULL,
},
.scan_line = tcp_xinfo_scan_line,
.get_addr = tcp_xinfo_get_addr,
.is_any_addr = tcp_xinfo_is_any_addr,
.family = AF_INET,
.l3_decorator = {"", ""},
};
static bool L4_verify_initial_line(const char *line)
{
/* At least we expect two white spaces. */
if (strncmp(line, " ", 2) != 0)
return false;
line += 2;
/* Skip white spaces. */
line = skip_space(line);
return strncmp(line, "sl", 2) == 0;
}
#define TCP_LINE_LEN 256
static void load_xinfo_from_proc_inet_L4(ino_t netns_inode, const char *proc_file,
const struct l4_xinfo_class *class,
enum sysfs_byteorder byteorder)
{
char line[TCP_LINE_LEN];
FILE *tcp_fp;
tcp_fp = fopen(proc_file, "r");
if (!tcp_fp)
return;
if (fgets(line, sizeof(line), tcp_fp) == NULL)
goto out;
if (!L4_verify_initial_line(line))
/* Unexpected line */
goto out;
while (fgets(line, sizeof(line), tcp_fp)) {
struct sock_xinfo *sock = class->scan_line(&class->sock, line, netns_inode, byteorder);
if (sock)
add_sock_info(sock);
}
out:
fclose(tcp_fp);
}
static void load_xinfo_from_proc_tcp(ino_t netns_inode, enum sysfs_byteorder byteorder)
{
load_xinfo_from_proc_inet_L4(netns_inode,
"/proc/net/tcp",
&tcp_xinfo_class,
byteorder);
}
/*
* UDP
*/
static char *udp_get_name(struct sock_xinfo *sock_xinfo,
struct sock *sock __attribute__((__unused__)))
{
char *str = NULL;
struct tcp_xinfo *tcp = ((struct tcp_xinfo *)sock_xinfo);
struct l4_xinfo *l4 = &tcp->l4;
unsigned int st = l4->st;
const char *st_str = l4_decode_state(st);
struct l4_xinfo_class *class = (struct l4_xinfo_class *)sock_xinfo->class;
void *laddr = class->get_addr(l4, L4_LOCAL);
void *raddr = class->get_addr(l4, L4_REMOTE);
char local_s[BUFSIZ];
char remote_s[BUFSIZ];
const char *start = class->l3_decorator[L3_DECO_START];
const char *end = class->l3_decorator[L3_DECO_END];
if (!inet_ntop(class->family, laddr, local_s, sizeof(local_s)))
xasprintf(&str, "state=%s", st_str);
else if ((class->is_any_addr(raddr) && tcp->remote_port == 0)
|| !inet_ntop(class->family, raddr, remote_s, sizeof(remote_s)))
xasprintf(&str, "state=%s laddr=%s%s%s:%"PRIu16,
st_str,
start, local_s, end, tcp->local_port);
else
xasprintf(&str, "state=%s laddr=%s%s%s:%"PRIu16" raddr=%s%s%s:%"PRIu16,
st_str,
start, local_s, end, tcp->local_port,
start, remote_s, end, tcp->remote_port);
return str;
}
static char *udp_get_type(struct sock_xinfo *sock_xinfo __attribute__((__unused__)),
struct sock *sock __attribute__((__unused__)))
{
return xstrdup("dgram");
}
static bool udp_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)
{
return l3_fill_column_handler(INET, sock_xinfo, column_id, str)
|| l4_fill_column_handler(UDP, sock_xinfo, column_id, str);
}
static const struct l4_xinfo_class udp_xinfo_class = {
.sock = {
.get_name = udp_get_name,
.get_type = udp_get_type,
.get_state = tcp_get_state,
.get_listening = NULL,
.fill_column = udp_fill_column,
.free = NULL,
},
.scan_line = tcp_xinfo_scan_line,
.get_addr = tcp_xinfo_get_addr,
.is_any_addr = tcp_xinfo_is_any_addr,
.family = AF_INET,
.l3_decorator = {"", ""},
};
static void load_xinfo_from_proc_udp(ino_t netns_inode, enum sysfs_byteorder byteorder)
{
load_xinfo_from_proc_inet_L4(netns_inode,
"/proc/net/udp",
&udp_xinfo_class,
byteorder);
}
/*
* UDP-Lite
*/
static bool udplite_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)
{
return l3_fill_column_handler(INET, sock_xinfo, column_id, str)
|| l4_fill_column_handler(UDPLITE, sock_xinfo, column_id, str);
}
static const struct l4_xinfo_class udplite_xinfo_class = {
.sock = {
.get_name = udp_get_name,
.get_type = udp_get_type,
.get_state = tcp_get_state,
.get_listening = NULL,
.fill_column = udplite_fill_column,
.free = NULL,
},
.scan_line = tcp_xinfo_scan_line,
.get_addr = tcp_xinfo_get_addr,
.is_any_addr = tcp_xinfo_is_any_addr,
.family = AF_INET,
.l3_decorator = {"", ""},
};
static void load_xinfo_from_proc_udplite(ino_t netns_inode, enum sysfs_byteorder byteorder)
{
load_xinfo_from_proc_inet_L4(netns_inode,
"/proc/net/udplite",
&udplite_xinfo_class,
byteorder);
}
/*
* RAW
*/
struct raw_xinfo {
struct l4_xinfo l4;
uint16_t protocol;
};
static char *raw_get_name_common(struct sock_xinfo *sock_xinfo,
struct sock *sock __attribute__((__unused__)),
const char *port_label)
{
char *str = NULL;
struct l4_xinfo_class *class = (struct l4_xinfo_class *)sock_xinfo->class;
struct raw_xinfo *raw = ((struct raw_xinfo *)sock_xinfo);
struct l4_xinfo *l4 = &raw->l4;
const char *st_str = l4_decode_state(l4->st);
void *laddr = class->get_addr(l4, L4_LOCAL);
void *raddr = class->get_addr(l4, L4_REMOTE);
char local_s[BUFSIZ];
char remote_s[BUFSIZ];
if (!inet_ntop(class->family, laddr, local_s, sizeof(local_s)))
xasprintf(&str, "state=%s", st_str);
else if (class->is_any_addr(raddr)
|| !inet_ntop(class->family, raddr, remote_s, sizeof(remote_s)))
xasprintf(&str, "state=%s %s=%"PRIu16" laddr=%s",
st_str,
port_label,
raw->protocol, local_s);
else
xasprintf(&str, "state=%s %s=%"PRIu16" laddr=%s raddr=%s",
st_str,
port_label,
raw->protocol, local_s, remote_s);
return str;
}
static char *raw_get_name(struct sock_xinfo *sock_xinfo,
struct sock *sock __attribute__((__unused__)))
{
return raw_get_name_common(sock_xinfo, sock, "protocol");
}
static char *raw_get_type(struct sock_xinfo *sock_xinfo __attribute__((__unused__)),
struct sock *sock __attribute__((__unused__)))
{
return xstrdup("raw");
}
static bool raw_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)
{
if (l3_fill_column_handler(INET, sock_xinfo, column_id, str))
return true;
if (column_id == COL_RAW_PROTOCOL) {
xasprintf(str, "%"PRIu16,
((struct raw_xinfo *)sock_xinfo)->protocol);
return true;
}
return false;
}
static struct sock_xinfo *raw_xinfo_scan_line(const struct sock_xinfo_class *class,
char * line,
ino_t netns_inode,
enum sysfs_byteorder byteorder)
{
unsigned long local_addr;
unsigned long protocol;
unsigned long remote_addr;
unsigned long st;
unsigned long long inode;
struct raw_xinfo *raw;
struct inet_xinfo *inet;
struct sock_xinfo *sock;
if (sscanf(line, "%*d: %lx:%lx %lx:%*x %lx %*x:%*x %*x:%*x %*x %*u %*u %lld",
&local_addr, &protocol, &remote_addr,
&st, &inode) != 5)
return NULL;
if (inode == 0)
return NULL;
raw = xcalloc(1, sizeof(*raw));
inet = &raw->l4.inet;
sock = &inet->sock;
sock->class = class;
sock->inode = (ino_t)inode;
sock->netns_inode = netns_inode;
inet->local_addr.s_addr = kernel32_to_cpu(byteorder, local_addr);
inet->remote_addr.s_addr = kernel32_to_cpu(byteorder, remote_addr);
raw->protocol = protocol;
raw->l4.st = st;
return sock;
}
static const struct l4_xinfo_class raw_xinfo_class = {
.sock = {
.get_name = raw_get_name,
.get_type = raw_get_type,
.get_state = tcp_get_state,
.get_listening = NULL,
.fill_column = raw_fill_column,
.free = NULL,
},
.scan_line = raw_xinfo_scan_line,
.get_addr = tcp_xinfo_get_addr,
.is_any_addr = tcp_xinfo_is_any_addr,
.family = AF_INET,
.l3_decorator = {"", ""},
};
static void load_xinfo_from_proc_raw(ino_t netns_inode, enum sysfs_byteorder byteorder)
{
load_xinfo_from_proc_inet_L4(netns_inode,
"/proc/net/raw",
&raw_xinfo_class,
byteorder);
}
/*
* PING
*/
static char *ping_get_name(struct sock_xinfo *sock_xinfo,
struct sock *sock __attribute__((__unused__)))
{
return raw_get_name_common(sock_xinfo, sock, "id");
}
static char *ping_get_type(struct sock_xinfo *sock_xinfo __attribute__((__unused__)),
struct sock *sock __attribute__((__unused__)))
{
return xstrdup("dgram");
}
static bool ping_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)
{
if (l3_fill_column_handler(INET, sock_xinfo, column_id, str))
return true;
if (column_id == COL_PING_ID) {
xasprintf(str, "%"PRIu16,
((struct raw_xinfo *)sock_xinfo)->protocol);
return true;
}
return false;
}
static const struct l4_xinfo_class ping_xinfo_class = {
.sock = {
.get_name = ping_get_name,
.get_type = ping_get_type,
.get_state = tcp_get_state,
.get_listening = NULL,
.fill_column = ping_fill_column,
.free = NULL,
},
.scan_line = raw_xinfo_scan_line,
.get_addr = tcp_xinfo_get_addr,
.is_any_addr = tcp_xinfo_is_any_addr,
.family = AF_INET,
.l3_decorator = {"", ""},
};
static void load_xinfo_from_proc_icmp(ino_t netns_inode, enum sysfs_byteorder byteorder)
{
load_xinfo_from_proc_inet_L4(netns_inode,
"/proc/net/icmp",
&ping_xinfo_class,
byteorder);
}
/*
* TCP6
*/
static struct sock_xinfo *tcp6_xinfo_scan_line(const struct sock_xinfo_class *class,
char * line,
ino_t netns_inode,
enum sysfs_byteorder byteorder)
{
uint32_t local_addr[4];
unsigned int local_port;
uint32_t remote_addr[4];
unsigned int remote_port;
unsigned int st;
unsigned long inode;
struct tcp_xinfo *tcp;
struct inet6_xinfo *inet6;
struct sock_xinfo *sock;
if (sscanf(line,
"%*d: "
"%08x%08x%08x%08x:%04x "
"%08x%08x%08x%08x:%04x "
"%x %*x:%*x %*x:%*x %*x %*u %*d %lu ",
local_addr+0, local_addr+1, local_addr+2, local_addr+3, &local_port,
remote_addr+0, remote_addr+1, remote_addr+2, remote_addr+3, &remote_port,
&st, &inode) != 12)
return NULL;
if (inode == 0)
return NULL;
tcp = xmalloc(sizeof(*tcp));
inet6 = &tcp->l4.inet6;
sock = &inet6->sock;
sock->class = class;
sock->inode = (ino_t)inode;
sock->netns_inode = netns_inode;
tcp->local_port = local_port;
for (int i = 0; i < 4; i++) {
inet6->local_addr.s6_addr32[i] = kernel32_to_cpu(byteorder, local_addr[i]);
inet6->remote_addr.s6_addr32[i] = kernel32_to_cpu(byteorder, remote_addr[i]);
}
tcp->remote_port = remote_port;
tcp->l4.st = st;
return sock;
}
static bool tcp6_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)
{
return l3_fill_column_handler(INET6, sock_xinfo, column_id, str)
|| l4_fill_column_handler(TCP, sock_xinfo, column_id, str);
}
static void *tcp6_xinfo_get_addr(struct l4_xinfo * l4, enum l4_side side)
{
return (side == L4_LOCAL)
? &l4->inet6.local_addr
: &l4->inet6.remote_addr;
}
static bool tcp6_xinfo_is_any_addr(void *addr)
{
return IN6_ARE_ADDR_EQUAL(addr, &(struct in6_addr)IN6ADDR_ANY_INIT);
}
static const struct l4_xinfo_class tcp6_xinfo_class = {
.sock = {
.get_name = tcp_get_name,
.get_type = tcp_get_type,
.get_state = tcp_get_state,
.get_listening = tcp_get_listening,
.fill_column = tcp6_fill_column,
.free = NULL,
},
.scan_line = tcp6_xinfo_scan_line,
.get_addr = tcp6_xinfo_get_addr,
.is_any_addr = tcp6_xinfo_is_any_addr,
.family = AF_INET6,
.l3_decorator = {"[", "]"},
};
static void load_xinfo_from_proc_tcp6(ino_t netns_inode, enum sysfs_byteorder byteorder)
{
load_xinfo_from_proc_inet_L4(netns_inode,
"/proc/net/tcp6",
&tcp6_xinfo_class,
byteorder);
}
/*
* UDP6
*/
static bool udp6_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)
{
return l3_fill_column_handler(INET6, sock_xinfo, column_id, str)
|| l4_fill_column_handler(UDP, sock_xinfo, column_id, str);
}
static const struct l4_xinfo_class udp6_xinfo_class = {
.sock = {
.get_name = udp_get_name,
.get_type = udp_get_type,
.get_state = tcp_get_state,
.get_listening = NULL,
.fill_column = udp6_fill_column,
.free = NULL,
},
.scan_line = tcp6_xinfo_scan_line,
.get_addr = tcp6_xinfo_get_addr,
.is_any_addr = tcp6_xinfo_is_any_addr,
.family = AF_INET6,
.l3_decorator = {"[", "]"},
};
static void load_xinfo_from_proc_udp6(ino_t netns_inode, enum sysfs_byteorder byteorder)
{
load_xinfo_from_proc_inet_L4(netns_inode,
"/proc/net/udp6",
&udp6_xinfo_class,
byteorder);
}
/*
* UDPLITEv6
*/
static bool udplite6_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)
{
return l3_fill_column_handler(INET6, sock_xinfo, column_id, str)
|| l4_fill_column_handler(UDPLITE, sock_xinfo, column_id, str);
}
static const struct l4_xinfo_class udplite6_xinfo_class = {
.sock = {
.get_name = udp_get_name,
.get_type = udp_get_type,
.get_state = tcp_get_state,
.get_listening = NULL,
.fill_column = udplite6_fill_column,
.free = NULL,
},
.scan_line = tcp6_xinfo_scan_line,
.get_addr = tcp6_xinfo_get_addr,
.is_any_addr = tcp6_xinfo_is_any_addr,
.family = AF_INET6,
.l3_decorator = {"[", "]"},
};
static void load_xinfo_from_proc_udplite6(ino_t netns_inode, enum sysfs_byteorder byteorder)
{
load_xinfo_from_proc_inet_L4(netns_inode,
"/proc/net/udplite6",
&udplite6_xinfo_class,
byteorder);
}
/*
* RAW6
*/
static struct sock_xinfo *raw6_xinfo_scan_line(const struct sock_xinfo_class *class,
char * line,
ino_t netns_inode,
enum sysfs_byteorder byteorder)
{
uint32_t local_addr[4];
unsigned int protocol;
uint32_t remote_addr[4];
unsigned int st;
unsigned long inode;
struct raw_xinfo *raw;
struct inet6_xinfo *inet6;
struct sock_xinfo *sock;
if (sscanf(line,
"%*d: "
"%08x%08x%08x%08x:%04x "
"%08x%08x%08x%08x:0000 "
"%x %*x:%*x %*x:%*x %*x %*u %*d %lu ",
local_addr+0, local_addr+1, local_addr+2, local_addr+3, &protocol,
remote_addr+0, remote_addr+1, remote_addr+2, remote_addr+3,
&st, &inode) != 11)
return NULL;
if (inode == 0)
return NULL;
raw = xmalloc(sizeof(*raw));
inet6 = &raw->l4.inet6;
sock = &inet6->sock;
sock->class = class;
sock->inode = (ino_t)inode;
sock->netns_inode = netns_inode;
for (int i = 0; i < 4; i++) {
inet6->local_addr.s6_addr32[i] = kernel32_to_cpu(byteorder, local_addr[i]);
inet6->remote_addr.s6_addr32[i] = kernel32_to_cpu(byteorder, remote_addr[i]);
}
raw->protocol = protocol;
raw->l4.st = st;
return sock;
}
static bool raw6_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 raw_xinfo *raw;
if (l3_fill_column_handler(INET6, sock_xinfo, column_id, str))
return true;
raw = (struct raw_xinfo *)sock_xinfo;
if (column_id == COL_RAW_PROTOCOL) {
xasprintf(str, "%"PRIu16, raw->protocol);
return true;
}
return false;
}
static const struct l4_xinfo_class raw6_xinfo_class = {
.sock = {
.get_name = raw_get_name,
.get_type = raw_get_type,
.get_state = tcp_get_state,
.get_listening = NULL,
.fill_column = raw6_fill_column,
.free = NULL,
},
.scan_line = raw6_xinfo_scan_line,
.get_addr = tcp6_xinfo_get_addr,
.is_any_addr = tcp6_xinfo_is_any_addr,
.family = AF_INET6,
.l3_decorator = {"[", "]"},
};
static void load_xinfo_from_proc_raw6(ino_t netns_inode, enum sysfs_byteorder byteorder)
{
load_xinfo_from_proc_inet_L4(netns_inode,
"/proc/net/raw6",
&raw6_xinfo_class,
byteorder);
}
/*
* PINGv6
*/
static bool ping6_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)
{
if (l3_fill_column_handler(INET6, sock_xinfo, column_id, str))
return true;
if (column_id == COL_PING_ID) {
xasprintf(str, "%"PRIu16,
((struct raw_xinfo *)sock_xinfo)->protocol);
return true;
}
return false;
}
static const struct l4_xinfo_class ping6_xinfo_class = {
.sock = {
.get_name = ping_get_name,
.get_type = ping_get_type,
.get_state = tcp_get_state,
.get_listening = NULL,
.fill_column = ping6_fill_column,
.free = NULL,
},
.scan_line = raw6_xinfo_scan_line,
.get_addr = tcp6_xinfo_get_addr,
.is_any_addr = tcp6_xinfo_is_any_addr,
.family = AF_INET6,
.l3_decorator = {"[", "]"},
};
static void load_xinfo_from_proc_icmp6(ino_t netns_inode, enum sysfs_byteorder byteorder)
{
load_xinfo_from_proc_inet_L4(netns_inode,
"/proc/net/icmp6",
&ping6_xinfo_class,
byteorder);
}
/*
* NETLINK
*/
struct netlink_xinfo {
struct sock_xinfo sock;
uint16_t protocol;
uint32_t lportid; /* netlink_diag may provide rportid. */
uint32_t groups;
};
static const char *netlink_decode_protocol(uint16_t protocol)
{
switch (protocol) {
case NETLINK_ROUTE:
return "route";
case NETLINK_UNUSED:
return "unused";
case NETLINK_USERSOCK:
return "usersock";
case NETLINK_FIREWALL:
return "firewall";
case NETLINK_SOCK_DIAG:
return "sock_diag";
case NETLINK_NFLOG:
return "nflog";
case NETLINK_XFRM:
return "xfrm";
case NETLINK_SELINUX:
return "selinux";
case NETLINK_ISCSI:
return "iscsi";
case NETLINK_AUDIT:
return "audit";
case NETLINK_FIB_LOOKUP:
return "fib_lookup";
case NETLINK_CONNECTOR:
return "connector";
case NETLINK_NETFILTER:
return "netfilter";
case NETLINK_IP6_FW:
return "ip6_fw";
case NETLINK_DNRTMSG:
return "dnrtmsg";
case NETLINK_KOBJECT_UEVENT:
return "kobject_uevent";
case NETLINK_GENERIC:
return "generic";
case NETLINK_SCSITRANSPORT:
return "scsitransport";
case NETLINK_ECRYPTFS:
return "ecryptfs";
case NETLINK_RDMA:
return "rdma";
case NETLINK_CRYPTO:
return "crypto";
#ifdef NETLINK_SMC
case NETLINK_SMC:
return "smc";
#endif
default:
return "unknown";
}
}
static char *netlink_get_name(struct sock_xinfo *sock_xinfo,
struct sock *sock __attribute__((__unused__)))
{
struct netlink_xinfo *nl = (struct netlink_xinfo *)sock_xinfo;
char *str = NULL;
const char *protocol = netlink_decode_protocol(nl->protocol);
if (nl->groups)
xasprintf(&str, "protocol=%s lport=%"PRIu16 " groups=%"PRIu32,
protocol,
nl->lportid, nl->groups);
else
xasprintf(&str, "protocol=%s lport=%"PRIu16,
protocol,
nl->lportid);
return str;
}
static char *netlink_get_type(struct sock_xinfo *sock_xinfo __attribute__((__unused__)),
struct sock *sock __attribute__((__unused__)))
{
return xstrdup("raw");
}
static bool netlink_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 netlink_xinfo *nl = (struct netlink_xinfo *)sock_xinfo;
switch (column_id) {
case COL_NETLINK_GROUPS:
xasprintf(str, "%"PRIu32, nl->groups);
return true;
case COL_NETLINK_LPORT:
xasprintf(str, "%"PRIu32, nl->lportid);
return true;
case COL_NETLINK_PROTOCOL:
*str = xstrdup(netlink_decode_protocol(nl->protocol));
return true;
}
return false;
}
static const struct sock_xinfo_class netlink_xinfo_class = {
.get_name = netlink_get_name,
.get_type = netlink_get_type,
.get_state = NULL,
.get_listening = NULL,
.fill_column = netlink_fill_column,
.free = NULL,
};
static void load_xinfo_from_proc_netlink(ino_t netns_inode)
{
char line[BUFSIZ];
FILE *netlink_fp;
netlink_fp = fopen("/proc/net/netlink", "r");
if (!netlink_fp)
return;
if (fgets(line, sizeof(line), netlink_fp) == NULL)
goto out;
if (!(line[0] == 's' && line[1] == 'k'))
/* Unexpected line */
goto out;
while (fgets(line, sizeof(line), netlink_fp)) {
uint16_t protocol;
uint32_t lportid;
uint32_t groups;
unsigned long inode;
struct netlink_xinfo *nl;
if (sscanf(line, "%*x %" SCNu16 " %" SCNu32 " %" SCNx32 " %*d %*d %*d %*d %*u %lu",
&protocol, &lportid, &groups, &inode) < 4)
continue;
if (inode == 0)
continue;
nl = xcalloc(1, sizeof(*nl));
nl->sock.class = &netlink_xinfo_class;
nl->sock.inode = (ino_t)inode;
nl->sock.netns_inode = netns_inode;
nl->protocol = protocol;
nl->lportid = lportid;
nl->groups = groups;
add_sock_info(&nl->sock);
}
out:
fclose(netlink_fp);
}
/*
* PACKET
*/
struct packet_xinfo {
struct sock_xinfo sock;
uint16_t type;
uint16_t protocol;
unsigned int iface;
};
static const char *packet_decode_protocol(uint16_t proto)
{
switch (proto) {
case 0:
return NULL;
case ETH_P_802_3:
return "802_3";
case ETH_P_AX25:
return "ax25";
case ETH_P_ALL:
return "all";
case ETH_P_802_2:
return "802_2";
case ETH_P_SNAP:
return "snap";
case ETH_P_DDCMP:
return "ddcmp";
case ETH_P_WAN_PPP:
return "wan_ppp";
case ETH_P_PPP_MP:
return "ppp_mp";
case ETH_P_LOCALTALK:
return "localtalk";
case ETH_P_CAN:
return "can";
case ETH_P_CANFD:
return "canfd";
#ifdef ETH_P_CANXL
case ETH_P_CANXL:
return "canxl";
#endif
case ETH_P_PPPTALK:
return "ppptalk";
case ETH_P_TR_802_2:
return "tr_802_2";
case ETH_P_MOBITEX:
return "mobitex";
case ETH_P_CONTROL:
return "control";
case ETH_P_IRDA:
return "irda";
case ETH_P_ECONET:
return "econet";
case ETH_P_HDLC:
return "hdlc";
case ETH_P_ARCNET:
return "arcnet";
case ETH_P_DSA:
return "dsa";
case ETH_P_TRAILER:
return "trailer";
case ETH_P_PHONET:
return "phonet";
case ETH_P_IEEE802154:
return "ieee802154";
case ETH_P_CAIF:
return "caif";
#ifdef ETH_P_XDSA
case ETH_P_XDSA:
return "xdsa";
#endif
#ifdef ETH_P_MAP
case ETH_P_MAP:
return "map";
#endif
#ifdef ETH_P_MCTP
case ETH_P_MCTP:
return "mctp";
#endif
case ETH_P_LOOP:
return "loop";
case ETH_P_PUP:
return "pup";
case ETH_P_PUPAT:
return "pupat";
#ifdef ETH_P_TSN
case ETH_P_TSN:
return "tsn";
#endif
#ifdef ETH_P_ERSPAN2
case ETH_P_ERSPAN2:
return "erspan2";
#endif
case ETH_P_IP:
return "ip";
case ETH_P_X25:
return "x25";
case ETH_P_ARP:
return "arp";
case ETH_P_BPQ:
return "bpq";
case ETH_P_IEEEPUP:
return "ieeepup";
case ETH_P_IEEEPUPAT:
return "ieeepupat";
case ETH_P_BATMAN:
return "batman";
case ETH_P_DEC:
return "dec";
case ETH_P_DNA_DL:
return "dna_dl";
case ETH_P_DNA_RC:
return "dna_rc";
case ETH_P_DNA_RT:
return "dna_rt";
case ETH_P_LAT:
return "lat";
case ETH_P_DIAG:
return "diag";
case ETH_P_CUST:
return "cust";
case ETH_P_SCA:
return "sca";
case ETH_P_TEB:
return "teb";
case ETH_P_RARP:
return "rarp";
case ETH_P_ATALK:
return "atalk";
case ETH_P_AARP:
return "aarp";
case ETH_P_8021Q:
return "8021q";
#ifdef ETH_P_ERSPAN
case ETH_P_ERSPAN:
return "erspan";
#endif
case ETH_P_IPX:
return "ipx";
case ETH_P_IPV6:
return "ipv6";
case ETH_P_PAUSE:
return "pause";
case ETH_P_SLOW:
return "slow";
case ETH_P_WCCP:
return "wccp";
case ETH_P_MPLS_UC:
return "mpls_uc";
case ETH_P_MPLS_MC:
return "mpls_mc";
case ETH_P_ATMMPOA:
return "atmmpoa";
#ifdef ETH_P_PPP_DISC
case ETH_P_PPP_DISC:
return "ppp_disc";
#endif
#ifdef ETH_P_PPP_SES
case ETH_P_PPP_SES:
return "ppp_ses";
#endif
case ETH_P_LINK_CTL:
return "link_ctl";
case ETH_P_ATMFATE:
return "atmfate";
case ETH_P_PAE:
return "pae";
#ifdef ETH_P_PROFINET
case ETH_P_PROFINET:
return "profinet";
#endif
#ifdef ETH_P_REALTEK
case ETH_P_REALTEK:
return "realtek";
#endif
case ETH_P_AOE:
return "aoe";
#ifdef ETH_P_ETHERCAT
case ETH_P_ETHERCAT:
return "ethercat";
#endif
case ETH_P_8021AD:
return "8021ad";
case ETH_P_802_EX1:
return "802_ex1";
#ifdef ETH_P_PREAUTH
case ETH_P_PREAUTH:
return "preauth";
#endif
case ETH_P_TIPC:
return "tipc";
#ifdef ETH_P_LLDP
case ETH_P_LLDP:
return "lldp";
#endif
#ifdef ETH_P_MRP
case ETH_P_MRP:
return "mrp";
#endif
#ifdef ETH_P_MACSEC
case ETH_P_MACSEC:
return "macsec";
#endif
case ETH_P_8021AH:
return "8021ah";
#ifdef ETH_P_MVRP
case ETH_P_MVRP:
return "mvrp";
#endif
case ETH_P_1588:
return "1588";
#ifdef ETH_P_NCSI
case ETH_P_NCSI:
return "ncsi";
#endif
#ifdef ETH_P_PRP
case ETH_P_PRP:
return "prp";
#endif
#ifdef ETH_P_CFM
case ETH_P_CFM:
return "cfm";
#endif
case ETH_P_FCOE:
return "fcoe";
#ifdef ETH_P_IBOE
case ETH_P_IBOE:
return "iboe";
#endif
case ETH_P_TDLS:
return "tdls";
case ETH_P_FIP:
return "fip";
#ifdef ETH_P_80221
case ETH_P_80221:
return "80221";
#endif
#ifdef ETH_P_HSR
case ETH_P_HSR:
return "hsr";
#endif
#ifdef ETH_P_NSH
case ETH_P_NSH:
return "nsh";
#endif
#ifdef ETH_P_LOOPBACK
case ETH_P_LOOPBACK:
return "loopback";
#endif
case ETH_P_QINQ1:
return "qinq1";
case ETH_P_QINQ2:
return "qinq2";
case ETH_P_QINQ3:
return "qinq3";
case ETH_P_EDSA:
return "edsa";
#ifdef ETH_P_DSA_8021Q
case ETH_P_DSA_8021Q:
return "dsa_8021q";
#endif
#ifdef ETH_P_DSA_A5PSW
case ETH_P_DSA_A5PSW:
return "dsa_a5psw";
#endif
#ifdef ETH_P_IFE
case ETH_P_IFE:
return "ife";
#endif
case ETH_P_AF_IUCV:
return "af_iucv";
#ifdef ETH_P_802_3_MIN
case ETH_P_802_3_MIN:
return "802_3_min";
#endif
default:
return "unknown";
}
}
static char *packet_get_name(struct sock_xinfo *sock_xinfo,
struct sock *sock __attribute__((__unused__)))
{
struct packet_xinfo *pkt = (struct packet_xinfo *)sock_xinfo;
char *str = NULL;
const char *type = sock_decode_type(pkt->type);
const char *proto = packet_decode_protocol(pkt->protocol);
const char *iface = get_iface_name(sock_xinfo->netns_inode,
pkt->iface);
if (iface && proto)
xasprintf(&str, "type=%s protocol=%s iface=%s",
type, proto, iface);
else if (proto)
xasprintf(&str, "type=%s protocol=%s",
type, proto);
else if (iface)
xasprintf(&str, "type=%s iface=%s",
type, iface);
else
xasprintf(&str, "type=%s", type);
return str;
}
static char *packet_get_type(struct sock_xinfo *sock_xinfo __attribute__((__unused__)),
struct sock *sock __attribute__((__unused__)))
{
const char *str;
struct packet_xinfo *pkt = (struct packet_xinfo *)sock_xinfo;
str = sock_decode_type(pkt->type);
return xstrdup(str);
}
static bool packet_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 packet_xinfo *pkt = (struct packet_xinfo *)sock_xinfo;
switch (column_id) {
case COL_PACKET_IFACE: {
const char *iface;
iface = get_iface_name(sock_xinfo->netns_inode,
pkt->iface);
if (iface) {
*str = xstrdup(iface);
return true;
}
break;
}
case COL_PACKET_PROTOCOL: {
const char *proto;
proto = packet_decode_protocol(pkt->protocol);
if (proto) {
*str = xstrdup(proto);
return true;
}
break;
}
default:
break;
}
return false;
}
static const struct sock_xinfo_class packet_xinfo_class = {
.get_name = packet_get_name,
.get_type = packet_get_type,
.get_state = NULL,
.get_listening = NULL,
.fill_column = packet_fill_column,
.free = NULL,
};
static void load_xinfo_from_proc_packet(ino_t netns_inode)
{
char line[BUFSIZ];
FILE *packet_fp;
packet_fp = fopen("/proc/net/packet", "r");
if (!packet_fp)
return;
if (fgets(line, sizeof(line), packet_fp) == NULL)
goto out;
if (!(line[0] == 's' && line[1] == 'k'))
/* Unexpected line */
goto out;
while (fgets(line, sizeof(line), packet_fp)) {
uint16_t type;
uint16_t protocol;
unsigned int iface;
unsigned long inode;
struct packet_xinfo *pkt;
if (sscanf(line, "%*x %*d %" SCNu16 " %" SCNu16 " %u %*d %*d %*d %lu",
&type, &protocol, &iface, &inode) < 4)
continue;
pkt = xcalloc(1, sizeof(*pkt));
pkt->sock.class = &packet_xinfo_class;
pkt->sock.inode = (ino_t)inode;
pkt->sock.netns_inode = netns_inode;
pkt->type = type;
pkt->protocol = protocol;
pkt->iface = iface;
add_sock_info(&pkt->sock);
}
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);
}