]>
Commit | Line | Data |
---|---|---|
0ee16e43 | 1 | /* |
5468bb45 | 2 | * lsfd-sock-xinfo.c - read various information from files under /proc/net/ and NETLINK_SOCK_DIAG |
0ee16e43 MY |
3 | * |
4 | * Copyright (C) 2022 Red Hat, Inc. All rights reserved. | |
5 | * Written by Masatake YAMATO <yamato@redhat.com> | |
6 | * | |
7 | * This program is free software; you can redistribute it and/or modify | |
8 | * it under the terms of the GNU General Public License as published by | |
9 | * the Free Software Foundation; either version 2 of the License, or | |
10 | * (at your option) any later version. | |
11 | * | |
12 | * This program is distributed in the hope that it would be useful, | |
13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
15 | * GNU General Public License for more details. | |
16 | * | |
17 | * You should have received a copy of the GNU General Public License | |
18 | * along with this program; if not, write to the Free Software Foundation, | |
19 | * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | |
20 | */ | |
7b2a9be0 | 21 | #include <arpa/inet.h> /* inet_ntop */ |
1656da13 | 22 | #include <netinet/in.h> /* in6_addr */ |
2e38e61c | 23 | #include <fcntl.h> /* open(2) */ |
97f54385 | 24 | #include <ifaddrs.h> /* getifaddrs */ |
73df46fc | 25 | #include <inttypes.h> /* SCNu16 */ |
97f54385 | 26 | #include <net/if.h> /* if_nametoindex */ |
adfc156a | 27 | #include <linux/if_ether.h> /* ETH_P_* */ |
2e38e61c | 28 | #include <linux/net.h> /* SS_* */ |
5468bb45 MY |
29 | #include <linux/netlink.h> /* NETLINK_*, NLMSG_* */ |
30 | #include <linux/rtnetlink.h> /* RTA_*, struct rtattr, */ | |
31 | #include <linux/sock_diag.h> /* SOCK_DIAG_BY_FAMILY */ | |
2e38e61c | 32 | #include <linux/un.h> /* UNIX_PATH_MAX */ |
5468bb45 MY |
33 | #include <linux/unix_diag.h> /* UNIX_DIAG_*, UDIAG_SHOW_*, |
34 | struct unix_diag_req */ | |
0ee16e43 | 35 | #include <sched.h> /* for setns(2) */ |
cb3126e1 | 36 | #include <search.h> /* tfind, tsearch */ |
2e38e61c MY |
37 | #include <stdint.h> |
38 | #include <string.h> | |
39 | #include <sys/socket.h> /* SOCK_* */ | |
0ee16e43 | 40 | |
db79a100 TW |
41 | #include "sysfs.h" |
42 | #include "bitops.h" | |
0ee16e43 MY |
43 | |
44 | #include "lsfd.h" | |
45 | #include "lsfd-sock.h" | |
46 | ||
891b24b7 MY |
47 | static void load_xinfo_from_proc_icmp(ino_t netns_inode, enum sysfs_byteorder byteorder); |
48 | static void load_xinfo_from_proc_icmp6(ino_t netns_inode, enum sysfs_byteorder byteorder); | |
2e38e61c | 49 | static void load_xinfo_from_proc_unix(ino_t netns_inode); |
891b24b7 MY |
50 | static void load_xinfo_from_proc_raw(ino_t netns_inode, enum sysfs_byteorder byteorder); |
51 | static void load_xinfo_from_proc_tcp(ino_t netns_inode, enum sysfs_byteorder byteorder); | |
52 | static void load_xinfo_from_proc_udp(ino_t netns_inode, enum sysfs_byteorder byteorder); | |
53 | static void load_xinfo_from_proc_udplite(ino_t netns_inode, enum sysfs_byteorder byteorder); | |
54 | static void load_xinfo_from_proc_tcp6(ino_t netns_inode, enum sysfs_byteorder byteorder); | |
55 | static void load_xinfo_from_proc_udp6(ino_t netns_inode, enum sysfs_byteorder byteorder); | |
56 | static void load_xinfo_from_proc_udplite6(ino_t netns_inode, enum sysfs_byteorder byteorder); | |
57 | static void load_xinfo_from_proc_raw6(ino_t netns_inode, enum sysfs_byteorder byteorder); | |
b53cc896 | 58 | static void load_xinfo_from_proc_netlink(ino_t netns_inode); |
adfc156a | 59 | static void load_xinfo_from_proc_packet(ino_t netns_inode); |
2e38e61c | 60 | |
5468bb45 MY |
61 | static void load_xinfo_from_diag_unix(int diag, ino_t netns_inode); |
62 | ||
0ee16e43 | 63 | static int self_netns_fd = -1; |
44f9aec7 | 64 | static struct stat self_netns_sb; |
0ee16e43 MY |
65 | |
66 | static void *xinfo_tree; /* for tsearch/tfind */ | |
67 | static void *netns_tree; | |
68 | ||
97f54385 MY |
69 | struct iface { |
70 | unsigned int index; | |
71 | char name[IF_NAMESIZE]; | |
72 | }; | |
73 | ||
74 | static const char *get_iface_name(ino_t netns, unsigned int iface_index); | |
75 | ||
e6e551e8 MY |
76 | struct netns { |
77 | ino_t inode; | |
97f54385 | 78 | struct iface *ifaces; |
e6e551e8 MY |
79 | }; |
80 | ||
0ee16e43 MY |
81 | static int netns_compare(const void *a, const void *b) |
82 | { | |
e6e551e8 MY |
83 | const struct netns *netns_a = a; |
84 | const struct netns *netns_b = b; | |
85 | ||
86 | return netns_a->inode - netns_b->inode; | |
87 | } | |
88 | ||
89 | static void netns_free(void *netns) | |
90 | { | |
97f54385 MY |
91 | struct netns *nsobj = netns; |
92 | ||
93 | free(nsobj->ifaces); | |
e6e551e8 | 94 | free(netns); |
0ee16e43 MY |
95 | } |
96 | ||
97f54385 MY |
97 | /* |
98 | * iface index -> iface name mappings | |
99 | */ | |
100 | static void load_ifaces_from_getifaddrs(struct netns *nsobj) | |
101 | { | |
102 | struct ifaddrs *ifa_list; | |
103 | struct ifaddrs *ifa; | |
104 | size_t i, count = 0; | |
105 | ||
106 | if (getifaddrs(&ifa_list) < 0) | |
107 | return; | |
108 | ||
109 | for (ifa = ifa_list; ifa != NULL; ifa = ifa->ifa_next) | |
110 | count++; | |
111 | ||
112 | nsobj->ifaces = xcalloc(count + 1, sizeof(*nsobj->ifaces)); | |
113 | ||
114 | for (ifa = ifa_list, i = 0; ifa != NULL; ifa = ifa->ifa_next, i++) { | |
115 | unsigned int if_index = if_nametoindex(ifa->ifa_name); | |
116 | ||
117 | nsobj->ifaces[i].index = if_index; | |
118 | strncpy(nsobj->ifaces[i].name, ifa->ifa_name, IF_NAMESIZE - 1); | |
119 | /* The slot for the last byte is already filled by calloc. */ | |
120 | } | |
121 | /* nsobj->ifaces[count] is the sentinel value. */ | |
122 | ||
123 | freeifaddrs(ifa_list); | |
124 | ||
125 | return; | |
126 | } | |
127 | ||
128 | static const char *get_iface_name(ino_t netns, unsigned int iface_index) | |
129 | { | |
816109f6 TW |
130 | struct netns key = { .inode = netns }; |
131 | struct netns **nsobj = tfind(&key, &netns_tree, netns_compare); | |
97f54385 MY |
132 | if (!nsobj) |
133 | return NULL; | |
134 | ||
135 | for (size_t i = 0; (*nsobj)->ifaces[i].index; i++) { | |
136 | if ((*nsobj)->ifaces[i].index == iface_index) | |
137 | return (*nsobj)->ifaces[i].name; | |
138 | } | |
139 | ||
140 | return NULL; | |
141 | } | |
142 | ||
0ee16e43 MY |
143 | static bool is_sock_xinfo_loaded(ino_t netns) |
144 | { | |
816109f6 TW |
145 | struct netns key = { .inode = netns }; |
146 | return tfind(&key, &netns_tree, netns_compare)? true: false; | |
0ee16e43 MY |
147 | } |
148 | ||
e6e551e8 | 149 | static struct netns *mark_sock_xinfo_loaded(ino_t ino) |
0ee16e43 | 150 | { |
97f54385 | 151 | struct netns *netns = xcalloc(1, sizeof(*netns)); |
0ee16e43 MY |
152 | ino_t **tmp; |
153 | ||
e6e551e8 | 154 | netns->inode = ino; |
0ee16e43 MY |
155 | tmp = tsearch(netns, &netns_tree, netns_compare); |
156 | if (tmp == NULL) | |
157 | errx(EXIT_FAILURE, _("failed to allocate memory")); | |
e6e551e8 | 158 | return *(struct netns **)tmp; |
0ee16e43 MY |
159 | } |
160 | ||
e6e551e8 | 161 | static void load_sock_xinfo_no_nsswitch(struct netns *nsobj) |
0ee16e43 | 162 | { |
e6e551e8 | 163 | ino_t netns = nsobj? nsobj->inode: 0; |
5468bb45 | 164 | int diagsd; |
891b24b7 | 165 | enum sysfs_byteorder byteorder = sysfs_get_byteorder(NULL); |
e6e551e8 | 166 | |
2e38e61c | 167 | load_xinfo_from_proc_unix(netns); |
891b24b7 MY |
168 | load_xinfo_from_proc_tcp(netns, byteorder); |
169 | load_xinfo_from_proc_udp(netns, byteorder); | |
170 | load_xinfo_from_proc_udplite(netns, byteorder); | |
171 | load_xinfo_from_proc_raw(netns, byteorder); | |
172 | load_xinfo_from_proc_tcp6(netns, byteorder); | |
173 | load_xinfo_from_proc_udp6(netns, byteorder); | |
174 | load_xinfo_from_proc_udplite6(netns, byteorder); | |
175 | load_xinfo_from_proc_raw6(netns, byteorder); | |
176 | load_xinfo_from_proc_icmp(netns, byteorder); | |
177 | load_xinfo_from_proc_icmp6(netns, byteorder); | |
b53cc896 | 178 | load_xinfo_from_proc_netlink(netns); |
adfc156a | 179 | load_xinfo_from_proc_packet(netns); |
97f54385 | 180 | |
5468bb45 MY |
181 | diagsd = socket(AF_NETLINK, SOCK_DGRAM, NETLINK_SOCK_DIAG); |
182 | if (diagsd >= 0) { | |
183 | load_xinfo_from_diag_unix(diagsd, netns); | |
184 | close(diagsd); | |
185 | } | |
186 | ||
97f54385 MY |
187 | if (nsobj) |
188 | load_ifaces_from_getifaddrs(nsobj); | |
0ee16e43 MY |
189 | } |
190 | ||
e6e551e8 | 191 | static void load_sock_xinfo_with_fd(int fd, struct netns *nsobj) |
0ee16e43 | 192 | { |
b3649945 | 193 | if (setns(fd, CLONE_NEWNET) == 0) { |
e6e551e8 | 194 | load_sock_xinfo_no_nsswitch(nsobj); |
b3649945 | 195 | setns(self_netns_fd, CLONE_NEWNET); |
0ee16e43 MY |
196 | } |
197 | } | |
198 | ||
199 | void load_sock_xinfo(struct path_cxt *pc, const char *name, ino_t netns) | |
200 | { | |
201 | if (self_netns_fd == -1) | |
202 | return; | |
203 | ||
204 | if (!is_sock_xinfo_loaded(netns)) { | |
205 | int fd; | |
e6e551e8 | 206 | struct netns *nsobj = mark_sock_xinfo_loaded(netns); |
0ee16e43 MY |
207 | fd = ul_path_open(pc, O_RDONLY, name); |
208 | if (fd < 0) | |
209 | return; | |
210 | ||
e6e551e8 | 211 | load_sock_xinfo_with_fd(fd, nsobj); |
0ee16e43 MY |
212 | close(fd); |
213 | } | |
214 | } | |
215 | ||
216 | void initialize_sock_xinfos(void) | |
217 | { | |
218 | struct path_cxt *pc; | |
219 | DIR *dir; | |
220 | struct dirent *d; | |
221 | ||
222 | self_netns_fd = open("/proc/self/ns/net", O_RDONLY); | |
223 | ||
224 | if (self_netns_fd < 0) | |
e6e551e8 | 225 | load_sock_xinfo_no_nsswitch(NULL); |
0ee16e43 MY |
226 | else { |
227 | if (fstat(self_netns_fd, &self_netns_sb) == 0) { | |
916b44a5 | 228 | unsigned long m; |
e6e551e8 MY |
229 | struct netns *nsobj = mark_sock_xinfo_loaded(self_netns_sb.st_ino); |
230 | load_sock_xinfo_no_nsswitch(nsobj); | |
916b44a5 MY |
231 | |
232 | m = minor(self_netns_sb.st_dev); | |
233 | add_nodev(m, "nsfs"); | |
0ee16e43 MY |
234 | } |
235 | } | |
236 | ||
237 | /* Load /proc/net/{unix,...} of the network namespace | |
238 | * specified with netns files under /var/run/netns/. | |
239 | * | |
240 | * `ip netns' command pins a network namespace on | |
241 | * /var/run/netns. | |
242 | */ | |
243 | pc = ul_new_path("/var/run/netns"); | |
244 | if (!pc) | |
245 | err(EXIT_FAILURE, _("failed to alloc path context for /var/run/netns")); | |
246 | dir = ul_path_opendir(pc, NULL); | |
247 | if (dir == NULL) { | |
248 | ul_unref_path(pc); | |
249 | return; | |
250 | } | |
251 | while ((d = readdir(dir))) { | |
252 | struct stat sb; | |
253 | int fd; | |
e6e551e8 | 254 | struct netns *nsobj; |
0ee16e43 MY |
255 | if (ul_path_stat(pc, &sb, 0, d->d_name) < 0) |
256 | continue; | |
257 | if (is_sock_xinfo_loaded(sb.st_ino)) | |
258 | continue; | |
e6e551e8 | 259 | nsobj = mark_sock_xinfo_loaded(sb.st_ino); |
0ee16e43 MY |
260 | fd = ul_path_open(pc, O_RDONLY, d->d_name); |
261 | if (fd < 0) | |
262 | continue; | |
e6e551e8 | 263 | load_sock_xinfo_with_fd(fd, nsobj); |
0ee16e43 MY |
264 | close(fd); |
265 | } | |
266 | closedir(dir); | |
267 | ul_unref_path(pc); | |
268 | } | |
269 | ||
b3649945 | 270 | static void free_sock_xinfo(void *node) |
0ee16e43 MY |
271 | { |
272 | struct sock_xinfo *xinfo = node; | |
273 | if (xinfo->class->free) | |
b3649945 | 274 | xinfo->class->free(xinfo); |
0ee16e43 MY |
275 | free(node); |
276 | } | |
277 | ||
278 | void finalize_sock_xinfos(void) | |
279 | { | |
280 | if (self_netns_fd != -1) | |
281 | close(self_netns_fd); | |
e6e551e8 | 282 | tdestroy(netns_tree, netns_free); |
0ee16e43 MY |
283 | tdestroy(xinfo_tree, free_sock_xinfo); |
284 | } | |
285 | ||
286 | static int xinfo_compare(const void *a, const void *b) | |
287 | { | |
0caccbf1 | 288 | return ((struct sock_xinfo *)a)->inode - ((struct sock_xinfo *)b)->inode; |
0ee16e43 MY |
289 | } |
290 | ||
2e38e61c MY |
291 | static void add_sock_info(struct sock_xinfo *xinfo) |
292 | { | |
293 | struct sock_xinfo **tmp = tsearch(xinfo, &xinfo_tree, xinfo_compare); | |
294 | ||
295 | if (tmp == NULL) | |
296 | errx(EXIT_FAILURE, _("failed to allocate memory")); | |
297 | } | |
298 | ||
b8bcca67 | 299 | struct sock_xinfo *get_sock_xinfo(ino_t inode) |
0ee16e43 | 300 | { |
816109f6 TW |
301 | struct sock_xinfo key = { .inode = inode }; |
302 | struct sock_xinfo **xinfo = tfind(&key, &xinfo_tree, xinfo_compare); | |
0ee16e43 MY |
303 | |
304 | if (xinfo) | |
305 | return *xinfo; | |
306 | return NULL; | |
307 | } | |
308 | ||
309 | bool is_nsfs_dev(dev_t dev) | |
310 | { | |
621cf7e9 | 311 | return dev == self_netns_sb.st_dev; |
0ee16e43 | 312 | } |
2e38e61c MY |
313 | |
314 | static const char *sock_decode_type(uint16_t type) | |
315 | { | |
316 | switch (type) { | |
317 | case SOCK_STREAM: | |
318 | return "stream"; | |
319 | case SOCK_DGRAM: | |
320 | return "dgram"; | |
321 | case SOCK_RAW: | |
322 | return "raw"; | |
323 | case SOCK_RDM: | |
324 | return "rdm"; | |
325 | case SOCK_SEQPACKET: | |
326 | return "seqpacket"; | |
327 | case SOCK_DCCP: | |
328 | return "dccp"; | |
329 | case SOCK_PACKET: | |
330 | return "packet"; | |
331 | default: | |
332 | return "unknown"; | |
333 | } | |
334 | } | |
335 | ||
5468bb45 MY |
336 | static void send_diag_request(int diagsd, void *req, size_t req_size, |
337 | bool (*cb)(ino_t, size_t, void *), | |
338 | ino_t netns) | |
339 | { | |
340 | struct sockaddr_nl nladdr = { | |
341 | .nl_family = AF_NETLINK, | |
342 | }; | |
343 | ||
344 | struct nlmsghdr nlh = { | |
345 | .nlmsg_len = sizeof(nlh) + req_size, | |
346 | .nlmsg_type = SOCK_DIAG_BY_FAMILY, | |
347 | .nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP, | |
348 | }; | |
349 | ||
350 | struct iovec iovecs[] = { | |
351 | { &nlh, sizeof(nlh) }, | |
352 | { req, req_size }, | |
353 | }; | |
354 | ||
355 | const struct msghdr mhd = { | |
356 | .msg_namelen = sizeof(nladdr), | |
357 | .msg_name = &nladdr, | |
358 | .msg_iovlen = ARRAY_SIZE(iovecs), | |
359 | .msg_iov = iovecs, | |
360 | }; | |
361 | ||
7cde78d7 | 362 | __attribute__((aligned(sizeof(void *)))) uint8_t buf[8192]; |
5468bb45 MY |
363 | |
364 | if (sendmsg(diagsd, &mhd, 0) < 0) | |
365 | return; | |
366 | ||
367 | for (;;) { | |
368 | const struct nlmsghdr *h; | |
369 | int r = recvfrom(diagsd, buf, sizeof(buf), 0, NULL, NULL); | |
370 | if (r < 0) | |
371 | return; | |
372 | ||
373 | h = (void *) buf; | |
374 | if (!NLMSG_OK(h, (size_t)r)) | |
375 | return; | |
376 | ||
377 | for (; NLMSG_OK(h, (size_t)r); h = NLMSG_NEXT(h, r)) { | |
378 | if (h->nlmsg_type == NLMSG_DONE) | |
379 | return; | |
380 | if (h->nlmsg_type == NLMSG_ERROR) | |
381 | return; | |
382 | ||
383 | if (h->nlmsg_type == SOCK_DIAG_BY_FAMILY) { | |
384 | if (!cb(netns, h->nlmsg_len, NLMSG_DATA(h))) | |
385 | return; | |
386 | } | |
387 | } | |
388 | } | |
389 | } | |
390 | ||
2e38e61c MY |
391 | /* |
392 | * Protocol specific code | |
393 | */ | |
394 | ||
395 | /* | |
396 | * UNIX | |
397 | */ | |
f9771d8d MY |
398 | struct unix_ipc { |
399 | struct ipc ipc; | |
400 | ino_t inode; | |
401 | ino_t ipeer; | |
402 | }; | |
403 | ||
2e38e61c MY |
404 | struct unix_xinfo { |
405 | struct sock_xinfo sock; | |
406 | int acceptcon; /* flags */ | |
407 | uint16_t type; | |
408 | uint8_t st; | |
426ff07f MY |
409 | #define is_shutdown_mask_set(mask) ((mask) & (1 << 2)) |
410 | #define set_shutdown_mask(mask) ((mask) |= (1 << 2)) | |
411 | uint8_t shutdown_mask:3; | |
f9771d8d | 412 | struct unix_ipc *unix_ipc; |
2e38e61c MY |
413 | char path[ |
414 | UNIX_PATH_MAX | |
415 | + 1 /* for @ */ | |
416 | + 1 /* \0? */ | |
417 | ]; | |
418 | }; | |
419 | ||
420 | static const char *unix_decode_state(uint8_t st) | |
421 | { | |
422 | switch (st) { | |
423 | case SS_FREE: | |
424 | return "free"; | |
425 | case SS_UNCONNECTED: | |
426 | return "unconnected"; | |
427 | case SS_CONNECTING: | |
428 | return "connecting"; | |
429 | case SS_CONNECTED: | |
430 | return "connected"; | |
431 | case SS_DISCONNECTING: | |
432 | return "disconnecting"; | |
433 | default: | |
434 | return "unknown"; | |
435 | } | |
436 | } | |
437 | ||
438 | static char *unix_get_name(struct sock_xinfo *sock_xinfo, | |
439 | struct sock *sock) | |
440 | { | |
441 | struct unix_xinfo *ux = (struct unix_xinfo *)sock_xinfo; | |
442 | const char *state = unix_decode_state(ux->st); | |
443 | char *str = NULL; | |
444 | ||
445 | if (sock->protoname && (strcmp(sock->protoname, "UNIX-STREAM") == 0)) | |
446 | xasprintf(&str, "state=%s%s%s", | |
447 | (ux->acceptcon)? "listen": state, | |
448 | *(ux->path)? " path=": "", | |
449 | *(ux->path)? ux->path: ""); | |
450 | else | |
451 | xasprintf(&str, "state=%s%s%s type=%s", | |
452 | (ux->acceptcon)? "listen": state, | |
453 | *(ux->path)? " path=": "", | |
454 | *(ux->path)? ux->path: "", | |
455 | sock_decode_type(ux->type)); | |
456 | return str; | |
457 | } | |
458 | ||
459 | static char *unix_get_type(struct sock_xinfo *sock_xinfo, | |
460 | struct sock *sock __attribute__((__unused__))) | |
461 | { | |
462 | const char *str; | |
463 | struct unix_xinfo *ux = (struct unix_xinfo *)sock_xinfo; | |
464 | ||
465 | str = sock_decode_type(ux->type); | |
e0dc84da | 466 | return xstrdup(str); |
2e38e61c MY |
467 | } |
468 | ||
469 | static char *unix_get_state(struct sock_xinfo *sock_xinfo, | |
470 | struct sock *sock __attribute__((__unused__))) | |
471 | { | |
472 | const char *str; | |
473 | struct unix_xinfo *ux = (struct unix_xinfo *)sock_xinfo; | |
474 | ||
475 | if (ux->acceptcon) | |
e0dc84da | 476 | return xstrdup("listen"); |
2e38e61c MY |
477 | |
478 | str = unix_decode_state(ux->st); | |
e0dc84da | 479 | return xstrdup(str); |
2e38e61c MY |
480 | } |
481 | ||
45d61bff MY |
482 | static bool unix_get_listening(struct sock_xinfo *sock_xinfo, |
483 | struct sock *sock __attribute__((__unused__))) | |
484 | { | |
485 | struct unix_xinfo *ux = (struct unix_xinfo *)sock_xinfo; | |
486 | ||
487 | return ux->acceptcon; | |
488 | } | |
489 | ||
f9771d8d MY |
490 | static unsigned int unix_get_hash(struct file *file) |
491 | { | |
492 | return (unsigned int)(file->stat.st_ino % UINT_MAX); | |
493 | } | |
494 | ||
495 | static bool unix_is_suitable_ipc(struct ipc *ipc, struct file *file) | |
496 | { | |
497 | return ((struct unix_ipc *)ipc)->inode == file->stat.st_ino; | |
498 | } | |
499 | ||
500 | /* For looking up an ipc struct for a sock inode, we need a sock strcuct | |
501 | * for the inode. See the signature o get_ipc(). | |
502 | * | |
503 | * However, there is a case that we have no sock strcuct for the inode; | |
504 | * in the context we know only the sock inode. | |
505 | * For the case, unix_make_dumy_sock() provides the way to make a | |
506 | * dummy sock struct for the inode. | |
507 | */ | |
508 | static void unix_make_dumy_sock(struct sock *original, ino_t ino, struct sock *dummy) | |
509 | { | |
510 | *dummy = *original; | |
511 | dummy->file.stat.st_ino = ino; | |
512 | } | |
513 | ||
514 | static struct ipc_class unix_ipc_class = { | |
515 | .size = sizeof(struct unix_ipc), | |
516 | .get_hash = unix_get_hash, | |
517 | .is_suitable_ipc = unix_is_suitable_ipc, | |
518 | .free = NULL, | |
519 | }; | |
520 | ||
521 | static struct ipc_class *unix_get_ipc_class(struct sock_xinfo *sock_xinfo __attribute__((__unused__)), | |
522 | struct sock *sock __attribute__((__unused__))) | |
523 | { | |
524 | return &unix_ipc_class; | |
525 | } | |
526 | ||
426ff07f MY |
527 | static bool unix_shutdown_chars(struct unix_xinfo *ux, char rw[2]) |
528 | { | |
529 | uint8_t mask = ux->shutdown_mask; | |
530 | ||
531 | if (is_shutdown_mask_set(mask)) { | |
532 | rw[0] = ((mask & (1 << 0))? '-': 'r'); | |
533 | rw[1] = ((mask & (1 << 1))? '-': 'w'); | |
534 | return true; | |
535 | } | |
536 | ||
537 | return false; | |
538 | } | |
539 | ||
f9771d8d MY |
540 | static inline char *unix_xstrendpoint(struct sock *sock) |
541 | { | |
542 | char *str = NULL; | |
3f3da329 MY |
543 | char shutdown_chars[3] = { 0 }; |
544 | ||
545 | if (!unix_shutdown_chars(((struct unix_xinfo *)sock->xinfo), shutdown_chars)) { | |
546 | shutdown_chars[0] = '?'; | |
547 | shutdown_chars[1] = '?'; | |
548 | } | |
549 | xasprintf(&str, "%d,%s,%d%c%c", | |
550 | sock->file.proc->pid, sock->file.proc->command, sock->file.association, | |
551 | shutdown_chars[0], shutdown_chars[1]); | |
552 | ||
f9771d8d MY |
553 | return str; |
554 | } | |
555 | ||
556 | static struct ipc *unix_get_peer_ipc(struct unix_xinfo *ux, | |
557 | struct sock *sock) | |
558 | { | |
559 | struct unix_ipc *unix_ipc; | |
560 | struct sock dummy_peer_sock; | |
561 | ||
562 | unix_ipc = ux->unix_ipc; | |
563 | if (!unix_ipc) | |
564 | return NULL; | |
565 | ||
566 | unix_make_dumy_sock(sock, unix_ipc->ipeer, &dummy_peer_sock); | |
567 | return get_ipc(&dummy_peer_sock.file); | |
568 | } | |
569 | ||
2e38e61c MY |
570 | static bool unix_fill_column(struct proc *proc __attribute__((__unused__)), |
571 | struct sock_xinfo *sock_xinfo, | |
f9771d8d | 572 | struct sock *sock, |
2e38e61c MY |
573 | struct libscols_line *ln __attribute__((__unused__)), |
574 | int column_id, | |
575 | size_t column_index __attribute__((__unused__)), | |
576 | char **str) | |
577 | { | |
578 | struct unix_xinfo *ux = (struct unix_xinfo *)sock_xinfo; | |
f9771d8d MY |
579 | struct ipc *peer_ipc; |
580 | struct list_head *e; | |
426ff07f | 581 | char shutdown_chars[3] = { 0 }; |
2e38e61c | 582 | |
854ddd0d | 583 | switch (column_id) { |
2e38e61c MY |
584 | case COL_UNIX_PATH: |
585 | if (*ux->path) { | |
e0dc84da | 586 | *str = xstrdup(ux->path); |
2e38e61c MY |
587 | return true; |
588 | } | |
589 | break; | |
f9771d8d MY |
590 | case COL_ENDPOINTS: |
591 | peer_ipc = unix_get_peer_ipc(ux, sock); | |
592 | if (!peer_ipc) | |
593 | break; | |
594 | ||
595 | list_for_each_backwardly(e, &peer_ipc->endpoints) { | |
596 | struct sock *peer_sock = list_entry(e, struct sock, endpoint.endpoints); | |
597 | char *estr; | |
598 | ||
599 | if (*str) | |
600 | xstrputc(str, '\n'); | |
601 | estr = unix_xstrendpoint(peer_sock); | |
602 | xstrappend(str, estr); | |
603 | free(estr); | |
604 | } | |
605 | if (*str) | |
606 | return true; | |
607 | break; | |
426ff07f MY |
608 | case COL_SOCK_SHUTDOWN: |
609 | if (unix_shutdown_chars(ux, shutdown_chars)) { | |
610 | *str = xstrdup(shutdown_chars); | |
611 | return true; | |
612 | } | |
613 | break; | |
2e38e61c MY |
614 | } |
615 | ||
616 | return false; | |
617 | } | |
618 | ||
54a06438 | 619 | static const struct sock_xinfo_class unix_xinfo_class = { |
2e38e61c MY |
620 | .get_name = unix_get_name, |
621 | .get_type = unix_get_type, | |
622 | .get_state = unix_get_state, | |
45d61bff | 623 | .get_listening = unix_get_listening, |
2e38e61c | 624 | .fill_column = unix_fill_column, |
f9771d8d | 625 | .get_ipc_class = unix_get_ipc_class, |
2e38e61c MY |
626 | .free = NULL, |
627 | }; | |
628 | ||
e8e14a28 | 629 | /* UNIX_LINE_LEN need at least 54 + 21 + UNIX_PATH_MAX + 1. |
eeaf7677 MY |
630 | * |
631 | * An actual number must be used in this definition | |
632 | * since UNIX_LINE_LEN is specified as an argument for | |
633 | * stringify_value(). | |
634 | */ | |
2e38e61c MY |
635 | #define UNIX_LINE_LEN 256 |
636 | static void load_xinfo_from_proc_unix(ino_t netns_inode) | |
637 | { | |
638 | char line[UNIX_LINE_LEN]; | |
639 | FILE *unix_fp; | |
640 | ||
641 | unix_fp = fopen("/proc/net/unix", "r"); | |
642 | if (!unix_fp) | |
643 | return; | |
644 | ||
645 | if (fgets(line, sizeof(line), unix_fp) == NULL) | |
646 | goto out; | |
b3649945 | 647 | if (!(line[0] == 'N' && line[1] == 'u' && line[2] == 'm')) |
2e38e61c MY |
648 | /* Unexpected line */ |
649 | goto out; | |
650 | ||
651 | while (fgets(line, sizeof(line), unix_fp)) { | |
652 | uint64_t flags; | |
653 | uint32_t type; | |
654 | unsigned int st; | |
655 | unsigned long inode; | |
2e38e61c | 656 | struct unix_xinfo *ux; |
eeaf7677 | 657 | char path[UNIX_LINE_LEN + 1] = { 0 }; |
91a484fe | 658 | |
2e38e61c | 659 | |
eeaf7677 MY |
660 | if (sscanf(line, "%*x: %*x %*x %" SCNx64 " %x %x %lu %" |
661 | stringify_value(UNIX_LINE_LEN) "[^\n]", | |
2e38e61c MY |
662 | &flags, &type, &st, &inode, path) < 4) |
663 | continue; | |
664 | ||
665 | if (inode == 0) | |
666 | continue; | |
667 | ||
8ff22ccb | 668 | ux = xcalloc(1, sizeof(*ux)); |
2e38e61c MY |
669 | ux->sock.class = &unix_xinfo_class; |
670 | ux->sock.inode = (ino_t)inode; | |
671 | ux->sock.netns_inode = netns_inode; | |
672 | ||
673 | ux->acceptcon = !!flags; | |
674 | ux->type = type; | |
675 | ux->st = st; | |
91a484fe | 676 | xstrncpy(ux->path, path, sizeof(ux->path)); |
2e38e61c | 677 | |
2ac8d1a3 | 678 | add_sock_info(&ux->sock); |
2e38e61c MY |
679 | } |
680 | ||
681 | out: | |
682 | fclose(unix_fp); | |
683 | } | |
7b2a9be0 | 684 | |
5468bb45 MY |
685 | /* The path name extracted from /proc/net/unix is unreliable; the line oriented interface cannot |
686 | * represent a file name including newlines. With unix_refill_name(), we patch the path | |
687 | * member of unix_xinfos with information received via netlink diag interface. */ | |
688 | static void unix_refill_name(struct sock_xinfo *xinfo, const char *name, size_t len) | |
689 | { | |
690 | struct unix_xinfo *ux = (struct unix_xinfo *)xinfo; | |
691 | size_t min_len; | |
692 | ||
693 | if (len == 0) | |
694 | return; | |
695 | ||
696 | min_len = min(sizeof(ux->path) - 1, len); | |
697 | memcpy(ux->path, name, min_len); | |
698 | if (ux->path[0] == '\0') { | |
699 | ux->path[0] = '@'; | |
700 | } | |
701 | ux->path[min_len] = '\0'; | |
702 | } | |
703 | ||
704 | static bool handle_diag_unix(ino_t netns __attribute__((__unused__)), | |
705 | size_t nlmsg_len, void *nlmsg_data) | |
706 | { | |
707 | const struct unix_diag_msg *diag = nlmsg_data; | |
708 | size_t rta_len; | |
709 | ino_t inode; | |
710 | struct sock_xinfo *xinfo; | |
f9771d8d | 711 | struct unix_xinfo *unix_xinfo; |
5468bb45 MY |
712 | |
713 | if (diag->udiag_family != AF_UNIX) | |
714 | return false; | |
715 | ||
716 | if (nlmsg_len < NLMSG_LENGTH(sizeof(*diag))) | |
717 | return false; | |
718 | ||
719 | inode = (ino_t)diag->udiag_ino; | |
720 | xinfo = get_sock_xinfo(inode); | |
721 | ||
722 | if (xinfo == NULL) | |
723 | /* The socket is found in the diag response | |
724 | but not in the proc fs. */ | |
725 | return true; | |
726 | ||
727 | if (xinfo->class != &unix_xinfo_class) | |
728 | return true; | |
f9771d8d | 729 | unix_xinfo = (struct unix_xinfo *)xinfo; |
5468bb45 MY |
730 | |
731 | rta_len = nlmsg_len - NLMSG_LENGTH(sizeof(*diag)); | |
732 | for (struct rtattr *attr = (struct rtattr *)(diag + 1); | |
733 | RTA_OK(attr, rta_len); | |
734 | attr = RTA_NEXT(attr, rta_len)) { | |
735 | size_t len = RTA_PAYLOAD(attr); | |
736 | ||
737 | switch (attr->rta_type) { | |
738 | case UNIX_DIAG_NAME: | |
739 | unix_refill_name(xinfo, RTA_DATA(attr), len); | |
740 | break; | |
f9771d8d | 741 | |
426ff07f MY |
742 | case UNIX_DIAG_SHUTDOWN: |
743 | if (len < 1) | |
744 | break; | |
745 | ||
746 | unix_xinfo->shutdown_mask = *(uint8_t *)RTA_DATA(attr); | |
747 | set_shutdown_mask(unix_xinfo->shutdown_mask); | |
748 | break; | |
749 | ||
f9771d8d MY |
750 | case UNIX_DIAG_PEER: |
751 | if (len < 4) | |
752 | break; | |
753 | ||
754 | unix_xinfo->unix_ipc = (struct unix_ipc *)new_ipc(&unix_ipc_class); | |
755 | unix_xinfo->unix_ipc->inode = inode; | |
756 | unix_xinfo->unix_ipc->ipeer = (ino_t)(*(uint32_t *)RTA_DATA(attr)); | |
757 | add_ipc(&unix_xinfo->unix_ipc->ipc, inode % UINT_MAX); | |
758 | break; | |
5468bb45 MY |
759 | } |
760 | } | |
761 | return true; | |
762 | } | |
763 | ||
764 | static void load_xinfo_from_diag_unix(int diagsd, ino_t netns) | |
765 | { | |
766 | struct unix_diag_req udr = { | |
767 | .sdiag_family = AF_UNIX, | |
768 | .udiag_states = -1, /* set the all bits. */ | |
426ff07f | 769 | .udiag_show = UDIAG_SHOW_NAME | UDIAG_SHOW_PEER | UNIX_DIAG_SHUTDOWN, |
5468bb45 MY |
770 | }; |
771 | ||
772 | send_diag_request(diagsd, &udr, sizeof(udr), handle_diag_unix, netns); | |
773 | } | |
774 | ||
7b2a9be0 MY |
775 | /* |
776 | * AF_INET | |
777 | */ | |
778 | struct inet_xinfo { | |
779 | struct sock_xinfo sock; | |
643f30c7 MY |
780 | struct in_addr local_addr; |
781 | struct in_addr remote_addr; | |
7b2a9be0 MY |
782 | }; |
783 | ||
9020c2e0 | 784 | static uint32_t kernel32_to_cpu(enum sysfs_byteorder byteorder, uint32_t v) |
7b2a9be0 | 785 | { |
9020c2e0 MY |
786 | if (byteorder == SYSFS_BYTEORDER_LITTLE) |
787 | return le32_to_cpu(v); | |
788 | else | |
789 | return be32_to_cpu(v); | |
7b2a9be0 MY |
790 | } |
791 | ||
1656da13 MY |
792 | /* |
793 | * AF_INET6 | |
794 | */ | |
795 | struct inet6_xinfo { | |
796 | struct sock_xinfo sock; | |
797 | struct in6_addr local_addr; | |
798 | struct in6_addr remote_addr; | |
799 | }; | |
800 | ||
7b2a9be0 | 801 | /* |
4a2b51c1 | 802 | * L4 abstract-layer for protocols stacked on IP and IP6. |
7b2a9be0 | 803 | */ |
303b41fe | 804 | enum l4_state { |
7b2a9be0 MY |
805 | /* |
806 | * Taken from linux/include/net/tcp_states.h. | |
807 | * (GPL-2.0-or-later) | |
303b41fe MY |
808 | * |
809 | * UDP and RAW sockets also uses the contents in Linux. | |
7b2a9be0 MY |
810 | */ |
811 | TCP_ESTABLISHED = 1, | |
812 | TCP_SYN_SENT, | |
813 | TCP_SYN_RECV, | |
814 | TCP_FIN_WAIT1, | |
815 | TCP_FIN_WAIT2, | |
816 | TCP_TIME_WAIT, | |
817 | TCP_CLOSE, | |
818 | TCP_CLOSE_WAIT, | |
819 | TCP_LAST_ACK, | |
820 | TCP_LISTEN, | |
821 | TCP_CLOSING, | |
822 | TCP_NEW_SYN_RECV, | |
823 | ||
824 | TCP_MAX_STATES /* Leave at the end! */ | |
825 | }; | |
826 | ||
303b41fe | 827 | static const char *l4_decode_state(enum l4_state st) |
7b2a9be0 | 828 | { |
86242cb0 | 829 | const char * const table [] = { |
7b2a9be0 MY |
830 | [TCP_ESTABLISHED] = "established", |
831 | [TCP_SYN_SENT] = "syn-sent", | |
832 | [TCP_SYN_RECV] = "syn-recv", | |
833 | [TCP_FIN_WAIT1] = "fin-wait1", | |
834 | [TCP_FIN_WAIT2] = "fin-wait2", | |
835 | [TCP_TIME_WAIT] = "time-wait", | |
836 | [TCP_CLOSE] = "close", | |
837 | [TCP_CLOSE_WAIT] = "close-wait", | |
838 | [TCP_LAST_ACK] = "last-ack", | |
839 | [TCP_LISTEN] = "listen", | |
840 | [TCP_CLOSING] = "closing", | |
841 | [TCP_NEW_SYN_RECV] = "new-syn-recv", | |
842 | }; | |
843 | ||
844 | if (st < TCP_MAX_STATES) | |
845 | return table[st]; | |
846 | return "unknown"; | |
847 | } | |
848 | ||
4a2b51c1 | 849 | struct l4_xinfo { |
1656da13 MY |
850 | union { |
851 | struct inet_xinfo inet; | |
852 | struct inet6_xinfo inet6; | |
853 | }; | |
303b41fe | 854 | enum l4_state st; |
4a2b51c1 MY |
855 | }; |
856 | ||
2a4f2b1b | 857 | enum l4_side { L4_LOCAL, L4_REMOTE }; |
1656da13 | 858 | enum l3_decorator { L3_DECO_START, L3_DECO_END }; |
2a4f2b1b | 859 | |
4a2b51c1 MY |
860 | struct l4_xinfo_class { |
861 | struct sock_xinfo_class sock; | |
19e40223 MY |
862 | struct sock_xinfo *(*scan_line)(const struct sock_xinfo_class *, |
863 | char *, | |
864 | ino_t, | |
865 | enum sysfs_byteorder); | |
2a4f2b1b MY |
866 | void * (*get_addr)(struct l4_xinfo *, enum l4_side); |
867 | bool (*is_any_addr)(void *); | |
868 | int family; | |
1656da13 | 869 | const char *l3_decorator[2]; |
4a2b51c1 MY |
870 | }; |
871 | ||
838fa270 MY |
872 | #define l3_fill_column_handler(L3, SOCK_XINFO, COLUMN_ID, STR) __extension__ \ |
873 | ({ \ | |
874 | struct l4_xinfo_class *class = (struct l4_xinfo_class *)SOCK_XINFO->class; \ | |
875 | struct l4_xinfo *l4 = (struct l4_xinfo *)SOCK_XINFO; \ | |
876 | void *n = NULL; \ | |
877 | char s[BUFSIZ]; \ | |
878 | bool r = false; \ | |
879 | \ | |
880 | switch (COLUMN_ID) { \ | |
881 | case COL_##L3##_LADDR: \ | |
882 | n = class->get_addr(l4, L4_LOCAL); \ | |
883 | break; \ | |
884 | case COL_##L3##_RADDR: \ | |
885 | n = class->get_addr(l4, L4_REMOTE); \ | |
886 | break; \ | |
887 | default: \ | |
888 | break; \ | |
889 | } \ | |
890 | \ | |
891 | if (n && inet_ntop(class->family, n, s, sizeof(s))) { \ | |
e0dc84da | 892 | *STR = xstrdup(s); \ |
838fa270 MY |
893 | r = true; \ |
894 | } \ | |
895 | r; \ | |
896 | }) | |
897 | ||
7b2a9be0 MY |
898 | /* |
899 | * TCP | |
900 | */ | |
901 | struct tcp_xinfo { | |
4a2b51c1 | 902 | struct l4_xinfo l4; |
7b2a9be0 MY |
903 | uint16_t local_port; |
904 | uint16_t remote_port; | |
7b2a9be0 MY |
905 | }; |
906 | ||
7b2a9be0 MY |
907 | static char *tcp_get_name(struct sock_xinfo *sock_xinfo, |
908 | struct sock *sock __attribute__((__unused__))) | |
909 | { | |
910 | char *str = NULL; | |
7b2a9be0 | 911 | struct tcp_xinfo *tcp = ((struct tcp_xinfo *)sock_xinfo); |
8db07ebc | 912 | struct l4_xinfo *l4 = &tcp->l4; |
303b41fe | 913 | const char *st_str = l4_decode_state(l4->st); |
838fa270 MY |
914 | struct l4_xinfo_class *class = (struct l4_xinfo_class *)sock_xinfo->class; |
915 | void *laddr = class->get_addr(l4, L4_LOCAL); | |
916 | void *raddr = class->get_addr(l4, L4_REMOTE); | |
917 | char local_s[BUFSIZ]; | |
918 | char remote_s[BUFSIZ]; | |
1656da13 MY |
919 | const char *start = class->l3_decorator[L3_DECO_START]; |
920 | const char *end = class->l3_decorator[L3_DECO_END]; | |
7b2a9be0 | 921 | |
838fa270 | 922 | if (!inet_ntop(class->family, laddr, local_s, sizeof(local_s))) |
3b182ee7 MY |
923 | xasprintf(&str, "state=%s", st_str); |
924 | else if (l4->st == TCP_LISTEN | |
838fa270 | 925 | || !inet_ntop(class->family, raddr, remote_s, sizeof(remote_s))) |
b4e39841 | 926 | xasprintf(&str, "state=%s laddr=%s%s%s:%"PRIu16, |
3b182ee7 | 927 | st_str, |
1656da13 | 928 | start, local_s, end, tcp->local_port); |
7b2a9be0 | 929 | else |
b4e39841 | 930 | xasprintf(&str, "state=%s laddr=%s%s%s:%"PRIu16" raddr=%s%s%s:%"PRIu16, |
3b182ee7 | 931 | st_str, |
1656da13 MY |
932 | start, local_s, end, tcp->local_port, |
933 | start, remote_s, end, tcp->remote_port); | |
7b2a9be0 MY |
934 | return str; |
935 | } | |
936 | ||
937 | static char *tcp_get_type(struct sock_xinfo *sock_xinfo __attribute__((__unused__)), | |
938 | struct sock *sock __attribute__((__unused__))) | |
939 | { | |
e0dc84da | 940 | return xstrdup("stream"); |
7b2a9be0 MY |
941 | } |
942 | ||
943 | static char *tcp_get_state(struct sock_xinfo *sock_xinfo, | |
944 | struct sock *sock __attribute__((__unused__))) | |
945 | { | |
e0dc84da | 946 | return xstrdup(l4_decode_state(((struct l4_xinfo *)sock_xinfo)->st)); |
7b2a9be0 MY |
947 | } |
948 | ||
949 | static bool tcp_get_listening(struct sock_xinfo *sock_xinfo, | |
950 | struct sock *sock __attribute__((__unused__))) | |
951 | { | |
8db07ebc | 952 | return ((struct l4_xinfo *)sock_xinfo)->st == TCP_LISTEN; |
7b2a9be0 MY |
953 | } |
954 | ||
838fa270 MY |
955 | #define l4_fill_column_handler(L4, SOCK_XINFO, COLUMN_ID, STR) __extension__ \ |
956 | ({ \ | |
957 | struct l4_xinfo_class *class = (struct l4_xinfo_class *)SOCK_XINFO->class; \ | |
958 | struct tcp_xinfo *tcp = (struct tcp_xinfo *)SOCK_XINFO; \ | |
959 | struct l4_xinfo *l4 = &tcp->l4; \ | |
960 | void *n = NULL; \ | |
92a0dbce | 961 | bool has_laddr = false; \ |
73df46fc | 962 | unsigned short p; \ |
92a0dbce | 963 | bool has_lport = false; \ |
838fa270 MY |
964 | char s[BUFSIZ]; \ |
965 | bool r = true; \ | |
92a0dbce | 966 | \ |
838fa270 | 967 | switch (COLUMN_ID) { \ |
cda2b2a0 | 968 | case COL_##L4##_LADDR: \ |
838fa270 | 969 | n = class->get_addr(l4, L4_LOCAL); \ |
92a0dbce | 970 | has_laddr = true; \ |
73df46fc | 971 | p = tcp->local_port; \ |
92a0dbce | 972 | /* FALL THROUGH */ \ |
cda2b2a0 | 973 | case COL_##L4##_RADDR: \ |
92a0dbce | 974 | if (!has_laddr) { \ |
838fa270 | 975 | n = class->get_addr(l4, L4_REMOTE); \ |
73df46fc | 976 | p = tcp->remote_port; \ |
92a0dbce | 977 | } \ |
838fa270 | 978 | if (n && inet_ntop(class->family, n, s, sizeof(s))) \ |
b4e39841 | 979 | xasprintf(STR, "%s%s%s:%"PRIu16, \ |
1656da13 MY |
980 | class->l3_decorator[L3_DECO_START], \ |
981 | s, \ | |
982 | class->l3_decorator[L3_DECO_END], \ | |
983 | p); \ | |
92a0dbce | 984 | break; \ |
cda2b2a0 | 985 | case COL_##L4##_LPORT: \ |
73df46fc | 986 | p = tcp->local_port; \ |
92a0dbce MY |
987 | has_lport = true; \ |
988 | /* FALL THROUGH */ \ | |
cda2b2a0 | 989 | case COL_##L4##_RPORT: \ |
92a0dbce | 990 | if (!has_lport) \ |
73df46fc | 991 | p = tcp->remote_port; \ |
b4e39841 | 992 | xasprintf(STR, "%"PRIu16, p); \ |
92a0dbce MY |
993 | break; \ |
994 | default: \ | |
838fa270 MY |
995 | r = false; \ |
996 | break; \ | |
92a0dbce | 997 | } \ |
838fa270 MY |
998 | r; \ |
999 | }) | |
7b2a9be0 | 1000 | |
19e40223 MY |
1001 | static struct sock_xinfo *tcp_xinfo_scan_line(const struct sock_xinfo_class *class, |
1002 | char * line, | |
1003 | ino_t netns_inode, | |
1004 | enum sysfs_byteorder byteorder) | |
1005 | { | |
1006 | unsigned long local_addr; | |
1007 | unsigned long local_port; | |
1008 | unsigned long remote_addr; | |
1009 | unsigned long remote_port; | |
1010 | unsigned long st; | |
1011 | unsigned long long inode; | |
1012 | struct tcp_xinfo *tcp; | |
1013 | struct inet_xinfo *inet; | |
1014 | struct sock_xinfo *sock; | |
1015 | ||
1016 | if (sscanf(line, "%*d: %lx:%lx %lx:%lx %lx %*x:%*x %*x:%*x %*x %*u %*u %lld", | |
1017 | &local_addr, &local_port, &remote_addr, &remote_port, | |
1018 | &st, &inode) != 6) | |
1019 | return NULL; | |
1020 | ||
1021 | if (inode == 0) | |
1022 | return NULL; | |
1023 | ||
8ff22ccb | 1024 | tcp = xcalloc(1, sizeof(*tcp)); |
19e40223 MY |
1025 | inet = &tcp->l4.inet; |
1026 | sock = &inet->sock; | |
1027 | sock->class = class; | |
1028 | sock->inode = (ino_t)inode; | |
1029 | sock->netns_inode = netns_inode; | |
1030 | inet->local_addr.s_addr = kernel32_to_cpu(byteorder, local_addr); | |
1031 | tcp->local_port = local_port; | |
1032 | inet->remote_addr.s_addr = kernel32_to_cpu(byteorder, remote_addr); | |
1033 | tcp->remote_port = remote_port; | |
8db07ebc | 1034 | tcp->l4.st = st; |
19e40223 MY |
1035 | |
1036 | return sock; | |
1037 | } | |
1038 | ||
2a4f2b1b MY |
1039 | static void *tcp_xinfo_get_addr(struct l4_xinfo *l4, enum l4_side side) |
1040 | { | |
1041 | return (side == L4_LOCAL) | |
1042 | ? &l4->inet.local_addr | |
1043 | : &l4->inet.remote_addr; | |
1044 | } | |
1045 | ||
1046 | static bool tcp_xinfo_is_any_addr(void *addr) | |
1047 | { | |
1048 | return ((struct in_addr *)addr)->s_addr == INADDR_ANY; | |
1049 | } | |
1050 | ||
838fa270 MY |
1051 | static bool tcp_fill_column(struct proc *proc __attribute__((__unused__)), |
1052 | struct sock_xinfo *sock_xinfo, | |
1053 | struct sock *sock __attribute__((__unused__)), | |
1054 | struct libscols_line *ln __attribute__((__unused__)), | |
1055 | int column_id, | |
1056 | size_t column_index __attribute__((__unused__)), | |
1057 | char **str) | |
1058 | { | |
1059 | return l3_fill_column_handler(INET, sock_xinfo, column_id, str) | |
1060 | || l4_fill_column_handler(TCP, sock_xinfo, column_id, str); | |
1061 | } | |
1062 | ||
4a2b51c1 MY |
1063 | static const struct l4_xinfo_class tcp_xinfo_class = { |
1064 | .sock = { | |
1065 | .get_name = tcp_get_name, | |
1066 | .get_type = tcp_get_type, | |
1067 | .get_state = tcp_get_state, | |
1068 | .get_listening = tcp_get_listening, | |
1069 | .fill_column = tcp_fill_column, | |
1070 | .free = NULL, | |
1071 | }, | |
19e40223 | 1072 | .scan_line = tcp_xinfo_scan_line, |
2a4f2b1b MY |
1073 | .get_addr = tcp_xinfo_get_addr, |
1074 | .is_any_addr = tcp_xinfo_is_any_addr, | |
1075 | .family = AF_INET, | |
1656da13 | 1076 | .l3_decorator = {"", ""}, |
7b2a9be0 MY |
1077 | }; |
1078 | ||
cda2b2a0 | 1079 | static bool L4_verify_initial_line(const char *line) |
58d2f31e MY |
1080 | { |
1081 | /* At least we expect two white spaces. */ | |
cb097fb7 | 1082 | if (strncmp(line, " ", 2) != 0) |
58d2f31e MY |
1083 | return false; |
1084 | line += 2; | |
1085 | ||
1086 | /* Skip white spaces. */ | |
4658bfb1 | 1087 | line = skip_space(line); |
58d2f31e | 1088 | |
621cf7e9 | 1089 | return strncmp(line, "sl", 2) == 0; |
db79a100 TW |
1090 | } |
1091 | ||
7b2a9be0 | 1092 | #define TCP_LINE_LEN 256 |
cda2b2a0 | 1093 | static void load_xinfo_from_proc_inet_L4(ino_t netns_inode, const char *proc_file, |
891b24b7 MY |
1094 | const struct l4_xinfo_class *class, |
1095 | enum sysfs_byteorder byteorder) | |
7b2a9be0 MY |
1096 | { |
1097 | char line[TCP_LINE_LEN]; | |
1098 | FILE *tcp_fp; | |
1099 | ||
92a0dbce | 1100 | tcp_fp = fopen(proc_file, "r"); |
7b2a9be0 MY |
1101 | if (!tcp_fp) |
1102 | return; | |
1103 | ||
1104 | if (fgets(line, sizeof(line), tcp_fp) == NULL) | |
1105 | goto out; | |
cda2b2a0 | 1106 | if (!L4_verify_initial_line(line)) |
7b2a9be0 MY |
1107 | /* Unexpected line */ |
1108 | goto out; | |
1109 | ||
1110 | while (fgets(line, sizeof(line), tcp_fp)) { | |
19e40223 MY |
1111 | struct sock_xinfo *sock = class->scan_line(&class->sock, line, netns_inode, byteorder); |
1112 | if (sock) | |
1113 | add_sock_info(sock); | |
7b2a9be0 MY |
1114 | } |
1115 | ||
1116 | out: | |
1117 | fclose(tcp_fp); | |
1118 | } | |
92a0dbce | 1119 | |
891b24b7 | 1120 | static void load_xinfo_from_proc_tcp(ino_t netns_inode, enum sysfs_byteorder byteorder) |
92a0dbce | 1121 | { |
cda2b2a0 | 1122 | load_xinfo_from_proc_inet_L4(netns_inode, |
58d2f31e | 1123 | "/proc/net/tcp", |
891b24b7 MY |
1124 | &tcp_xinfo_class, |
1125 | byteorder); | |
92a0dbce MY |
1126 | } |
1127 | ||
1128 | /* | |
1129 | * UDP | |
1130 | */ | |
1131 | static char *udp_get_name(struct sock_xinfo *sock_xinfo, | |
1132 | struct sock *sock __attribute__((__unused__))) | |
1133 | { | |
1134 | char *str = NULL; | |
92a0dbce | 1135 | struct tcp_xinfo *tcp = ((struct tcp_xinfo *)sock_xinfo); |
8db07ebc | 1136 | struct l4_xinfo *l4 = &tcp->l4; |
8db07ebc | 1137 | unsigned int st = l4->st; |
303b41fe | 1138 | const char *st_str = l4_decode_state(st); |
838fa270 MY |
1139 | struct l4_xinfo_class *class = (struct l4_xinfo_class *)sock_xinfo->class; |
1140 | void *laddr = class->get_addr(l4, L4_LOCAL); | |
1141 | void *raddr = class->get_addr(l4, L4_REMOTE); | |
1142 | char local_s[BUFSIZ]; | |
1143 | char remote_s[BUFSIZ]; | |
28cf2b21 MY |
1144 | const char *start = class->l3_decorator[L3_DECO_START]; |
1145 | const char *end = class->l3_decorator[L3_DECO_END]; | |
92a0dbce | 1146 | |
838fa270 | 1147 | if (!inet_ntop(class->family, laddr, local_s, sizeof(local_s))) |
3b182ee7 | 1148 | xasprintf(&str, "state=%s", st_str); |
838fa270 MY |
1149 | else if ((class->is_any_addr(raddr) && tcp->remote_port == 0) |
1150 | || !inet_ntop(class->family, raddr, remote_s, sizeof(remote_s))) | |
28cf2b21 | 1151 | xasprintf(&str, "state=%s laddr=%s%s%s:%"PRIu16, |
3b182ee7 | 1152 | st_str, |
28cf2b21 | 1153 | start, local_s, end, tcp->local_port); |
92a0dbce | 1154 | else |
28cf2b21 | 1155 | xasprintf(&str, "state=%s laddr=%s%s%s:%"PRIu16" raddr=%s%s%s:%"PRIu16, |
3b182ee7 | 1156 | st_str, |
28cf2b21 MY |
1157 | start, local_s, end, tcp->local_port, |
1158 | start, remote_s, end, tcp->remote_port); | |
92a0dbce MY |
1159 | return str; |
1160 | } | |
1161 | ||
1162 | static char *udp_get_type(struct sock_xinfo *sock_xinfo __attribute__((__unused__)), | |
1163 | struct sock *sock __attribute__((__unused__))) | |
1164 | { | |
e0dc84da | 1165 | return xstrdup("dgram"); |
92a0dbce MY |
1166 | } |
1167 | ||
838fa270 MY |
1168 | static bool udp_fill_column(struct proc *proc __attribute__((__unused__)), |
1169 | struct sock_xinfo *sock_xinfo, | |
1170 | struct sock *sock __attribute__((__unused__)), | |
1171 | struct libscols_line *ln __attribute__((__unused__)), | |
1172 | int column_id, | |
1173 | size_t column_index __attribute__((__unused__)), | |
1174 | char **str) | |
1175 | { | |
1176 | return l3_fill_column_handler(INET, sock_xinfo, column_id, str) | |
1177 | || l4_fill_column_handler(UDP, sock_xinfo, column_id, str); | |
1178 | } | |
1179 | ||
4a2b51c1 MY |
1180 | static const struct l4_xinfo_class udp_xinfo_class = { |
1181 | .sock = { | |
1182 | .get_name = udp_get_name, | |
1183 | .get_type = udp_get_type, | |
1184 | .get_state = tcp_get_state, | |
1185 | .get_listening = NULL, | |
1186 | .fill_column = udp_fill_column, | |
1187 | .free = NULL, | |
1188 | }, | |
19e40223 | 1189 | .scan_line = tcp_xinfo_scan_line, |
2a4f2b1b MY |
1190 | .get_addr = tcp_xinfo_get_addr, |
1191 | .is_any_addr = tcp_xinfo_is_any_addr, | |
1192 | .family = AF_INET, | |
1656da13 | 1193 | .l3_decorator = {"", ""}, |
92a0dbce MY |
1194 | }; |
1195 | ||
891b24b7 | 1196 | static void load_xinfo_from_proc_udp(ino_t netns_inode, enum sysfs_byteorder byteorder) |
92a0dbce | 1197 | { |
cda2b2a0 | 1198 | load_xinfo_from_proc_inet_L4(netns_inode, |
92a0dbce | 1199 | "/proc/net/udp", |
891b24b7 MY |
1200 | &udp_xinfo_class, |
1201 | byteorder); | |
92a0dbce | 1202 | } |
0188afb3 | 1203 | |
c779f412 MY |
1204 | /* |
1205 | * UDP-Lite | |
1206 | */ | |
1207 | static bool udplite_fill_column(struct proc *proc __attribute__((__unused__)), | |
1208 | struct sock_xinfo *sock_xinfo, | |
1209 | struct sock *sock __attribute__((__unused__)), | |
1210 | struct libscols_line *ln __attribute__((__unused__)), | |
1211 | int column_id, | |
1212 | size_t column_index __attribute__((__unused__)), | |
1213 | char **str) | |
1214 | { | |
1215 | return l3_fill_column_handler(INET, sock_xinfo, column_id, str) | |
1216 | || l4_fill_column_handler(UDPLITE, sock_xinfo, column_id, str); | |
1217 | } | |
1218 | ||
1219 | static const struct l4_xinfo_class udplite_xinfo_class = { | |
1220 | .sock = { | |
1221 | .get_name = udp_get_name, | |
1222 | .get_type = udp_get_type, | |
1223 | .get_state = tcp_get_state, | |
1224 | .get_listening = NULL, | |
1225 | .fill_column = udplite_fill_column, | |
1226 | .free = NULL, | |
1227 | }, | |
1228 | .scan_line = tcp_xinfo_scan_line, | |
1229 | .get_addr = tcp_xinfo_get_addr, | |
1230 | .is_any_addr = tcp_xinfo_is_any_addr, | |
1231 | .family = AF_INET, | |
1232 | .l3_decorator = {"", ""}, | |
1233 | }; | |
1234 | ||
891b24b7 | 1235 | static void load_xinfo_from_proc_udplite(ino_t netns_inode, enum sysfs_byteorder byteorder) |
c779f412 MY |
1236 | { |
1237 | load_xinfo_from_proc_inet_L4(netns_inode, | |
1238 | "/proc/net/udplite", | |
891b24b7 MY |
1239 | &udplite_xinfo_class, |
1240 | byteorder); | |
c779f412 MY |
1241 | } |
1242 | ||
0188afb3 MY |
1243 | /* |
1244 | * RAW | |
1245 | */ | |
1246 | struct raw_xinfo { | |
1247 | struct l4_xinfo l4; | |
1248 | uint16_t protocol; | |
1249 | }; | |
1250 | ||
a7cba6f3 MY |
1251 | static char *raw_get_name_common(struct sock_xinfo *sock_xinfo, |
1252 | struct sock *sock __attribute__((__unused__)), | |
1253 | const char *port_label) | |
0188afb3 MY |
1254 | { |
1255 | char *str = NULL; | |
1256 | struct l4_xinfo_class *class = (struct l4_xinfo_class *)sock_xinfo->class; | |
1257 | struct raw_xinfo *raw = ((struct raw_xinfo *)sock_xinfo); | |
1258 | struct l4_xinfo *l4 = &raw->l4; | |
303b41fe | 1259 | const char *st_str = l4_decode_state(l4->st); |
0188afb3 MY |
1260 | void *laddr = class->get_addr(l4, L4_LOCAL); |
1261 | void *raddr = class->get_addr(l4, L4_REMOTE); | |
1262 | char local_s[BUFSIZ]; | |
1263 | char remote_s[BUFSIZ]; | |
1264 | ||
1265 | if (!inet_ntop(class->family, laddr, local_s, sizeof(local_s))) | |
1266 | xasprintf(&str, "state=%s", st_str); | |
1267 | else if (class->is_any_addr(raddr) | |
1268 | || !inet_ntop(class->family, raddr, remote_s, sizeof(remote_s))) | |
a7cba6f3 | 1269 | xasprintf(&str, "state=%s %s=%"PRIu16" laddr=%s", |
0188afb3 | 1270 | st_str, |
a7cba6f3 | 1271 | port_label, |
0188afb3 MY |
1272 | raw->protocol, local_s); |
1273 | else | |
a7cba6f3 | 1274 | xasprintf(&str, "state=%s %s=%"PRIu16" laddr=%s raddr=%s", |
0188afb3 | 1275 | st_str, |
a7cba6f3 | 1276 | port_label, |
0188afb3 MY |
1277 | raw->protocol, local_s, remote_s); |
1278 | return str; | |
1279 | } | |
1280 | ||
a7cba6f3 MY |
1281 | static char *raw_get_name(struct sock_xinfo *sock_xinfo, |
1282 | struct sock *sock __attribute__((__unused__))) | |
1283 | { | |
1284 | return raw_get_name_common(sock_xinfo, sock, "protocol"); | |
1285 | } | |
1286 | ||
0188afb3 MY |
1287 | static char *raw_get_type(struct sock_xinfo *sock_xinfo __attribute__((__unused__)), |
1288 | struct sock *sock __attribute__((__unused__))) | |
1289 | { | |
e0dc84da | 1290 | return xstrdup("raw"); |
0188afb3 MY |
1291 | } |
1292 | ||
1293 | static bool raw_fill_column(struct proc *proc __attribute__((__unused__)), | |
1294 | struct sock_xinfo *sock_xinfo, | |
1295 | struct sock *sock __attribute__((__unused__)), | |
1296 | struct libscols_line *ln __attribute__((__unused__)), | |
1297 | int column_id, | |
1298 | size_t column_index __attribute__((__unused__)), | |
1299 | char **str) | |
1300 | { | |
1301 | if (l3_fill_column_handler(INET, sock_xinfo, column_id, str)) | |
1302 | return true; | |
1303 | ||
1304 | if (column_id == COL_RAW_PROTOCOL) { | |
b4e39841 | 1305 | xasprintf(str, "%"PRIu16, |
73df46fc | 1306 | ((struct raw_xinfo *)sock_xinfo)->protocol); |
0188afb3 MY |
1307 | return true; |
1308 | } | |
1309 | ||
1310 | return false; | |
1311 | } | |
1312 | ||
1313 | static struct sock_xinfo *raw_xinfo_scan_line(const struct sock_xinfo_class *class, | |
1314 | char * line, | |
1315 | ino_t netns_inode, | |
1316 | enum sysfs_byteorder byteorder) | |
1317 | { | |
1318 | unsigned long local_addr; | |
1319 | unsigned long protocol; | |
1320 | unsigned long remote_addr; | |
1321 | unsigned long st; | |
1322 | unsigned long long inode; | |
1323 | struct raw_xinfo *raw; | |
1324 | struct inet_xinfo *inet; | |
1325 | struct sock_xinfo *sock; | |
1326 | ||
1327 | if (sscanf(line, "%*d: %lx:%lx %lx:%*x %lx %*x:%*x %*x:%*x %*x %*u %*u %lld", | |
1328 | &local_addr, &protocol, &remote_addr, | |
1329 | &st, &inode) != 5) | |
1330 | return NULL; | |
1331 | ||
1332 | if (inode == 0) | |
1333 | return NULL; | |
1334 | ||
8ff22ccb | 1335 | raw = xcalloc(1, sizeof(*raw)); |
0188afb3 MY |
1336 | inet = &raw->l4.inet; |
1337 | sock = &inet->sock; | |
1338 | sock->class = class; | |
1339 | sock->inode = (ino_t)inode; | |
1340 | sock->netns_inode = netns_inode; | |
1341 | inet->local_addr.s_addr = kernel32_to_cpu(byteorder, local_addr); | |
1342 | inet->remote_addr.s_addr = kernel32_to_cpu(byteorder, remote_addr); | |
1343 | raw->protocol = protocol; | |
1344 | raw->l4.st = st; | |
1345 | ||
1346 | return sock; | |
1347 | } | |
1348 | ||
1349 | static const struct l4_xinfo_class raw_xinfo_class = { | |
1350 | .sock = { | |
1351 | .get_name = raw_get_name, | |
1352 | .get_type = raw_get_type, | |
1353 | .get_state = tcp_get_state, | |
1354 | .get_listening = NULL, | |
1355 | .fill_column = raw_fill_column, | |
1356 | .free = NULL, | |
1357 | }, | |
1358 | .scan_line = raw_xinfo_scan_line, | |
1359 | .get_addr = tcp_xinfo_get_addr, | |
1360 | .is_any_addr = tcp_xinfo_is_any_addr, | |
1361 | .family = AF_INET, | |
1656da13 | 1362 | .l3_decorator = {"", ""}, |
0188afb3 MY |
1363 | }; |
1364 | ||
891b24b7 | 1365 | static void load_xinfo_from_proc_raw(ino_t netns_inode, enum sysfs_byteorder byteorder) |
0188afb3 MY |
1366 | { |
1367 | load_xinfo_from_proc_inet_L4(netns_inode, | |
1368 | "/proc/net/raw", | |
891b24b7 MY |
1369 | &raw_xinfo_class, |
1370 | byteorder); | |
0188afb3 | 1371 | } |
1656da13 | 1372 | |
a7cba6f3 MY |
1373 | /* |
1374 | * PING | |
1375 | */ | |
1376 | static char *ping_get_name(struct sock_xinfo *sock_xinfo, | |
1377 | struct sock *sock __attribute__((__unused__))) | |
1378 | { | |
1379 | return raw_get_name_common(sock_xinfo, sock, "id"); | |
1380 | } | |
1381 | ||
1382 | static char *ping_get_type(struct sock_xinfo *sock_xinfo __attribute__((__unused__)), | |
1383 | struct sock *sock __attribute__((__unused__))) | |
1384 | { | |
e0dc84da | 1385 | return xstrdup("dgram"); |
a7cba6f3 MY |
1386 | } |
1387 | ||
1388 | static bool ping_fill_column(struct proc *proc __attribute__((__unused__)), | |
1389 | struct sock_xinfo *sock_xinfo, | |
1390 | struct sock *sock __attribute__((__unused__)), | |
1391 | struct libscols_line *ln __attribute__((__unused__)), | |
1392 | int column_id, | |
1393 | size_t column_index __attribute__((__unused__)), | |
1394 | char **str) | |
1395 | { | |
1396 | if (l3_fill_column_handler(INET, sock_xinfo, column_id, str)) | |
1397 | return true; | |
1398 | ||
1399 | if (column_id == COL_PING_ID) { | |
1400 | xasprintf(str, "%"PRIu16, | |
1401 | ((struct raw_xinfo *)sock_xinfo)->protocol); | |
1402 | return true; | |
1403 | } | |
1404 | ||
1405 | return false; | |
1406 | } | |
1407 | ||
1408 | static const struct l4_xinfo_class ping_xinfo_class = { | |
1409 | .sock = { | |
1410 | .get_name = ping_get_name, | |
1411 | .get_type = ping_get_type, | |
1412 | .get_state = tcp_get_state, | |
1413 | .get_listening = NULL, | |
1414 | .fill_column = ping_fill_column, | |
1415 | .free = NULL, | |
1416 | }, | |
1417 | .scan_line = raw_xinfo_scan_line, | |
1418 | .get_addr = tcp_xinfo_get_addr, | |
1419 | .is_any_addr = tcp_xinfo_is_any_addr, | |
1420 | .family = AF_INET, | |
1421 | .l3_decorator = {"", ""}, | |
1422 | }; | |
1423 | ||
891b24b7 | 1424 | static void load_xinfo_from_proc_icmp(ino_t netns_inode, enum sysfs_byteorder byteorder) |
a7cba6f3 MY |
1425 | { |
1426 | load_xinfo_from_proc_inet_L4(netns_inode, | |
1427 | "/proc/net/icmp", | |
891b24b7 MY |
1428 | &ping_xinfo_class, |
1429 | byteorder); | |
a7cba6f3 MY |
1430 | } |
1431 | ||
1656da13 MY |
1432 | /* |
1433 | * TCP6 | |
1434 | */ | |
1435 | static struct sock_xinfo *tcp6_xinfo_scan_line(const struct sock_xinfo_class *class, | |
1436 | char * line, | |
1437 | ino_t netns_inode, | |
1438 | enum sysfs_byteorder byteorder) | |
1439 | { | |
1440 | uint32_t local_addr[4]; | |
1441 | unsigned int local_port; | |
1442 | uint32_t remote_addr[4]; | |
1443 | unsigned int remote_port; | |
1444 | unsigned int st; | |
1445 | unsigned long inode; | |
1446 | struct tcp_xinfo *tcp; | |
1447 | struct inet6_xinfo *inet6; | |
1448 | struct sock_xinfo *sock; | |
1449 | ||
1450 | if (sscanf(line, | |
1451 | "%*d: " | |
1452 | "%08x%08x%08x%08x:%04x " | |
1453 | "%08x%08x%08x%08x:%04x " | |
1454 | "%x %*x:%*x %*x:%*x %*x %*u %*d %lu ", | |
1455 | local_addr+0, local_addr+1, local_addr+2, local_addr+3, &local_port, | |
1456 | remote_addr+0, remote_addr+1, remote_addr+2, remote_addr+3, &remote_port, | |
1457 | &st, &inode) != 12) | |
1458 | return NULL; | |
1459 | ||
1460 | if (inode == 0) | |
1461 | return NULL; | |
1462 | ||
8ff22ccb | 1463 | tcp = xmalloc(sizeof(*tcp)); |
1656da13 MY |
1464 | inet6 = &tcp->l4.inet6; |
1465 | sock = &inet6->sock; | |
1466 | sock->class = class; | |
1467 | sock->inode = (ino_t)inode; | |
1468 | sock->netns_inode = netns_inode; | |
1469 | tcp->local_port = local_port; | |
1470 | for (int i = 0; i < 4; i++) { | |
1471 | inet6->local_addr.s6_addr32[i] = kernel32_to_cpu(byteorder, local_addr[i]); | |
1472 | inet6->remote_addr.s6_addr32[i] = kernel32_to_cpu(byteorder, remote_addr[i]); | |
1473 | } | |
1474 | tcp->remote_port = remote_port; | |
1475 | tcp->l4.st = st; | |
1476 | ||
1477 | return sock; | |
1478 | } | |
1479 | ||
1480 | static bool tcp6_fill_column(struct proc *proc __attribute__((__unused__)), | |
1481 | struct sock_xinfo *sock_xinfo, | |
1482 | struct sock *sock __attribute__((__unused__)), | |
1483 | struct libscols_line *ln __attribute__((__unused__)), | |
1484 | int column_id, | |
1485 | size_t column_index __attribute__((__unused__)), | |
1486 | char **str) | |
1487 | { | |
1488 | return l3_fill_column_handler(INET6, sock_xinfo, column_id, str) | |
1489 | || l4_fill_column_handler(TCP, sock_xinfo, column_id, str); | |
1490 | } | |
1491 | ||
1492 | static void *tcp6_xinfo_get_addr(struct l4_xinfo * l4, enum l4_side side) | |
1493 | { | |
1494 | return (side == L4_LOCAL) | |
1495 | ? &l4->inet6.local_addr | |
1496 | : &l4->inet6.remote_addr; | |
1497 | } | |
1498 | ||
1499 | static bool tcp6_xinfo_is_any_addr(void *addr) | |
1500 | { | |
1501 | return IN6_ARE_ADDR_EQUAL(addr, &(struct in6_addr)IN6ADDR_ANY_INIT); | |
1502 | } | |
1503 | ||
1504 | static const struct l4_xinfo_class tcp6_xinfo_class = { | |
1505 | .sock = { | |
1506 | .get_name = tcp_get_name, | |
1507 | .get_type = tcp_get_type, | |
1508 | .get_state = tcp_get_state, | |
1509 | .get_listening = tcp_get_listening, | |
1510 | .fill_column = tcp6_fill_column, | |
1511 | .free = NULL, | |
1512 | }, | |
1513 | .scan_line = tcp6_xinfo_scan_line, | |
1514 | .get_addr = tcp6_xinfo_get_addr, | |
1515 | .is_any_addr = tcp6_xinfo_is_any_addr, | |
1516 | .family = AF_INET6, | |
1517 | .l3_decorator = {"[", "]"}, | |
1518 | }; | |
1519 | ||
891b24b7 | 1520 | static void load_xinfo_from_proc_tcp6(ino_t netns_inode, enum sysfs_byteorder byteorder) |
1656da13 MY |
1521 | { |
1522 | load_xinfo_from_proc_inet_L4(netns_inode, | |
1523 | "/proc/net/tcp6", | |
891b24b7 MY |
1524 | &tcp6_xinfo_class, |
1525 | byteorder); | |
1656da13 | 1526 | } |
28cf2b21 MY |
1527 | |
1528 | /* | |
1529 | * UDP6 | |
1530 | */ | |
1531 | static bool udp6_fill_column(struct proc *proc __attribute__((__unused__)), | |
1532 | struct sock_xinfo *sock_xinfo, | |
1533 | struct sock *sock __attribute__((__unused__)), | |
1534 | struct libscols_line *ln __attribute__((__unused__)), | |
1535 | int column_id, | |
1536 | size_t column_index __attribute__((__unused__)), | |
1537 | char **str) | |
1538 | { | |
1539 | return l3_fill_column_handler(INET6, sock_xinfo, column_id, str) | |
1540 | || l4_fill_column_handler(UDP, sock_xinfo, column_id, str); | |
1541 | } | |
1542 | ||
1543 | static const struct l4_xinfo_class udp6_xinfo_class = { | |
1544 | .sock = { | |
1545 | .get_name = udp_get_name, | |
1546 | .get_type = udp_get_type, | |
1547 | .get_state = tcp_get_state, | |
1548 | .get_listening = NULL, | |
1549 | .fill_column = udp6_fill_column, | |
1550 | .free = NULL, | |
1551 | }, | |
1552 | .scan_line = tcp6_xinfo_scan_line, | |
1553 | .get_addr = tcp6_xinfo_get_addr, | |
1554 | .is_any_addr = tcp6_xinfo_is_any_addr, | |
1555 | .family = AF_INET6, | |
1556 | .l3_decorator = {"[", "]"}, | |
1557 | }; | |
1558 | ||
891b24b7 | 1559 | static void load_xinfo_from_proc_udp6(ino_t netns_inode, enum sysfs_byteorder byteorder) |
28cf2b21 MY |
1560 | { |
1561 | load_xinfo_from_proc_inet_L4(netns_inode, | |
1562 | "/proc/net/udp6", | |
891b24b7 MY |
1563 | &udp6_xinfo_class, |
1564 | byteorder); | |
28cf2b21 | 1565 | } |
2dd373c3 | 1566 | |
93bca151 MY |
1567 | /* |
1568 | * UDPLITEv6 | |
1569 | */ | |
1570 | static bool udplite6_fill_column(struct proc *proc __attribute__((__unused__)), | |
1571 | struct sock_xinfo *sock_xinfo, | |
1572 | struct sock *sock __attribute__((__unused__)), | |
1573 | struct libscols_line *ln __attribute__((__unused__)), | |
1574 | int column_id, | |
1575 | size_t column_index __attribute__((__unused__)), | |
1576 | char **str) | |
1577 | { | |
1578 | return l3_fill_column_handler(INET6, sock_xinfo, column_id, str) | |
1579 | || l4_fill_column_handler(UDPLITE, sock_xinfo, column_id, str); | |
1580 | } | |
1581 | ||
1582 | static const struct l4_xinfo_class udplite6_xinfo_class = { | |
1583 | .sock = { | |
1584 | .get_name = udp_get_name, | |
1585 | .get_type = udp_get_type, | |
1586 | .get_state = tcp_get_state, | |
1587 | .get_listening = NULL, | |
1588 | .fill_column = udplite6_fill_column, | |
1589 | .free = NULL, | |
1590 | }, | |
1591 | .scan_line = tcp6_xinfo_scan_line, | |
1592 | .get_addr = tcp6_xinfo_get_addr, | |
1593 | .is_any_addr = tcp6_xinfo_is_any_addr, | |
1594 | .family = AF_INET6, | |
1595 | .l3_decorator = {"[", "]"}, | |
1596 | }; | |
1597 | ||
891b24b7 | 1598 | static void load_xinfo_from_proc_udplite6(ino_t netns_inode, enum sysfs_byteorder byteorder) |
93bca151 MY |
1599 | { |
1600 | load_xinfo_from_proc_inet_L4(netns_inode, | |
1601 | "/proc/net/udplite6", | |
891b24b7 MY |
1602 | &udplite6_xinfo_class, |
1603 | byteorder); | |
93bca151 MY |
1604 | } |
1605 | ||
2dd373c3 MY |
1606 | /* |
1607 | * RAW6 | |
1608 | */ | |
1609 | static struct sock_xinfo *raw6_xinfo_scan_line(const struct sock_xinfo_class *class, | |
1610 | char * line, | |
1611 | ino_t netns_inode, | |
1612 | enum sysfs_byteorder byteorder) | |
1613 | { | |
1614 | uint32_t local_addr[4]; | |
1615 | unsigned int protocol; | |
1616 | uint32_t remote_addr[4]; | |
1617 | unsigned int st; | |
1618 | unsigned long inode; | |
1619 | struct raw_xinfo *raw; | |
1620 | struct inet6_xinfo *inet6; | |
1621 | struct sock_xinfo *sock; | |
1622 | ||
1623 | if (sscanf(line, | |
1624 | "%*d: " | |
1625 | "%08x%08x%08x%08x:%04x " | |
1626 | "%08x%08x%08x%08x:0000 " | |
1627 | "%x %*x:%*x %*x:%*x %*x %*u %*d %lu ", | |
1628 | local_addr+0, local_addr+1, local_addr+2, local_addr+3, &protocol, | |
1629 | remote_addr+0, remote_addr+1, remote_addr+2, remote_addr+3, | |
1630 | &st, &inode) != 11) | |
1631 | return NULL; | |
1632 | ||
1633 | if (inode == 0) | |
1634 | return NULL; | |
1635 | ||
1636 | raw = xmalloc(sizeof(*raw)); | |
1637 | inet6 = &raw->l4.inet6; | |
1638 | sock = &inet6->sock; | |
1639 | sock->class = class; | |
1640 | sock->inode = (ino_t)inode; | |
1641 | sock->netns_inode = netns_inode; | |
1642 | for (int i = 0; i < 4; i++) { | |
1643 | inet6->local_addr.s6_addr32[i] = kernel32_to_cpu(byteorder, local_addr[i]); | |
1644 | inet6->remote_addr.s6_addr32[i] = kernel32_to_cpu(byteorder, remote_addr[i]); | |
1645 | } | |
1646 | raw->protocol = protocol; | |
1647 | raw->l4.st = st; | |
1648 | ||
1649 | return sock; | |
1650 | } | |
1651 | ||
1652 | static bool raw6_fill_column(struct proc *proc __attribute__((__unused__)), | |
1653 | struct sock_xinfo *sock_xinfo, | |
1654 | struct sock *sock __attribute__((__unused__)), | |
1655 | struct libscols_line *ln __attribute__((__unused__)), | |
1656 | int column_id, | |
1657 | size_t column_index __attribute__((__unused__)), | |
1658 | char **str) | |
1659 | { | |
1660 | struct raw_xinfo *raw; | |
1661 | ||
1662 | if (l3_fill_column_handler(INET6, sock_xinfo, column_id, str)) | |
1663 | return true; | |
1664 | ||
1665 | raw = (struct raw_xinfo *)sock_xinfo; | |
1666 | if (column_id == COL_RAW_PROTOCOL) { | |
1667 | xasprintf(str, "%"PRIu16, raw->protocol); | |
1668 | return true; | |
1669 | } | |
1670 | ||
1671 | return false; | |
1672 | } | |
1673 | ||
1674 | static const struct l4_xinfo_class raw6_xinfo_class = { | |
1675 | .sock = { | |
1676 | .get_name = raw_get_name, | |
1677 | .get_type = raw_get_type, | |
1678 | .get_state = tcp_get_state, | |
1679 | .get_listening = NULL, | |
1680 | .fill_column = raw6_fill_column, | |
1681 | .free = NULL, | |
1682 | }, | |
1683 | .scan_line = raw6_xinfo_scan_line, | |
1684 | .get_addr = tcp6_xinfo_get_addr, | |
1685 | .is_any_addr = tcp6_xinfo_is_any_addr, | |
1686 | .family = AF_INET6, | |
1687 | .l3_decorator = {"[", "]"}, | |
1688 | }; | |
1689 | ||
891b24b7 | 1690 | static void load_xinfo_from_proc_raw6(ino_t netns_inode, enum sysfs_byteorder byteorder) |
2dd373c3 MY |
1691 | { |
1692 | load_xinfo_from_proc_inet_L4(netns_inode, | |
1693 | "/proc/net/raw6", | |
891b24b7 MY |
1694 | &raw6_xinfo_class, |
1695 | byteorder); | |
2dd373c3 | 1696 | } |
0b1dfd03 MY |
1697 | |
1698 | /* | |
1699 | * PINGv6 | |
1700 | */ | |
1701 | static bool ping6_fill_column(struct proc *proc __attribute__((__unused__)), | |
1702 | struct sock_xinfo *sock_xinfo, | |
1703 | struct sock *sock __attribute__((__unused__)), | |
1704 | struct libscols_line *ln __attribute__((__unused__)), | |
1705 | int column_id, | |
1706 | size_t column_index __attribute__((__unused__)), | |
1707 | char **str) | |
1708 | { | |
1709 | if (l3_fill_column_handler(INET6, sock_xinfo, column_id, str)) | |
1710 | return true; | |
1711 | ||
1712 | if (column_id == COL_PING_ID) { | |
1713 | xasprintf(str, "%"PRIu16, | |
1714 | ((struct raw_xinfo *)sock_xinfo)->protocol); | |
1715 | return true; | |
1716 | } | |
1717 | ||
1718 | return false; | |
1719 | } | |
1720 | ||
1721 | static const struct l4_xinfo_class ping6_xinfo_class = { | |
1722 | .sock = { | |
1723 | .get_name = ping_get_name, | |
1724 | .get_type = ping_get_type, | |
1725 | .get_state = tcp_get_state, | |
1726 | .get_listening = NULL, | |
1727 | .fill_column = ping6_fill_column, | |
1728 | .free = NULL, | |
1729 | }, | |
1730 | .scan_line = raw6_xinfo_scan_line, | |
1731 | .get_addr = tcp6_xinfo_get_addr, | |
1732 | .is_any_addr = tcp6_xinfo_is_any_addr, | |
1733 | .family = AF_INET6, | |
1734 | .l3_decorator = {"[", "]"}, | |
1735 | }; | |
1736 | ||
891b24b7 | 1737 | static void load_xinfo_from_proc_icmp6(ino_t netns_inode, enum sysfs_byteorder byteorder) |
0b1dfd03 MY |
1738 | { |
1739 | load_xinfo_from_proc_inet_L4(netns_inode, | |
1740 | "/proc/net/icmp6", | |
891b24b7 MY |
1741 | &ping6_xinfo_class, |
1742 | byteorder); | |
0b1dfd03 | 1743 | } |
b53cc896 MY |
1744 | |
1745 | /* | |
1746 | * NETLINK | |
1747 | */ | |
1748 | struct netlink_xinfo { | |
1749 | struct sock_xinfo sock; | |
1750 | uint16_t protocol; | |
1751 | uint32_t lportid; /* netlink_diag may provide rportid. */ | |
1752 | uint32_t groups; | |
1753 | }; | |
1754 | ||
1755 | static const char *netlink_decode_protocol(uint16_t protocol) | |
1756 | { | |
1757 | switch (protocol) { | |
1758 | case NETLINK_ROUTE: | |
1759 | return "route"; | |
1760 | case NETLINK_UNUSED: | |
1761 | return "unused"; | |
1762 | case NETLINK_USERSOCK: | |
1763 | return "usersock"; | |
1764 | case NETLINK_FIREWALL: | |
1765 | return "firewall"; | |
1766 | case NETLINK_SOCK_DIAG: | |
1767 | return "sock_diag"; | |
1768 | case NETLINK_NFLOG: | |
1769 | return "nflog"; | |
1770 | case NETLINK_XFRM: | |
1771 | return "xfrm"; | |
1772 | case NETLINK_SELINUX: | |
1773 | return "selinux"; | |
1774 | case NETLINK_ISCSI: | |
1775 | return "iscsi"; | |
1776 | case NETLINK_AUDIT: | |
1777 | return "audit"; | |
1778 | case NETLINK_FIB_LOOKUP: | |
1779 | return "fib_lookup"; | |
1780 | case NETLINK_CONNECTOR: | |
1781 | return "connector"; | |
1782 | case NETLINK_NETFILTER: | |
1783 | return "netfilter"; | |
1784 | case NETLINK_IP6_FW: | |
1785 | return "ip6_fw"; | |
1786 | case NETLINK_DNRTMSG: | |
1787 | return "dnrtmsg"; | |
1788 | case NETLINK_KOBJECT_UEVENT: | |
1789 | return "kobject_uevent"; | |
1790 | case NETLINK_GENERIC: | |
1791 | return "generic"; | |
1792 | case NETLINK_SCSITRANSPORT: | |
1793 | return "scsitransport"; | |
1794 | case NETLINK_ECRYPTFS: | |
1795 | return "ecryptfs"; | |
1796 | case NETLINK_RDMA: | |
1797 | return "rdma"; | |
1798 | case NETLINK_CRYPTO: | |
1799 | return "crypto"; | |
1800 | #ifdef NETLINK_SMC | |
1801 | case NETLINK_SMC: | |
1802 | return "smc"; | |
1803 | #endif | |
1804 | default: | |
1805 | return "unknown"; | |
1806 | } | |
1807 | } | |
1808 | ||
1809 | static char *netlink_get_name(struct sock_xinfo *sock_xinfo, | |
1810 | struct sock *sock __attribute__((__unused__))) | |
1811 | { | |
1812 | struct netlink_xinfo *nl = (struct netlink_xinfo *)sock_xinfo; | |
1813 | char *str = NULL; | |
1814 | const char *protocol = netlink_decode_protocol(nl->protocol); | |
1815 | ||
1816 | if (nl->groups) | |
1817 | xasprintf(&str, "protocol=%s lport=%"PRIu16 " groups=%"PRIu32, | |
1818 | protocol, | |
1819 | nl->lportid, nl->groups); | |
1820 | else | |
1821 | xasprintf(&str, "protocol=%s lport=%"PRIu16, | |
1822 | protocol, | |
1823 | nl->lportid); | |
1824 | return str; | |
1825 | } | |
1826 | ||
1827 | static char *netlink_get_type(struct sock_xinfo *sock_xinfo __attribute__((__unused__)), | |
1828 | struct sock *sock __attribute__((__unused__))) | |
1829 | { | |
e0dc84da | 1830 | return xstrdup("raw"); |
b53cc896 MY |
1831 | } |
1832 | ||
1833 | static bool netlink_fill_column(struct proc *proc __attribute__((__unused__)), | |
1834 | struct sock_xinfo *sock_xinfo, | |
1835 | struct sock *sock __attribute__((__unused__)), | |
1836 | struct libscols_line *ln __attribute__((__unused__)), | |
1837 | int column_id, | |
1838 | size_t column_index __attribute__((__unused__)), | |
1839 | char **str) | |
1840 | { | |
1841 | struct netlink_xinfo *nl = (struct netlink_xinfo *)sock_xinfo; | |
1842 | ||
1843 | switch (column_id) { | |
1844 | case COL_NETLINK_GROUPS: | |
1845 | xasprintf(str, "%"PRIu32, nl->groups); | |
1846 | return true; | |
1847 | case COL_NETLINK_LPORT: | |
1848 | xasprintf(str, "%"PRIu32, nl->lportid); | |
1849 | return true; | |
1850 | case COL_NETLINK_PROTOCOL: | |
e0dc84da | 1851 | *str = xstrdup(netlink_decode_protocol(nl->protocol)); |
b53cc896 MY |
1852 | return true; |
1853 | } | |
1854 | ||
1855 | return false; | |
1856 | } | |
1857 | ||
1858 | static const struct sock_xinfo_class netlink_xinfo_class = { | |
1859 | .get_name = netlink_get_name, | |
1860 | .get_type = netlink_get_type, | |
1861 | .get_state = NULL, | |
1862 | .get_listening = NULL, | |
1863 | .fill_column = netlink_fill_column, | |
1864 | .free = NULL, | |
1865 | }; | |
1866 | ||
1867 | static void load_xinfo_from_proc_netlink(ino_t netns_inode) | |
1868 | { | |
1869 | char line[BUFSIZ]; | |
1870 | FILE *netlink_fp; | |
1871 | ||
1872 | netlink_fp = fopen("/proc/net/netlink", "r"); | |
1873 | if (!netlink_fp) | |
1874 | return; | |
1875 | ||
1876 | if (fgets(line, sizeof(line), netlink_fp) == NULL) | |
1877 | goto out; | |
1878 | if (!(line[0] == 's' && line[1] == 'k')) | |
1879 | /* Unexpected line */ | |
1880 | goto out; | |
1881 | ||
1882 | while (fgets(line, sizeof(line), netlink_fp)) { | |
1883 | uint16_t protocol; | |
1884 | uint32_t lportid; | |
1885 | uint32_t groups; | |
1886 | unsigned long inode; | |
1887 | struct netlink_xinfo *nl; | |
1888 | ||
1889 | if (sscanf(line, "%*x %" SCNu16 " %" SCNu32 " %" SCNx32 " %*d %*d %*d %*d %*u %lu", | |
1890 | &protocol, &lportid, &groups, &inode) < 4) | |
1891 | continue; | |
1892 | ||
1893 | if (inode == 0) | |
1894 | continue; | |
1895 | ||
1896 | nl = xcalloc(1, sizeof(*nl)); | |
1897 | nl->sock.class = &netlink_xinfo_class; | |
1898 | nl->sock.inode = (ino_t)inode; | |
1899 | nl->sock.netns_inode = netns_inode; | |
1900 | ||
1901 | nl->protocol = protocol; | |
1902 | nl->lportid = lportid; | |
1903 | nl->groups = groups; | |
1904 | ||
1905 | add_sock_info(&nl->sock); | |
1906 | } | |
1907 | ||
1908 | out: | |
1909 | fclose(netlink_fp); | |
1910 | } | |
adfc156a MY |
1911 | |
1912 | /* | |
1913 | * PACKET | |
1914 | */ | |
1915 | struct packet_xinfo { | |
1916 | struct sock_xinfo sock; | |
1917 | uint16_t type; | |
1918 | uint16_t protocol; | |
1919 | unsigned int iface; | |
1920 | }; | |
1921 | ||
1922 | static const char *packet_decode_protocol(uint16_t proto) | |
1923 | { | |
1924 | switch (proto) { | |
1925 | case 0: | |
1926 | return NULL; | |
1927 | case ETH_P_802_3: | |
1928 | return "802_3"; | |
1929 | case ETH_P_AX25: | |
1930 | return "ax25"; | |
1931 | case ETH_P_ALL: | |
1932 | return "all"; | |
1933 | case ETH_P_802_2: | |
1934 | return "802_2"; | |
1935 | case ETH_P_SNAP: | |
1936 | return "snap"; | |
1937 | case ETH_P_DDCMP: | |
1938 | return "ddcmp"; | |
1939 | case ETH_P_WAN_PPP: | |
1940 | return "wan_ppp"; | |
1941 | case ETH_P_PPP_MP: | |
1942 | return "ppp_mp"; | |
1943 | case ETH_P_LOCALTALK: | |
1944 | return "localtalk"; | |
1945 | case ETH_P_CAN: | |
1946 | return "can"; | |
1947 | case ETH_P_CANFD: | |
1948 | return "canfd"; | |
1949 | #ifdef ETH_P_CANXL | |
1950 | case ETH_P_CANXL: | |
1951 | return "canxl"; | |
1952 | #endif | |
1953 | case ETH_P_PPPTALK: | |
1954 | return "ppptalk"; | |
1955 | case ETH_P_TR_802_2: | |
1956 | return "tr_802_2"; | |
1957 | case ETH_P_MOBITEX: | |
1958 | return "mobitex"; | |
1959 | case ETH_P_CONTROL: | |
1960 | return "control"; | |
1961 | case ETH_P_IRDA: | |
1962 | return "irda"; | |
1963 | case ETH_P_ECONET: | |
1964 | return "econet"; | |
1965 | case ETH_P_HDLC: | |
1966 | return "hdlc"; | |
1967 | case ETH_P_ARCNET: | |
1968 | return "arcnet"; | |
1969 | case ETH_P_DSA: | |
1970 | return "dsa"; | |
1971 | case ETH_P_TRAILER: | |
1972 | return "trailer"; | |
1973 | case ETH_P_PHONET: | |
1974 | return "phonet"; | |
1975 | case ETH_P_IEEE802154: | |
1976 | return "ieee802154"; | |
1977 | case ETH_P_CAIF: | |
1978 | return "caif"; | |
1979 | #ifdef ETH_P_XDSA | |
1980 | case ETH_P_XDSA: | |
1981 | return "xdsa"; | |
1982 | #endif | |
1983 | #ifdef ETH_P_MAP | |
1984 | case ETH_P_MAP: | |
1985 | return "map"; | |
1986 | #endif | |
1987 | #ifdef ETH_P_MCTP | |
1988 | case ETH_P_MCTP: | |
1989 | return "mctp"; | |
1990 | #endif | |
1991 | case ETH_P_LOOP: | |
1992 | return "loop"; | |
1993 | case ETH_P_PUP: | |
1994 | return "pup"; | |
1995 | case ETH_P_PUPAT: | |
1996 | return "pupat"; | |
1997 | #ifdef ETH_P_TSN | |
1998 | case ETH_P_TSN: | |
1999 | return "tsn"; | |
2000 | #endif | |
2001 | #ifdef ETH_P_ERSPAN2 | |
2002 | case ETH_P_ERSPAN2: | |
2003 | return "erspan2"; | |
2004 | #endif | |
2005 | case ETH_P_IP: | |
2006 | return "ip"; | |
2007 | case ETH_P_X25: | |
2008 | return "x25"; | |
2009 | case ETH_P_ARP: | |
2010 | return "arp"; | |
2011 | case ETH_P_BPQ: | |
2012 | return "bpq"; | |
2013 | case ETH_P_IEEEPUP: | |
2014 | return "ieeepup"; | |
2015 | case ETH_P_IEEEPUPAT: | |
2016 | return "ieeepupat"; | |
2017 | case ETH_P_BATMAN: | |
2018 | return "batman"; | |
2019 | case ETH_P_DEC: | |
2020 | return "dec"; | |
2021 | case ETH_P_DNA_DL: | |
2022 | return "dna_dl"; | |
2023 | case ETH_P_DNA_RC: | |
2024 | return "dna_rc"; | |
2025 | case ETH_P_DNA_RT: | |
2026 | return "dna_rt"; | |
2027 | case ETH_P_LAT: | |
2028 | return "lat"; | |
2029 | case ETH_P_DIAG: | |
2030 | return "diag"; | |
2031 | case ETH_P_CUST: | |
2032 | return "cust"; | |
2033 | case ETH_P_SCA: | |
2034 | return "sca"; | |
2035 | case ETH_P_TEB: | |
2036 | return "teb"; | |
2037 | case ETH_P_RARP: | |
2038 | return "rarp"; | |
2039 | case ETH_P_ATALK: | |
2040 | return "atalk"; | |
2041 | case ETH_P_AARP: | |
2042 | return "aarp"; | |
2043 | case ETH_P_8021Q: | |
2044 | return "8021q"; | |
2045 | #ifdef ETH_P_ERSPAN | |
2046 | case ETH_P_ERSPAN: | |
2047 | return "erspan"; | |
2048 | #endif | |
2049 | case ETH_P_IPX: | |
2050 | return "ipx"; | |
2051 | case ETH_P_IPV6: | |
2052 | return "ipv6"; | |
2053 | case ETH_P_PAUSE: | |
2054 | return "pause"; | |
2055 | case ETH_P_SLOW: | |
2056 | return "slow"; | |
2057 | case ETH_P_WCCP: | |
2058 | return "wccp"; | |
2059 | case ETH_P_MPLS_UC: | |
2060 | return "mpls_uc"; | |
2061 | case ETH_P_MPLS_MC: | |
2062 | return "mpls_mc"; | |
2063 | case ETH_P_ATMMPOA: | |
2064 | return "atmmpoa"; | |
2065 | #ifdef ETH_P_PPP_DISC | |
2066 | case ETH_P_PPP_DISC: | |
2067 | return "ppp_disc"; | |
2068 | #endif | |
2069 | #ifdef ETH_P_PPP_SES | |
2070 | case ETH_P_PPP_SES: | |
2071 | return "ppp_ses"; | |
2072 | #endif | |
2073 | case ETH_P_LINK_CTL: | |
2074 | return "link_ctl"; | |
2075 | case ETH_P_ATMFATE: | |
2076 | return "atmfate"; | |
2077 | case ETH_P_PAE: | |
2078 | return "pae"; | |
2079 | #ifdef ETH_P_PROFINET | |
2080 | case ETH_P_PROFINET: | |
2081 | return "profinet"; | |
2082 | #endif | |
2083 | #ifdef ETH_P_REALTEK | |
2084 | case ETH_P_REALTEK: | |
2085 | return "realtek"; | |
2086 | #endif | |
2087 | case ETH_P_AOE: | |
2088 | return "aoe"; | |
2089 | #ifdef ETH_P_ETHERCAT | |
2090 | case ETH_P_ETHERCAT: | |
2091 | return "ethercat"; | |
2092 | #endif | |
2093 | case ETH_P_8021AD: | |
2094 | return "8021ad"; | |
2095 | case ETH_P_802_EX1: | |
2096 | return "802_ex1"; | |
2097 | #ifdef ETH_P_PREAUTH | |
2098 | case ETH_P_PREAUTH: | |
2099 | return "preauth"; | |
2100 | #endif | |
2101 | case ETH_P_TIPC: | |
2102 | return "tipc"; | |
2103 | #ifdef ETH_P_LLDP | |
2104 | case ETH_P_LLDP: | |
2105 | return "lldp"; | |
2106 | #endif | |
2107 | #ifdef ETH_P_MRP | |
2108 | case ETH_P_MRP: | |
2109 | return "mrp"; | |
2110 | #endif | |
2111 | #ifdef ETH_P_MACSEC | |
2112 | case ETH_P_MACSEC: | |
2113 | return "macsec"; | |
2114 | #endif | |
2115 | case ETH_P_8021AH: | |
2116 | return "8021ah"; | |
2117 | #ifdef ETH_P_MVRP | |
2118 | case ETH_P_MVRP: | |
2119 | return "mvrp"; | |
2120 | #endif | |
2121 | case ETH_P_1588: | |
2122 | return "1588"; | |
2123 | #ifdef ETH_P_NCSI | |
2124 | case ETH_P_NCSI: | |
2125 | return "ncsi"; | |
2126 | #endif | |
2127 | #ifdef ETH_P_PRP | |
2128 | case ETH_P_PRP: | |
2129 | return "prp"; | |
2130 | #endif | |
2131 | #ifdef ETH_P_CFM | |
2132 | case ETH_P_CFM: | |
2133 | return "cfm"; | |
2134 | #endif | |
2135 | case ETH_P_FCOE: | |
2136 | return "fcoe"; | |
2137 | #ifdef ETH_P_IBOE | |
2138 | case ETH_P_IBOE: | |
2139 | return "iboe"; | |
2140 | #endif | |
2141 | case ETH_P_TDLS: | |
2142 | return "tdls"; | |
2143 | case ETH_P_FIP: | |
2144 | return "fip"; | |
2145 | #ifdef ETH_P_80221 | |
2146 | case ETH_P_80221: | |
2147 | return "80221"; | |
2148 | #endif | |
2149 | #ifdef ETH_P_HSR | |
2150 | case ETH_P_HSR: | |
2151 | return "hsr"; | |
2152 | #endif | |
2153 | #ifdef ETH_P_NSH | |
2154 | case ETH_P_NSH: | |
2155 | return "nsh"; | |
2156 | #endif | |
2157 | #ifdef ETH_P_LOOPBACK | |
2158 | case ETH_P_LOOPBACK: | |
2159 | return "loopback"; | |
2160 | #endif | |
2161 | case ETH_P_QINQ1: | |
2162 | return "qinq1"; | |
2163 | case ETH_P_QINQ2: | |
2164 | return "qinq2"; | |
2165 | case ETH_P_QINQ3: | |
2166 | return "qinq3"; | |
2167 | case ETH_P_EDSA: | |
2168 | return "edsa"; | |
2169 | #ifdef ETH_P_DSA_8021Q | |
2170 | case ETH_P_DSA_8021Q: | |
2171 | return "dsa_8021q"; | |
2172 | #endif | |
2173 | #ifdef ETH_P_DSA_A5PSW | |
2174 | case ETH_P_DSA_A5PSW: | |
2175 | return "dsa_a5psw"; | |
2176 | #endif | |
2177 | #ifdef ETH_P_IFE | |
2178 | case ETH_P_IFE: | |
2179 | return "ife"; | |
2180 | #endif | |
2181 | case ETH_P_AF_IUCV: | |
2182 | return "af_iucv"; | |
2183 | #ifdef ETH_P_802_3_MIN | |
2184 | case ETH_P_802_3_MIN: | |
2185 | return "802_3_min"; | |
2186 | #endif | |
2187 | default: | |
2188 | return "unknown"; | |
2189 | } | |
2190 | } | |
2191 | ||
2192 | static char *packet_get_name(struct sock_xinfo *sock_xinfo, | |
2193 | struct sock *sock __attribute__((__unused__))) | |
2194 | { | |
2195 | struct packet_xinfo *pkt = (struct packet_xinfo *)sock_xinfo; | |
2196 | char *str = NULL; | |
2197 | const char *type = sock_decode_type(pkt->type); | |
2198 | const char *proto = packet_decode_protocol(pkt->protocol); | |
2199 | const char *iface = get_iface_name(sock_xinfo->netns_inode, | |
2200 | pkt->iface); | |
2201 | ||
2202 | if (iface && proto) | |
2203 | xasprintf(&str, "type=%s protocol=%s iface=%s", | |
2204 | type, proto, iface); | |
2205 | else if (proto) | |
2206 | xasprintf(&str, "type=%s protocol=%s", | |
2207 | type, proto); | |
2208 | else if (iface) | |
2209 | xasprintf(&str, "type=%s iface=%s", | |
2210 | type, iface); | |
2211 | else | |
2212 | xasprintf(&str, "type=%s", type); | |
2213 | ||
2214 | return str; | |
2215 | } | |
2216 | ||
2217 | static char *packet_get_type(struct sock_xinfo *sock_xinfo __attribute__((__unused__)), | |
2218 | struct sock *sock __attribute__((__unused__))) | |
2219 | { | |
2220 | const char *str; | |
2221 | struct packet_xinfo *pkt = (struct packet_xinfo *)sock_xinfo; | |
2222 | ||
2223 | str = sock_decode_type(pkt->type); | |
2224 | return xstrdup(str); | |
2225 | } | |
2226 | ||
2227 | static bool packet_fill_column(struct proc *proc __attribute__((__unused__)), | |
2228 | struct sock_xinfo *sock_xinfo, | |
2229 | struct sock *sock __attribute__((__unused__)), | |
2230 | struct libscols_line *ln __attribute__((__unused__)), | |
2231 | int column_id, | |
2232 | size_t column_index __attribute__((__unused__)), | |
2233 | char **str) | |
2234 | { | |
2235 | struct packet_xinfo *pkt = (struct packet_xinfo *)sock_xinfo; | |
2236 | ||
2237 | switch (column_id) { | |
2238 | case COL_PACKET_IFACE: { | |
2239 | const char *iface; | |
2240 | iface = get_iface_name(sock_xinfo->netns_inode, | |
2241 | pkt->iface); | |
2242 | if (iface) { | |
2243 | *str = xstrdup(iface); | |
2244 | return true; | |
2245 | } | |
2246 | break; | |
2247 | } | |
2248 | case COL_PACKET_PROTOCOL: { | |
2249 | const char *proto; | |
2250 | proto = packet_decode_protocol(pkt->protocol); | |
2251 | if (proto) { | |
2252 | *str = xstrdup(proto); | |
2253 | return true; | |
2254 | } | |
2255 | break; | |
2256 | } | |
2257 | default: | |
2258 | break; | |
2259 | } | |
2260 | return false; | |
2261 | } | |
2262 | ||
2263 | static const struct sock_xinfo_class packet_xinfo_class = { | |
2264 | .get_name = packet_get_name, | |
2265 | .get_type = packet_get_type, | |
2266 | .get_state = NULL, | |
2267 | .get_listening = NULL, | |
2268 | .fill_column = packet_fill_column, | |
2269 | .free = NULL, | |
2270 | }; | |
2271 | ||
2272 | static void load_xinfo_from_proc_packet(ino_t netns_inode) | |
2273 | { | |
2274 | char line[BUFSIZ]; | |
2275 | FILE *packet_fp; | |
2276 | ||
2277 | packet_fp = fopen("/proc/net/packet", "r"); | |
2278 | if (!packet_fp) | |
2279 | return; | |
2280 | ||
2281 | if (fgets(line, sizeof(line), packet_fp) == NULL) | |
2282 | goto out; | |
2283 | if (!(line[0] == 's' && line[1] == 'k')) | |
2284 | /* Unexpected line */ | |
2285 | goto out; | |
2286 | ||
2287 | while (fgets(line, sizeof(line), packet_fp)) { | |
2288 | uint16_t type; | |
2289 | uint16_t protocol; | |
2290 | unsigned int iface; | |
2291 | unsigned long inode; | |
2292 | struct packet_xinfo *pkt; | |
2293 | ||
2294 | if (sscanf(line, "%*x %*d %" SCNu16 " %" SCNu16 " %u %*d %*d %*d %lu", | |
2295 | &type, &protocol, &iface, &inode) < 4) | |
2296 | continue; | |
2297 | ||
2298 | pkt = xcalloc(1, sizeof(*pkt)); | |
2299 | pkt->sock.class = &packet_xinfo_class; | |
2300 | pkt->sock.inode = (ino_t)inode; | |
2301 | pkt->sock.netns_inode = netns_inode; | |
2302 | ||
2303 | pkt->type = type; | |
2304 | pkt->protocol = protocol; | |
2305 | pkt->iface = iface; | |
2306 | ||
2307 | add_sock_info(&pkt->sock); | |
2308 | } | |
2309 | ||
2310 | out: | |
2311 | fclose(packet_fp); | |
2312 | } |