summaryrefslogtreecommitdiffstats
path: root/misc-utils/lsfd-sock-xinfo.c
diff options
context:
space:
mode:
Diffstat (limited to 'misc-utils/lsfd-sock-xinfo.c')
-rw-r--r--misc-utils/lsfd-sock-xinfo.c2025
1 files changed, 2025 insertions, 0 deletions
diff --git a/misc-utils/lsfd-sock-xinfo.c b/misc-utils/lsfd-sock-xinfo.c
new file mode 100644
index 0000000..61b8607
--- /dev/null
+++ b/misc-utils/lsfd-sock-xinfo.c
@@ -0,0 +1,2025 @@
+/*
+ * lsfd-sock-xinfo.c - read various information from files under /proc/net/
+ *
+ * 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_* */
+#include <linux/un.h> /* UNIX_PATH_MAX */
+#include <sched.h> /* for setns(2) */
+#include <search.h>
+#include <stdint.h>
+#include <string.h>
+#include <sys/socket.h> /* SOCK_* */
+
+#include "xalloc.h"
+#include "nls.h"
+#include "libsmartcols.h"
+#include "sysfs.h"
+#include "bitops.h"
+
+#include "lsfd.h"
+#include "lsfd-sock.h"
+
+static void load_xinfo_from_proc_icmp(ino_t netns_inode);
+static void load_xinfo_from_proc_icmp6(ino_t netns_inode);
+static void load_xinfo_from_proc_unix(ino_t netns_inode);
+static void load_xinfo_from_proc_raw(ino_t netns_inode);
+static void load_xinfo_from_proc_tcp(ino_t netns_inode);
+static void load_xinfo_from_proc_udp(ino_t netns_inode);
+static void load_xinfo_from_proc_udplite(ino_t netns_inode);
+static void load_xinfo_from_proc_tcp6(ino_t netns_inode);
+static void load_xinfo_from_proc_udp6(ino_t netns_inode);
+static void load_xinfo_from_proc_udplite6(ino_t netns_inode);
+static void load_xinfo_from_proc_raw6(ino_t netns_inode);
+static void load_xinfo_from_proc_netlink(ino_t netns_inode);
+static void load_xinfo_from_proc_packet(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;
+
+ load_xinfo_from_proc_unix(netns);
+ load_xinfo_from_proc_tcp(netns);
+ load_xinfo_from_proc_udp(netns);
+ load_xinfo_from_proc_udplite(netns);
+ load_xinfo_from_proc_raw(netns);
+ load_xinfo_from_proc_tcp6(netns);
+ load_xinfo_from_proc_udp6(netns);
+ load_xinfo_from_proc_udplite6(netns);
+ load_xinfo_from_proc_raw6(netns);
+ load_xinfo_from_proc_icmp(netns);
+ load_xinfo_from_proc_icmp6(netns);
+ load_xinfo_from_proc_netlink(netns);
+ load_xinfo_from_proc_packet(netns);
+
+ 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 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) {
+ struct netns *nsobj = mark_sock_xinfo_loaded(self_netns_sb.st_ino);
+ load_sock_xinfo_no_nsswitch(nsobj);
+ }
+ }
+
+ /* 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 netns_inode)
+{
+ struct sock_xinfo key = { .inode = netns_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";
+ }
+}
+
+/*
+ * Protocol specific code
+ */
+
+/*
+ * UNIX
+ */
+struct unix_xinfo {
+ struct sock_xinfo sock;
+ int acceptcon; /* flags */
+ uint16_t type;
+ uint8_t st;
+ 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 bool unix_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 unix_xinfo *ux = (struct unix_xinfo *)sock_xinfo;
+
+ switch (column_id) {
+ case COL_UNIX_PATH:
+ if (*ux->path) {
+ *str = xstrdup(ux->path);
+ 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,
+ .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");
+ 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 };
+
+
+ if (sscanf(line, "%*x: %*x %*x %" SCNx64 " %x %x %lu %"
+ stringify_value(UNIX_LINE_LEN) "[^\n]",
+ &flags, &type, &st, &inode, path) < 4)
+ continue;
+
+ 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));
+
+ add_sock_info(&ux->sock);
+ }
+
+ out:
+ fclose(unix_fp);
+}
+
+/*
+ * 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 * 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)
+{
+ 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;
+
+ enum sysfs_byteorder byteorder = sysfs_get_byteorder(NULL);
+
+ 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)
+{
+ load_xinfo_from_proc_inet_L4(netns_inode,
+ "/proc/net/tcp",
+ &tcp_xinfo_class);
+}
+
+/*
+ * 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)
+{
+ load_xinfo_from_proc_inet_L4(netns_inode,
+ "/proc/net/udp",
+ &udp_xinfo_class);
+}
+
+/*
+ * 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)
+{
+ load_xinfo_from_proc_inet_L4(netns_inode,
+ "/proc/net/udplite",
+ &udplite_xinfo_class);
+}
+
+/*
+ * 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)
+{
+ load_xinfo_from_proc_inet_L4(netns_inode,
+ "/proc/net/raw",
+ &raw_xinfo_class);
+}
+
+/*
+ * 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)
+{
+ load_xinfo_from_proc_inet_L4(netns_inode,
+ "/proc/net/icmp",
+ &ping_xinfo_class);
+}
+
+/*
+ * 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)
+{
+ load_xinfo_from_proc_inet_L4(netns_inode,
+ "/proc/net/tcp6",
+ &tcp6_xinfo_class);
+}
+
+/*
+ * 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)
+{
+ load_xinfo_from_proc_inet_L4(netns_inode,
+ "/proc/net/udp6",
+ &udp6_xinfo_class);
+}
+
+/*
+ * 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)
+{
+ load_xinfo_from_proc_inet_L4(netns_inode,
+ "/proc/net/udplite6",
+ &udplite6_xinfo_class);
+}
+
+/*
+ * 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)
+{
+ load_xinfo_from_proc_inet_L4(netns_inode,
+ "/proc/net/raw6",
+ &raw6_xinfo_class);
+}
+
+/*
+ * 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)
+{
+ load_xinfo_from_proc_inet_L4(netns_inode,
+ "/proc/net/icmp6",
+ &ping6_xinfo_class);
+}
+
+/*
+ * 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);
+}