[PATCHv4 1/2] Support unix sockets in -yy option

Masatake YAMATO yamato at redhat.com
Wed Dec 24 11:59:31 UTC 2014


This patch extends -yy option; the peer address of a unix socket can be
printed like an inet socket.

About a listening socket, its socket inode and socket path are printed.
About an accepted socket, its socket inode, the peer inode, and the
socket path are printed.
About a client socket, its socket inode and the peer inode are printed.

An example of server side with netcat:

   $ ./strace -yy -e network nc -l -U /tmp/example.sock
   socket(PF_LOCAL, SOCK_STREAM, 0)        = 3
   setsockopt(3<UNIX:[14728348]>, SOL_SOCKET, SO_REUSEADDR, [1], 4) = 0
   bind(3<UNIX:[14728348]>, {sa_family=AF_LOCAL, sun_path="/tmp/example.sock"}, 19) = 0
   listen(3<UNIX:[14728348,"/tmp/example.sock"]>, 10) = 0
   accept(3<UNIX:[14728348,"/tmp/example.sock"]>, {sa_family=AF_LOCAL, NULL}, [2]) = 4<UNIX:[14727246->14727245,"/tmp/example.sock"]>
   recvfrom(4<UNIX:[14727246->14727245,"/tmp/example.sock"]>, "INPUT\n", 8192, 0, NULL, NULL) = 6
   INPUT

An example of client side with netcat:

   $  ./strace -yy -e network nc -U /tmp/example.sock
   socket(PF_LOCAL, SOCK_STREAM, 0)        = 3
   connect(3<UNIX:[14727245]>, {sa_family=AF_LOCAL, sun_path="/tmp/example.sock"}, 19) = 0
   getsockopt(3<UNIX:[14727245]>, SOL_SOCKET, SO_ERROR, [0], [4]) = 0
   INPUT
   ...
   sendto(3<UNIX:[14727245->14727246]>, "INPUT\n", 6, 0, NULL, 0) = 6

* socketutils.c (unix_print): New function.
  (unix_send_query): New function.
  (inet_send_query): Rename send_query.
  (unix_parse_response): New function.
  (inet_parse_response): Rename parse_response.
  (print_sockaddr_by_inode): Call unix_print.
  Replace NETLINK_INET_DIAG with NETLINK_SOCK_DIAG.
  The values of the both names are the same but
  NETLINK_SOCK_DIAG impresses people as more generic
  name.
  (receive_responses): Add a new argument named parser.
  parser deals with the protocol specific evaluation
  of data parts of diag messages.

* linux/unix_diag.h: New file.

changes in v2 patch:

* Change the output format,
* Remove shrunk version of rtnelink.h.

changes in v3 patch:

* Use uintN_t instead of _uN (N = 8, 16, and 32) in unix_diag_req definition.
* Remove incorrect comment about including file.
* Define UNIX_PATH_MAX as an value calculated from sockaddr_un.
* Remove unnecessary code for initizliing a field in structure literal.
* Surround "or" operators with spaces.
* Unify the variable name convention (s/rtalen/rta_len/).
* Remove redundant local variables in unix_parse_response.
* Remove redundant branch in unix_parse_response.
* Put brances on line after.
* Rename local variable `l' to `path_len'.
* Decode a path for abstract unix socket in the same way as in `printsock' does'.

Signed-off-by: Masatake YAMATO <yamato at redhat.com>
---
 linux/unix_diag.h |  25 +++++++++++
 socketutils.c     | 130 +++++++++++++++++++++++++++++++++++++++++++++++++++---
 2 files changed, 148 insertions(+), 7 deletions(-)
 create mode 100644 linux/unix_diag.h

diff --git a/linux/unix_diag.h b/linux/unix_diag.h
new file mode 100644
index 0000000..3329ccb
--- /dev/null
+++ b/linux/unix_diag.h
@@ -0,0 +1,25 @@
+struct unix_diag_req {
+	uint8_t	 sdiag_family;
+	uint8_t	 sdiag_protocol;
+	uint16_t pad;
+	uint32_t udiag_states;
+	uint32_t udiag_ino;
+	uint32_t udiag_show;
+	uint32_t udiag_cookie[2];
+};
+
+#define UDIAG_SHOW_NAME		0x00000001
+#define UDIAG_SHOW_PEER		0x00000004
+
+struct unix_diag_msg {
+	uint8_t	 udiag_family;
+	uint8_t	 udiag_type;
+	uint8_t	 udiag_state;
+	uint8_t	 pad;
+
+	uint32_t udiag_ino;
+	uint32_t udiag_cookie[2];
+};
+
+#define UNIX_DIAG_NAME 0
+#define UNIX_DIAG_PEER 2
diff --git a/socketutils.c b/socketutils.c
index f57c304..fbc8515 100644
--- a/socketutils.c
+++ b/socketutils.c
@@ -5,9 +5,16 @@
 #include <linux/netlink.h>
 #include <linux/sock_diag.h>
 #include <linux/inet_diag.h>
+#include <linux/unix_diag.h>
+#include <linux/rtnetlink.h>
+
+#include <sys/un.h>
+#ifndef UNIX_PATH_MAX
+#define UNIX_PATH_MAX sizeof(((struct sockaddr_un *)0)->sun_path)
+#endif
 
 static bool
-send_query(const int fd, const int family, const int proto)
+inet_send_query(const int fd, const int family, const int proto)
 {
 	struct sockaddr_nl nladdr = {
 		.nl_family = AF_NETLINK
@@ -49,8 +56,9 @@ send_query(const int fd, const int family, const int proto)
 }
 
 static bool
-parse_response(const struct inet_diag_msg *diag_msg, const unsigned long inode)
+inet_parse_response(const void *data, int data_len, const unsigned long inode)
 {
+	const struct inet_diag_msg *diag_msg = data;
 	static const char zero_addr[sizeof(struct in6_addr)];
 	socklen_t addr_size, text_size;
 
@@ -95,7 +103,8 @@ parse_response(const struct inet_diag_msg *diag_msg, const unsigned long inode)
 }
 
 static bool
-receive_responses(const int fd, const unsigned long inode)
+receive_responses(const int fd, const unsigned long inode,
+		  bool (* parser) (const void*, int, const unsigned long))
 {
 	static char buf[8192];
 	struct sockaddr_nl nladdr = {
@@ -132,7 +141,7 @@ receive_responses(const int fd, const unsigned long inode)
 				case NLMSG_ERROR:
 					return false;
 			}
-			if (parse_response(NLMSG_DATA(h), inode))
+			if (parser(NLMSG_DATA(h), h->nlmsg_len, inode))
 				return true;
 		}
 	}
@@ -141,10 +150,115 @@ receive_responses(const int fd, const unsigned long inode)
 static bool
 inet_print(int fd, int family, int protocol, const unsigned long inode)
 {
-	return send_query(fd, family, protocol)
-		&& receive_responses(fd, inode);
+	return inet_send_query(fd, family, protocol)
+		&& receive_responses(fd, inode, inet_parse_response);
+}
+
+
+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;
+	} req = {
+		.nlh = {
+			.nlmsg_len = sizeof(req),
+			.nlmsg_type = SOCK_DIAG_BY_FAMILY,
+			.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,
+		}
+	};
+	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;
+	}
+}
+
+static bool
+unix_parse_response(const void *data, int data_len, const unsigned long inode)
+{
+	const struct unix_diag_msg *diag_msg = data;
+	int rta_len = data_len - NLMSG_LENGTH(sizeof(*diag_msg));
+	struct rtattr *attr;
+	size_t path_len = 0;
+	char path[UNIX_PATH_MAX + 1] = { [0] = '\0' };
+
+	uint32_t peer = 0;
+	if (diag_msg->udiag_ino != inode)
+		return false;
+	if (diag_msg->udiag_family != AF_UNIX)
+		return false;
+
+	attr = (struct rtattr*) (diag_msg +1);
+	while (RTA_OK(attr, rta_len)) {
+		switch (attr->rta_type) {
+		case UNIX_DIAG_NAME:
+			if (!path_len) {
+				path_len = RTA_PAYLOAD(attr);
+				if (path_len > UNIX_PATH_MAX)
+					path_len = UNIX_PATH_MAX;
+				memcpy(path, RTA_DATA(attr), path_len);
+				break;
+			}
+		case UNIX_DIAG_PEER:
+			if (RTA_PAYLOAD(attr) >= 4)
+				peer = *(uint32_t *)RTA_DATA(attr);
+			break;
+		}
+		attr = RTA_NEXT(attr, rta_len);
+	}
+
+	/* prints the getting information with following format:
+	 * "UNIX:[" SELF_INODE [ "->" PEER_INODE ][ "," SOCKET_FILE ] "]" */
+	if (peer || path_len) {
+		tprintf("UNIX:[%lu", inode);
+		if (peer)
+			tprintf("->%u", peer);
+		if (path_len) {
+			if (path[0] == '\0')
+				tprintf(",@\"%s\"", path + 1);
+			else
+				tprintf(",\"%s\"", path);
+		}
+		tprints("]");
+		return true;
+	}
+	else
+		return false;
+}
+
+static bool
+unix_print(int fd, const unsigned long inode)
+{
+	return unix_send_query(fd, inode)
+		&& receive_responses(fd, inode, unix_parse_response);
 }
 
+
 /* Given an inode number of a socket, print out the details
  * of the ip address and port. */
 bool
@@ -153,7 +267,7 @@ print_sockaddr_by_inode(const unsigned long inode, const char *proto_name)
 	int fd;
 	bool r = false;
 
-	fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_INET_DIAG);
+	fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_SOCK_DIAG);
 	if (fd < 0)
 		return false;
 
@@ -166,6 +280,8 @@ print_sockaddr_by_inode(const unsigned long inode, const char *proto_name)
 			r = inet_print(fd, AF_INET6, IPPROTO_TCP, inode);
 		else if (strcmp(proto_name, "UDPv6") == 0)
 			r = inet_print(fd, AF_INET6, IPPROTO_UDP, inode);
+		else if (strcmp(proto_name, "UNIX") == 0)
+			r = unix_print(fd, inode);
 	} else {
 		const int families[] = {AF_INET, AF_INET6};
 		const int protocols[] = {IPPROTO_TCP, IPPROTO_UDP};
-- 
1.9.3





More information about the Strace-devel mailing list