From: Masatake YAMATO Date: Fri, 21 Jul 2023 19:02:38 +0000 (+0900) Subject: lsfd: re-fill unix socket paths with sockdiag netlink interface X-Git-Tag: v2.40-rc1~321^2~1 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=5468bb453c244531d7fa8424569bdb5b35267ea3;p=thirdparty%2Futil-linux.git lsfd: re-fill unix socket paths with sockdiag netlink interface This commit is mostly based on Thomas Weißschuh's work, https://github.com/t-8ch/util-linux/commit/06030390a5e6a16cc4b914bbf5fcedd3b6d83608. Unlike the original work, this commit keeps /proc related code. Unlike /proc interface, the sockdiag information source doesn't provide enough information for filling struct unix_xinfo::st member. So this commit uses /proc interface for filling the most of all unix_xinfo members as before. On the other hande, as discussed in https://github.com/util-linux/util-linux/pull/2067, the /proc interface in unreliable if a unix path name includes newline characters. This commit uses the sockdiag interface to compensate for the weakness of the /proc interface. --- diff --git a/misc-utils/lsfd-sock-xinfo.c b/misc-utils/lsfd-sock-xinfo.c index 0aa5c191ce..65dc1ec467 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 @@ -26,10 +26,15 @@ #include /* if_nametoindex */ #include /* ETH_P_* */ #include /* SS_* */ -#include /* NETLINK_* */ +#include /* NETLINK_*, NLMSG_* */ +#include /* RTA_*, struct rtattr, */ +#include /* SOCK_DIAG_BY_FAMILY */ #include /* UNIX_PATH_MAX */ +#include /* UNIX_DIAG_*, UDIAG_SHOW_*, + struct unix_diag_req */ #include /* for setns(2) */ #include /* tfind, tsearch */ +#include /* alignas */ #include #include #include /* SOCK_* */ @@ -54,6 +59,8 @@ 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 void load_xinfo_from_diag_unix(int diag, ino_t netns_inode); + static int self_netns_fd = -1; static struct stat self_netns_sb; @@ -153,6 +160,7 @@ 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; load_xinfo_from_proc_unix(netns); load_xinfo_from_proc_tcp(netns); @@ -168,6 +176,12 @@ static void load_sock_xinfo_no_nsswitch(struct netns *nsobj) 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); } @@ -316,6 +330,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, + }; + + alignas(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 */ @@ -492,6 +561,76 @@ 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; + + 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; + + 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; + } + } + 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, + }; + + send_diag_request(diagsd, &udr, sizeof(udr), handle_diag_unix, netns); +} + /* * AF_INET */