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