]> granicus.if.org Git - strace/commitdiff
Add -yy option: print ip and port associated with socket descriptors
authorDmitry V. Levin <ldv@altlinux.org>
Thu, 21 Aug 2014 03:17:48 +0000 (03:17 +0000)
committerDmitry V. Levin <ldv@altlinux.org>
Wed, 17 Sep 2014 20:03:38 +0000 (20:03 +0000)
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
defs.h
linux/inet_diag.h [new file with mode: 0644]
linux/sock_diag.h [new file with mode: 0644]
socketutils.c [new file with mode: 0644]
strace.1
strace.c
util.c

index 7dfdf2c80fe722be8cb936a284b4f117fd61df14..a84375dcc900f11d9ed8a3fb0dc1581f573fec03 100644 (file)
@@ -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 396e519634a98a66874e4caf717e87e72cc522c3..21c7b2e6ce5c4a1b630590432940d999743fe1ec 100644 (file)
--- 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 (file)
index 0000000..723a1b1
--- /dev/null
@@ -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 (file)
index 0000000..e5dd066
--- /dev/null
@@ -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 (file)
index 0000000..d5cddf0
--- /dev/null
@@ -0,0 +1,172 @@
+#include "defs.h"
+#include <netinet/in.h>
+#include <sys/socket.h>
+#include <arpa/inet.h>
+#include <linux/netlink.h>
+#include <linux/sock_diag.h>
+#include <linux/inet_diag.h>
+
+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;
+}
index 2a24c38304cf1124fd26b24ec9e9a88d756d003d..1e69217f35c43c7caa1cf96f3f4e1652cc48a94d 100644 (file)
--- 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
index 0ef44aa116c78a3f0d8e9b0d7a200e2ab344fddf..cb9675856fda13360e735e50a2347a83397ffe3f 100644 (file)
--- 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 c8dc5914bc09d27c4bf09feb051964788c1790a1..f10c011cefb5a97e1fc3e6388e407923261afbbf 100644 (file)
--- 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);
 }