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.
This commit is contained in:
parent
c3a5c01051
commit
2f6510c8a6
@ -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 \
|
||||
|
1
defs.h
1
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);
|
||||
|
38
linux/inet_diag.h
Normal file
38
linux/inet_diag.h
Normal file
@ -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;
|
||||
};
|
6
linux/sock_diag.h
Normal file
6
linux/sock_diag.h
Normal file
@ -0,0 +1,6 @@
|
||||
#define SOCK_DIAG_BY_FAMILY 20
|
||||
|
||||
struct sock_diag_req {
|
||||
uint8_t sdiag_family;
|
||||
uint8_t sdiag_protocol;
|
||||
};
|
172
socketutils.c
Normal file
172
socketutils.c
Normal file
@ -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;
|
||||
}
|
3
strace.1
3
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
|
||||
|
1
strace.c
1
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\
|
||||
|
20
util.c
20
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) {
|
||||
static const char socket_prefix[] = "socket:[";
|
||||
const size_t socket_prefix_len = sizeof(socket_prefix) - 1;
|
||||
size_t path_len;
|
||||
|
||||
if (show_fd_path && getfdpath(tcp, fd, path, sizeof(path)) >= 0)
|
||||
tprintf("%d<%s>", fd, path);
|
||||
else
|
||||
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);
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user