]> granicus.if.org Git - strace/blobdiff - socketutils.c
Introduce HAVE_STRUCT_TCB_EXT_ARG macro
[strace] / socketutils.c
index 6e56228b73a62afa9eb7966739cf1ca5386c1f96..5d8d3ed98802b9c17be185c0c17e238f0f5daa49 100644 (file)
@@ -34,7 +34,9 @@
 #include <linux/sock_diag.h>
 #include <linux/inet_diag.h>
 #include <linux/unix_diag.h>
+#include <linux/netlink_diag.h>
 #include <linux/rtnetlink.h>
+#include "xlat/netlink_protocols.h"
 
 #if !defined NETLINK_SOCK_DIAG && defined NETLINK_INET_DIAG
 # define NETLINK_SOCK_DIAG NETLINK_INET_DIAG
@@ -55,7 +57,7 @@ static cache_entry cache[CACHE_SIZE];
 #define CACHE_MASK (CACHE_SIZE - 1)
 
 static int
-cache_and_print_inode_details(const unsigned long inode, char *details)
+cache_and_print_inode_details(const unsigned long inode, char *const details)
 {
        cache_entry *e = &cache[inode & CACHE_MASK];
        free(e->details);
@@ -69,7 +71,7 @@ cache_and_print_inode_details(const unsigned long inode, char *details)
 bool
 print_sockaddr_by_inode_cached(const unsigned long inode)
 {
-       const cache_entry *e = &cache[inode & CACHE_MASK];
+       const cache_entry *const e = &cache[inode & CACHE_MASK];
        if (e && inode == e->inode) {
                tprints(e->details);
                return true;
@@ -78,32 +80,17 @@ print_sockaddr_by_inode_cached(const unsigned long inode)
 }
 
 static bool
-inet_send_query(const int fd, const int family, const int proto)
+send_query(const int fd, void *req, size_t req_size)
 {
        struct sockaddr_nl nladdr = {
                .nl_family = AF_NETLINK
        };
-       struct {
-               struct nlmsghdr nlh;
-               struct inet_diag_req_v2 idr;
-       } req = {
-               .nlh = {
-                       .nlmsg_len = sizeof(req),
-                       .nlmsg_type = SOCK_DIAG_BY_FAMILY,
-                       .nlmsg_flags = NLM_F_DUMP | NLM_F_REQUEST
-               },
-               .idr = {
-                       .sdiag_family = family,
-                       .sdiag_protocol = proto,
-                       .idiag_states = -1
-               }
-       };
        struct iovec iov = {
-               .iov_base = &req,
-               .iov_len = sizeof(req)
+               .iov_base = req,
+               .iov_len = req_size
        };
-       struct msghdr msg = {
-               .msg_name = (void *) &nladdr,
+       const struct msghdr msg = {
+               .msg_name = &nladdr,
                .msg_namelen = sizeof(nladdr),
                .msg_iov = &iov,
                .msg_iovlen = 1
@@ -119,11 +106,32 @@ inet_send_query(const int fd, const int family, const int proto)
        }
 }
 
+static bool
+inet_send_query(const int fd, const int family, const int proto)
+{
+       struct {
+               const struct nlmsghdr nlh;
+               const struct inet_diag_req_v2 idr;
+       } req = {
+               .nlh = {
+                       .nlmsg_len = sizeof(req),
+                       .nlmsg_type = SOCK_DIAG_BY_FAMILY,
+                       .nlmsg_flags = NLM_F_DUMP | NLM_F_REQUEST
+               },
+               .idr = {
+                       .sdiag_family = family,
+                       .sdiag_protocol = proto,
+                       .idiag_states = -1
+               }
+       };
+       return send_query(fd, &req, sizeof(req));
+}
+
 static int
-inet_parse_response(const char *proto_name, const void *data,
+inet_parse_response(const char *const proto_name, const void *const data,
                    const int data_len, const unsigned long inode)
 {
-       const struct inet_diag_msg *diag_msg = data;
+       const struct inet_diag_msg *const diag_msg = data;
        static const char zero_addr[sizeof(struct in6_addr)];
        socklen_t addr_size, text_size;
 
@@ -179,40 +187,43 @@ receive_responses(const int fd, const unsigned long inode,
                  int (* parser) (const char *, const void *,
                                  int, unsigned long))
 {
-       static long buf[8192 / sizeof(long)];
+       static union {
+               struct nlmsghdr hdr;
+               long buf[8192 / sizeof(long)];
+       } hdr_buf;
+
        struct sockaddr_nl nladdr = {
                .nl_family = AF_NETLINK
        };
        struct iovec iov = {
-               .iov_base = buf,
-               .iov_len = sizeof(buf)
+               .iov_base = hdr_buf.buf,
+               .iov_len = sizeof(hdr_buf.buf)
        };
        int flags = 0;
 
        for (;;) {
-               ssize_t ret;
                struct msghdr msg = {
-                       .msg_name = (void *) &nladdr,
+                       .msg_name = &nladdr,
                        .msg_namelen = sizeof(nladdr),
                        .msg_iov = &iov,
                        .msg_iovlen = 1
                };
 
-               ret = recvmsg(fd, &msg, flags);
+               ssize_t ret = recvmsg(fd, &msg, flags);
                if (ret < 0) {
                        if (errno == EINTR)
                                continue;
                        return false;
                }
 
-               struct nlmsghdr *h = (struct nlmsghdr *) buf;
+               const struct nlmsghdr *h = &hdr_buf.hdr;
                if (!NLMSG_OK(h, ret))
                        return false;
                for (; NLMSG_OK(h, ret); h = NLMSG_NEXT(h, ret)) {
                        if (h->nlmsg_type != SOCK_DIAG_BY_FAMILY)
                                return false;
-                       int rc = parser(proto_name, NLMSG_DATA(h),
-                                       h->nlmsg_len, inode);
+                       const int rc = parser(proto_name, NLMSG_DATA(h),
+                                             h->nlmsg_len, inode);
                        if (rc > 0)
                                return true;
                        if (rc < 0)
@@ -233,45 +244,23 @@ inet_print(const int fd, const int family, const int protocol,
 static bool
 unix_send_query(const int fd, const unsigned long inode)
 {
-       struct sockaddr_nl nladdr = {
-               .nl_family = AF_NETLINK
-       };
        struct {
-               struct nlmsghdr nlh;
-               struct unix_diag_req udr;
+               const struct nlmsghdr nlh;
+               const struct unix_diag_req udr;
        } req = {
                .nlh = {
                        .nlmsg_len = sizeof(req),
                        .nlmsg_type = SOCK_DIAG_BY_FAMILY,
-                       .nlmsg_flags = NLM_F_REQUEST
+                       .nlmsg_flags = NLM_F_DUMP | NLM_F_REQUEST
                },
                .udr = {
                        .sdiag_family = AF_UNIX,
                        .udiag_ino = inode,
                        .udiag_states = -1,
-                       .udiag_show = UDIAG_SHOW_NAME | UDIAG_SHOW_PEER,
-                       .udiag_cookie = { ~0U, ~0U }
+                       .udiag_show = UDIAG_SHOW_NAME | UDIAG_SHOW_PEER
                }
        };
-       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
-       };
-
-       for (;;) {
-               if (sendmsg(fd, &msg, 0) < 0) {
-                       if (errno == EINTR)
-                               continue;
-                       return false;
-               }
-               return true;
-       }
+       return send_query(fd, &req, sizeof(req));
 }
 
 static int
@@ -352,7 +341,66 @@ unix_parse_response(const char *proto_name, const void *data,
 }
 
 static bool
-unix_print(int fd, const unsigned long inode)
+netlink_send_query(const int fd, const unsigned long inode)
+{
+       struct {
+               const struct nlmsghdr nlh;
+               const struct netlink_diag_req ndr;
+       } req = {
+               .nlh = {
+                       .nlmsg_len = sizeof(req),
+                       .nlmsg_type = SOCK_DIAG_BY_FAMILY,
+                       .nlmsg_flags = NLM_F_DUMP | NLM_F_REQUEST
+               },
+               .ndr = {
+                       .sdiag_family = AF_NETLINK,
+                       .sdiag_protocol = NDIAG_PROTO_ALL,
+                       .ndiag_show = NDIAG_SHOW_MEMINFO
+               }
+       };
+       return send_query(fd, &req, sizeof(req));
+}
+
+static int
+netlink_parse_response(const char *proto_name, const void *data,
+                   const int data_len, const unsigned long inode)
+{
+       const struct netlink_diag_msg *const diag_msg = data;
+       const char *netlink_proto;
+       char *details;
+
+       if (data_len < (int) NLMSG_LENGTH(sizeof(*diag_msg)))
+               return -1;
+       if (diag_msg->ndiag_ino != inode)
+               return 0;
+
+       if (diag_msg->ndiag_family != AF_NETLINK)
+               return -1;
+
+       netlink_proto = xlookup(netlink_protocols,
+                               diag_msg->ndiag_protocol);
+
+       if (netlink_proto) {
+               static const char netlink_prefix[] = "NETLINK_";
+               const size_t netlink_prefix_len =
+                       sizeof(netlink_prefix) -1;
+               if (strncmp(netlink_proto, netlink_prefix,
+                           netlink_prefix_len) == 0)
+                       netlink_proto += netlink_prefix_len;
+               if (asprintf(&details, "%s:[%s:%u]", proto_name,
+                            netlink_proto, diag_msg->ndiag_portid) < 0)
+                       return -1;
+       } else {
+               if (asprintf(&details, "%s:[%u]", proto_name,
+                            (unsigned) diag_msg->ndiag_protocol) < 0)
+                       return -1;
+       }
+
+       return cache_and_print_inode_details(inode, details);
+}
+
+static bool
+unix_print(const int fd, const unsigned long inode)
 {
        return unix_send_query(fd, inode)
                && receive_responses(fd, inode, "UNIX", unix_parse_response);
@@ -382,10 +430,18 @@ udp_v6_print(const int fd, const unsigned long inode)
        return inet_print(fd, AF_INET6, IPPROTO_UDP, inode, "UDPv6");
 }
 
+static bool
+netlink_print(const int fd, const unsigned long inode)
+{
+       return netlink_send_query(fd, inode)
+               && receive_responses(fd, inode, "NETLINK",
+                                    netlink_parse_response);
+}
+
 /* 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 char *proto_name)
+print_sockaddr_by_inode(const unsigned long inode, const char *const proto_name)
 {
        static const struct {
                const char *const name;
@@ -395,7 +451,8 @@ print_sockaddr_by_inode(const unsigned long inode, const char *proto_name)
                { "UDP", udp_v4_print },
                { "TCPv6", tcp_v6_print },
                { "UDPv6", udp_v6_print },
-               { "UNIX", unix_print }
+               { "UNIX", unix_print },
+               { "NETLINK", netlink_print }
        };
 
        const int fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_SOCK_DIAG);