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