blob: 0605aad201c01337f64550695d0ec40db016125b [file] [log] [blame]
Dmitry V. Levin2f6510c2014-08-21 03:17:48 +00001#include "defs.h"
2#include <netinet/in.h>
3#include <sys/socket.h>
4#include <arpa/inet.h>
5#include <linux/netlink.h>
6#include <linux/sock_diag.h>
7#include <linux/inet_diag.h>
Masatake YAMATO120e5db2014-12-24 20:59:31 +09008#include <linux/unix_diag.h>
9#include <linux/rtnetlink.h>
10
11#include <sys/un.h>
12#ifndef UNIX_PATH_MAX
13# define UNIX_PATH_MAX sizeof(((struct sockaddr_un *) 0)->sun_path)
14#endif
Dmitry V. Levin2f6510c2014-08-21 03:17:48 +000015
16static bool
Masatake YAMATO120e5db2014-12-24 20:59:31 +090017inet_send_query(const int fd, const int family, const int proto)
Dmitry V. Levin2f6510c2014-08-21 03:17:48 +000018{
Dmitry V. Levinaf534b82014-11-21 19:59:16 +000019 struct sockaddr_nl nladdr = {
20 .nl_family = AF_NETLINK
21 };
Dmitry V. Levin2f6510c2014-08-21 03:17:48 +000022 struct {
23 struct nlmsghdr nlh;
24 struct inet_diag_req_v2 idr;
Dmitry V. Levinaf534b82014-11-21 19:59:16 +000025 } req = {
26 .nlh = {
27 .nlmsg_len = sizeof(req),
28 .nlmsg_type = SOCK_DIAG_BY_FAMILY,
29 .nlmsg_flags = NLM_F_DUMP | NLM_F_REQUEST
30 },
31 .idr = {
32 .sdiag_family = family,
33 .sdiag_protocol = proto,
34 .idiag_states = -1
35 }
36 };
Dmitry V. Levin2f6510c2014-08-21 03:17:48 +000037 struct iovec iov = {
38 .iov_base = &req,
39 .iov_len = sizeof(req)
40 };
41 struct msghdr msg = {
42 .msg_name = (void*)&nladdr,
43 .msg_namelen = sizeof(nladdr),
44 .msg_iov = &iov,
45 .msg_iovlen = 1
46 };
47
Dmitry V. Levin2f6510c2014-08-21 03:17:48 +000048 for (;;) {
49 if (sendmsg(fd, &msg, 0) < 0) {
50 if (errno == EINTR)
51 continue;
52 return false;
53 }
54 return true;
55 }
56}
57
58static bool
Dmitry V. Levin959205c2014-12-26 23:29:26 +000059inet_parse_response(const char *proto_name, const void *data, int data_len,
60 const unsigned long inode)
Dmitry V. Levin2f6510c2014-08-21 03:17:48 +000061{
Masatake YAMATO120e5db2014-12-24 20:59:31 +090062 const struct inet_diag_msg *diag_msg = data;
Dmitry V. Levin2f6510c2014-08-21 03:17:48 +000063 static const char zero_addr[sizeof(struct in6_addr)];
64 socklen_t addr_size, text_size;
65
66 if (diag_msg->idiag_inode != inode)
67 return false;
68
69 switch(diag_msg->idiag_family) {
70 case AF_INET:
71 addr_size = sizeof(struct in_addr);
72 text_size = INET_ADDRSTRLEN;
73 break;
74 case AF_INET6:
75 addr_size = sizeof(struct in6_addr);
76 text_size = INET6_ADDRSTRLEN;
77 break;
78 default:
79 return false;
80 }
81
82 char src_buf[text_size];
83
84 if (!inet_ntop(diag_msg->idiag_family, diag_msg->id.idiag_src,
85 src_buf, text_size))
86 return false;
87
88 if (diag_msg->id.idiag_dport ||
89 memcmp(zero_addr, diag_msg->id.idiag_dst, addr_size)) {
90 char dst_buf[text_size];
91
92 if (!inet_ntop(diag_msg->idiag_family, diag_msg->id.idiag_dst,
93 dst_buf, text_size))
94 return false;
95
Dmitry V. Levin959205c2014-12-26 23:29:26 +000096 tprintf("%s:[%s:%u->%s:%u]",
97 proto_name,
Dmitry V. Levin2f6510c2014-08-21 03:17:48 +000098 src_buf, ntohs(diag_msg->id.idiag_sport),
99 dst_buf, ntohs(diag_msg->id.idiag_dport));
100 } else {
Dmitry V. Levin959205c2014-12-26 23:29:26 +0000101 tprintf("%s:[%s:%u]", proto_name, src_buf,
102 ntohs(diag_msg->id.idiag_sport));
Dmitry V. Levin2f6510c2014-08-21 03:17:48 +0000103 }
104
105 return true;
106}
107
108static bool
Masatake YAMATO120e5db2014-12-24 20:59:31 +0900109receive_responses(const int fd, const unsigned long inode,
Dmitry V. Levin959205c2014-12-26 23:29:26 +0000110 const char *proto_name,
111 bool (* parser) (const char *, const void *, int, const unsigned long))
Dmitry V. Levin2f6510c2014-08-21 03:17:48 +0000112{
113 static char buf[8192];
Dmitry V. Levinaf534b82014-11-21 19:59:16 +0000114 struct sockaddr_nl nladdr = {
115 .nl_family = AF_NETLINK
116 };
Dmitry V. Levin2f6510c2014-08-21 03:17:48 +0000117 struct iovec iov = {
118 .iov_base = buf,
119 .iov_len = sizeof(buf)
120 };
121
Dmitry V. Levin2f6510c2014-08-21 03:17:48 +0000122 for (;;) {
123 ssize_t ret;
124 struct nlmsghdr *h;
125 struct msghdr msg = {
126 .msg_name = (void*)&nladdr,
127 .msg_namelen = sizeof(nladdr),
128 .msg_iov = &iov,
Dmitry V. Levinaf534b82014-11-21 19:59:16 +0000129 .msg_iovlen = 1
Dmitry V. Levin2f6510c2014-08-21 03:17:48 +0000130 };
131
132 ret = recvmsg(fd, &msg, 0);
133 if (ret < 0) {
134 if (errno == EINTR)
135 continue;
136 return false;
137 }
138 if (!ret)
139 return false;
140 for (h = (struct nlmsghdr*)buf;
141 NLMSG_OK(h, ret);
142 h = NLMSG_NEXT(h, ret)) {
143 switch (h->nlmsg_type) {
144 case NLMSG_DONE:
145 case NLMSG_ERROR:
146 return false;
147 }
Dmitry V. Levin959205c2014-12-26 23:29:26 +0000148 if (parser(proto_name, NLMSG_DATA(h), h->nlmsg_len, inode))
Dmitry V. Levin2f6510c2014-08-21 03:17:48 +0000149 return true;
150 }
151 }
152}
153
Masatake YAMATOf605e922014-12-10 12:55:06 +0900154static bool
Dmitry V. Levin959205c2014-12-26 23:29:26 +0000155inet_print(const int fd, const int family, const int protocol,
156 const unsigned long inode, const char *proto_name)
Masatake YAMATOf605e922014-12-10 12:55:06 +0900157{
Masatake YAMATO120e5db2014-12-24 20:59:31 +0900158 return inet_send_query(fd, family, protocol)
Dmitry V. Levin959205c2014-12-26 23:29:26 +0000159 && receive_responses(fd, inode, proto_name, inet_parse_response);
Masatake YAMATO120e5db2014-12-24 20:59:31 +0900160}
161
162static bool
163unix_send_query(const int fd, const unsigned long inode)
164{
165 struct sockaddr_nl nladdr = {
166 .nl_family = AF_NETLINK
167 };
168 struct {
169 struct nlmsghdr nlh;
170 struct unix_diag_req udr;
171 } req = {
172 .nlh = {
173 .nlmsg_len = sizeof(req),
174 .nlmsg_type = SOCK_DIAG_BY_FAMILY,
175 .nlmsg_flags = NLM_F_DUMP | NLM_F_REQUEST
176 },
177 .udr = {
178 .sdiag_family = AF_UNIX,
179 .udiag_ino = inode,
180 .udiag_states = -1,
181 .udiag_show = UDIAG_SHOW_NAME | UDIAG_SHOW_PEER
182 }
183 };
184 struct iovec iov = {
185 .iov_base = &req,
186 .iov_len = sizeof(req)
187 };
188 struct msghdr msg = {
189 .msg_name = (void*)&nladdr,
190 .msg_namelen = sizeof(nladdr),
191 .msg_iov = &iov,
192 .msg_iovlen = 1
193 };
194
195 for (;;) {
196 if (sendmsg(fd, &msg, 0) < 0) {
197 if (errno == EINTR)
198 continue;
199 return false;
200 }
201 return true;
202 }
203}
204
205static bool
Dmitry V. Levin959205c2014-12-26 23:29:26 +0000206unix_parse_response(const char *proto_name, const void *data, int data_len,
207 const unsigned long inode)
Masatake YAMATO120e5db2014-12-24 20:59:31 +0900208{
209 const struct unix_diag_msg *diag_msg = data;
210 struct rtattr *attr;
211 int rta_len = data_len - NLMSG_LENGTH(sizeof(*diag_msg));
212 uint32_t peer = 0;
213 size_t path_len = 0;
214 char path[UNIX_PATH_MAX + 1];
215
216 if (diag_msg->udiag_ino != inode)
217 return false;
218 if (diag_msg->udiag_family != AF_UNIX)
219 return false;
220
221 for (attr = (struct rtattr *) (diag_msg + 1);
222 RTA_OK(attr, rta_len);
223 attr = RTA_NEXT(attr, rta_len)) {
224 switch (attr->rta_type) {
225 case UNIX_DIAG_NAME:
226 if (!path_len) {
227 path_len = RTA_PAYLOAD(attr);
228 if (path_len > UNIX_PATH_MAX)
229 path_len = UNIX_PATH_MAX;
230 memcpy(path, RTA_DATA(attr), path_len);
231 path[path_len] = '\0';
232 }
233 break;
234 case UNIX_DIAG_PEER:
235 if (RTA_PAYLOAD(attr) >= 4)
236 peer = *(uint32_t *)RTA_DATA(attr);
237 break;
238 }
239 }
240
241 /*
242 * print obtained information in the following format:
243 * "UNIX:[" SELF_INODE [ "->" PEER_INODE ][ "," SOCKET_FILE ] "]"
244 */
245 if (peer || path_len) {
Dmitry V. Levin959205c2014-12-26 23:29:26 +0000246 tprintf("%s:[%lu", proto_name, inode);
Masatake YAMATO120e5db2014-12-24 20:59:31 +0900247 if (peer)
248 tprintf("->%u", peer);
249 if (path_len) {
250 if (path[0] == '\0') {
251 char *outstr = alloca(4 * path_len - 1);
252 string_quote(path + 1, outstr, -1, path_len);
253 tprintf(",@%s", outstr);
254 } else {
255 char *outstr = alloca(4 * path_len + 3);
256 string_quote(path, outstr, -1, path_len + 1);
257 tprintf(",%s", outstr);
258 }
259 }
260 tprints("]");
261 return true;
262 }
263 else
264 return false;
265}
266
267static bool
268unix_print(int fd, const unsigned long inode)
269{
270 return unix_send_query(fd, inode)
Dmitry V. Levin959205c2014-12-26 23:29:26 +0000271 && receive_responses(fd, inode, "UNIX", unix_parse_response);
Masatake YAMATOf605e922014-12-10 12:55:06 +0900272}
273
Dmitry V. Levin2f6510c2014-08-21 03:17:48 +0000274/* Given an inode number of a socket, print out the details
275 * of the ip address and port. */
276bool
Masatake YAMATOf605e922014-12-10 12:55:06 +0900277print_sockaddr_by_inode(const unsigned long inode, const char *proto_name)
Dmitry V. Levin2f6510c2014-08-21 03:17:48 +0000278{
Dmitry V. Levin2f6510c2014-08-21 03:17:48 +0000279 int fd;
Masatake YAMATOf605e922014-12-10 12:55:06 +0900280 bool r = false;
Dmitry V. Levin2f6510c2014-08-21 03:17:48 +0000281
Masatake YAMATO120e5db2014-12-24 20:59:31 +0900282 fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_SOCK_DIAG);
Dmitry V. Levin2f6510c2014-08-21 03:17:48 +0000283 if (fd < 0)
284 return false;
285
Masatake YAMATOf605e922014-12-10 12:55:06 +0900286 if (proto_name) {
287 if (strcmp(proto_name, "TCP") == 0)
Dmitry V. Levin959205c2014-12-26 23:29:26 +0000288 r = inet_print(fd, AF_INET, IPPROTO_TCP, inode, "TCP");
Masatake YAMATOf605e922014-12-10 12:55:06 +0900289 else if (strcmp(proto_name, "UDP") == 0)
Dmitry V. Levin959205c2014-12-26 23:29:26 +0000290 r = inet_print(fd, AF_INET, IPPROTO_UDP, inode, "UDP");
Masatake YAMATOf605e922014-12-10 12:55:06 +0900291 else if (strcmp(proto_name, "TCPv6") == 0)
Dmitry V. Levin959205c2014-12-26 23:29:26 +0000292 r = inet_print(fd, AF_INET6, IPPROTO_TCP, inode, "TCPv6");
Masatake YAMATOf605e922014-12-10 12:55:06 +0900293 else if (strcmp(proto_name, "UDPv6") == 0)
Dmitry V. Levin959205c2014-12-26 23:29:26 +0000294 r = inet_print(fd, AF_INET6, IPPROTO_UDP, inode, "UDPv6");
Masatake YAMATO120e5db2014-12-24 20:59:31 +0900295 else if (strcmp(proto_name, "UNIX") == 0)
296 r = unix_print(fd, inode);
Masatake YAMATOf605e922014-12-10 12:55:06 +0900297 } else {
Dmitry V. Levin959205c2014-12-26 23:29:26 +0000298 const struct {
299 const int family;
300 const int protocol;
301 const char *name;
302 } protocols[] = {
303 { AF_INET, IPPROTO_TCP, "TCP" },
304 { AF_INET, IPPROTO_UDP, "UDP" },
305 { AF_INET6, IPPROTO_TCP, "TCPv6" },
306 { AF_INET6, IPPROTO_UDP, "UDPv6" }
307 };
308 size_t i;
Masatake YAMATOf605e922014-12-10 12:55:06 +0900309
Dmitry V. Levin959205c2014-12-26 23:29:26 +0000310 for (i = 0; i < ARRAY_SIZE(protocols); ++i) {
311 if ((r = inet_print(fd, protocols[i].family,
312 protocols[i].protocol, inode,
313 protocols[i].name)))
314 break;
Dmitry V. Levin2f6510c2014-08-21 03:17:48 +0000315 }
316 }
Dmitry V. Levin959205c2014-12-26 23:29:26 +0000317
Dmitry V. Levin2f6510c2014-08-21 03:17:48 +0000318 close(fd);
Masatake YAMATOf605e922014-12-10 12:55:06 +0900319 return r;
Dmitry V. Levin2f6510c2014-08-21 03:17:48 +0000320}