Dmitry V. Levin
2f6510c8a6
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.
173 lines
3.6 KiB
C
173 lines
3.6 KiB
C
#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;
|
|
}
|