]>
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; | |
541 | xasprintf(&str, "%d,%s,%d", | |
542 | sock->file.proc->pid, sock->file.proc->command, sock->file.association); | |
543 | return str; | |
544 | } | |
545 | ||
546 | static struct ipc *unix_get_peer_ipc(struct unix_xinfo *ux, | |
547 | struct sock *sock) | |
548 | { | |
549 | struct unix_ipc *unix_ipc; | |
550 | struct sock dummy_peer_sock; | |
551 | ||
552 | unix_ipc = ux->unix_ipc; | |
553 | if (!unix_ipc) | |
554 | return NULL; | |
555 | ||
556 | unix_make_dumy_sock(sock, unix_ipc->ipeer, &dummy_peer_sock); | |
557 | return get_ipc(&dummy_peer_sock.file); | |
558 | } | |
559 | ||
2e38e61c MY |
560 | static bool unix_fill_column(struct proc *proc __attribute__((__unused__)), |
561 | struct sock_xinfo *sock_xinfo, | |
f9771d8d | 562 | struct sock *sock, |
2e38e61c MY |
563 | struct libscols_line *ln __attribute__((__unused__)), |
564 | int column_id, | |
565 | size_t column_index __attribute__((__unused__)), | |
566 | char **str) | |
567 | { | |
568 | struct unix_xinfo *ux = (struct unix_xinfo *)sock_xinfo; | |
f9771d8d MY |
569 | struct ipc *peer_ipc; |
570 | struct list_head *e; | |
426ff07f | 571 | char shutdown_chars[3] = { 0 }; |
2e38e61c | 572 | |
854ddd0d | 573 | switch (column_id) { |
2e38e61c MY |
574 | case COL_UNIX_PATH: |
575 | if (*ux->path) { | |
e0dc84da | 576 | *str = xstrdup(ux->path); |
2e38e61c MY |
577 | return true; |
578 | } | |
579 | break; | |
f9771d8d MY |
580 | case COL_ENDPOINTS: |
581 | peer_ipc = unix_get_peer_ipc(ux, sock); | |
582 | if (!peer_ipc) | |
583 | break; | |
584 | ||
585 | list_for_each_backwardly(e, &peer_ipc->endpoints) { | |
586 | struct sock *peer_sock = list_entry(e, struct sock, endpoint.endpoints); | |
587 | char *estr; | |
588 | ||
589 | if (*str) | |
590 | xstrputc(str, '\n'); | |
591 | estr = unix_xstrendpoint(peer_sock); | |
592 | xstrappend(str, estr); | |
593 | free(estr); | |
594 | } | |
595 | if (*str) | |
596 | return true; | |
597 | break; | |
426ff07f MY |
598 | case COL_SOCK_SHUTDOWN: |
599 | if (unix_shutdown_chars(ux, shutdown_chars)) { | |
600 | *str = xstrdup(shutdown_chars); | |
601 | return true; | |
602 | } | |
603 | break; | |
2e38e61c MY |
604 | } |
605 | ||
606 | return false; | |
607 | } | |
608 | ||
54a06438 | 609 | static const struct sock_xinfo_class unix_xinfo_class = { |
2e38e61c MY |
610 | .get_name = unix_get_name, |
611 | .get_type = unix_get_type, | |
612 | .get_state = unix_get_state, | |
45d61bff | 613 | .get_listening = unix_get_listening, |
2e38e61c | 614 | .fill_column = unix_fill_column, |
f9771d8d | 615 | .get_ipc_class = unix_get_ipc_class, |
2e38e61c MY |
616 | .free = NULL, |
617 | }; | |
618 | ||
e8e14a28 | 619 | /* UNIX_LINE_LEN need at least 54 + 21 + UNIX_PATH_MAX + 1. |
eeaf7677 MY |
620 | * |
621 | * An actual number must be used in this definition | |
622 | * since UNIX_LINE_LEN is specified as an argument for | |
623 | * stringify_value(). | |
624 | */ | |
2e38e61c MY |
625 | #define UNIX_LINE_LEN 256 |
626 | static void load_xinfo_from_proc_unix(ino_t netns_inode) | |
627 | { | |
628 | char line[UNIX_LINE_LEN]; | |
629 | FILE *unix_fp; | |
630 | ||
631 | unix_fp = fopen("/proc/net/unix", "r"); | |
632 | if (!unix_fp) | |
633 | return; | |
634 | ||
635 | if (fgets(line, sizeof(line), unix_fp) == NULL) | |
636 | goto out; | |
b3649945 | 637 | if (!(line[0] == 'N' && line[1] == 'u' && line[2] == 'm')) |
2e38e61c MY |
638 | /* Unexpected line */ |
639 | goto out; | |
640 | ||
641 | while (fgets(line, sizeof(line), unix_fp)) { | |
642 | uint64_t flags; | |
643 | uint32_t type; | |
644 | unsigned int st; | |
645 | unsigned long inode; | |
2e38e61c | 646 | struct unix_xinfo *ux; |
eeaf7677 | 647 | char path[UNIX_LINE_LEN + 1] = { 0 }; |
91a484fe | 648 | |
2e38e61c | 649 | |
eeaf7677 MY |
650 | if (sscanf(line, "%*x: %*x %*x %" SCNx64 " %x %x %lu %" |
651 | stringify_value(UNIX_LINE_LEN) "[^\n]", | |
2e38e61c MY |
652 | &flags, &type, &st, &inode, path) < 4) |
653 | continue; | |
654 | ||
655 | if (inode == 0) | |
656 | continue; | |
657 | ||
8ff22ccb | 658 | ux = xcalloc(1, sizeof(*ux)); |
2e38e61c MY |
659 | ux->sock.class = &unix_xinfo_class; |
660 | ux->sock.inode = (ino_t)inode; | |
661 | ux->sock.netns_inode = netns_inode; | |
662 | ||
663 | ux->acceptcon = !!flags; | |
664 | ux->type = type; | |
665 | ux->st = st; | |
91a484fe | 666 | xstrncpy(ux->path, path, sizeof(ux->path)); |
2e38e61c | 667 | |
2ac8d1a3 | 668 | add_sock_info(&ux->sock); |
2e38e61c MY |
669 | } |
670 | ||
671 | out: | |
672 | fclose(unix_fp); | |
673 | } | |
7b2a9be0 | 674 | |
5468bb45 MY |
675 | /* The path name extracted from /proc/net/unix is unreliable; the line oriented interface cannot |
676 | * represent a file name including newlines. With unix_refill_name(), we patch the path | |
677 | * member of unix_xinfos with information received via netlink diag interface. */ | |
678 | static void unix_refill_name(struct sock_xinfo *xinfo, const char *name, size_t len) | |
679 | { | |
680 | struct unix_xinfo *ux = (struct unix_xinfo *)xinfo; | |
681 | size_t min_len; | |
682 | ||
683 | if (len == 0) | |
684 | return; | |
685 | ||
686 | min_len = min(sizeof(ux->path) - 1, len); | |
687 | memcpy(ux->path, name, min_len); | |
688 | if (ux->path[0] == '\0') { | |
689 | ux->path[0] = '@'; | |
690 | } | |
691 | ux->path[min_len] = '\0'; | |
692 | } | |
693 | ||
694 | static bool handle_diag_unix(ino_t netns __attribute__((__unused__)), | |
695 | size_t nlmsg_len, void *nlmsg_data) | |
696 | { | |
697 | const struct unix_diag_msg *diag = nlmsg_data; | |
698 | size_t rta_len; | |
699 | ino_t inode; | |
700 | struct sock_xinfo *xinfo; | |
f9771d8d | 701 | struct unix_xinfo *unix_xinfo; |
5468bb45 MY |
702 | |
703 | if (diag->udiag_family != AF_UNIX) | |
704 | return false; | |
705 | ||
706 | if (nlmsg_len < NLMSG_LENGTH(sizeof(*diag))) | |
707 | return false; | |
708 | ||
709 | inode = (ino_t)diag->udiag_ino; | |
710 | xinfo = get_sock_xinfo(inode); | |
711 | ||
712 | if (xinfo == NULL) | |
713 | /* The socket is found in the diag response | |
714 | but not in the proc fs. */ | |
715 | return true; | |
716 | ||
717 | if (xinfo->class != &unix_xinfo_class) | |
718 | return true; | |
f9771d8d | 719 | unix_xinfo = (struct unix_xinfo *)xinfo; |
5468bb45 MY |
720 | |
721 | rta_len = nlmsg_len - NLMSG_LENGTH(sizeof(*diag)); | |
722 | for (struct rtattr *attr = (struct rtattr *)(diag + 1); | |
723 | RTA_OK(attr, rta_len); | |
724 | attr = RTA_NEXT(attr, rta_len)) { | |
725 | size_t len = RTA_PAYLOAD(attr); | |
726 | ||
727 | switch (attr->rta_type) { | |
728 | case UNIX_DIAG_NAME: | |
729 | unix_refill_name(xinfo, RTA_DATA(attr), len); | |
730 | break; | |
f9771d8d | 731 | |
426ff07f MY |
732 | case UNIX_DIAG_SHUTDOWN: |
733 | if (len < 1) | |
734 | break; | |
735 | ||
736 | unix_xinfo->shutdown_mask = *(uint8_t *)RTA_DATA(attr); | |
737 | set_shutdown_mask(unix_xinfo->shutdown_mask); | |
738 | break; | |
739 | ||
f9771d8d MY |
740 | case UNIX_DIAG_PEER: |
741 | if (len < 4) | |
742 | break; | |
743 | ||
744 | unix_xinfo->unix_ipc = (struct unix_ipc *)new_ipc(&unix_ipc_class); | |
745 | unix_xinfo->unix_ipc->inode = inode; | |
746 | unix_xinfo->unix_ipc->ipeer = (ino_t)(*(uint32_t *)RTA_DATA(attr)); | |
747 | add_ipc(&unix_xinfo->unix_ipc->ipc, inode % UINT_MAX); | |
748 | break; | |
5468bb45 MY |
749 | } |
750 | } | |
751 | return true; | |
752 | } | |
753 | ||
754 | static void load_xinfo_from_diag_unix(int diagsd, ino_t netns) | |
755 | { | |
756 | struct unix_diag_req udr = { | |
757 | .sdiag_family = AF_UNIX, | |
758 | .udiag_states = -1, /* set the all bits. */ | |
426ff07f | 759 | .udiag_show = UDIAG_SHOW_NAME | UDIAG_SHOW_PEER | UNIX_DIAG_SHUTDOWN, |
5468bb45 MY |
760 | }; |
761 | ||
762 | send_diag_request(diagsd, &udr, sizeof(udr), handle_diag_unix, netns); | |
763 | } | |
764 | ||
7b2a9be0 MY |
765 | /* |
766 | * AF_INET | |
767 | */ | |
768 | struct inet_xinfo { | |
769 | struct sock_xinfo sock; | |
643f30c7 MY |
770 | struct in_addr local_addr; |
771 | struct in_addr remote_addr; | |
7b2a9be0 MY |
772 | }; |
773 | ||
9020c2e0 | 774 | static uint32_t kernel32_to_cpu(enum sysfs_byteorder byteorder, uint32_t v) |
7b2a9be0 | 775 | { |
9020c2e0 MY |
776 | if (byteorder == SYSFS_BYTEORDER_LITTLE) |
777 | return le32_to_cpu(v); | |
778 | else | |
779 | return be32_to_cpu(v); | |
7b2a9be0 MY |
780 | } |
781 | ||
1656da13 MY |
782 | /* |
783 | * AF_INET6 | |
784 | */ | |
785 | struct inet6_xinfo { | |
786 | struct sock_xinfo sock; | |
787 | struct in6_addr local_addr; | |
788 | struct in6_addr remote_addr; | |
789 | }; | |
790 | ||
7b2a9be0 | 791 | /* |
4a2b51c1 | 792 | * L4 abstract-layer for protocols stacked on IP and IP6. |
7b2a9be0 | 793 | */ |
303b41fe | 794 | enum l4_state { |
7b2a9be0 MY |
795 | /* |
796 | * Taken from linux/include/net/tcp_states.h. | |
797 | * (GPL-2.0-or-later) | |
303b41fe MY |
798 | * |
799 | * UDP and RAW sockets also uses the contents in Linux. | |
7b2a9be0 MY |
800 | */ |
801 | TCP_ESTABLISHED = 1, | |
802 | TCP_SYN_SENT, | |
803 | TCP_SYN_RECV, | |
804 | TCP_FIN_WAIT1, | |
805 | TCP_FIN_WAIT2, | |
806 | TCP_TIME_WAIT, | |
807 | TCP_CLOSE, | |
808 | TCP_CLOSE_WAIT, | |
809 | TCP_LAST_ACK, | |
810 | TCP_LISTEN, | |
811 | TCP_CLOSING, | |
812 | TCP_NEW_SYN_RECV, | |
813 | ||
814 | TCP_MAX_STATES /* Leave at the end! */ | |
815 | }; | |
816 | ||
303b41fe | 817 | static const char *l4_decode_state(enum l4_state st) |
7b2a9be0 | 818 | { |
86242cb0 | 819 | const char * const table [] = { |
7b2a9be0 MY |
820 | [TCP_ESTABLISHED] = "established", |
821 | [TCP_SYN_SENT] = "syn-sent", | |
822 | [TCP_SYN_RECV] = "syn-recv", | |
823 | [TCP_FIN_WAIT1] = "fin-wait1", | |
824 | [TCP_FIN_WAIT2] = "fin-wait2", | |
825 | [TCP_TIME_WAIT] = "time-wait", | |
826 | [TCP_CLOSE] = "close", | |
827 | [TCP_CLOSE_WAIT] = "close-wait", | |
828 | [TCP_LAST_ACK] = "last-ack", | |
829 | [TCP_LISTEN] = "listen", | |
830 | [TCP_CLOSING] = "closing", | |
831 | [TCP_NEW_SYN_RECV] = "new-syn-recv", | |
832 | }; | |
833 | ||
834 | if (st < TCP_MAX_STATES) | |
835 | return table[st]; | |
836 | return "unknown"; | |
837 | } | |
838 | ||
4a2b51c1 | 839 | struct l4_xinfo { |
1656da13 MY |
840 | union { |
841 | struct inet_xinfo inet; | |
842 | struct inet6_xinfo inet6; | |
843 | }; | |
303b41fe | 844 | enum l4_state st; |
4a2b51c1 MY |
845 | }; |
846 | ||
2a4f2b1b | 847 | enum l4_side { L4_LOCAL, L4_REMOTE }; |
1656da13 | 848 | enum l3_decorator { L3_DECO_START, L3_DECO_END }; |
2a4f2b1b | 849 | |
4a2b51c1 MY |
850 | struct l4_xinfo_class { |
851 | struct sock_xinfo_class sock; | |
19e40223 MY |
852 | struct sock_xinfo *(*scan_line)(const struct sock_xinfo_class *, |
853 | char *, | |
854 | ino_t, | |
855 | enum sysfs_byteorder); | |
2a4f2b1b MY |
856 | void * (*get_addr)(struct l4_xinfo *, enum l4_side); |
857 | bool (*is_any_addr)(void *); | |
858 | int family; | |
1656da13 | 859 | const char *l3_decorator[2]; |
4a2b51c1 MY |
860 | }; |
861 | ||
838fa270 MY |
862 | #define l3_fill_column_handler(L3, SOCK_XINFO, COLUMN_ID, STR) __extension__ \ |
863 | ({ \ | |
864 | struct l4_xinfo_class *class = (struct l4_xinfo_class *)SOCK_XINFO->class; \ | |
865 | struct l4_xinfo *l4 = (struct l4_xinfo *)SOCK_XINFO; \ | |
866 | void *n = NULL; \ | |
867 | char s[BUFSIZ]; \ | |
868 | bool r = false; \ | |
869 | \ | |
870 | switch (COLUMN_ID) { \ | |
871 | case COL_##L3##_LADDR: \ | |
872 | n = class->get_addr(l4, L4_LOCAL); \ | |
873 | break; \ | |
874 | case COL_##L3##_RADDR: \ | |
875 | n = class->get_addr(l4, L4_REMOTE); \ | |
876 | break; \ | |
877 | default: \ | |
878 | break; \ | |
879 | } \ | |
880 | \ | |
881 | if (n && inet_ntop(class->family, n, s, sizeof(s))) { \ | |
e0dc84da | 882 | *STR = xstrdup(s); \ |
838fa270 MY |
883 | r = true; \ |
884 | } \ | |
885 | r; \ | |
886 | }) | |
887 | ||
7b2a9be0 MY |
888 | /* |
889 | * TCP | |
890 | */ | |
891 | struct tcp_xinfo { | |
4a2b51c1 | 892 | struct l4_xinfo l4; |
7b2a9be0 MY |
893 | uint16_t local_port; |
894 | uint16_t remote_port; | |
7b2a9be0 MY |
895 | }; |
896 | ||
7b2a9be0 MY |
897 | static char *tcp_get_name(struct sock_xinfo *sock_xinfo, |
898 | struct sock *sock __attribute__((__unused__))) | |
899 | { | |
900 | char *str = NULL; | |
7b2a9be0 | 901 | struct tcp_xinfo *tcp = ((struct tcp_xinfo *)sock_xinfo); |
8db07ebc | 902 | struct l4_xinfo *l4 = &tcp->l4; |
303b41fe | 903 | const char *st_str = l4_decode_state(l4->st); |
838fa270 MY |
904 | struct l4_xinfo_class *class = (struct l4_xinfo_class *)sock_xinfo->class; |
905 | void *laddr = class->get_addr(l4, L4_LOCAL); | |
906 | void *raddr = class->get_addr(l4, L4_REMOTE); | |
907 | char local_s[BUFSIZ]; | |
908 | char remote_s[BUFSIZ]; | |
1656da13 MY |
909 | const char *start = class->l3_decorator[L3_DECO_START]; |
910 | const char *end = class->l3_decorator[L3_DECO_END]; | |
7b2a9be0 | 911 | |
838fa270 | 912 | if (!inet_ntop(class->family, laddr, local_s, sizeof(local_s))) |
3b182ee7 MY |
913 | xasprintf(&str, "state=%s", st_str); |
914 | else if (l4->st == TCP_LISTEN | |
838fa270 | 915 | || !inet_ntop(class->family, raddr, remote_s, sizeof(remote_s))) |
b4e39841 | 916 | xasprintf(&str, "state=%s laddr=%s%s%s:%"PRIu16, |
3b182ee7 | 917 | st_str, |
1656da13 | 918 | start, local_s, end, tcp->local_port); |
7b2a9be0 | 919 | else |
b4e39841 | 920 | xasprintf(&str, "state=%s laddr=%s%s%s:%"PRIu16" raddr=%s%s%s:%"PRIu16, |
3b182ee7 | 921 | st_str, |
1656da13 MY |
922 | start, local_s, end, tcp->local_port, |
923 | start, remote_s, end, tcp->remote_port); | |
7b2a9be0 MY |
924 | return str; |
925 | } | |
926 | ||
927 | static char *tcp_get_type(struct sock_xinfo *sock_xinfo __attribute__((__unused__)), | |
928 | struct sock *sock __attribute__((__unused__))) | |
929 | { | |
e0dc84da | 930 | return xstrdup("stream"); |
7b2a9be0 MY |
931 | } |
932 | ||
933 | static char *tcp_get_state(struct sock_xinfo *sock_xinfo, | |
934 | struct sock *sock __attribute__((__unused__))) | |
935 | { | |
e0dc84da | 936 | return xstrdup(l4_decode_state(((struct l4_xinfo *)sock_xinfo)->st)); |
7b2a9be0 MY |
937 | } |
938 | ||
939 | static bool tcp_get_listening(struct sock_xinfo *sock_xinfo, | |
940 | struct sock *sock __attribute__((__unused__))) | |
941 | { | |
8db07ebc | 942 | return ((struct l4_xinfo *)sock_xinfo)->st == TCP_LISTEN; |
7b2a9be0 MY |
943 | } |
944 | ||
838fa270 MY |
945 | #define l4_fill_column_handler(L4, SOCK_XINFO, COLUMN_ID, STR) __extension__ \ |
946 | ({ \ | |
947 | struct l4_xinfo_class *class = (struct l4_xinfo_class *)SOCK_XINFO->class; \ | |
948 | struct tcp_xinfo *tcp = (struct tcp_xinfo *)SOCK_XINFO; \ | |
949 | struct l4_xinfo *l4 = &tcp->l4; \ | |
950 | void *n = NULL; \ | |
92a0dbce | 951 | bool has_laddr = false; \ |
73df46fc | 952 | unsigned short p; \ |
92a0dbce | 953 | bool has_lport = false; \ |
838fa270 MY |
954 | char s[BUFSIZ]; \ |
955 | bool r = true; \ | |
92a0dbce | 956 | \ |
838fa270 | 957 | switch (COLUMN_ID) { \ |
cda2b2a0 | 958 | case COL_##L4##_LADDR: \ |
838fa270 | 959 | n = class->get_addr(l4, L4_LOCAL); \ |
92a0dbce | 960 | has_laddr = true; \ |
73df46fc | 961 | p = tcp->local_port; \ |
92a0dbce | 962 | /* FALL THROUGH */ \ |
cda2b2a0 | 963 | case COL_##L4##_RADDR: \ |
92a0dbce | 964 | if (!has_laddr) { \ |
838fa270 | 965 | n = class->get_addr(l4, L4_REMOTE); \ |
73df46fc | 966 | p = tcp->remote_port; \ |
92a0dbce | 967 | } \ |
838fa270 | 968 | if (n && inet_ntop(class->family, n, s, sizeof(s))) \ |
b4e39841 | 969 | xasprintf(STR, "%s%s%s:%"PRIu16, \ |
1656da13 MY |
970 | class->l3_decorator[L3_DECO_START], \ |
971 | s, \ | |
972 | class->l3_decorator[L3_DECO_END], \ | |
973 | p); \ | |
92a0dbce | 974 | break; \ |
cda2b2a0 | 975 | case COL_##L4##_LPORT: \ |
73df46fc | 976 | p = tcp->local_port; \ |
92a0dbce MY |
977 | has_lport = true; \ |
978 | /* FALL THROUGH */ \ | |
cda2b2a0 | 979 | case COL_##L4##_RPORT: \ |
92a0dbce | 980 | if (!has_lport) \ |
73df46fc | 981 | p = tcp->remote_port; \ |
b4e39841 | 982 | xasprintf(STR, "%"PRIu16, p); \ |
92a0dbce MY |
983 | break; \ |
984 | default: \ | |
838fa270 MY |
985 | r = false; \ |
986 | break; \ | |
92a0dbce | 987 | } \ |
838fa270 MY |
988 | r; \ |
989 | }) | |
7b2a9be0 | 990 | |
19e40223 MY |
991 | static struct sock_xinfo *tcp_xinfo_scan_line(const struct sock_xinfo_class *class, |
992 | char * line, | |
993 | ino_t netns_inode, | |
994 | enum sysfs_byteorder byteorder) | |
995 | { | |
996 | unsigned long local_addr; | |
997 | unsigned long local_port; | |
998 | unsigned long remote_addr; | |
999 | unsigned long remote_port; | |
1000 | unsigned long st; | |
1001 | unsigned long long inode; | |
1002 | struct tcp_xinfo *tcp; | |
1003 | struct inet_xinfo *inet; | |
1004 | struct sock_xinfo *sock; | |
1005 | ||
1006 | if (sscanf(line, "%*d: %lx:%lx %lx:%lx %lx %*x:%*x %*x:%*x %*x %*u %*u %lld", | |
1007 | &local_addr, &local_port, &remote_addr, &remote_port, | |
1008 | &st, &inode) != 6) | |
1009 | return NULL; | |
1010 | ||
1011 | if (inode == 0) | |
1012 | return NULL; | |
1013 | ||
8ff22ccb | 1014 | tcp = xcalloc(1, sizeof(*tcp)); |
19e40223 MY |
1015 | inet = &tcp->l4.inet; |
1016 | sock = &inet->sock; | |
1017 | sock->class = class; | |
1018 | sock->inode = (ino_t)inode; | |
1019 | sock->netns_inode = netns_inode; | |
1020 | inet->local_addr.s_addr = kernel32_to_cpu(byteorder, local_addr); | |
1021 | tcp->local_port = local_port; | |
1022 | inet->remote_addr.s_addr = kernel32_to_cpu(byteorder, remote_addr); | |
1023 | tcp->remote_port = remote_port; | |
8db07ebc | 1024 | tcp->l4.st = st; |
19e40223 MY |
1025 | |
1026 | return sock; | |
1027 | } | |
1028 | ||
2a4f2b1b MY |
1029 | static void *tcp_xinfo_get_addr(struct l4_xinfo *l4, enum l4_side side) |
1030 | { | |
1031 | return (side == L4_LOCAL) | |
1032 | ? &l4->inet.local_addr | |
1033 | : &l4->inet.remote_addr; | |
1034 | } | |
1035 | ||
1036 | static bool tcp_xinfo_is_any_addr(void *addr) | |
1037 | { | |
1038 | return ((struct in_addr *)addr)->s_addr == INADDR_ANY; | |
1039 | } | |
1040 | ||
838fa270 MY |
1041 | static bool tcp_fill_column(struct proc *proc __attribute__((__unused__)), |
1042 | struct sock_xinfo *sock_xinfo, | |
1043 | struct sock *sock __attribute__((__unused__)), | |
1044 | struct libscols_line *ln __attribute__((__unused__)), | |
1045 | int column_id, | |
1046 | size_t column_index __attribute__((__unused__)), | |
1047 | char **str) | |
1048 | { | |
1049 | return l3_fill_column_handler(INET, sock_xinfo, column_id, str) | |
1050 | || l4_fill_column_handler(TCP, sock_xinfo, column_id, str); | |
1051 | } | |
1052 | ||
4a2b51c1 MY |
1053 | static const struct l4_xinfo_class tcp_xinfo_class = { |
1054 | .sock = { | |
1055 | .get_name = tcp_get_name, | |
1056 | .get_type = tcp_get_type, | |
1057 | .get_state = tcp_get_state, | |
1058 | .get_listening = tcp_get_listening, | |
1059 | .fill_column = tcp_fill_column, | |
1060 | .free = NULL, | |
1061 | }, | |
19e40223 | 1062 | .scan_line = tcp_xinfo_scan_line, |
2a4f2b1b MY |
1063 | .get_addr = tcp_xinfo_get_addr, |
1064 | .is_any_addr = tcp_xinfo_is_any_addr, | |
1065 | .family = AF_INET, | |
1656da13 | 1066 | .l3_decorator = {"", ""}, |
7b2a9be0 MY |
1067 | }; |
1068 | ||
cda2b2a0 | 1069 | static bool L4_verify_initial_line(const char *line) |
58d2f31e MY |
1070 | { |
1071 | /* At least we expect two white spaces. */ | |
cb097fb7 | 1072 | if (strncmp(line, " ", 2) != 0) |
58d2f31e MY |
1073 | return false; |
1074 | line += 2; | |
1075 | ||
1076 | /* Skip white spaces. */ | |
4658bfb1 | 1077 | line = skip_space(line); |
58d2f31e | 1078 | |
621cf7e9 | 1079 | return strncmp(line, "sl", 2) == 0; |
db79a100 TW |
1080 | } |
1081 | ||
7b2a9be0 | 1082 | #define TCP_LINE_LEN 256 |
cda2b2a0 | 1083 | static void load_xinfo_from_proc_inet_L4(ino_t netns_inode, const char *proc_file, |
891b24b7 MY |
1084 | const struct l4_xinfo_class *class, |
1085 | enum sysfs_byteorder byteorder) | |
7b2a9be0 MY |
1086 | { |
1087 | char line[TCP_LINE_LEN]; | |
1088 | FILE *tcp_fp; | |
1089 | ||
92a0dbce | 1090 | tcp_fp = fopen(proc_file, "r"); |
7b2a9be0 MY |
1091 | if (!tcp_fp) |
1092 | return; | |
1093 | ||
1094 | if (fgets(line, sizeof(line), tcp_fp) == NULL) | |
1095 | goto out; | |
cda2b2a0 | 1096 | if (!L4_verify_initial_line(line)) |
7b2a9be0 MY |
1097 | /* Unexpected line */ |
1098 | goto out; | |
1099 | ||
1100 | while (fgets(line, sizeof(line), tcp_fp)) { | |
19e40223 MY |
1101 | struct sock_xinfo *sock = class->scan_line(&class->sock, line, netns_inode, byteorder); |
1102 | if (sock) | |
1103 | add_sock_info(sock); | |
7b2a9be0 MY |
1104 | } |
1105 | ||
1106 | out: | |
1107 | fclose(tcp_fp); | |
1108 | } | |
92a0dbce | 1109 | |
891b24b7 | 1110 | static void load_xinfo_from_proc_tcp(ino_t netns_inode, enum sysfs_byteorder byteorder) |
92a0dbce | 1111 | { |
cda2b2a0 | 1112 | load_xinfo_from_proc_inet_L4(netns_inode, |
58d2f31e | 1113 | "/proc/net/tcp", |
891b24b7 MY |
1114 | &tcp_xinfo_class, |
1115 | byteorder); | |
92a0dbce MY |
1116 | } |
1117 | ||
1118 | /* | |
1119 | * UDP | |
1120 | */ | |
1121 | static char *udp_get_name(struct sock_xinfo *sock_xinfo, | |
1122 | struct sock *sock __attribute__((__unused__))) | |
1123 | { | |
1124 | char *str = NULL; | |
92a0dbce | 1125 | struct tcp_xinfo *tcp = ((struct tcp_xinfo *)sock_xinfo); |
8db07ebc | 1126 | struct l4_xinfo *l4 = &tcp->l4; |
8db07ebc | 1127 | unsigned int st = l4->st; |
303b41fe | 1128 | const char *st_str = l4_decode_state(st); |
838fa270 MY |
1129 | struct l4_xinfo_class *class = (struct l4_xinfo_class *)sock_xinfo->class; |
1130 | void *laddr = class->get_addr(l4, L4_LOCAL); | |
1131 | void *raddr = class->get_addr(l4, L4_REMOTE); | |
1132 | char local_s[BUFSIZ]; | |
1133 | char remote_s[BUFSIZ]; | |
28cf2b21 MY |
1134 | const char *start = class->l3_decorator[L3_DECO_START]; |
1135 | const char *end = class->l3_decorator[L3_DECO_END]; | |
92a0dbce | 1136 | |
838fa270 | 1137 | if (!inet_ntop(class->family, laddr, local_s, sizeof(local_s))) |
3b182ee7 | 1138 | xasprintf(&str, "state=%s", st_str); |
838fa270 MY |
1139 | else if ((class->is_any_addr(raddr) && tcp->remote_port == 0) |
1140 | || !inet_ntop(class->family, raddr, remote_s, sizeof(remote_s))) | |
28cf2b21 | 1141 | xasprintf(&str, "state=%s laddr=%s%s%s:%"PRIu16, |
3b182ee7 | 1142 | st_str, |
28cf2b21 | 1143 | start, local_s, end, tcp->local_port); |
92a0dbce | 1144 | else |
28cf2b21 | 1145 | xasprintf(&str, "state=%s laddr=%s%s%s:%"PRIu16" raddr=%s%s%s:%"PRIu16, |
3b182ee7 | 1146 | st_str, |
28cf2b21 MY |
1147 | start, local_s, end, tcp->local_port, |
1148 | start, remote_s, end, tcp->remote_port); | |
92a0dbce MY |
1149 | return str; |
1150 | } | |
1151 | ||
1152 | static char *udp_get_type(struct sock_xinfo *sock_xinfo __attribute__((__unused__)), | |
1153 | struct sock *sock __attribute__((__unused__))) | |
1154 | { | |
e0dc84da | 1155 | return xstrdup("dgram"); |
92a0dbce MY |
1156 | } |
1157 | ||
838fa270 MY |
1158 | static bool udp_fill_column(struct proc *proc __attribute__((__unused__)), |
1159 | struct sock_xinfo *sock_xinfo, | |
1160 | struct sock *sock __attribute__((__unused__)), | |
1161 | struct libscols_line *ln __attribute__((__unused__)), | |
1162 | int column_id, | |
1163 | size_t column_index __attribute__((__unused__)), | |
1164 | char **str) | |
1165 | { | |
1166 | return l3_fill_column_handler(INET, sock_xinfo, column_id, str) | |
1167 | || l4_fill_column_handler(UDP, sock_xinfo, column_id, str); | |
1168 | } | |
1169 | ||
4a2b51c1 MY |
1170 | static const struct l4_xinfo_class udp_xinfo_class = { |
1171 | .sock = { | |
1172 | .get_name = udp_get_name, | |
1173 | .get_type = udp_get_type, | |
1174 | .get_state = tcp_get_state, | |
1175 | .get_listening = NULL, | |
1176 | .fill_column = udp_fill_column, | |
1177 | .free = NULL, | |
1178 | }, | |
19e40223 | 1179 | .scan_line = tcp_xinfo_scan_line, |
2a4f2b1b MY |
1180 | .get_addr = tcp_xinfo_get_addr, |
1181 | .is_any_addr = tcp_xinfo_is_any_addr, | |
1182 | .family = AF_INET, | |
1656da13 | 1183 | .l3_decorator = {"", ""}, |
92a0dbce MY |
1184 | }; |
1185 | ||
891b24b7 | 1186 | static void load_xinfo_from_proc_udp(ino_t netns_inode, enum sysfs_byteorder byteorder) |
92a0dbce | 1187 | { |
cda2b2a0 | 1188 | load_xinfo_from_proc_inet_L4(netns_inode, |
92a0dbce | 1189 | "/proc/net/udp", |
891b24b7 MY |
1190 | &udp_xinfo_class, |
1191 | byteorder); | |
92a0dbce | 1192 | } |
0188afb3 | 1193 | |
c779f412 MY |
1194 | /* |
1195 | * UDP-Lite | |
1196 | */ | |
1197 | static bool udplite_fill_column(struct proc *proc __attribute__((__unused__)), | |
1198 | struct sock_xinfo *sock_xinfo, | |
1199 | struct sock *sock __attribute__((__unused__)), | |
1200 | struct libscols_line *ln __attribute__((__unused__)), | |
1201 | int column_id, | |
1202 | size_t column_index __attribute__((__unused__)), | |
1203 | char **str) | |
1204 | { | |
1205 | return l3_fill_column_handler(INET, sock_xinfo, column_id, str) | |
1206 | || l4_fill_column_handler(UDPLITE, sock_xinfo, column_id, str); | |
1207 | } | |
1208 | ||
1209 | static const struct l4_xinfo_class udplite_xinfo_class = { | |
1210 | .sock = { | |
1211 | .get_name = udp_get_name, | |
1212 | .get_type = udp_get_type, | |
1213 | .get_state = tcp_get_state, | |
1214 | .get_listening = NULL, | |
1215 | .fill_column = udplite_fill_column, | |
1216 | .free = NULL, | |
1217 | }, | |
1218 | .scan_line = tcp_xinfo_scan_line, | |
1219 | .get_addr = tcp_xinfo_get_addr, | |
1220 | .is_any_addr = tcp_xinfo_is_any_addr, | |
1221 | .family = AF_INET, | |
1222 | .l3_decorator = {"", ""}, | |
1223 | }; | |
1224 | ||
891b24b7 | 1225 | static void load_xinfo_from_proc_udplite(ino_t netns_inode, enum sysfs_byteorder byteorder) |
c779f412 MY |
1226 | { |
1227 | load_xinfo_from_proc_inet_L4(netns_inode, | |
1228 | "/proc/net/udplite", | |
891b24b7 MY |
1229 | &udplite_xinfo_class, |
1230 | byteorder); | |
c779f412 MY |
1231 | } |
1232 | ||
0188afb3 MY |
1233 | /* |
1234 | * RAW | |
1235 | */ | |
1236 | struct raw_xinfo { | |
1237 | struct l4_xinfo l4; | |
1238 | uint16_t protocol; | |
1239 | }; | |
1240 | ||
a7cba6f3 MY |
1241 | static char *raw_get_name_common(struct sock_xinfo *sock_xinfo, |
1242 | struct sock *sock __attribute__((__unused__)), | |
1243 | const char *port_label) | |
0188afb3 MY |
1244 | { |
1245 | char *str = NULL; | |
1246 | struct l4_xinfo_class *class = (struct l4_xinfo_class *)sock_xinfo->class; | |
1247 | struct raw_xinfo *raw = ((struct raw_xinfo *)sock_xinfo); | |
1248 | struct l4_xinfo *l4 = &raw->l4; | |
303b41fe | 1249 | const char *st_str = l4_decode_state(l4->st); |
0188afb3 MY |
1250 | void *laddr = class->get_addr(l4, L4_LOCAL); |
1251 | void *raddr = class->get_addr(l4, L4_REMOTE); | |
1252 | char local_s[BUFSIZ]; | |
1253 | char remote_s[BUFSIZ]; | |
1254 | ||
1255 | if (!inet_ntop(class->family, laddr, local_s, sizeof(local_s))) | |
1256 | xasprintf(&str, "state=%s", st_str); | |
1257 | else if (class->is_any_addr(raddr) | |
1258 | || !inet_ntop(class->family, raddr, remote_s, sizeof(remote_s))) | |
a7cba6f3 | 1259 | xasprintf(&str, "state=%s %s=%"PRIu16" laddr=%s", |
0188afb3 | 1260 | st_str, |
a7cba6f3 | 1261 | port_label, |
0188afb3 MY |
1262 | raw->protocol, local_s); |
1263 | else | |
a7cba6f3 | 1264 | xasprintf(&str, "state=%s %s=%"PRIu16" laddr=%s raddr=%s", |
0188afb3 | 1265 | st_str, |
a7cba6f3 | 1266 | port_label, |
0188afb3 MY |
1267 | raw->protocol, local_s, remote_s); |
1268 | return str; | |
1269 | } | |
1270 | ||
a7cba6f3 MY |
1271 | static char *raw_get_name(struct sock_xinfo *sock_xinfo, |
1272 | struct sock *sock __attribute__((__unused__))) | |
1273 | { | |
1274 | return raw_get_name_common(sock_xinfo, sock, "protocol"); | |
1275 | } | |
1276 | ||
0188afb3 MY |
1277 | static char *raw_get_type(struct sock_xinfo *sock_xinfo __attribute__((__unused__)), |
1278 | struct sock *sock __attribute__((__unused__))) | |
1279 | { | |
e0dc84da | 1280 | return xstrdup("raw"); |
0188afb3 MY |
1281 | } |
1282 | ||
1283 | static bool raw_fill_column(struct proc *proc __attribute__((__unused__)), | |
1284 | struct sock_xinfo *sock_xinfo, | |
1285 | struct sock *sock __attribute__((__unused__)), | |
1286 | struct libscols_line *ln __attribute__((__unused__)), | |
1287 | int column_id, | |
1288 | size_t column_index __attribute__((__unused__)), | |
1289 | char **str) | |
1290 | { | |
1291 | if (l3_fill_column_handler(INET, sock_xinfo, column_id, str)) | |
1292 | return true; | |
1293 | ||
1294 | if (column_id == COL_RAW_PROTOCOL) { | |
b4e39841 | 1295 | xasprintf(str, "%"PRIu16, |
73df46fc | 1296 | ((struct raw_xinfo *)sock_xinfo)->protocol); |
0188afb3 MY |
1297 | return true; |
1298 | } | |
1299 | ||
1300 | return false; | |
1301 | } | |
1302 | ||
1303 | static struct sock_xinfo *raw_xinfo_scan_line(const struct sock_xinfo_class *class, | |
1304 | char * line, | |
1305 | ino_t netns_inode, | |
1306 | enum sysfs_byteorder byteorder) | |
1307 | { | |
1308 | unsigned long local_addr; | |
1309 | unsigned long protocol; | |
1310 | unsigned long remote_addr; | |
1311 | unsigned long st; | |
1312 | unsigned long long inode; | |
1313 | struct raw_xinfo *raw; | |
1314 | struct inet_xinfo *inet; | |
1315 | struct sock_xinfo *sock; | |
1316 | ||
1317 | if (sscanf(line, "%*d: %lx:%lx %lx:%*x %lx %*x:%*x %*x:%*x %*x %*u %*u %lld", | |
1318 | &local_addr, &protocol, &remote_addr, | |
1319 | &st, &inode) != 5) | |
1320 | return NULL; | |
1321 | ||
1322 | if (inode == 0) | |
1323 | return NULL; | |
1324 | ||
8ff22ccb | 1325 | raw = xcalloc(1, sizeof(*raw)); |
0188afb3 MY |
1326 | inet = &raw->l4.inet; |
1327 | sock = &inet->sock; | |
1328 | sock->class = class; | |
1329 | sock->inode = (ino_t)inode; | |
1330 | sock->netns_inode = netns_inode; | |
1331 | inet->local_addr.s_addr = kernel32_to_cpu(byteorder, local_addr); | |
1332 | inet->remote_addr.s_addr = kernel32_to_cpu(byteorder, remote_addr); | |
1333 | raw->protocol = protocol; | |
1334 | raw->l4.st = st; | |
1335 | ||
1336 | return sock; | |
1337 | } | |
1338 | ||
1339 | static const struct l4_xinfo_class raw_xinfo_class = { | |
1340 | .sock = { | |
1341 | .get_name = raw_get_name, | |
1342 | .get_type = raw_get_type, | |
1343 | .get_state = tcp_get_state, | |
1344 | .get_listening = NULL, | |
1345 | .fill_column = raw_fill_column, | |
1346 | .free = NULL, | |
1347 | }, | |
1348 | .scan_line = raw_xinfo_scan_line, | |
1349 | .get_addr = tcp_xinfo_get_addr, | |
1350 | .is_any_addr = tcp_xinfo_is_any_addr, | |
1351 | .family = AF_INET, | |
1656da13 | 1352 | .l3_decorator = {"", ""}, |
0188afb3 MY |
1353 | }; |
1354 | ||
891b24b7 | 1355 | static void load_xinfo_from_proc_raw(ino_t netns_inode, enum sysfs_byteorder byteorder) |
0188afb3 MY |
1356 | { |
1357 | load_xinfo_from_proc_inet_L4(netns_inode, | |
1358 | "/proc/net/raw", | |
891b24b7 MY |
1359 | &raw_xinfo_class, |
1360 | byteorder); | |
0188afb3 | 1361 | } |
1656da13 | 1362 | |
a7cba6f3 MY |
1363 | /* |
1364 | * PING | |
1365 | */ | |
1366 | static char *ping_get_name(struct sock_xinfo *sock_xinfo, | |
1367 | struct sock *sock __attribute__((__unused__))) | |
1368 | { | |
1369 | return raw_get_name_common(sock_xinfo, sock, "id"); | |
1370 | } | |
1371 | ||
1372 | static char *ping_get_type(struct sock_xinfo *sock_xinfo __attribute__((__unused__)), | |
1373 | struct sock *sock __attribute__((__unused__))) | |
1374 | { | |
e0dc84da | 1375 | return xstrdup("dgram"); |
a7cba6f3 MY |
1376 | } |
1377 | ||
1378 | static bool ping_fill_column(struct proc *proc __attribute__((__unused__)), | |
1379 | struct sock_xinfo *sock_xinfo, | |
1380 | struct sock *sock __attribute__((__unused__)), | |
1381 | struct libscols_line *ln __attribute__((__unused__)), | |
1382 | int column_id, | |
1383 | size_t column_index __attribute__((__unused__)), | |
1384 | char **str) | |
1385 | { | |
1386 | if (l3_fill_column_handler(INET, sock_xinfo, column_id, str)) | |
1387 | return true; | |
1388 | ||
1389 | if (column_id == COL_PING_ID) { | |
1390 | xasprintf(str, "%"PRIu16, | |
1391 | ((struct raw_xinfo *)sock_xinfo)->protocol); | |
1392 | return true; | |
1393 | } | |
1394 | ||
1395 | return false; | |
1396 | } | |
1397 | ||
1398 | static const struct l4_xinfo_class ping_xinfo_class = { | |
1399 | .sock = { | |
1400 | .get_name = ping_get_name, | |
1401 | .get_type = ping_get_type, | |
1402 | .get_state = tcp_get_state, | |
1403 | .get_listening = NULL, | |
1404 | .fill_column = ping_fill_column, | |
1405 | .free = NULL, | |
1406 | }, | |
1407 | .scan_line = raw_xinfo_scan_line, | |
1408 | .get_addr = tcp_xinfo_get_addr, | |
1409 | .is_any_addr = tcp_xinfo_is_any_addr, | |
1410 | .family = AF_INET, | |
1411 | .l3_decorator = {"", ""}, | |
1412 | }; | |
1413 | ||
891b24b7 | 1414 | static void load_xinfo_from_proc_icmp(ino_t netns_inode, enum sysfs_byteorder byteorder) |
a7cba6f3 MY |
1415 | { |
1416 | load_xinfo_from_proc_inet_L4(netns_inode, | |
1417 | "/proc/net/icmp", | |
891b24b7 MY |
1418 | &ping_xinfo_class, |
1419 | byteorder); | |
a7cba6f3 MY |
1420 | } |
1421 | ||
1656da13 MY |
1422 | /* |
1423 | * TCP6 | |
1424 | */ | |
1425 | static struct sock_xinfo *tcp6_xinfo_scan_line(const struct sock_xinfo_class *class, | |
1426 | char * line, | |
1427 | ino_t netns_inode, | |
1428 | enum sysfs_byteorder byteorder) | |
1429 | { | |
1430 | uint32_t local_addr[4]; | |
1431 | unsigned int local_port; | |
1432 | uint32_t remote_addr[4]; | |
1433 | unsigned int remote_port; | |
1434 | unsigned int st; | |
1435 | unsigned long inode; | |
1436 | struct tcp_xinfo *tcp; | |
1437 | struct inet6_xinfo *inet6; | |
1438 | struct sock_xinfo *sock; | |
1439 | ||
1440 | if (sscanf(line, | |
1441 | "%*d: " | |
1442 | "%08x%08x%08x%08x:%04x " | |
1443 | "%08x%08x%08x%08x:%04x " | |
1444 | "%x %*x:%*x %*x:%*x %*x %*u %*d %lu ", | |
1445 | local_addr+0, local_addr+1, local_addr+2, local_addr+3, &local_port, | |
1446 | remote_addr+0, remote_addr+1, remote_addr+2, remote_addr+3, &remote_port, | |
1447 | &st, &inode) != 12) | |
1448 | return NULL; | |
1449 | ||
1450 | if (inode == 0) | |
1451 | return NULL; | |
1452 | ||
8ff22ccb | 1453 | tcp = xmalloc(sizeof(*tcp)); |
1656da13 MY |
1454 | inet6 = &tcp->l4.inet6; |
1455 | sock = &inet6->sock; | |
1456 | sock->class = class; | |
1457 | sock->inode = (ino_t)inode; | |
1458 | sock->netns_inode = netns_inode; | |
1459 | tcp->local_port = local_port; | |
1460 | for (int i = 0; i < 4; i++) { | |
1461 | inet6->local_addr.s6_addr32[i] = kernel32_to_cpu(byteorder, local_addr[i]); | |
1462 | inet6->remote_addr.s6_addr32[i] = kernel32_to_cpu(byteorder, remote_addr[i]); | |
1463 | } | |
1464 | tcp->remote_port = remote_port; | |
1465 | tcp->l4.st = st; | |
1466 | ||
1467 | return sock; | |
1468 | } | |
1469 | ||
1470 | static bool tcp6_fill_column(struct proc *proc __attribute__((__unused__)), | |
1471 | struct sock_xinfo *sock_xinfo, | |
1472 | struct sock *sock __attribute__((__unused__)), | |
1473 | struct libscols_line *ln __attribute__((__unused__)), | |
1474 | int column_id, | |
1475 | size_t column_index __attribute__((__unused__)), | |
1476 | char **str) | |
1477 | { | |
1478 | return l3_fill_column_handler(INET6, sock_xinfo, column_id, str) | |
1479 | || l4_fill_column_handler(TCP, sock_xinfo, column_id, str); | |
1480 | } | |
1481 | ||
1482 | static void *tcp6_xinfo_get_addr(struct l4_xinfo * l4, enum l4_side side) | |
1483 | { | |
1484 | return (side == L4_LOCAL) | |
1485 | ? &l4->inet6.local_addr | |
1486 | : &l4->inet6.remote_addr; | |
1487 | } | |
1488 | ||
1489 | static bool tcp6_xinfo_is_any_addr(void *addr) | |
1490 | { | |
1491 | return IN6_ARE_ADDR_EQUAL(addr, &(struct in6_addr)IN6ADDR_ANY_INIT); | |
1492 | } | |
1493 | ||
1494 | static const struct l4_xinfo_class tcp6_xinfo_class = { | |
1495 | .sock = { | |
1496 | .get_name = tcp_get_name, | |
1497 | .get_type = tcp_get_type, | |
1498 | .get_state = tcp_get_state, | |
1499 | .get_listening = tcp_get_listening, | |
1500 | .fill_column = tcp6_fill_column, | |
1501 | .free = NULL, | |
1502 | }, | |
1503 | .scan_line = tcp6_xinfo_scan_line, | |
1504 | .get_addr = tcp6_xinfo_get_addr, | |
1505 | .is_any_addr = tcp6_xinfo_is_any_addr, | |
1506 | .family = AF_INET6, | |
1507 | .l3_decorator = {"[", "]"}, | |
1508 | }; | |
1509 | ||
891b24b7 | 1510 | static void load_xinfo_from_proc_tcp6(ino_t netns_inode, enum sysfs_byteorder byteorder) |
1656da13 MY |
1511 | { |
1512 | load_xinfo_from_proc_inet_L4(netns_inode, | |
1513 | "/proc/net/tcp6", | |
891b24b7 MY |
1514 | &tcp6_xinfo_class, |
1515 | byteorder); | |
1656da13 | 1516 | } |
28cf2b21 MY |
1517 | |
1518 | /* | |
1519 | * UDP6 | |
1520 | */ | |
1521 | static bool udp6_fill_column(struct proc *proc __attribute__((__unused__)), | |
1522 | struct sock_xinfo *sock_xinfo, | |
1523 | struct sock *sock __attribute__((__unused__)), | |
1524 | struct libscols_line *ln __attribute__((__unused__)), | |
1525 | int column_id, | |
1526 | size_t column_index __attribute__((__unused__)), | |
1527 | char **str) | |
1528 | { | |
1529 | return l3_fill_column_handler(INET6, sock_xinfo, column_id, str) | |
1530 | || l4_fill_column_handler(UDP, sock_xinfo, column_id, str); | |
1531 | } | |
1532 | ||
1533 | static const struct l4_xinfo_class udp6_xinfo_class = { | |
1534 | .sock = { | |
1535 | .get_name = udp_get_name, | |
1536 | .get_type = udp_get_type, | |
1537 | .get_state = tcp_get_state, | |
1538 | .get_listening = NULL, | |
1539 | .fill_column = udp6_fill_column, | |
1540 | .free = NULL, | |
1541 | }, | |
1542 | .scan_line = tcp6_xinfo_scan_line, | |
1543 | .get_addr = tcp6_xinfo_get_addr, | |
1544 | .is_any_addr = tcp6_xinfo_is_any_addr, | |
1545 | .family = AF_INET6, | |
1546 | .l3_decorator = {"[", "]"}, | |
1547 | }; | |
1548 | ||
891b24b7 | 1549 | static void load_xinfo_from_proc_udp6(ino_t netns_inode, enum sysfs_byteorder byteorder) |
28cf2b21 MY |
1550 | { |
1551 | load_xinfo_from_proc_inet_L4(netns_inode, | |
1552 | "/proc/net/udp6", | |
891b24b7 MY |
1553 | &udp6_xinfo_class, |
1554 | byteorder); | |
28cf2b21 | 1555 | } |
2dd373c3 | 1556 | |
93bca151 MY |
1557 | /* |
1558 | * UDPLITEv6 | |
1559 | */ | |
1560 | static bool udplite6_fill_column(struct proc *proc __attribute__((__unused__)), | |
1561 | struct sock_xinfo *sock_xinfo, | |
1562 | struct sock *sock __attribute__((__unused__)), | |
1563 | struct libscols_line *ln __attribute__((__unused__)), | |
1564 | int column_id, | |
1565 | size_t column_index __attribute__((__unused__)), | |
1566 | char **str) | |
1567 | { | |
1568 | return l3_fill_column_handler(INET6, sock_xinfo, column_id, str) | |
1569 | || l4_fill_column_handler(UDPLITE, sock_xinfo, column_id, str); | |
1570 | } | |
1571 | ||
1572 | static const struct l4_xinfo_class udplite6_xinfo_class = { | |
1573 | .sock = { | |
1574 | .get_name = udp_get_name, | |
1575 | .get_type = udp_get_type, | |
1576 | .get_state = tcp_get_state, | |
1577 | .get_listening = NULL, | |
1578 | .fill_column = udplite6_fill_column, | |
1579 | .free = NULL, | |
1580 | }, | |
1581 | .scan_line = tcp6_xinfo_scan_line, | |
1582 | .get_addr = tcp6_xinfo_get_addr, | |
1583 | .is_any_addr = tcp6_xinfo_is_any_addr, | |
1584 | .family = AF_INET6, | |
1585 | .l3_decorator = {"[", "]"}, | |
1586 | }; | |
1587 | ||
891b24b7 | 1588 | static void load_xinfo_from_proc_udplite6(ino_t netns_inode, enum sysfs_byteorder byteorder) |
93bca151 MY |
1589 | { |
1590 | load_xinfo_from_proc_inet_L4(netns_inode, | |
1591 | "/proc/net/udplite6", | |
891b24b7 MY |
1592 | &udplite6_xinfo_class, |
1593 | byteorder); | |
93bca151 MY |
1594 | } |
1595 | ||
2dd373c3 MY |
1596 | /* |
1597 | * RAW6 | |
1598 | */ | |
1599 | static struct sock_xinfo *raw6_xinfo_scan_line(const struct sock_xinfo_class *class, | |
1600 | char * line, | |
1601 | ino_t netns_inode, | |
1602 | enum sysfs_byteorder byteorder) | |
1603 | { | |
1604 | uint32_t local_addr[4]; | |
1605 | unsigned int protocol; | |
1606 | uint32_t remote_addr[4]; | |
1607 | unsigned int st; | |
1608 | unsigned long inode; | |
1609 | struct raw_xinfo *raw; | |
1610 | struct inet6_xinfo *inet6; | |
1611 | struct sock_xinfo *sock; | |
1612 | ||
1613 | if (sscanf(line, | |
1614 | "%*d: " | |
1615 | "%08x%08x%08x%08x:%04x " | |
1616 | "%08x%08x%08x%08x:0000 " | |
1617 | "%x %*x:%*x %*x:%*x %*x %*u %*d %lu ", | |
1618 | local_addr+0, local_addr+1, local_addr+2, local_addr+3, &protocol, | |
1619 | remote_addr+0, remote_addr+1, remote_addr+2, remote_addr+3, | |
1620 | &st, &inode) != 11) | |
1621 | return NULL; | |
1622 | ||
1623 | if (inode == 0) | |
1624 | return NULL; | |
1625 | ||
1626 | raw = xmalloc(sizeof(*raw)); | |
1627 | inet6 = &raw->l4.inet6; | |
1628 | sock = &inet6->sock; | |
1629 | sock->class = class; | |
1630 | sock->inode = (ino_t)inode; | |
1631 | sock->netns_inode = netns_inode; | |
1632 | for (int i = 0; i < 4; i++) { | |
1633 | inet6->local_addr.s6_addr32[i] = kernel32_to_cpu(byteorder, local_addr[i]); | |
1634 | inet6->remote_addr.s6_addr32[i] = kernel32_to_cpu(byteorder, remote_addr[i]); | |
1635 | } | |
1636 | raw->protocol = protocol; | |
1637 | raw->l4.st = st; | |
1638 | ||
1639 | return sock; | |
1640 | } | |
1641 | ||
1642 | static bool raw6_fill_column(struct proc *proc __attribute__((__unused__)), | |
1643 | struct sock_xinfo *sock_xinfo, | |
1644 | struct sock *sock __attribute__((__unused__)), | |
1645 | struct libscols_line *ln __attribute__((__unused__)), | |
1646 | int column_id, | |
1647 | size_t column_index __attribute__((__unused__)), | |
1648 | char **str) | |
1649 | { | |
1650 | struct raw_xinfo *raw; | |
1651 | ||
1652 | if (l3_fill_column_handler(INET6, sock_xinfo, column_id, str)) | |
1653 | return true; | |
1654 | ||
1655 | raw = (struct raw_xinfo *)sock_xinfo; | |
1656 | if (column_id == COL_RAW_PROTOCOL) { | |
1657 | xasprintf(str, "%"PRIu16, raw->protocol); | |
1658 | return true; | |
1659 | } | |
1660 | ||
1661 | return false; | |
1662 | } | |
1663 | ||
1664 | static const struct l4_xinfo_class raw6_xinfo_class = { | |
1665 | .sock = { | |
1666 | .get_name = raw_get_name, | |
1667 | .get_type = raw_get_type, | |
1668 | .get_state = tcp_get_state, | |
1669 | .get_listening = NULL, | |
1670 | .fill_column = raw6_fill_column, | |
1671 | .free = NULL, | |
1672 | }, | |
1673 | .scan_line = raw6_xinfo_scan_line, | |
1674 | .get_addr = tcp6_xinfo_get_addr, | |
1675 | .is_any_addr = tcp6_xinfo_is_any_addr, | |
1676 | .family = AF_INET6, | |
1677 | .l3_decorator = {"[", "]"}, | |
1678 | }; | |
1679 | ||
891b24b7 | 1680 | static void load_xinfo_from_proc_raw6(ino_t netns_inode, enum sysfs_byteorder byteorder) |
2dd373c3 MY |
1681 | { |
1682 | load_xinfo_from_proc_inet_L4(netns_inode, | |
1683 | "/proc/net/raw6", | |
891b24b7 MY |
1684 | &raw6_xinfo_class, |
1685 | byteorder); | |
2dd373c3 | 1686 | } |
0b1dfd03 MY |
1687 | |
1688 | /* | |
1689 | * PINGv6 | |
1690 | */ | |
1691 | static bool ping6_fill_column(struct proc *proc __attribute__((__unused__)), | |
1692 | struct sock_xinfo *sock_xinfo, | |
1693 | struct sock *sock __attribute__((__unused__)), | |
1694 | struct libscols_line *ln __attribute__((__unused__)), | |
1695 | int column_id, | |
1696 | size_t column_index __attribute__((__unused__)), | |
1697 | char **str) | |
1698 | { | |
1699 | if (l3_fill_column_handler(INET6, sock_xinfo, column_id, str)) | |
1700 | return true; | |
1701 | ||
1702 | if (column_id == COL_PING_ID) { | |
1703 | xasprintf(str, "%"PRIu16, | |
1704 | ((struct raw_xinfo *)sock_xinfo)->protocol); | |
1705 | return true; | |
1706 | } | |
1707 | ||
1708 | return false; | |
1709 | } | |
1710 | ||
1711 | static const struct l4_xinfo_class ping6_xinfo_class = { | |
1712 | .sock = { | |
1713 | .get_name = ping_get_name, | |
1714 | .get_type = ping_get_type, | |
1715 | .get_state = tcp_get_state, | |
1716 | .get_listening = NULL, | |
1717 | .fill_column = ping6_fill_column, | |
1718 | .free = NULL, | |
1719 | }, | |
1720 | .scan_line = raw6_xinfo_scan_line, | |
1721 | .get_addr = tcp6_xinfo_get_addr, | |
1722 | .is_any_addr = tcp6_xinfo_is_any_addr, | |
1723 | .family = AF_INET6, | |
1724 | .l3_decorator = {"[", "]"}, | |
1725 | }; | |
1726 | ||
891b24b7 | 1727 | static void load_xinfo_from_proc_icmp6(ino_t netns_inode, enum sysfs_byteorder byteorder) |
0b1dfd03 MY |
1728 | { |
1729 | load_xinfo_from_proc_inet_L4(netns_inode, | |
1730 | "/proc/net/icmp6", | |
891b24b7 MY |
1731 | &ping6_xinfo_class, |
1732 | byteorder); | |
0b1dfd03 | 1733 | } |
b53cc896 MY |
1734 | |
1735 | /* | |
1736 | * NETLINK | |
1737 | */ | |
1738 | struct netlink_xinfo { | |
1739 | struct sock_xinfo sock; | |
1740 | uint16_t protocol; | |
1741 | uint32_t lportid; /* netlink_diag may provide rportid. */ | |
1742 | uint32_t groups; | |
1743 | }; | |
1744 | ||
1745 | static const char *netlink_decode_protocol(uint16_t protocol) | |
1746 | { | |
1747 | switch (protocol) { | |
1748 | case NETLINK_ROUTE: | |
1749 | return "route"; | |
1750 | case NETLINK_UNUSED: | |
1751 | return "unused"; | |
1752 | case NETLINK_USERSOCK: | |
1753 | return "usersock"; | |
1754 | case NETLINK_FIREWALL: | |
1755 | return "firewall"; | |
1756 | case NETLINK_SOCK_DIAG: | |
1757 | return "sock_diag"; | |
1758 | case NETLINK_NFLOG: | |
1759 | return "nflog"; | |
1760 | case NETLINK_XFRM: | |
1761 | return "xfrm"; | |
1762 | case NETLINK_SELINUX: | |
1763 | return "selinux"; | |
1764 | case NETLINK_ISCSI: | |
1765 | return "iscsi"; | |
1766 | case NETLINK_AUDIT: | |
1767 | return "audit"; | |
1768 | case NETLINK_FIB_LOOKUP: | |
1769 | return "fib_lookup"; | |
1770 | case NETLINK_CONNECTOR: | |
1771 | return "connector"; | |
1772 | case NETLINK_NETFILTER: | |
1773 | return "netfilter"; | |
1774 | case NETLINK_IP6_FW: | |
1775 | return "ip6_fw"; | |
1776 | case NETLINK_DNRTMSG: | |
1777 | return "dnrtmsg"; | |
1778 | case NETLINK_KOBJECT_UEVENT: | |
1779 | return "kobject_uevent"; | |
1780 | case NETLINK_GENERIC: | |
1781 | return "generic"; | |
1782 | case NETLINK_SCSITRANSPORT: | |
1783 | return "scsitransport"; | |
1784 | case NETLINK_ECRYPTFS: | |
1785 | return "ecryptfs"; | |
1786 | case NETLINK_RDMA: | |
1787 | return "rdma"; | |
1788 | case NETLINK_CRYPTO: | |
1789 | return "crypto"; | |
1790 | #ifdef NETLINK_SMC | |
1791 | case NETLINK_SMC: | |
1792 | return "smc"; | |
1793 | #endif | |
1794 | default: | |
1795 | return "unknown"; | |
1796 | } | |
1797 | } | |
1798 | ||
1799 | static char *netlink_get_name(struct sock_xinfo *sock_xinfo, | |
1800 | struct sock *sock __attribute__((__unused__))) | |
1801 | { | |
1802 | struct netlink_xinfo *nl = (struct netlink_xinfo *)sock_xinfo; | |
1803 | char *str = NULL; | |
1804 | const char *protocol = netlink_decode_protocol(nl->protocol); | |
1805 | ||
1806 | if (nl->groups) | |
1807 | xasprintf(&str, "protocol=%s lport=%"PRIu16 " groups=%"PRIu32, | |
1808 | protocol, | |
1809 | nl->lportid, nl->groups); | |
1810 | else | |
1811 | xasprintf(&str, "protocol=%s lport=%"PRIu16, | |
1812 | protocol, | |
1813 | nl->lportid); | |
1814 | return str; | |
1815 | } | |
1816 | ||
1817 | static char *netlink_get_type(struct sock_xinfo *sock_xinfo __attribute__((__unused__)), | |
1818 | struct sock *sock __attribute__((__unused__))) | |
1819 | { | |
e0dc84da | 1820 | return xstrdup("raw"); |
b53cc896 MY |
1821 | } |
1822 | ||
1823 | static bool netlink_fill_column(struct proc *proc __attribute__((__unused__)), | |
1824 | struct sock_xinfo *sock_xinfo, | |
1825 | struct sock *sock __attribute__((__unused__)), | |
1826 | struct libscols_line *ln __attribute__((__unused__)), | |
1827 | int column_id, | |
1828 | size_t column_index __attribute__((__unused__)), | |
1829 | char **str) | |
1830 | { | |
1831 | struct netlink_xinfo *nl = (struct netlink_xinfo *)sock_xinfo; | |
1832 | ||
1833 | switch (column_id) { | |
1834 | case COL_NETLINK_GROUPS: | |
1835 | xasprintf(str, "%"PRIu32, nl->groups); | |
1836 | return true; | |
1837 | case COL_NETLINK_LPORT: | |
1838 | xasprintf(str, "%"PRIu32, nl->lportid); | |
1839 | return true; | |
1840 | case COL_NETLINK_PROTOCOL: | |
e0dc84da | 1841 | *str = xstrdup(netlink_decode_protocol(nl->protocol)); |
b53cc896 MY |
1842 | return true; |
1843 | } | |
1844 | ||
1845 | return false; | |
1846 | } | |
1847 | ||
1848 | static const struct sock_xinfo_class netlink_xinfo_class = { | |
1849 | .get_name = netlink_get_name, | |
1850 | .get_type = netlink_get_type, | |
1851 | .get_state = NULL, | |
1852 | .get_listening = NULL, | |
1853 | .fill_column = netlink_fill_column, | |
1854 | .free = NULL, | |
1855 | }; | |
1856 | ||
1857 | static void load_xinfo_from_proc_netlink(ino_t netns_inode) | |
1858 | { | |
1859 | char line[BUFSIZ]; | |
1860 | FILE *netlink_fp; | |
1861 | ||
1862 | netlink_fp = fopen("/proc/net/netlink", "r"); | |
1863 | if (!netlink_fp) | |
1864 | return; | |
1865 | ||
1866 | if (fgets(line, sizeof(line), netlink_fp) == NULL) | |
1867 | goto out; | |
1868 | if (!(line[0] == 's' && line[1] == 'k')) | |
1869 | /* Unexpected line */ | |
1870 | goto out; | |
1871 | ||
1872 | while (fgets(line, sizeof(line), netlink_fp)) { | |
1873 | uint16_t protocol; | |
1874 | uint32_t lportid; | |
1875 | uint32_t groups; | |
1876 | unsigned long inode; | |
1877 | struct netlink_xinfo *nl; | |
1878 | ||
1879 | if (sscanf(line, "%*x %" SCNu16 " %" SCNu32 " %" SCNx32 " %*d %*d %*d %*d %*u %lu", | |
1880 | &protocol, &lportid, &groups, &inode) < 4) | |
1881 | continue; | |
1882 | ||
1883 | if (inode == 0) | |
1884 | continue; | |
1885 | ||
1886 | nl = xcalloc(1, sizeof(*nl)); | |
1887 | nl->sock.class = &netlink_xinfo_class; | |
1888 | nl->sock.inode = (ino_t)inode; | |
1889 | nl->sock.netns_inode = netns_inode; | |
1890 | ||
1891 | nl->protocol = protocol; | |
1892 | nl->lportid = lportid; | |
1893 | nl->groups = groups; | |
1894 | ||
1895 | add_sock_info(&nl->sock); | |
1896 | } | |
1897 | ||
1898 | out: | |
1899 | fclose(netlink_fp); | |
1900 | } | |
adfc156a MY |
1901 | |
1902 | /* | |
1903 | * PACKET | |
1904 | */ | |
1905 | struct packet_xinfo { | |
1906 | struct sock_xinfo sock; | |
1907 | uint16_t type; | |
1908 | uint16_t protocol; | |
1909 | unsigned int iface; | |
1910 | }; | |
1911 | ||
1912 | static const char *packet_decode_protocol(uint16_t proto) | |
1913 | { | |
1914 | switch (proto) { | |
1915 | case 0: | |
1916 | return NULL; | |
1917 | case ETH_P_802_3: | |
1918 | return "802_3"; | |
1919 | case ETH_P_AX25: | |
1920 | return "ax25"; | |
1921 | case ETH_P_ALL: | |
1922 | return "all"; | |
1923 | case ETH_P_802_2: | |
1924 | return "802_2"; | |
1925 | case ETH_P_SNAP: | |
1926 | return "snap"; | |
1927 | case ETH_P_DDCMP: | |
1928 | return "ddcmp"; | |
1929 | case ETH_P_WAN_PPP: | |
1930 | return "wan_ppp"; | |
1931 | case ETH_P_PPP_MP: | |
1932 | return "ppp_mp"; | |
1933 | case ETH_P_LOCALTALK: | |
1934 | return "localtalk"; | |
1935 | case ETH_P_CAN: | |
1936 | return "can"; | |
1937 | case ETH_P_CANFD: | |
1938 | return "canfd"; | |
1939 | #ifdef ETH_P_CANXL | |
1940 | case ETH_P_CANXL: | |
1941 | return "canxl"; | |
1942 | #endif | |
1943 | case ETH_P_PPPTALK: | |
1944 | return "ppptalk"; | |
1945 | case ETH_P_TR_802_2: | |
1946 | return "tr_802_2"; | |
1947 | case ETH_P_MOBITEX: | |
1948 | return "mobitex"; | |
1949 | case ETH_P_CONTROL: | |
1950 | return "control"; | |
1951 | case ETH_P_IRDA: | |
1952 | return "irda"; | |
1953 | case ETH_P_ECONET: | |
1954 | return "econet"; | |
1955 | case ETH_P_HDLC: | |
1956 | return "hdlc"; | |
1957 | case ETH_P_ARCNET: | |
1958 | return "arcnet"; | |
1959 | case ETH_P_DSA: | |
1960 | return "dsa"; | |
1961 | case ETH_P_TRAILER: | |
1962 | return "trailer"; | |
1963 | case ETH_P_PHONET: | |
1964 | return "phonet"; | |
1965 | case ETH_P_IEEE802154: | |
1966 | return "ieee802154"; | |
1967 | case ETH_P_CAIF: | |
1968 | return "caif"; | |
1969 | #ifdef ETH_P_XDSA | |
1970 | case ETH_P_XDSA: | |
1971 | return "xdsa"; | |
1972 | #endif | |
1973 | #ifdef ETH_P_MAP | |
1974 | case ETH_P_MAP: | |
1975 | return "map"; | |
1976 | #endif | |
1977 | #ifdef ETH_P_MCTP | |
1978 | case ETH_P_MCTP: | |
1979 | return "mctp"; | |
1980 | #endif | |
1981 | case ETH_P_LOOP: | |
1982 | return "loop"; | |
1983 | case ETH_P_PUP: | |
1984 | return "pup"; | |
1985 | case ETH_P_PUPAT: | |
1986 | return "pupat"; | |
1987 | #ifdef ETH_P_TSN | |
1988 | case ETH_P_TSN: | |
1989 | return "tsn"; | |
1990 | #endif | |
1991 | #ifdef ETH_P_ERSPAN2 | |
1992 | case ETH_P_ERSPAN2: | |
1993 | return "erspan2"; | |
1994 | #endif | |
1995 | case ETH_P_IP: | |
1996 | return "ip"; | |
1997 | case ETH_P_X25: | |
1998 | return "x25"; | |
1999 | case ETH_P_ARP: | |
2000 | return "arp"; | |
2001 | case ETH_P_BPQ: | |
2002 | return "bpq"; | |
2003 | case ETH_P_IEEEPUP: | |
2004 | return "ieeepup"; | |
2005 | case ETH_P_IEEEPUPAT: | |
2006 | return "ieeepupat"; | |
2007 | case ETH_P_BATMAN: | |
2008 | return "batman"; | |
2009 | case ETH_P_DEC: | |
2010 | return "dec"; | |
2011 | case ETH_P_DNA_DL: | |
2012 | return "dna_dl"; | |
2013 | case ETH_P_DNA_RC: | |
2014 | return "dna_rc"; | |
2015 | case ETH_P_DNA_RT: | |
2016 | return "dna_rt"; | |
2017 | case ETH_P_LAT: | |
2018 | return "lat"; | |
2019 | case ETH_P_DIAG: | |
2020 | return "diag"; | |
2021 | case ETH_P_CUST: | |
2022 | return "cust"; | |
2023 | case ETH_P_SCA: | |
2024 | return "sca"; | |
2025 | case ETH_P_TEB: | |
2026 | return "teb"; | |
2027 | case ETH_P_RARP: | |
2028 | return "rarp"; | |
2029 | case ETH_P_ATALK: | |
2030 | return "atalk"; | |
2031 | case ETH_P_AARP: | |
2032 | return "aarp"; | |
2033 | case ETH_P_8021Q: | |
2034 | return "8021q"; | |
2035 | #ifdef ETH_P_ERSPAN | |
2036 | case ETH_P_ERSPAN: | |
2037 | return "erspan"; | |
2038 | #endif | |
2039 | case ETH_P_IPX: | |
2040 | return "ipx"; | |
2041 | case ETH_P_IPV6: | |
2042 | return "ipv6"; | |
2043 | case ETH_P_PAUSE: | |
2044 | return "pause"; | |
2045 | case ETH_P_SLOW: | |
2046 | return "slow"; | |
2047 | case ETH_P_WCCP: | |
2048 | return "wccp"; | |
2049 | case ETH_P_MPLS_UC: | |
2050 | return "mpls_uc"; | |
2051 | case ETH_P_MPLS_MC: | |
2052 | return "mpls_mc"; | |
2053 | case ETH_P_ATMMPOA: | |
2054 | return "atmmpoa"; | |
2055 | #ifdef ETH_P_PPP_DISC | |
2056 | case ETH_P_PPP_DISC: | |
2057 | return "ppp_disc"; | |
2058 | #endif | |
2059 | #ifdef ETH_P_PPP_SES | |
2060 | case ETH_P_PPP_SES: | |
2061 | return "ppp_ses"; | |
2062 | #endif | |
2063 | case ETH_P_LINK_CTL: | |
2064 | return "link_ctl"; | |
2065 | case ETH_P_ATMFATE: | |
2066 | return "atmfate"; | |
2067 | case ETH_P_PAE: | |
2068 | return "pae"; | |
2069 | #ifdef ETH_P_PROFINET | |
2070 | case ETH_P_PROFINET: | |
2071 | return "profinet"; | |
2072 | #endif | |
2073 | #ifdef ETH_P_REALTEK | |
2074 | case ETH_P_REALTEK: | |
2075 | return "realtek"; | |
2076 | #endif | |
2077 | case ETH_P_AOE: | |
2078 | return "aoe"; | |
2079 | #ifdef ETH_P_ETHERCAT | |
2080 | case ETH_P_ETHERCAT: | |
2081 | return "ethercat"; | |
2082 | #endif | |
2083 | case ETH_P_8021AD: | |
2084 | return "8021ad"; | |
2085 | case ETH_P_802_EX1: | |
2086 | return "802_ex1"; | |
2087 | #ifdef ETH_P_PREAUTH | |
2088 | case ETH_P_PREAUTH: | |
2089 | return "preauth"; | |
2090 | #endif | |
2091 | case ETH_P_TIPC: | |
2092 | return "tipc"; | |
2093 | #ifdef ETH_P_LLDP | |
2094 | case ETH_P_LLDP: | |
2095 | return "lldp"; | |
2096 | #endif | |
2097 | #ifdef ETH_P_MRP | |
2098 | case ETH_P_MRP: | |
2099 | return "mrp"; | |
2100 | #endif | |
2101 | #ifdef ETH_P_MACSEC | |
2102 | case ETH_P_MACSEC: | |
2103 | return "macsec"; | |
2104 | #endif | |
2105 | case ETH_P_8021AH: | |
2106 | return "8021ah"; | |
2107 | #ifdef ETH_P_MVRP | |
2108 | case ETH_P_MVRP: | |
2109 | return "mvrp"; | |
2110 | #endif | |
2111 | case ETH_P_1588: | |
2112 | return "1588"; | |
2113 | #ifdef ETH_P_NCSI | |
2114 | case ETH_P_NCSI: | |
2115 | return "ncsi"; | |
2116 | #endif | |
2117 | #ifdef ETH_P_PRP | |
2118 | case ETH_P_PRP: | |
2119 | return "prp"; | |
2120 | #endif | |
2121 | #ifdef ETH_P_CFM | |
2122 | case ETH_P_CFM: | |
2123 | return "cfm"; | |
2124 | #endif | |
2125 | case ETH_P_FCOE: | |
2126 | return "fcoe"; | |
2127 | #ifdef ETH_P_IBOE | |
2128 | case ETH_P_IBOE: | |
2129 | return "iboe"; | |
2130 | #endif | |
2131 | case ETH_P_TDLS: | |
2132 | return "tdls"; | |
2133 | case ETH_P_FIP: | |
2134 | return "fip"; | |
2135 | #ifdef ETH_P_80221 | |
2136 | case ETH_P_80221: | |
2137 | return "80221"; | |
2138 | #endif | |
2139 | #ifdef ETH_P_HSR | |
2140 | case ETH_P_HSR: | |
2141 | return "hsr"; | |
2142 | #endif | |
2143 | #ifdef ETH_P_NSH | |
2144 | case ETH_P_NSH: | |
2145 | return "nsh"; | |
2146 | #endif | |
2147 | #ifdef ETH_P_LOOPBACK | |
2148 | case ETH_P_LOOPBACK: | |
2149 | return "loopback"; | |
2150 | #endif | |
2151 | case ETH_P_QINQ1: | |
2152 | return "qinq1"; | |
2153 | case ETH_P_QINQ2: | |
2154 | return "qinq2"; | |
2155 | case ETH_P_QINQ3: | |
2156 | return "qinq3"; | |
2157 | case ETH_P_EDSA: | |
2158 | return "edsa"; | |
2159 | #ifdef ETH_P_DSA_8021Q | |
2160 | case ETH_P_DSA_8021Q: | |
2161 | return "dsa_8021q"; | |
2162 | #endif | |
2163 | #ifdef ETH_P_DSA_A5PSW | |
2164 | case ETH_P_DSA_A5PSW: | |
2165 | return "dsa_a5psw"; | |
2166 | #endif | |
2167 | #ifdef ETH_P_IFE | |
2168 | case ETH_P_IFE: | |
2169 | return "ife"; | |
2170 | #endif | |
2171 | case ETH_P_AF_IUCV: | |
2172 | return "af_iucv"; | |
2173 | #ifdef ETH_P_802_3_MIN | |
2174 | case ETH_P_802_3_MIN: | |
2175 | return "802_3_min"; | |
2176 | #endif | |
2177 | default: | |
2178 | return "unknown"; | |
2179 | } | |
2180 | } | |
2181 | ||
2182 | static char *packet_get_name(struct sock_xinfo *sock_xinfo, | |
2183 | struct sock *sock __attribute__((__unused__))) | |
2184 | { | |
2185 | struct packet_xinfo *pkt = (struct packet_xinfo *)sock_xinfo; | |
2186 | char *str = NULL; | |
2187 | const char *type = sock_decode_type(pkt->type); | |
2188 | const char *proto = packet_decode_protocol(pkt->protocol); | |
2189 | const char *iface = get_iface_name(sock_xinfo->netns_inode, | |
2190 | pkt->iface); | |
2191 | ||
2192 | if (iface && proto) | |
2193 | xasprintf(&str, "type=%s protocol=%s iface=%s", | |
2194 | type, proto, iface); | |
2195 | else if (proto) | |
2196 | xasprintf(&str, "type=%s protocol=%s", | |
2197 | type, proto); | |
2198 | else if (iface) | |
2199 | xasprintf(&str, "type=%s iface=%s", | |
2200 | type, iface); | |
2201 | else | |
2202 | xasprintf(&str, "type=%s", type); | |
2203 | ||
2204 | return str; | |
2205 | } | |
2206 | ||
2207 | static char *packet_get_type(struct sock_xinfo *sock_xinfo __attribute__((__unused__)), | |
2208 | struct sock *sock __attribute__((__unused__))) | |
2209 | { | |
2210 | const char *str; | |
2211 | struct packet_xinfo *pkt = (struct packet_xinfo *)sock_xinfo; | |
2212 | ||
2213 | str = sock_decode_type(pkt->type); | |
2214 | return xstrdup(str); | |
2215 | } | |
2216 | ||
2217 | static bool packet_fill_column(struct proc *proc __attribute__((__unused__)), | |
2218 | struct sock_xinfo *sock_xinfo, | |
2219 | struct sock *sock __attribute__((__unused__)), | |
2220 | struct libscols_line *ln __attribute__((__unused__)), | |
2221 | int column_id, | |
2222 | size_t column_index __attribute__((__unused__)), | |
2223 | char **str) | |
2224 | { | |
2225 | struct packet_xinfo *pkt = (struct packet_xinfo *)sock_xinfo; | |
2226 | ||
2227 | switch (column_id) { | |
2228 | case COL_PACKET_IFACE: { | |
2229 | const char *iface; | |
2230 | iface = get_iface_name(sock_xinfo->netns_inode, | |
2231 | pkt->iface); | |
2232 | if (iface) { | |
2233 | *str = xstrdup(iface); | |
2234 | return true; | |
2235 | } | |
2236 | break; | |
2237 | } | |
2238 | case COL_PACKET_PROTOCOL: { | |
2239 | const char *proto; | |
2240 | proto = packet_decode_protocol(pkt->protocol); | |
2241 | if (proto) { | |
2242 | *str = xstrdup(proto); | |
2243 | return true; | |
2244 | } | |
2245 | break; | |
2246 | } | |
2247 | default: | |
2248 | break; | |
2249 | } | |
2250 | return false; | |
2251 | } | |
2252 | ||
2253 | static const struct sock_xinfo_class packet_xinfo_class = { | |
2254 | .get_name = packet_get_name, | |
2255 | .get_type = packet_get_type, | |
2256 | .get_state = NULL, | |
2257 | .get_listening = NULL, | |
2258 | .fill_column = packet_fill_column, | |
2259 | .free = NULL, | |
2260 | }; | |
2261 | ||
2262 | static void load_xinfo_from_proc_packet(ino_t netns_inode) | |
2263 | { | |
2264 | char line[BUFSIZ]; | |
2265 | FILE *packet_fp; | |
2266 | ||
2267 | packet_fp = fopen("/proc/net/packet", "r"); | |
2268 | if (!packet_fp) | |
2269 | return; | |
2270 | ||
2271 | if (fgets(line, sizeof(line), packet_fp) == NULL) | |
2272 | goto out; | |
2273 | if (!(line[0] == 's' && line[1] == 'k')) | |
2274 | /* Unexpected line */ | |
2275 | goto out; | |
2276 | ||
2277 | while (fgets(line, sizeof(line), packet_fp)) { | |
2278 | uint16_t type; | |
2279 | uint16_t protocol; | |
2280 | unsigned int iface; | |
2281 | unsigned long inode; | |
2282 | struct packet_xinfo *pkt; | |
2283 | ||
2284 | if (sscanf(line, "%*x %*d %" SCNu16 " %" SCNu16 " %u %*d %*d %*d %lu", | |
2285 | &type, &protocol, &iface, &inode) < 4) | |
2286 | continue; | |
2287 | ||
2288 | pkt = xcalloc(1, sizeof(*pkt)); | |
2289 | pkt->sock.class = &packet_xinfo_class; | |
2290 | pkt->sock.inode = (ino_t)inode; | |
2291 | pkt->sock.netns_inode = netns_inode; | |
2292 | ||
2293 | pkt->type = type; | |
2294 | pkt->protocol = protocol; | |
2295 | pkt->iface = iface; | |
2296 | ||
2297 | add_sock_info(&pkt->sock); | |
2298 | } | |
2299 | ||
2300 | out: | |
2301 | fclose(packet_fp); | |
2302 | } |