diff options
Diffstat (limited to 'misc-utils/lsfd-sock-xinfo.c')
-rw-r--r-- | misc-utils/lsfd-sock-xinfo.c | 393 |
1 files changed, 340 insertions, 53 deletions
diff --git a/misc-utils/lsfd-sock-xinfo.c b/misc-utils/lsfd-sock-xinfo.c index 61b8607..a293806 100644 --- a/misc-utils/lsfd-sock-xinfo.c +++ b/misc-utils/lsfd-sock-xinfo.c @@ -1,5 +1,5 @@ /* - * lsfd-sock-xinfo.c - read various information from files under /proc/net/ + * 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> @@ -26,37 +26,40 @@ #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/netlink.h> /* NETLINK_*, NLMSG_* */ +#include <linux/rtnetlink.h> /* RTA_*, struct rtattr, */ +#include <linux/sock_diag.h> /* SOCK_DIAG_BY_FAMILY */ #include <linux/un.h> /* UNIX_PATH_MAX */ +#include <linux/unix_diag.h> /* UNIX_DIAG_*, UDIAG_SHOW_*, + struct unix_diag_req */ #include <sched.h> /* for setns(2) */ -#include <search.h> +#include <search.h> /* tfind, tsearch */ #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_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); -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_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 int self_netns_fd = -1; static struct stat self_netns_sb; @@ -158,21 +161,29 @@ static struct netns *mark_sock_xinfo_loaded(ino_t ino) 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); - 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_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); + if (diagsd >= 0) { + load_xinfo_from_diag_unix(diagsd, netns); + close(diagsd); + } + if (nsobj) load_ifaces_from_getifaddrs(nsobj); } @@ -214,8 +225,12 @@ void initialize_sock_xinfos(void) 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"); } } @@ -281,9 +296,9 @@ static void add_sock_info(struct sock_xinfo *xinfo) errx(EXIT_FAILURE, _("failed to allocate memory")); } -struct sock_xinfo *get_sock_xinfo(ino_t netns_inode) +struct sock_xinfo *get_sock_xinfo(ino_t inode) { - struct sock_xinfo key = { .inode = netns_inode }; + struct sock_xinfo key = { .inode = inode }; struct sock_xinfo **xinfo = tfind(&key, &xinfo_tree, xinfo_compare); if (xinfo) @@ -318,6 +333,61 @@ static const char *sock_decode_type(uint16_t type) } } +static void send_diag_request(int diagsd, void *req, size_t req_size, + bool (*cb)(ino_t, size_t, void *), + ino_t netns) +{ + 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]; + + if (sendmsg(diagsd, &mhd, 0) < 0) + return; + + for (;;) { + const struct nlmsghdr *h; + int r = recvfrom(diagsd, buf, sizeof(buf), 0, NULL, NULL); + if (r < 0) + return; + + h = (void *) buf; + 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) + return; + if (h->nlmsg_type == NLMSG_ERROR) + return; + + if (h->nlmsg_type == SOCK_DIAG_BY_FAMILY) { + if (!cb(netns, h->nlmsg_len, NLMSG_DATA(h))) + return; + } + } + } +} + /* * Protocol specific code */ @@ -325,11 +395,21 @@ static const char *sock_decode_type(uint16_t type) /* * 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 @ */ @@ -407,15 +487,98 @@ static bool unix_get_listening(struct sock_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_dumy_sock() provides the way to make a + * dummy sock struct for the inode. + */ +static void unix_make_dumy_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_dumy_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 __attribute__((__unused__)), + 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: @@ -424,6 +587,30 @@ static bool unix_fill_column(struct proc *proc __attribute__((__unused__)), 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; @@ -435,6 +622,7 @@ static const struct sock_xinfo_class unix_xinfo_class = { .get_state = unix_get_state, .get_listening = unix_get_listening, .fill_column = unix_fill_column, + .get_ipc_class = unix_get_ipc_class, .free = NULL, }; @@ -494,6 +682,96 @@ static void load_xinfo_from_proc_unix(ino_t netns_inode) 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; + + if (nlmsg_len < NLMSG_LENGTH(sizeof(*diag))) + return false; + + inode = (ino_t)diag->udiag_ino; + xinfo = get_sock_xinfo(inode); + + if (xinfo == NULL) + /* The socket is found in the diag response + but not in the proc fs. */ + return true; + + if (xinfo->class != &unix_xinfo_class) + return true; + unix_xinfo = (struct unix_xinfo *)xinfo; + + rta_len = nlmsg_len - NLMSG_LENGTH(sizeof(*diag)); + 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); + + 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 */ @@ -548,7 +826,7 @@ enum l4_state { static const char *l4_decode_state(enum l4_state st) { - const char * table [] = { + const char * const table [] = { [TCP_ESTABLISHED] = "established", [TCP_SYN_SENT] = "syn-sent", [TCP_SYN_RECV] = "syn-recv", @@ -813,7 +1091,8 @@ static bool L4_verify_initial_line(const char *line) #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) + const struct l4_xinfo_class *class, + enum sysfs_byteorder byteorder) { char line[TCP_LINE_LEN]; FILE *tcp_fp; @@ -828,8 +1107,6 @@ static void load_xinfo_from_proc_inet_L4(ino_t netns_inode, const char *proc_fil /* 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) @@ -840,11 +1117,12 @@ static void load_xinfo_from_proc_inet_L4(ino_t netns_inode, const char *proc_fil fclose(tcp_fp); } -static void load_xinfo_from_proc_tcp(ino_t netns_inode) +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); + &tcp_xinfo_class, + byteorder); } /* @@ -915,11 +1193,12 @@ static const struct l4_xinfo_class udp_xinfo_class = { .l3_decorator = {"", ""}, }; -static void load_xinfo_from_proc_udp(ino_t netns_inode) +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); + &udp_xinfo_class, + byteorder); } /* @@ -953,11 +1232,12 @@ static const struct l4_xinfo_class udplite_xinfo_class = { .l3_decorator = {"", ""}, }; -static void load_xinfo_from_proc_udplite(ino_t netns_inode) +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); + &udplite_xinfo_class, + byteorder); } /* @@ -1082,11 +1362,12 @@ static const struct l4_xinfo_class raw_xinfo_class = { .l3_decorator = {"", ""}, }; -static void load_xinfo_from_proc_raw(ino_t netns_inode) +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); + &raw_xinfo_class, + byteorder); } /* @@ -1140,11 +1421,12 @@ static const struct l4_xinfo_class ping_xinfo_class = { .l3_decorator = {"", ""}, }; -static void load_xinfo_from_proc_icmp(ino_t netns_inode) +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); + &ping_xinfo_class, + byteorder); } /* @@ -1235,11 +1517,12 @@ static const struct l4_xinfo_class tcp6_xinfo_class = { .l3_decorator = {"[", "]"}, }; -static void load_xinfo_from_proc_tcp6(ino_t netns_inode) +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); + &tcp6_xinfo_class, + byteorder); } /* @@ -1273,11 +1556,12 @@ static const struct l4_xinfo_class udp6_xinfo_class = { .l3_decorator = {"[", "]"}, }; -static void load_xinfo_from_proc_udp6(ino_t netns_inode) +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); + &udp6_xinfo_class, + byteorder); } /* @@ -1311,11 +1595,12 @@ static const struct l4_xinfo_class udplite6_xinfo_class = { .l3_decorator = {"[", "]"}, }; -static void load_xinfo_from_proc_udplite6(ino_t netns_inode) +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); + &udplite6_xinfo_class, + byteorder); } /* @@ -1402,11 +1687,12 @@ static const struct l4_xinfo_class raw6_xinfo_class = { .l3_decorator = {"[", "]"}, }; -static void load_xinfo_from_proc_raw6(ino_t netns_inode) +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); + &raw6_xinfo_class, + byteorder); } /* @@ -1448,11 +1734,12 @@ static const struct l4_xinfo_class ping6_xinfo_class = { .l3_decorator = {"[", "]"}, }; -static void load_xinfo_from_proc_icmp6(ino_t netns_inode) +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); + &ping6_xinfo_class, + byteorder); } /* |