From 2f6510c8a6a358ec00f56a491318181bc068a1fc Mon Sep 17 00:00:00 2001 From: "Dmitry V. Levin" Date: Thu, 21 Aug 2014 03:17:48 +0000 Subject: [PATCH] Add -yy option: print ip and port associated with socket descriptors When two ore more -y options are given, print local and remote ip:port pairs associated with socket descriptors. This implementation uses NETLINK_INET_DIAG for sockaddr lookup; it's based on the patch prepared by Zubin Mithra as a part of his GSoC 2014 strace project. * Makefile.am (strace_SOURCES): Add socketutils.c (EXTRA_DIST): Add linux/inet_diag.h and linux/sock_diag.h. * defs.h (print_sockaddr_by_inode): New prototype. * linux/inet_diag.h: New file. * linux/sock_diag.h: Likewise. * socketutils.c: Likewise. * strace.1: Document -yy option. * strace.c (usage): Likewise. * util.c (printfd): Use print_sockaddr_by_inode. --- Makefile.am | 3 + defs.h | 1 + linux/inet_diag.h | 38 ++++++++++ linux/sock_diag.h | 6 ++ socketutils.c | 172 ++++++++++++++++++++++++++++++++++++++++++++++ strace.1 | 3 + strace.c | 1 + util.c | 22 ++++-- 8 files changed, 242 insertions(+), 4 deletions(-) create mode 100644 linux/inet_diag.h create mode 100644 linux/sock_diag.h create mode 100644 socketutils.c diff --git a/Makefile.am b/Makefile.am index 7dfdf2c8..a84375dc 100644 --- a/Makefile.am +++ b/Makefile.am @@ -46,6 +46,7 @@ strace_SOURCES = \ scsi.c \ signal.c \ sock.c \ + socketutils.c \ strace.c \ stream.c \ syscall.c \ @@ -122,6 +123,7 @@ EXTRA_DIST = \ linux/ia64/ioctlent.h.in \ linux/ia64/signalent.h \ linux/ia64/syscallent.h \ + linux/inet_diag.h \ linux/inotify.h \ linux/ioctlent-filter.awk \ linux/ioctlent.h.in \ @@ -165,6 +167,7 @@ EXTRA_DIST = \ linux/sh64/ioctlent.h.in \ linux/sh64/syscallent.h \ linux/signalent.h \ + linux/sock_diag.h \ linux/sparc/dummy2.h \ linux/sparc/errnoent.h \ linux/sparc/errnoent1.h \ diff --git a/defs.h b/defs.h index 396e5196..21c7b2e6 100644 --- a/defs.h +++ b/defs.h @@ -694,6 +694,7 @@ extern void printsiginfo(siginfo_t *, int); extern void printsiginfo_at(struct tcb *tcp, long addr); #endif extern void printfd(struct tcb *, int); +extern bool print_sockaddr_by_inode(const unsigned long); extern void print_dirfd(struct tcb *, int); extern void printsock(struct tcb *, long, int); extern void print_sock_optmgmt(struct tcb *, long, int); diff --git a/linux/inet_diag.h b/linux/inet_diag.h new file mode 100644 index 00000000..723a1b18 --- /dev/null +++ b/linux/inet_diag.h @@ -0,0 +1,38 @@ +#define TCPDIAG_GETSOCK 18 +#define DCCPDIAG_GETSOCK 19 + +/* Socket identity */ +struct inet_diag_sockid { + uint16_t idiag_sport; + uint16_t idiag_dport; + uint32_t idiag_src[4]; + uint32_t idiag_dst[4]; + uint32_t idiag_if; + uint32_t idiag_cookie[2]; +}; + +/* Request structure */ +struct inet_diag_req_v2 { + uint8_t sdiag_family; + uint8_t sdiag_protocol; + uint8_t idiag_ext; + uint8_t pad; + uint32_t idiag_states; + struct inet_diag_sockid id; +}; + +/* Info structure */ +struct inet_diag_msg { + uint8_t idiag_family; + uint8_t idiag_state; + uint8_t idiag_timer; + uint8_t idiag_retrans; + + struct inet_diag_sockid id; + + uint32_t idiag_expires; + uint32_t idiag_rqueue; + uint32_t idiag_wqueue; + uint32_t idiag_uid; + uint32_t idiag_inode; +}; diff --git a/linux/sock_diag.h b/linux/sock_diag.h new file mode 100644 index 00000000..e5dd0668 --- /dev/null +++ b/linux/sock_diag.h @@ -0,0 +1,6 @@ +#define SOCK_DIAG_BY_FAMILY 20 + +struct sock_diag_req { + uint8_t sdiag_family; + uint8_t sdiag_protocol; +}; diff --git a/socketutils.c b/socketutils.c new file mode 100644 index 00000000..d5cddf0c --- /dev/null +++ b/socketutils.c @@ -0,0 +1,172 @@ +#include "defs.h" +#include +#include +#include +#include +#include +#include + +static bool +send_query(const int fd, const int family, const int proto) +{ + struct sockaddr_nl nladdr; + struct { + struct nlmsghdr nlh; + struct inet_diag_req_v2 idr; + } req; + struct iovec iov = { + .iov_base = &req, + .iov_len = sizeof(req) + }; + struct msghdr msg = { + .msg_name = (void*)&nladdr, + .msg_namelen = sizeof(nladdr), + .msg_iov = &iov, + .msg_iovlen = 1 + }; + + memset(&nladdr, 0, sizeof(nladdr)); + nladdr.nl_family = AF_NETLINK; + + memset(&req, 0, sizeof(req)); + req.nlh.nlmsg_len = sizeof(req); + req.nlh.nlmsg_type = SOCK_DIAG_BY_FAMILY; + req.nlh.nlmsg_flags = NLM_F_DUMP | NLM_F_REQUEST; + req.idr.sdiag_family = family; + req.idr.sdiag_protocol = proto; + req.idr.idiag_states = -1; + + for (;;) { + if (sendmsg(fd, &msg, 0) < 0) { + if (errno == EINTR) + continue; + return false; + } + return true; + } +} + +static bool +parse_response(const struct inet_diag_msg *diag_msg, const unsigned long inode) +{ + static const char zero_addr[sizeof(struct in6_addr)]; + socklen_t addr_size, text_size; + + if (diag_msg->idiag_inode != inode) + return false; + + switch(diag_msg->idiag_family) { + case AF_INET: + addr_size = sizeof(struct in_addr); + text_size = INET_ADDRSTRLEN; + break; + case AF_INET6: + addr_size = sizeof(struct in6_addr); + text_size = INET6_ADDRSTRLEN; + break; + default: + return false; + } + + char src_buf[text_size]; + + if (!inet_ntop(diag_msg->idiag_family, diag_msg->id.idiag_src, + src_buf, text_size)) + return false; + + if (diag_msg->id.idiag_dport || + memcmp(zero_addr, diag_msg->id.idiag_dst, addr_size)) { + char dst_buf[text_size]; + + if (!inet_ntop(diag_msg->idiag_family, diag_msg->id.idiag_dst, + dst_buf, text_size)) + return false; + + tprintf("%s:%u->%s:%u", + src_buf, ntohs(diag_msg->id.idiag_sport), + dst_buf, ntohs(diag_msg->id.idiag_dport)); + } else { + tprintf("%s:%u", src_buf, ntohs(diag_msg->id.idiag_sport)); + } + + return true; +} + +static bool +receive_responses(const int fd, const unsigned long inode) +{ + static char buf[8192]; + struct sockaddr_nl nladdr; + struct iovec iov = { + .iov_base = buf, + .iov_len = sizeof(buf) + }; + + memset(&nladdr, 0, sizeof(nladdr)); + nladdr.nl_family = AF_NETLINK; + + for (;;) { + ssize_t ret; + struct nlmsghdr *h; + struct msghdr msg = { + .msg_name = (void*)&nladdr, + .msg_namelen = sizeof(nladdr), + .msg_iov = &iov, + .msg_iovlen = 1, + .msg_control = NULL, + .msg_controllen = 0, + .msg_flags = 0 + }; + + ret = recvmsg(fd, &msg, 0); + if (ret < 0) { + if (errno == EINTR) + continue; + return false; + } + if (!ret) + return false; + for (h = (struct nlmsghdr*)buf; + NLMSG_OK(h, ret); + h = NLMSG_NEXT(h, ret)) { + switch (h->nlmsg_type) { + case NLMSG_DONE: + case NLMSG_ERROR: + return false; + } + if (parse_response(NLMSG_DATA(h), inode)) + return true; + } + } +} + +/* Given an inode number of a socket, print out the details + * of the ip address and port. */ +bool +print_sockaddr_by_inode(const unsigned long inode) +{ + const int families[] = {AF_INET, AF_INET6}; + const int protocols[] = {IPPROTO_TCP, IPPROTO_UDP}; + const size_t flen = ARRAY_SIZE(families); + const size_t plen = ARRAY_SIZE(protocols); + size_t fi, pi; + int fd; + + fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_INET_DIAG); + if (fd < 0) + return false; + + for (fi = 0; fi < flen; ++fi) { + for (pi = 0; pi < plen; ++pi) { + if (!send_query(fd, families[fi], protocols[pi])) + continue; + if (receive_responses(fd, inode)) { + close(fd); + return true; + } + } + } + + close(fd); + return false; +} diff --git a/strace.1 b/strace.1 index 2a24c383..1e69217f 100644 --- a/strace.1 +++ b/strace.1 @@ -321,6 +321,9 @@ Print all strings in hexadecimal string format. .B \-y Print paths associated with file descriptor arguments. .TP +.B \-yy +Print ip:port pairs associated with socket file descriptors. +.TP .BI "\-a " column Align return values in a specific column (default column 40). .TP diff --git a/strace.c b/strace.c index 0ef44aa1..cb967585 100644 --- a/strace.c +++ b/strace.c @@ -216,6 +216,7 @@ usage: strace [-CdffhiqrtttTvVxxy] [-I n] [-e expr]...\n\ -v -- verbose mode: print unabbreviated argv, stat, termios, etc. args\n\ -x -- print non-ascii strings in hex, -xx -- print all strings in hex\n\ -y -- print paths associated with file descriptor arguments\n\ +-yy -- print ip:port pairs associated with socket file descriptors\n\ -h -- print help message, -V -- print version\n\ -a column -- alignment COLUMN for printing syscall results (default %d)\n\ -b execve -- detach on this syscall\n\ diff --git a/util.c b/util.c index c8dc5914..f10c011c 100644 --- a/util.c +++ b/util.c @@ -404,10 +404,24 @@ void printfd(struct tcb *tcp, int fd) { char path[PATH_MAX + 1]; - - if (show_fd_path && getfdpath(tcp, fd, path, sizeof(path)) >= 0) - tprintf("%d<%s>", fd, path); - else + if (show_fd_path && getfdpath(tcp, fd, path, sizeof(path)) >= 0) { + static const char socket_prefix[] = "socket:["; + const size_t socket_prefix_len = sizeof(socket_prefix) - 1; + size_t path_len; + + if (show_fd_path > 1 && + strncmp(path, socket_prefix, socket_prefix_len) == 0 && + path[(path_len = strlen(path)) - 1] == ']') { + unsigned long inodenr; + inodenr = strtoul(path + socket_prefix_len, NULL, 10); + tprintf("%d<", fd); + if (!print_sockaddr_by_inode(inodenr)) + tprints(path); + tprints(">"); + } else { + tprintf("%d<%s>", fd, path); + } + } else tprintf("%d", fd); } -- 2.40.0