blob: 8b0d6159a443b4d64d91a42efc5f499276649cd4 [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
Masatake YAMATO120e5db2014-12-24 20:59:31 +090059inet_parse_response(const void *data, int data_len, const unsigned long inode)
Dmitry V. Levin2f6510c2014-08-21 03:17:48 +000060{
Masatake YAMATO120e5db2014-12-24 20:59:31 +090061 const struct inet_diag_msg *diag_msg = data;
Dmitry V. Levin2f6510c2014-08-21 03:17:48 +000062 static const char zero_addr[sizeof(struct in6_addr)];
63 socklen_t addr_size, text_size;
64
65 if (diag_msg->idiag_inode != inode)
66 return false;
67
68 switch(diag_msg->idiag_family) {
69 case AF_INET:
70 addr_size = sizeof(struct in_addr);
71 text_size = INET_ADDRSTRLEN;
72 break;
73 case AF_INET6:
74 addr_size = sizeof(struct in6_addr);
75 text_size = INET6_ADDRSTRLEN;
76 break;
77 default:
78 return false;
79 }
80
81 char src_buf[text_size];
82
83 if (!inet_ntop(diag_msg->idiag_family, diag_msg->id.idiag_src,
84 src_buf, text_size))
85 return false;
86
87 if (diag_msg->id.idiag_dport ||
88 memcmp(zero_addr, diag_msg->id.idiag_dst, addr_size)) {
89 char dst_buf[text_size];
90
91 if (!inet_ntop(diag_msg->idiag_family, diag_msg->id.idiag_dst,
92 dst_buf, text_size))
93 return false;
94
95 tprintf("%s:%u->%s:%u",
96 src_buf, ntohs(diag_msg->id.idiag_sport),
97 dst_buf, ntohs(diag_msg->id.idiag_dport));
98 } else {
99 tprintf("%s:%u", src_buf, ntohs(diag_msg->id.idiag_sport));
100 }
101
102 return true;
103}
104
105static bool
Masatake YAMATO120e5db2014-12-24 20:59:31 +0900106receive_responses(const int fd, const unsigned long inode,
107 bool (* parser) (const void*, int, const unsigned long))
Dmitry V. Levin2f6510c2014-08-21 03:17:48 +0000108{
109 static char buf[8192];
Dmitry V. Levinaf534b82014-11-21 19:59:16 +0000110 struct sockaddr_nl nladdr = {
111 .nl_family = AF_NETLINK
112 };
Dmitry V. Levin2f6510c2014-08-21 03:17:48 +0000113 struct iovec iov = {
114 .iov_base = buf,
115 .iov_len = sizeof(buf)
116 };
117
Dmitry V. Levin2f6510c2014-08-21 03:17:48 +0000118 for (;;) {
119 ssize_t ret;
120 struct nlmsghdr *h;
121 struct msghdr msg = {
122 .msg_name = (void*)&nladdr,
123 .msg_namelen = sizeof(nladdr),
124 .msg_iov = &iov,
Dmitry V. Levinaf534b82014-11-21 19:59:16 +0000125 .msg_iovlen = 1
Dmitry V. Levin2f6510c2014-08-21 03:17:48 +0000126 };
127
128 ret = recvmsg(fd, &msg, 0);
129 if (ret < 0) {
130 if (errno == EINTR)
131 continue;
132 return false;
133 }
134 if (!ret)
135 return false;
136 for (h = (struct nlmsghdr*)buf;
137 NLMSG_OK(h, ret);
138 h = NLMSG_NEXT(h, ret)) {
139 switch (h->nlmsg_type) {
140 case NLMSG_DONE:
141 case NLMSG_ERROR:
142 return false;
143 }
Masatake YAMATO120e5db2014-12-24 20:59:31 +0900144 if (parser(NLMSG_DATA(h), h->nlmsg_len, inode))
Dmitry V. Levin2f6510c2014-08-21 03:17:48 +0000145 return true;
146 }
147 }
148}
149
Masatake YAMATOf605e922014-12-10 12:55:06 +0900150static bool
151inet_print(int fd, int family, int protocol, const unsigned long inode)
152{
Masatake YAMATO120e5db2014-12-24 20:59:31 +0900153 return inet_send_query(fd, family, protocol)
154 && receive_responses(fd, inode, inet_parse_response);
155}
156
157static bool
158unix_send_query(const int fd, const unsigned long inode)
159{
160 struct sockaddr_nl nladdr = {
161 .nl_family = AF_NETLINK
162 };
163 struct {
164 struct nlmsghdr nlh;
165 struct unix_diag_req udr;
166 } req = {
167 .nlh = {
168 .nlmsg_len = sizeof(req),
169 .nlmsg_type = SOCK_DIAG_BY_FAMILY,
170 .nlmsg_flags = NLM_F_DUMP | NLM_F_REQUEST
171 },
172 .udr = {
173 .sdiag_family = AF_UNIX,
174 .udiag_ino = inode,
175 .udiag_states = -1,
176 .udiag_show = UDIAG_SHOW_NAME | UDIAG_SHOW_PEER
177 }
178 };
179 struct iovec iov = {
180 .iov_base = &req,
181 .iov_len = sizeof(req)
182 };
183 struct msghdr msg = {
184 .msg_name = (void*)&nladdr,
185 .msg_namelen = sizeof(nladdr),
186 .msg_iov = &iov,
187 .msg_iovlen = 1
188 };
189
190 for (;;) {
191 if (sendmsg(fd, &msg, 0) < 0) {
192 if (errno == EINTR)
193 continue;
194 return false;
195 }
196 return true;
197 }
198}
199
200static bool
201unix_parse_response(const void *data, int data_len, const unsigned long inode)
202{
203 const struct unix_diag_msg *diag_msg = data;
204 struct rtattr *attr;
205 int rta_len = data_len - NLMSG_LENGTH(sizeof(*diag_msg));
206 uint32_t peer = 0;
207 size_t path_len = 0;
208 char path[UNIX_PATH_MAX + 1];
209
210 if (diag_msg->udiag_ino != inode)
211 return false;
212 if (diag_msg->udiag_family != AF_UNIX)
213 return false;
214
215 for (attr = (struct rtattr *) (diag_msg + 1);
216 RTA_OK(attr, rta_len);
217 attr = RTA_NEXT(attr, rta_len)) {
218 switch (attr->rta_type) {
219 case UNIX_DIAG_NAME:
220 if (!path_len) {
221 path_len = RTA_PAYLOAD(attr);
222 if (path_len > UNIX_PATH_MAX)
223 path_len = UNIX_PATH_MAX;
224 memcpy(path, RTA_DATA(attr), path_len);
225 path[path_len] = '\0';
226 }
227 break;
228 case UNIX_DIAG_PEER:
229 if (RTA_PAYLOAD(attr) >= 4)
230 peer = *(uint32_t *)RTA_DATA(attr);
231 break;
232 }
233 }
234
235 /*
236 * print obtained information in the following format:
237 * "UNIX:[" SELF_INODE [ "->" PEER_INODE ][ "," SOCKET_FILE ] "]"
238 */
239 if (peer || path_len) {
240 tprintf("UNIX:[%lu", inode);
241 if (peer)
242 tprintf("->%u", peer);
243 if (path_len) {
244 if (path[0] == '\0') {
245 char *outstr = alloca(4 * path_len - 1);
246 string_quote(path + 1, outstr, -1, path_len);
247 tprintf(",@%s", outstr);
248 } else {
249 char *outstr = alloca(4 * path_len + 3);
250 string_quote(path, outstr, -1, path_len + 1);
251 tprintf(",%s", outstr);
252 }
253 }
254 tprints("]");
255 return true;
256 }
257 else
258 return false;
259}
260
261static bool
262unix_print(int fd, const unsigned long inode)
263{
264 return unix_send_query(fd, inode)
265 && receive_responses(fd, inode, unix_parse_response);
Masatake YAMATOf605e922014-12-10 12:55:06 +0900266}
267
Dmitry V. Levin2f6510c2014-08-21 03:17:48 +0000268/* Given an inode number of a socket, print out the details
269 * of the ip address and port. */
270bool
Masatake YAMATOf605e922014-12-10 12:55:06 +0900271print_sockaddr_by_inode(const unsigned long inode, const char *proto_name)
Dmitry V. Levin2f6510c2014-08-21 03:17:48 +0000272{
Dmitry V. Levin2f6510c2014-08-21 03:17:48 +0000273 int fd;
Masatake YAMATOf605e922014-12-10 12:55:06 +0900274 bool r = false;
Dmitry V. Levin2f6510c2014-08-21 03:17:48 +0000275
Masatake YAMATO120e5db2014-12-24 20:59:31 +0900276 fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_SOCK_DIAG);
Dmitry V. Levin2f6510c2014-08-21 03:17:48 +0000277 if (fd < 0)
278 return false;
279
Masatake YAMATOf605e922014-12-10 12:55:06 +0900280 if (proto_name) {
281 if (strcmp(proto_name, "TCP") == 0)
282 r = inet_print(fd, AF_INET, IPPROTO_TCP, inode);
283 else if (strcmp(proto_name, "UDP") == 0)
284 r = inet_print(fd, AF_INET, IPPROTO_UDP, inode);
285 else if (strcmp(proto_name, "TCPv6") == 0)
286 r = inet_print(fd, AF_INET6, IPPROTO_TCP, inode);
287 else if (strcmp(proto_name, "UDPv6") == 0)
288 r = inet_print(fd, AF_INET6, IPPROTO_UDP, inode);
Masatake YAMATO120e5db2014-12-24 20:59:31 +0900289 else if (strcmp(proto_name, "UNIX") == 0)
290 r = unix_print(fd, inode);
Masatake YAMATOf605e922014-12-10 12:55:06 +0900291 } else {
292 const int families[] = {AF_INET, AF_INET6};
293 const int protocols[] = {IPPROTO_TCP, IPPROTO_UDP};
294 const size_t flen = ARRAY_SIZE(families);
295 const size_t plen = ARRAY_SIZE(protocols);
296 size_t fi, pi;
297
298 for (fi = 0; fi < flen; ++fi) {
299 for (pi = 0; pi < plen; ++pi) {
300 if ((r = inet_print(fd, families[fi], protocols[pi], inode)))
301 goto out;
Dmitry V. Levin2f6510c2014-08-21 03:17:48 +0000302 }
303 }
304 }
Masatake YAMATOf605e922014-12-10 12:55:06 +0900305out:
Dmitry V. Levin2f6510c2014-08-21 03:17:48 +0000306 close(fd);
Masatake YAMATOf605e922014-12-10 12:55:06 +0900307 return r;
Dmitry V. Levin2f6510c2014-08-21 03:17:48 +0000308}