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