]>
Commit | Line | Data |
---|---|---|
0ee16e43 MY |
1 | /* |
2 | * lsfd-sock-xinfo.c - read various information from files under /proc/net/ | |
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) */ |
73df46fc | 24 | #include <inttypes.h> /* SCNu16 */ |
2e38e61c MY |
25 | #include <linux/net.h> /* SS_* */ |
26 | #include <linux/un.h> /* UNIX_PATH_MAX */ | |
0ee16e43 MY |
27 | #include <sched.h> /* for setns(2) */ |
28 | #include <search.h> | |
2e38e61c MY |
29 | #include <stdint.h> |
30 | #include <string.h> | |
31 | #include <sys/socket.h> /* SOCK_* */ | |
0ee16e43 MY |
32 | |
33 | #include "xalloc.h" | |
34 | #include "nls.h" | |
35 | #include "libsmartcols.h" | |
db79a100 TW |
36 | #include "sysfs.h" |
37 | #include "bitops.h" | |
0ee16e43 MY |
38 | |
39 | #include "lsfd.h" | |
40 | #include "lsfd-sock.h" | |
41 | ||
2e38e61c | 42 | static void load_xinfo_from_proc_unix(ino_t netns_inode); |
0188afb3 | 43 | static void load_xinfo_from_proc_raw(ino_t netns_inode); |
7b2a9be0 | 44 | static void load_xinfo_from_proc_tcp(ino_t netns_inode); |
92a0dbce | 45 | static void load_xinfo_from_proc_udp(ino_t netns_inode); |
c779f412 | 46 | static void load_xinfo_from_proc_udplite(ino_t netns_inode); |
1656da13 | 47 | static void load_xinfo_from_proc_tcp6(ino_t netns_inode); |
28cf2b21 | 48 | static void load_xinfo_from_proc_udp6(ino_t netns_inode); |
93bca151 | 49 | static void load_xinfo_from_proc_udplite6(ino_t netns_inode); |
2dd373c3 | 50 | static void load_xinfo_from_proc_raw6(ino_t netns_inode); |
2e38e61c | 51 | |
0ee16e43 | 52 | static int self_netns_fd = -1; |
44f9aec7 | 53 | static struct stat self_netns_sb; |
0ee16e43 MY |
54 | |
55 | static void *xinfo_tree; /* for tsearch/tfind */ | |
56 | static void *netns_tree; | |
57 | ||
58 | static int netns_compare(const void *a, const void *b) | |
59 | { | |
60 | if (*(ino_t *)a < *(ino_t *)b) | |
61 | return -1; | |
62 | else if (*(ino_t *)a > *(ino_t *)b) | |
63 | return 1; | |
64 | else | |
65 | return 0; | |
66 | } | |
67 | ||
68 | static bool is_sock_xinfo_loaded(ino_t netns) | |
69 | { | |
70 | return tfind(&netns, &netns_tree, netns_compare)? true: false; | |
71 | } | |
72 | ||
73 | static void mark_sock_xinfo_loaded(ino_t ino) | |
74 | { | |
75 | ino_t *netns = xmalloc(sizeof(ino)); | |
76 | ino_t **tmp; | |
77 | ||
78 | *netns = ino; | |
79 | tmp = tsearch(netns, &netns_tree, netns_compare); | |
80 | if (tmp == NULL) | |
81 | errx(EXIT_FAILURE, _("failed to allocate memory")); | |
82 | } | |
83 | ||
2e38e61c | 84 | static void load_sock_xinfo_no_nsswitch(ino_t netns) |
0ee16e43 | 85 | { |
2e38e61c | 86 | load_xinfo_from_proc_unix(netns); |
7b2a9be0 | 87 | load_xinfo_from_proc_tcp(netns); |
92a0dbce | 88 | load_xinfo_from_proc_udp(netns); |
c779f412 | 89 | load_xinfo_from_proc_udplite(netns); |
0188afb3 | 90 | load_xinfo_from_proc_raw(netns); |
1656da13 | 91 | load_xinfo_from_proc_tcp6(netns); |
28cf2b21 | 92 | load_xinfo_from_proc_udp6(netns); |
93bca151 | 93 | load_xinfo_from_proc_udplite6(netns); |
2dd373c3 | 94 | load_xinfo_from_proc_raw6(netns); |
0ee16e43 MY |
95 | } |
96 | ||
97 | static void load_sock_xinfo_with_fd(int fd, ino_t netns) | |
98 | { | |
b3649945 | 99 | if (setns(fd, CLONE_NEWNET) == 0) { |
0ee16e43 | 100 | load_sock_xinfo_no_nsswitch(netns); |
b3649945 | 101 | setns(self_netns_fd, CLONE_NEWNET); |
0ee16e43 MY |
102 | } |
103 | } | |
104 | ||
105 | void load_sock_xinfo(struct path_cxt *pc, const char *name, ino_t netns) | |
106 | { | |
107 | if (self_netns_fd == -1) | |
108 | return; | |
109 | ||
110 | if (!is_sock_xinfo_loaded(netns)) { | |
111 | int fd; | |
112 | ||
113 | mark_sock_xinfo_loaded(netns); | |
114 | fd = ul_path_open(pc, O_RDONLY, name); | |
115 | if (fd < 0) | |
116 | return; | |
117 | ||
118 | load_sock_xinfo_with_fd(fd, netns); | |
119 | close(fd); | |
120 | } | |
121 | } | |
122 | ||
123 | void initialize_sock_xinfos(void) | |
124 | { | |
125 | struct path_cxt *pc; | |
126 | DIR *dir; | |
127 | struct dirent *d; | |
128 | ||
129 | self_netns_fd = open("/proc/self/ns/net", O_RDONLY); | |
130 | ||
131 | if (self_netns_fd < 0) | |
132 | load_sock_xinfo_no_nsswitch(0); | |
133 | else { | |
134 | if (fstat(self_netns_fd, &self_netns_sb) == 0) { | |
135 | mark_sock_xinfo_loaded(self_netns_sb.st_ino); | |
136 | load_sock_xinfo_no_nsswitch(self_netns_sb.st_ino); | |
137 | } | |
138 | } | |
139 | ||
140 | /* Load /proc/net/{unix,...} of the network namespace | |
141 | * specified with netns files under /var/run/netns/. | |
142 | * | |
143 | * `ip netns' command pins a network namespace on | |
144 | * /var/run/netns. | |
145 | */ | |
146 | pc = ul_new_path("/var/run/netns"); | |
147 | if (!pc) | |
148 | err(EXIT_FAILURE, _("failed to alloc path context for /var/run/netns")); | |
149 | dir = ul_path_opendir(pc, NULL); | |
150 | if (dir == NULL) { | |
151 | ul_unref_path(pc); | |
152 | return; | |
153 | } | |
154 | while ((d = readdir(dir))) { | |
155 | struct stat sb; | |
156 | int fd; | |
157 | if (ul_path_stat(pc, &sb, 0, d->d_name) < 0) | |
158 | continue; | |
159 | if (is_sock_xinfo_loaded(sb.st_ino)) | |
160 | continue; | |
161 | mark_sock_xinfo_loaded(sb.st_ino); | |
162 | fd = ul_path_open(pc, O_RDONLY, d->d_name); | |
163 | if (fd < 0) | |
164 | continue; | |
165 | load_sock_xinfo_with_fd(fd, sb.st_ino); | |
166 | close(fd); | |
167 | } | |
168 | closedir(dir); | |
169 | ul_unref_path(pc); | |
170 | } | |
171 | ||
b3649945 | 172 | static void free_sock_xinfo(void *node) |
0ee16e43 MY |
173 | { |
174 | struct sock_xinfo *xinfo = node; | |
175 | if (xinfo->class->free) | |
b3649945 | 176 | xinfo->class->free(xinfo); |
0ee16e43 MY |
177 | free(node); |
178 | } | |
179 | ||
180 | void finalize_sock_xinfos(void) | |
181 | { | |
182 | if (self_netns_fd != -1) | |
183 | close(self_netns_fd); | |
184 | tdestroy(netns_tree, free); | |
185 | tdestroy(xinfo_tree, free_sock_xinfo); | |
186 | } | |
187 | ||
188 | static int xinfo_compare(const void *a, const void *b) | |
189 | { | |
190 | if (((struct sock_xinfo *)a)->inode < ((struct sock_xinfo *)b)->inode) | |
191 | return -1; | |
192 | if (((struct sock_xinfo *)a)->inode > ((struct sock_xinfo *)b)->inode) | |
193 | return 1; | |
194 | return 0; | |
195 | } | |
196 | ||
2e38e61c MY |
197 | static void add_sock_info(struct sock_xinfo *xinfo) |
198 | { | |
199 | struct sock_xinfo **tmp = tsearch(xinfo, &xinfo_tree, xinfo_compare); | |
200 | ||
201 | if (tmp == NULL) | |
202 | errx(EXIT_FAILURE, _("failed to allocate memory")); | |
203 | } | |
204 | ||
0ee16e43 MY |
205 | struct sock_xinfo *get_sock_xinfo(ino_t netns_inode) |
206 | { | |
207 | struct sock_xinfo **xinfo = tfind(&netns_inode, &xinfo_tree, xinfo_compare); | |
208 | ||
209 | if (xinfo) | |
210 | return *xinfo; | |
211 | return NULL; | |
212 | } | |
213 | ||
214 | bool is_nsfs_dev(dev_t dev) | |
215 | { | |
621cf7e9 | 216 | return dev == self_netns_sb.st_dev; |
0ee16e43 | 217 | } |
2e38e61c MY |
218 | |
219 | static const char *sock_decode_type(uint16_t type) | |
220 | { | |
221 | switch (type) { | |
222 | case SOCK_STREAM: | |
223 | return "stream"; | |
224 | case SOCK_DGRAM: | |
225 | return "dgram"; | |
226 | case SOCK_RAW: | |
227 | return "raw"; | |
228 | case SOCK_RDM: | |
229 | return "rdm"; | |
230 | case SOCK_SEQPACKET: | |
231 | return "seqpacket"; | |
232 | case SOCK_DCCP: | |
233 | return "dccp"; | |
234 | case SOCK_PACKET: | |
235 | return "packet"; | |
236 | default: | |
237 | return "unknown"; | |
238 | } | |
239 | } | |
240 | ||
241 | /* | |
242 | * Protocol specific code | |
243 | */ | |
244 | ||
245 | /* | |
246 | * UNIX | |
247 | */ | |
248 | struct unix_xinfo { | |
249 | struct sock_xinfo sock; | |
250 | int acceptcon; /* flags */ | |
251 | uint16_t type; | |
252 | uint8_t st; | |
253 | char path[ | |
254 | UNIX_PATH_MAX | |
255 | + 1 /* for @ */ | |
256 | + 1 /* \0? */ | |
257 | ]; | |
258 | }; | |
259 | ||
260 | static const char *unix_decode_state(uint8_t st) | |
261 | { | |
262 | switch (st) { | |
263 | case SS_FREE: | |
264 | return "free"; | |
265 | case SS_UNCONNECTED: | |
266 | return "unconnected"; | |
267 | case SS_CONNECTING: | |
268 | return "connecting"; | |
269 | case SS_CONNECTED: | |
270 | return "connected"; | |
271 | case SS_DISCONNECTING: | |
272 | return "disconnecting"; | |
273 | default: | |
274 | return "unknown"; | |
275 | } | |
276 | } | |
277 | ||
278 | static char *unix_get_name(struct sock_xinfo *sock_xinfo, | |
279 | struct sock *sock) | |
280 | { | |
281 | struct unix_xinfo *ux = (struct unix_xinfo *)sock_xinfo; | |
282 | const char *state = unix_decode_state(ux->st); | |
283 | char *str = NULL; | |
284 | ||
285 | if (sock->protoname && (strcmp(sock->protoname, "UNIX-STREAM") == 0)) | |
286 | xasprintf(&str, "state=%s%s%s", | |
287 | (ux->acceptcon)? "listen": state, | |
288 | *(ux->path)? " path=": "", | |
289 | *(ux->path)? ux->path: ""); | |
290 | else | |
291 | xasprintf(&str, "state=%s%s%s type=%s", | |
292 | (ux->acceptcon)? "listen": state, | |
293 | *(ux->path)? " path=": "", | |
294 | *(ux->path)? ux->path: "", | |
295 | sock_decode_type(ux->type)); | |
296 | return str; | |
297 | } | |
298 | ||
299 | static char *unix_get_type(struct sock_xinfo *sock_xinfo, | |
300 | struct sock *sock __attribute__((__unused__))) | |
301 | { | |
302 | const char *str; | |
303 | struct unix_xinfo *ux = (struct unix_xinfo *)sock_xinfo; | |
304 | ||
305 | str = sock_decode_type(ux->type); | |
306 | return strdup(str); | |
307 | } | |
308 | ||
309 | static char *unix_get_state(struct sock_xinfo *sock_xinfo, | |
310 | struct sock *sock __attribute__((__unused__))) | |
311 | { | |
312 | const char *str; | |
313 | struct unix_xinfo *ux = (struct unix_xinfo *)sock_xinfo; | |
314 | ||
315 | if (ux->acceptcon) | |
316 | return strdup("listen"); | |
317 | ||
318 | str = unix_decode_state(ux->st); | |
319 | return strdup(str); | |
320 | } | |
321 | ||
45d61bff MY |
322 | static bool unix_get_listening(struct sock_xinfo *sock_xinfo, |
323 | struct sock *sock __attribute__((__unused__))) | |
324 | { | |
325 | struct unix_xinfo *ux = (struct unix_xinfo *)sock_xinfo; | |
326 | ||
327 | return ux->acceptcon; | |
328 | } | |
329 | ||
2e38e61c MY |
330 | static bool unix_fill_column(struct proc *proc __attribute__((__unused__)), |
331 | struct sock_xinfo *sock_xinfo, | |
332 | struct sock *sock __attribute__((__unused__)), | |
333 | struct libscols_line *ln __attribute__((__unused__)), | |
334 | int column_id, | |
335 | size_t column_index __attribute__((__unused__)), | |
336 | char **str) | |
337 | { | |
338 | struct unix_xinfo *ux = (struct unix_xinfo *)sock_xinfo; | |
339 | ||
854ddd0d | 340 | switch (column_id) { |
2e38e61c MY |
341 | case COL_UNIX_PATH: |
342 | if (*ux->path) { | |
343 | *str = strdup(ux->path); | |
344 | return true; | |
345 | } | |
346 | break; | |
347 | } | |
348 | ||
349 | return false; | |
350 | } | |
351 | ||
54a06438 | 352 | static const struct sock_xinfo_class unix_xinfo_class = { |
2e38e61c MY |
353 | .get_name = unix_get_name, |
354 | .get_type = unix_get_type, | |
355 | .get_state = unix_get_state, | |
45d61bff | 356 | .get_listening = unix_get_listening, |
2e38e61c MY |
357 | .fill_column = unix_fill_column, |
358 | .free = NULL, | |
359 | }; | |
360 | ||
e8e14a28 | 361 | /* UNIX_LINE_LEN need at least 54 + 21 + UNIX_PATH_MAX + 1. |
eeaf7677 MY |
362 | * |
363 | * An actual number must be used in this definition | |
364 | * since UNIX_LINE_LEN is specified as an argument for | |
365 | * stringify_value(). | |
366 | */ | |
2e38e61c MY |
367 | #define UNIX_LINE_LEN 256 |
368 | static void load_xinfo_from_proc_unix(ino_t netns_inode) | |
369 | { | |
370 | char line[UNIX_LINE_LEN]; | |
371 | FILE *unix_fp; | |
372 | ||
373 | unix_fp = fopen("/proc/net/unix", "r"); | |
374 | if (!unix_fp) | |
375 | return; | |
376 | ||
377 | if (fgets(line, sizeof(line), unix_fp) == NULL) | |
378 | goto out; | |
b3649945 | 379 | if (!(line[0] == 'N' && line[1] == 'u' && line[2] == 'm')) |
2e38e61c MY |
380 | /* Unexpected line */ |
381 | goto out; | |
382 | ||
383 | while (fgets(line, sizeof(line), unix_fp)) { | |
384 | uint64_t flags; | |
385 | uint32_t type; | |
386 | unsigned int st; | |
387 | unsigned long inode; | |
2e38e61c | 388 | struct unix_xinfo *ux; |
eeaf7677 | 389 | char path[UNIX_LINE_LEN + 1] = { 0 }; |
91a484fe | 390 | |
2e38e61c | 391 | |
eeaf7677 MY |
392 | if (sscanf(line, "%*x: %*x %*x %" SCNx64 " %x %x %lu %" |
393 | stringify_value(UNIX_LINE_LEN) "[^\n]", | |
2e38e61c MY |
394 | &flags, &type, &st, &inode, path) < 4) |
395 | continue; | |
396 | ||
397 | if (inode == 0) | |
398 | continue; | |
399 | ||
8ff22ccb | 400 | ux = xcalloc(1, sizeof(*ux)); |
2e38e61c MY |
401 | ux->sock.class = &unix_xinfo_class; |
402 | ux->sock.inode = (ino_t)inode; | |
403 | ux->sock.netns_inode = netns_inode; | |
404 | ||
405 | ux->acceptcon = !!flags; | |
406 | ux->type = type; | |
407 | ux->st = st; | |
91a484fe | 408 | xstrncpy(ux->path, path, sizeof(ux->path)); |
2e38e61c | 409 | |
2ac8d1a3 | 410 | add_sock_info(&ux->sock); |
2e38e61c MY |
411 | } |
412 | ||
413 | out: | |
414 | fclose(unix_fp); | |
415 | } | |
7b2a9be0 MY |
416 | |
417 | /* | |
418 | * AF_INET | |
419 | */ | |
420 | struct inet_xinfo { | |
421 | struct sock_xinfo sock; | |
643f30c7 MY |
422 | struct in_addr local_addr; |
423 | struct in_addr remote_addr; | |
7b2a9be0 MY |
424 | }; |
425 | ||
9020c2e0 | 426 | static uint32_t kernel32_to_cpu(enum sysfs_byteorder byteorder, uint32_t v) |
7b2a9be0 | 427 | { |
9020c2e0 MY |
428 | if (byteorder == SYSFS_BYTEORDER_LITTLE) |
429 | return le32_to_cpu(v); | |
430 | else | |
431 | return be32_to_cpu(v); | |
7b2a9be0 MY |
432 | } |
433 | ||
1656da13 MY |
434 | /* |
435 | * AF_INET6 | |
436 | */ | |
437 | struct inet6_xinfo { | |
438 | struct sock_xinfo sock; | |
439 | struct in6_addr local_addr; | |
440 | struct in6_addr remote_addr; | |
441 | }; | |
442 | ||
7b2a9be0 | 443 | /* |
4a2b51c1 | 444 | * L4 abstract-layer for protocols stacked on IP and IP6. |
7b2a9be0 | 445 | */ |
303b41fe | 446 | enum l4_state { |
7b2a9be0 MY |
447 | /* |
448 | * Taken from linux/include/net/tcp_states.h. | |
449 | * (GPL-2.0-or-later) | |
303b41fe MY |
450 | * |
451 | * UDP and RAW sockets also uses the contents in Linux. | |
7b2a9be0 MY |
452 | */ |
453 | TCP_ESTABLISHED = 1, | |
454 | TCP_SYN_SENT, | |
455 | TCP_SYN_RECV, | |
456 | TCP_FIN_WAIT1, | |
457 | TCP_FIN_WAIT2, | |
458 | TCP_TIME_WAIT, | |
459 | TCP_CLOSE, | |
460 | TCP_CLOSE_WAIT, | |
461 | TCP_LAST_ACK, | |
462 | TCP_LISTEN, | |
463 | TCP_CLOSING, | |
464 | TCP_NEW_SYN_RECV, | |
465 | ||
466 | TCP_MAX_STATES /* Leave at the end! */ | |
467 | }; | |
468 | ||
303b41fe | 469 | static const char *l4_decode_state(enum l4_state st) |
7b2a9be0 MY |
470 | { |
471 | const char * table [] = { | |
472 | [TCP_ESTABLISHED] = "established", | |
473 | [TCP_SYN_SENT] = "syn-sent", | |
474 | [TCP_SYN_RECV] = "syn-recv", | |
475 | [TCP_FIN_WAIT1] = "fin-wait1", | |
476 | [TCP_FIN_WAIT2] = "fin-wait2", | |
477 | [TCP_TIME_WAIT] = "time-wait", | |
478 | [TCP_CLOSE] = "close", | |
479 | [TCP_CLOSE_WAIT] = "close-wait", | |
480 | [TCP_LAST_ACK] = "last-ack", | |
481 | [TCP_LISTEN] = "listen", | |
482 | [TCP_CLOSING] = "closing", | |
483 | [TCP_NEW_SYN_RECV] = "new-syn-recv", | |
484 | }; | |
485 | ||
486 | if (st < TCP_MAX_STATES) | |
487 | return table[st]; | |
488 | return "unknown"; | |
489 | } | |
490 | ||
4a2b51c1 | 491 | struct l4_xinfo { |
1656da13 MY |
492 | union { |
493 | struct inet_xinfo inet; | |
494 | struct inet6_xinfo inet6; | |
495 | }; | |
303b41fe | 496 | enum l4_state st; |
4a2b51c1 MY |
497 | }; |
498 | ||
2a4f2b1b | 499 | enum l4_side { L4_LOCAL, L4_REMOTE }; |
1656da13 | 500 | enum l3_decorator { L3_DECO_START, L3_DECO_END }; |
2a4f2b1b | 501 | |
4a2b51c1 MY |
502 | struct l4_xinfo_class { |
503 | struct sock_xinfo_class sock; | |
19e40223 MY |
504 | struct sock_xinfo *(*scan_line)(const struct sock_xinfo_class *, |
505 | char *, | |
506 | ino_t, | |
507 | enum sysfs_byteorder); | |
2a4f2b1b MY |
508 | void * (*get_addr)(struct l4_xinfo *, enum l4_side); |
509 | bool (*is_any_addr)(void *); | |
510 | int family; | |
1656da13 | 511 | const char *l3_decorator[2]; |
4a2b51c1 MY |
512 | }; |
513 | ||
838fa270 MY |
514 | #define l3_fill_column_handler(L3, SOCK_XINFO, COLUMN_ID, STR) __extension__ \ |
515 | ({ \ | |
516 | struct l4_xinfo_class *class = (struct l4_xinfo_class *)SOCK_XINFO->class; \ | |
517 | struct l4_xinfo *l4 = (struct l4_xinfo *)SOCK_XINFO; \ | |
518 | void *n = NULL; \ | |
519 | char s[BUFSIZ]; \ | |
520 | bool r = false; \ | |
521 | \ | |
522 | switch (COLUMN_ID) { \ | |
523 | case COL_##L3##_LADDR: \ | |
524 | n = class->get_addr(l4, L4_LOCAL); \ | |
525 | break; \ | |
526 | case COL_##L3##_RADDR: \ | |
527 | n = class->get_addr(l4, L4_REMOTE); \ | |
528 | break; \ | |
529 | default: \ | |
530 | break; \ | |
531 | } \ | |
532 | \ | |
533 | if (n && inet_ntop(class->family, n, s, sizeof(s))) { \ | |
534 | *STR = strdup(s); \ | |
535 | r = true; \ | |
536 | } \ | |
537 | r; \ | |
538 | }) | |
539 | ||
7b2a9be0 MY |
540 | /* |
541 | * TCP | |
542 | */ | |
543 | struct tcp_xinfo { | |
4a2b51c1 | 544 | struct l4_xinfo l4; |
7b2a9be0 MY |
545 | uint16_t local_port; |
546 | uint16_t remote_port; | |
7b2a9be0 MY |
547 | }; |
548 | ||
7b2a9be0 MY |
549 | static char *tcp_get_name(struct sock_xinfo *sock_xinfo, |
550 | struct sock *sock __attribute__((__unused__))) | |
551 | { | |
552 | char *str = NULL; | |
7b2a9be0 | 553 | struct tcp_xinfo *tcp = ((struct tcp_xinfo *)sock_xinfo); |
8db07ebc | 554 | struct l4_xinfo *l4 = &tcp->l4; |
303b41fe | 555 | const char *st_str = l4_decode_state(l4->st); |
838fa270 MY |
556 | struct l4_xinfo_class *class = (struct l4_xinfo_class *)sock_xinfo->class; |
557 | void *laddr = class->get_addr(l4, L4_LOCAL); | |
558 | void *raddr = class->get_addr(l4, L4_REMOTE); | |
559 | char local_s[BUFSIZ]; | |
560 | char remote_s[BUFSIZ]; | |
1656da13 MY |
561 | const char *start = class->l3_decorator[L3_DECO_START]; |
562 | const char *end = class->l3_decorator[L3_DECO_END]; | |
7b2a9be0 | 563 | |
838fa270 | 564 | if (!inet_ntop(class->family, laddr, local_s, sizeof(local_s))) |
3b182ee7 MY |
565 | xasprintf(&str, "state=%s", st_str); |
566 | else if (l4->st == TCP_LISTEN | |
838fa270 | 567 | || !inet_ntop(class->family, raddr, remote_s, sizeof(remote_s))) |
b4e39841 | 568 | xasprintf(&str, "state=%s laddr=%s%s%s:%"PRIu16, |
3b182ee7 | 569 | st_str, |
1656da13 | 570 | start, local_s, end, tcp->local_port); |
7b2a9be0 | 571 | else |
b4e39841 | 572 | xasprintf(&str, "state=%s laddr=%s%s%s:%"PRIu16" raddr=%s%s%s:%"PRIu16, |
3b182ee7 | 573 | st_str, |
1656da13 MY |
574 | start, local_s, end, tcp->local_port, |
575 | start, remote_s, end, tcp->remote_port); | |
7b2a9be0 MY |
576 | return str; |
577 | } | |
578 | ||
579 | static char *tcp_get_type(struct sock_xinfo *sock_xinfo __attribute__((__unused__)), | |
580 | struct sock *sock __attribute__((__unused__))) | |
581 | { | |
582 | return strdup("stream"); | |
583 | } | |
584 | ||
585 | static char *tcp_get_state(struct sock_xinfo *sock_xinfo, | |
586 | struct sock *sock __attribute__((__unused__))) | |
587 | { | |
303b41fe | 588 | return strdup(l4_decode_state(((struct l4_xinfo *)sock_xinfo)->st)); |
7b2a9be0 MY |
589 | } |
590 | ||
591 | static bool tcp_get_listening(struct sock_xinfo *sock_xinfo, | |
592 | struct sock *sock __attribute__((__unused__))) | |
593 | { | |
8db07ebc | 594 | return ((struct l4_xinfo *)sock_xinfo)->st == TCP_LISTEN; |
7b2a9be0 MY |
595 | } |
596 | ||
838fa270 MY |
597 | #define l4_fill_column_handler(L4, SOCK_XINFO, COLUMN_ID, STR) __extension__ \ |
598 | ({ \ | |
599 | struct l4_xinfo_class *class = (struct l4_xinfo_class *)SOCK_XINFO->class; \ | |
600 | struct tcp_xinfo *tcp = (struct tcp_xinfo *)SOCK_XINFO; \ | |
601 | struct l4_xinfo *l4 = &tcp->l4; \ | |
602 | void *n = NULL; \ | |
92a0dbce | 603 | bool has_laddr = false; \ |
73df46fc | 604 | unsigned short p; \ |
92a0dbce | 605 | bool has_lport = false; \ |
838fa270 MY |
606 | char s[BUFSIZ]; \ |
607 | bool r = true; \ | |
92a0dbce | 608 | \ |
838fa270 | 609 | switch (COLUMN_ID) { \ |
cda2b2a0 | 610 | case COL_##L4##_LADDR: \ |
838fa270 | 611 | n = class->get_addr(l4, L4_LOCAL); \ |
92a0dbce | 612 | has_laddr = true; \ |
73df46fc | 613 | p = tcp->local_port; \ |
92a0dbce | 614 | /* FALL THROUGH */ \ |
cda2b2a0 | 615 | case COL_##L4##_RADDR: \ |
92a0dbce | 616 | if (!has_laddr) { \ |
838fa270 | 617 | n = class->get_addr(l4, L4_REMOTE); \ |
73df46fc | 618 | p = tcp->remote_port; \ |
92a0dbce | 619 | } \ |
838fa270 | 620 | if (n && inet_ntop(class->family, n, s, sizeof(s))) \ |
b4e39841 | 621 | xasprintf(STR, "%s%s%s:%"PRIu16, \ |
1656da13 MY |
622 | class->l3_decorator[L3_DECO_START], \ |
623 | s, \ | |
624 | class->l3_decorator[L3_DECO_END], \ | |
625 | p); \ | |
92a0dbce | 626 | break; \ |
cda2b2a0 | 627 | case COL_##L4##_LPORT: \ |
73df46fc | 628 | p = tcp->local_port; \ |
92a0dbce MY |
629 | has_lport = true; \ |
630 | /* FALL THROUGH */ \ | |
cda2b2a0 | 631 | case COL_##L4##_RPORT: \ |
92a0dbce | 632 | if (!has_lport) \ |
73df46fc | 633 | p = tcp->remote_port; \ |
b4e39841 | 634 | xasprintf(STR, "%"PRIu16, p); \ |
92a0dbce MY |
635 | break; \ |
636 | default: \ | |
838fa270 MY |
637 | r = false; \ |
638 | break; \ | |
92a0dbce | 639 | } \ |
838fa270 MY |
640 | r; \ |
641 | }) | |
7b2a9be0 | 642 | |
19e40223 MY |
643 | static struct sock_xinfo *tcp_xinfo_scan_line(const struct sock_xinfo_class *class, |
644 | char * line, | |
645 | ino_t netns_inode, | |
646 | enum sysfs_byteorder byteorder) | |
647 | { | |
648 | unsigned long local_addr; | |
649 | unsigned long local_port; | |
650 | unsigned long remote_addr; | |
651 | unsigned long remote_port; | |
652 | unsigned long st; | |
653 | unsigned long long inode; | |
654 | struct tcp_xinfo *tcp; | |
655 | struct inet_xinfo *inet; | |
656 | struct sock_xinfo *sock; | |
657 | ||
658 | if (sscanf(line, "%*d: %lx:%lx %lx:%lx %lx %*x:%*x %*x:%*x %*x %*u %*u %lld", | |
659 | &local_addr, &local_port, &remote_addr, &remote_port, | |
660 | &st, &inode) != 6) | |
661 | return NULL; | |
662 | ||
663 | if (inode == 0) | |
664 | return NULL; | |
665 | ||
8ff22ccb | 666 | tcp = xcalloc(1, sizeof(*tcp)); |
19e40223 MY |
667 | inet = &tcp->l4.inet; |
668 | sock = &inet->sock; | |
669 | sock->class = class; | |
670 | sock->inode = (ino_t)inode; | |
671 | sock->netns_inode = netns_inode; | |
672 | inet->local_addr.s_addr = kernel32_to_cpu(byteorder, local_addr); | |
673 | tcp->local_port = local_port; | |
674 | inet->remote_addr.s_addr = kernel32_to_cpu(byteorder, remote_addr); | |
675 | tcp->remote_port = remote_port; | |
8db07ebc | 676 | tcp->l4.st = st; |
19e40223 MY |
677 | |
678 | return sock; | |
679 | } | |
680 | ||
2a4f2b1b MY |
681 | static void *tcp_xinfo_get_addr(struct l4_xinfo *l4, enum l4_side side) |
682 | { | |
683 | return (side == L4_LOCAL) | |
684 | ? &l4->inet.local_addr | |
685 | : &l4->inet.remote_addr; | |
686 | } | |
687 | ||
688 | static bool tcp_xinfo_is_any_addr(void *addr) | |
689 | { | |
690 | return ((struct in_addr *)addr)->s_addr == INADDR_ANY; | |
691 | } | |
692 | ||
838fa270 MY |
693 | static bool tcp_fill_column(struct proc *proc __attribute__((__unused__)), |
694 | struct sock_xinfo *sock_xinfo, | |
695 | struct sock *sock __attribute__((__unused__)), | |
696 | struct libscols_line *ln __attribute__((__unused__)), | |
697 | int column_id, | |
698 | size_t column_index __attribute__((__unused__)), | |
699 | char **str) | |
700 | { | |
701 | return l3_fill_column_handler(INET, sock_xinfo, column_id, str) | |
702 | || l4_fill_column_handler(TCP, sock_xinfo, column_id, str); | |
703 | } | |
704 | ||
4a2b51c1 MY |
705 | static const struct l4_xinfo_class tcp_xinfo_class = { |
706 | .sock = { | |
707 | .get_name = tcp_get_name, | |
708 | .get_type = tcp_get_type, | |
709 | .get_state = tcp_get_state, | |
710 | .get_listening = tcp_get_listening, | |
711 | .fill_column = tcp_fill_column, | |
712 | .free = NULL, | |
713 | }, | |
19e40223 | 714 | .scan_line = tcp_xinfo_scan_line, |
2a4f2b1b MY |
715 | .get_addr = tcp_xinfo_get_addr, |
716 | .is_any_addr = tcp_xinfo_is_any_addr, | |
717 | .family = AF_INET, | |
1656da13 | 718 | .l3_decorator = {"", ""}, |
7b2a9be0 MY |
719 | }; |
720 | ||
cda2b2a0 | 721 | static bool L4_verify_initial_line(const char *line) |
58d2f31e MY |
722 | { |
723 | /* At least we expect two white spaces. */ | |
cb097fb7 | 724 | if (strncmp(line, " ", 2) != 0) |
58d2f31e MY |
725 | return false; |
726 | line += 2; | |
727 | ||
728 | /* Skip white spaces. */ | |
4658bfb1 | 729 | line = skip_space(line); |
58d2f31e | 730 | |
621cf7e9 | 731 | return strncmp(line, "sl", 2) == 0; |
db79a100 TW |
732 | } |
733 | ||
7b2a9be0 | 734 | #define TCP_LINE_LEN 256 |
cda2b2a0 | 735 | static void load_xinfo_from_proc_inet_L4(ino_t netns_inode, const char *proc_file, |
4a2b51c1 | 736 | const struct l4_xinfo_class *class) |
7b2a9be0 MY |
737 | { |
738 | char line[TCP_LINE_LEN]; | |
739 | FILE *tcp_fp; | |
740 | ||
92a0dbce | 741 | tcp_fp = fopen(proc_file, "r"); |
7b2a9be0 MY |
742 | if (!tcp_fp) |
743 | return; | |
744 | ||
745 | if (fgets(line, sizeof(line), tcp_fp) == NULL) | |
746 | goto out; | |
cda2b2a0 | 747 | if (!L4_verify_initial_line(line)) |
7b2a9be0 MY |
748 | /* Unexpected line */ |
749 | goto out; | |
750 | ||
7578e03f | 751 | enum sysfs_byteorder byteorder = sysfs_get_byteorder(NULL); |
db79a100 | 752 | |
7b2a9be0 | 753 | while (fgets(line, sizeof(line), tcp_fp)) { |
19e40223 MY |
754 | struct sock_xinfo *sock = class->scan_line(&class->sock, line, netns_inode, byteorder); |
755 | if (sock) | |
756 | add_sock_info(sock); | |
7b2a9be0 MY |
757 | } |
758 | ||
759 | out: | |
760 | fclose(tcp_fp); | |
761 | } | |
92a0dbce | 762 | |
92a0dbce MY |
763 | static void load_xinfo_from_proc_tcp(ino_t netns_inode) |
764 | { | |
cda2b2a0 | 765 | load_xinfo_from_proc_inet_L4(netns_inode, |
58d2f31e | 766 | "/proc/net/tcp", |
92a0dbce MY |
767 | &tcp_xinfo_class); |
768 | } | |
769 | ||
770 | /* | |
771 | * UDP | |
772 | */ | |
773 | static char *udp_get_name(struct sock_xinfo *sock_xinfo, | |
774 | struct sock *sock __attribute__((__unused__))) | |
775 | { | |
776 | char *str = NULL; | |
92a0dbce | 777 | struct tcp_xinfo *tcp = ((struct tcp_xinfo *)sock_xinfo); |
8db07ebc | 778 | struct l4_xinfo *l4 = &tcp->l4; |
8db07ebc | 779 | unsigned int st = l4->st; |
303b41fe | 780 | const char *st_str = l4_decode_state(st); |
838fa270 MY |
781 | struct l4_xinfo_class *class = (struct l4_xinfo_class *)sock_xinfo->class; |
782 | void *laddr = class->get_addr(l4, L4_LOCAL); | |
783 | void *raddr = class->get_addr(l4, L4_REMOTE); | |
784 | char local_s[BUFSIZ]; | |
785 | char remote_s[BUFSIZ]; | |
28cf2b21 MY |
786 | const char *start = class->l3_decorator[L3_DECO_START]; |
787 | const char *end = class->l3_decorator[L3_DECO_END]; | |
92a0dbce | 788 | |
838fa270 | 789 | if (!inet_ntop(class->family, laddr, local_s, sizeof(local_s))) |
3b182ee7 | 790 | xasprintf(&str, "state=%s", st_str); |
838fa270 MY |
791 | else if ((class->is_any_addr(raddr) && tcp->remote_port == 0) |
792 | || !inet_ntop(class->family, raddr, remote_s, sizeof(remote_s))) | |
28cf2b21 | 793 | xasprintf(&str, "state=%s laddr=%s%s%s:%"PRIu16, |
3b182ee7 | 794 | st_str, |
28cf2b21 | 795 | start, local_s, end, tcp->local_port); |
92a0dbce | 796 | else |
28cf2b21 | 797 | xasprintf(&str, "state=%s laddr=%s%s%s:%"PRIu16" raddr=%s%s%s:%"PRIu16, |
3b182ee7 | 798 | st_str, |
28cf2b21 MY |
799 | start, local_s, end, tcp->local_port, |
800 | start, remote_s, end, tcp->remote_port); | |
92a0dbce MY |
801 | return str; |
802 | } | |
803 | ||
804 | static char *udp_get_type(struct sock_xinfo *sock_xinfo __attribute__((__unused__)), | |
805 | struct sock *sock __attribute__((__unused__))) | |
806 | { | |
807 | return strdup("dgram"); | |
808 | } | |
809 | ||
838fa270 MY |
810 | static bool udp_fill_column(struct proc *proc __attribute__((__unused__)), |
811 | struct sock_xinfo *sock_xinfo, | |
812 | struct sock *sock __attribute__((__unused__)), | |
813 | struct libscols_line *ln __attribute__((__unused__)), | |
814 | int column_id, | |
815 | size_t column_index __attribute__((__unused__)), | |
816 | char **str) | |
817 | { | |
818 | return l3_fill_column_handler(INET, sock_xinfo, column_id, str) | |
819 | || l4_fill_column_handler(UDP, sock_xinfo, column_id, str); | |
820 | } | |
821 | ||
4a2b51c1 MY |
822 | static const struct l4_xinfo_class udp_xinfo_class = { |
823 | .sock = { | |
824 | .get_name = udp_get_name, | |
825 | .get_type = udp_get_type, | |
826 | .get_state = tcp_get_state, | |
827 | .get_listening = NULL, | |
828 | .fill_column = udp_fill_column, | |
829 | .free = NULL, | |
830 | }, | |
19e40223 | 831 | .scan_line = tcp_xinfo_scan_line, |
2a4f2b1b MY |
832 | .get_addr = tcp_xinfo_get_addr, |
833 | .is_any_addr = tcp_xinfo_is_any_addr, | |
834 | .family = AF_INET, | |
1656da13 | 835 | .l3_decorator = {"", ""}, |
92a0dbce MY |
836 | }; |
837 | ||
92a0dbce MY |
838 | static void load_xinfo_from_proc_udp(ino_t netns_inode) |
839 | { | |
cda2b2a0 | 840 | load_xinfo_from_proc_inet_L4(netns_inode, |
92a0dbce | 841 | "/proc/net/udp", |
92a0dbce MY |
842 | &udp_xinfo_class); |
843 | } | |
0188afb3 | 844 | |
c779f412 MY |
845 | /* |
846 | * UDP-Lite | |
847 | */ | |
848 | static bool udplite_fill_column(struct proc *proc __attribute__((__unused__)), | |
849 | struct sock_xinfo *sock_xinfo, | |
850 | struct sock *sock __attribute__((__unused__)), | |
851 | struct libscols_line *ln __attribute__((__unused__)), | |
852 | int column_id, | |
853 | size_t column_index __attribute__((__unused__)), | |
854 | char **str) | |
855 | { | |
856 | return l3_fill_column_handler(INET, sock_xinfo, column_id, str) | |
857 | || l4_fill_column_handler(UDPLITE, sock_xinfo, column_id, str); | |
858 | } | |
859 | ||
860 | static const struct l4_xinfo_class udplite_xinfo_class = { | |
861 | .sock = { | |
862 | .get_name = udp_get_name, | |
863 | .get_type = udp_get_type, | |
864 | .get_state = tcp_get_state, | |
865 | .get_listening = NULL, | |
866 | .fill_column = udplite_fill_column, | |
867 | .free = NULL, | |
868 | }, | |
869 | .scan_line = tcp_xinfo_scan_line, | |
870 | .get_addr = tcp_xinfo_get_addr, | |
871 | .is_any_addr = tcp_xinfo_is_any_addr, | |
872 | .family = AF_INET, | |
873 | .l3_decorator = {"", ""}, | |
874 | }; | |
875 | ||
876 | static void load_xinfo_from_proc_udplite(ino_t netns_inode) | |
877 | { | |
878 | load_xinfo_from_proc_inet_L4(netns_inode, | |
879 | "/proc/net/udplite", | |
880 | &udplite_xinfo_class); | |
881 | } | |
882 | ||
883 | ||
0188afb3 MY |
884 | /* |
885 | * RAW | |
886 | */ | |
887 | struct raw_xinfo { | |
888 | struct l4_xinfo l4; | |
889 | uint16_t protocol; | |
890 | }; | |
891 | ||
892 | static char *raw_get_name(struct sock_xinfo *sock_xinfo, | |
893 | struct sock *sock __attribute__((__unused__))) | |
894 | { | |
895 | char *str = NULL; | |
896 | struct l4_xinfo_class *class = (struct l4_xinfo_class *)sock_xinfo->class; | |
897 | struct raw_xinfo *raw = ((struct raw_xinfo *)sock_xinfo); | |
898 | struct l4_xinfo *l4 = &raw->l4; | |
303b41fe | 899 | const char *st_str = l4_decode_state(l4->st); |
0188afb3 MY |
900 | void *laddr = class->get_addr(l4, L4_LOCAL); |
901 | void *raddr = class->get_addr(l4, L4_REMOTE); | |
902 | char local_s[BUFSIZ]; | |
903 | char remote_s[BUFSIZ]; | |
904 | ||
905 | if (!inet_ntop(class->family, laddr, local_s, sizeof(local_s))) | |
906 | xasprintf(&str, "state=%s", st_str); | |
907 | else if (class->is_any_addr(raddr) | |
908 | || !inet_ntop(class->family, raddr, remote_s, sizeof(remote_s))) | |
b4e39841 | 909 | xasprintf(&str, "state=%s protocol=%"PRIu16" laddr=%s", |
0188afb3 MY |
910 | st_str, |
911 | raw->protocol, local_s); | |
912 | else | |
b4e39841 | 913 | xasprintf(&str, "state=%s protocol=%"PRIu16" laddr=%s raddr=%s", |
0188afb3 MY |
914 | st_str, |
915 | raw->protocol, local_s, remote_s); | |
916 | return str; | |
917 | } | |
918 | ||
919 | static char *raw_get_type(struct sock_xinfo *sock_xinfo __attribute__((__unused__)), | |
920 | struct sock *sock __attribute__((__unused__))) | |
921 | { | |
922 | return strdup("raw"); | |
923 | } | |
924 | ||
925 | static bool raw_fill_column(struct proc *proc __attribute__((__unused__)), | |
926 | struct sock_xinfo *sock_xinfo, | |
927 | struct sock *sock __attribute__((__unused__)), | |
928 | struct libscols_line *ln __attribute__((__unused__)), | |
929 | int column_id, | |
930 | size_t column_index __attribute__((__unused__)), | |
931 | char **str) | |
932 | { | |
933 | if (l3_fill_column_handler(INET, sock_xinfo, column_id, str)) | |
934 | return true; | |
935 | ||
936 | if (column_id == COL_RAW_PROTOCOL) { | |
b4e39841 | 937 | xasprintf(str, "%"PRIu16, |
73df46fc | 938 | ((struct raw_xinfo *)sock_xinfo)->protocol); |
0188afb3 MY |
939 | return true; |
940 | } | |
941 | ||
942 | return false; | |
943 | } | |
944 | ||
945 | static struct sock_xinfo *raw_xinfo_scan_line(const struct sock_xinfo_class *class, | |
946 | char * line, | |
947 | ino_t netns_inode, | |
948 | enum sysfs_byteorder byteorder) | |
949 | { | |
950 | unsigned long local_addr; | |
951 | unsigned long protocol; | |
952 | unsigned long remote_addr; | |
953 | unsigned long st; | |
954 | unsigned long long inode; | |
955 | struct raw_xinfo *raw; | |
956 | struct inet_xinfo *inet; | |
957 | struct sock_xinfo *sock; | |
958 | ||
959 | if (sscanf(line, "%*d: %lx:%lx %lx:%*x %lx %*x:%*x %*x:%*x %*x %*u %*u %lld", | |
960 | &local_addr, &protocol, &remote_addr, | |
961 | &st, &inode) != 5) | |
962 | return NULL; | |
963 | ||
964 | if (inode == 0) | |
965 | return NULL; | |
966 | ||
8ff22ccb | 967 | raw = xcalloc(1, sizeof(*raw)); |
0188afb3 MY |
968 | inet = &raw->l4.inet; |
969 | sock = &inet->sock; | |
970 | sock->class = class; | |
971 | sock->inode = (ino_t)inode; | |
972 | sock->netns_inode = netns_inode; | |
973 | inet->local_addr.s_addr = kernel32_to_cpu(byteorder, local_addr); | |
974 | inet->remote_addr.s_addr = kernel32_to_cpu(byteorder, remote_addr); | |
975 | raw->protocol = protocol; | |
976 | raw->l4.st = st; | |
977 | ||
978 | return sock; | |
979 | } | |
980 | ||
981 | static const struct l4_xinfo_class raw_xinfo_class = { | |
982 | .sock = { | |
983 | .get_name = raw_get_name, | |
984 | .get_type = raw_get_type, | |
985 | .get_state = tcp_get_state, | |
986 | .get_listening = NULL, | |
987 | .fill_column = raw_fill_column, | |
988 | .free = NULL, | |
989 | }, | |
990 | .scan_line = raw_xinfo_scan_line, | |
991 | .get_addr = tcp_xinfo_get_addr, | |
992 | .is_any_addr = tcp_xinfo_is_any_addr, | |
993 | .family = AF_INET, | |
1656da13 | 994 | .l3_decorator = {"", ""}, |
0188afb3 MY |
995 | }; |
996 | ||
997 | static void load_xinfo_from_proc_raw(ino_t netns_inode) | |
998 | { | |
999 | load_xinfo_from_proc_inet_L4(netns_inode, | |
1000 | "/proc/net/raw", | |
1001 | &raw_xinfo_class); | |
1002 | } | |
1656da13 MY |
1003 | |
1004 | /* | |
1005 | * TCP6 | |
1006 | */ | |
1007 | static struct sock_xinfo *tcp6_xinfo_scan_line(const struct sock_xinfo_class *class, | |
1008 | char * line, | |
1009 | ino_t netns_inode, | |
1010 | enum sysfs_byteorder byteorder) | |
1011 | { | |
1012 | uint32_t local_addr[4]; | |
1013 | unsigned int local_port; | |
1014 | uint32_t remote_addr[4]; | |
1015 | unsigned int remote_port; | |
1016 | unsigned int st; | |
1017 | unsigned long inode; | |
1018 | struct tcp_xinfo *tcp; | |
1019 | struct inet6_xinfo *inet6; | |
1020 | struct sock_xinfo *sock; | |
1021 | ||
1022 | if (sscanf(line, | |
1023 | "%*d: " | |
1024 | "%08x%08x%08x%08x:%04x " | |
1025 | "%08x%08x%08x%08x:%04x " | |
1026 | "%x %*x:%*x %*x:%*x %*x %*u %*d %lu ", | |
1027 | local_addr+0, local_addr+1, local_addr+2, local_addr+3, &local_port, | |
1028 | remote_addr+0, remote_addr+1, remote_addr+2, remote_addr+3, &remote_port, | |
1029 | &st, &inode) != 12) | |
1030 | return NULL; | |
1031 | ||
1032 | if (inode == 0) | |
1033 | return NULL; | |
1034 | ||
8ff22ccb | 1035 | tcp = xmalloc(sizeof(*tcp)); |
1656da13 MY |
1036 | inet6 = &tcp->l4.inet6; |
1037 | sock = &inet6->sock; | |
1038 | sock->class = class; | |
1039 | sock->inode = (ino_t)inode; | |
1040 | sock->netns_inode = netns_inode; | |
1041 | tcp->local_port = local_port; | |
1042 | for (int i = 0; i < 4; i++) { | |
1043 | inet6->local_addr.s6_addr32[i] = kernel32_to_cpu(byteorder, local_addr[i]); | |
1044 | inet6->remote_addr.s6_addr32[i] = kernel32_to_cpu(byteorder, remote_addr[i]); | |
1045 | } | |
1046 | tcp->remote_port = remote_port; | |
1047 | tcp->l4.st = st; | |
1048 | ||
1049 | return sock; | |
1050 | } | |
1051 | ||
1052 | static bool tcp6_fill_column(struct proc *proc __attribute__((__unused__)), | |
1053 | struct sock_xinfo *sock_xinfo, | |
1054 | struct sock *sock __attribute__((__unused__)), | |
1055 | struct libscols_line *ln __attribute__((__unused__)), | |
1056 | int column_id, | |
1057 | size_t column_index __attribute__((__unused__)), | |
1058 | char **str) | |
1059 | { | |
1060 | return l3_fill_column_handler(INET6, sock_xinfo, column_id, str) | |
1061 | || l4_fill_column_handler(TCP, sock_xinfo, column_id, str); | |
1062 | } | |
1063 | ||
1064 | static void *tcp6_xinfo_get_addr(struct l4_xinfo * l4, enum l4_side side) | |
1065 | { | |
1066 | return (side == L4_LOCAL) | |
1067 | ? &l4->inet6.local_addr | |
1068 | : &l4->inet6.remote_addr; | |
1069 | } | |
1070 | ||
1071 | static bool tcp6_xinfo_is_any_addr(void *addr) | |
1072 | { | |
1073 | return IN6_ARE_ADDR_EQUAL(addr, &(struct in6_addr)IN6ADDR_ANY_INIT); | |
1074 | } | |
1075 | ||
1076 | static const struct l4_xinfo_class tcp6_xinfo_class = { | |
1077 | .sock = { | |
1078 | .get_name = tcp_get_name, | |
1079 | .get_type = tcp_get_type, | |
1080 | .get_state = tcp_get_state, | |
1081 | .get_listening = tcp_get_listening, | |
1082 | .fill_column = tcp6_fill_column, | |
1083 | .free = NULL, | |
1084 | }, | |
1085 | .scan_line = tcp6_xinfo_scan_line, | |
1086 | .get_addr = tcp6_xinfo_get_addr, | |
1087 | .is_any_addr = tcp6_xinfo_is_any_addr, | |
1088 | .family = AF_INET6, | |
1089 | .l3_decorator = {"[", "]"}, | |
1090 | }; | |
1091 | ||
1092 | static void load_xinfo_from_proc_tcp6(ino_t netns_inode) | |
1093 | { | |
1094 | load_xinfo_from_proc_inet_L4(netns_inode, | |
1095 | "/proc/net/tcp6", | |
1096 | &tcp6_xinfo_class); | |
1097 | } | |
28cf2b21 MY |
1098 | |
1099 | /* | |
1100 | * UDP6 | |
1101 | */ | |
1102 | static bool udp6_fill_column(struct proc *proc __attribute__((__unused__)), | |
1103 | struct sock_xinfo *sock_xinfo, | |
1104 | struct sock *sock __attribute__((__unused__)), | |
1105 | struct libscols_line *ln __attribute__((__unused__)), | |
1106 | int column_id, | |
1107 | size_t column_index __attribute__((__unused__)), | |
1108 | char **str) | |
1109 | { | |
1110 | return l3_fill_column_handler(INET6, sock_xinfo, column_id, str) | |
1111 | || l4_fill_column_handler(UDP, sock_xinfo, column_id, str); | |
1112 | } | |
1113 | ||
1114 | static const struct l4_xinfo_class udp6_xinfo_class = { | |
1115 | .sock = { | |
1116 | .get_name = udp_get_name, | |
1117 | .get_type = udp_get_type, | |
1118 | .get_state = tcp_get_state, | |
1119 | .get_listening = NULL, | |
1120 | .fill_column = udp6_fill_column, | |
1121 | .free = NULL, | |
1122 | }, | |
1123 | .scan_line = tcp6_xinfo_scan_line, | |
1124 | .get_addr = tcp6_xinfo_get_addr, | |
1125 | .is_any_addr = tcp6_xinfo_is_any_addr, | |
1126 | .family = AF_INET6, | |
1127 | .l3_decorator = {"[", "]"}, | |
1128 | }; | |
1129 | ||
1130 | static void load_xinfo_from_proc_udp6(ino_t netns_inode) | |
1131 | { | |
1132 | load_xinfo_from_proc_inet_L4(netns_inode, | |
1133 | "/proc/net/udp6", | |
1134 | &udp6_xinfo_class); | |
1135 | } | |
2dd373c3 | 1136 | |
93bca151 MY |
1137 | /* |
1138 | * UDPLITEv6 | |
1139 | */ | |
1140 | static bool udplite6_fill_column(struct proc *proc __attribute__((__unused__)), | |
1141 | struct sock_xinfo *sock_xinfo, | |
1142 | struct sock *sock __attribute__((__unused__)), | |
1143 | struct libscols_line *ln __attribute__((__unused__)), | |
1144 | int column_id, | |
1145 | size_t column_index __attribute__((__unused__)), | |
1146 | char **str) | |
1147 | { | |
1148 | return l3_fill_column_handler(INET6, sock_xinfo, column_id, str) | |
1149 | || l4_fill_column_handler(UDPLITE, sock_xinfo, column_id, str); | |
1150 | } | |
1151 | ||
1152 | static const struct l4_xinfo_class udplite6_xinfo_class = { | |
1153 | .sock = { | |
1154 | .get_name = udp_get_name, | |
1155 | .get_type = udp_get_type, | |
1156 | .get_state = tcp_get_state, | |
1157 | .get_listening = NULL, | |
1158 | .fill_column = udplite6_fill_column, | |
1159 | .free = NULL, | |
1160 | }, | |
1161 | .scan_line = tcp6_xinfo_scan_line, | |
1162 | .get_addr = tcp6_xinfo_get_addr, | |
1163 | .is_any_addr = tcp6_xinfo_is_any_addr, | |
1164 | .family = AF_INET6, | |
1165 | .l3_decorator = {"[", "]"}, | |
1166 | }; | |
1167 | ||
1168 | static void load_xinfo_from_proc_udplite6(ino_t netns_inode) | |
1169 | { | |
1170 | load_xinfo_from_proc_inet_L4(netns_inode, | |
1171 | "/proc/net/udplite6", | |
1172 | &udplite6_xinfo_class); | |
1173 | } | |
1174 | ||
2dd373c3 MY |
1175 | /* |
1176 | * RAW6 | |
1177 | */ | |
1178 | static struct sock_xinfo *raw6_xinfo_scan_line(const struct sock_xinfo_class *class, | |
1179 | char * line, | |
1180 | ino_t netns_inode, | |
1181 | enum sysfs_byteorder byteorder) | |
1182 | { | |
1183 | uint32_t local_addr[4]; | |
1184 | unsigned int protocol; | |
1185 | uint32_t remote_addr[4]; | |
1186 | unsigned int st; | |
1187 | unsigned long inode; | |
1188 | struct raw_xinfo *raw; | |
1189 | struct inet6_xinfo *inet6; | |
1190 | struct sock_xinfo *sock; | |
1191 | ||
1192 | if (sscanf(line, | |
1193 | "%*d: " | |
1194 | "%08x%08x%08x%08x:%04x " | |
1195 | "%08x%08x%08x%08x:0000 " | |
1196 | "%x %*x:%*x %*x:%*x %*x %*u %*d %lu ", | |
1197 | local_addr+0, local_addr+1, local_addr+2, local_addr+3, &protocol, | |
1198 | remote_addr+0, remote_addr+1, remote_addr+2, remote_addr+3, | |
1199 | &st, &inode) != 11) | |
1200 | return NULL; | |
1201 | ||
1202 | if (inode == 0) | |
1203 | return NULL; | |
1204 | ||
1205 | raw = xmalloc(sizeof(*raw)); | |
1206 | inet6 = &raw->l4.inet6; | |
1207 | sock = &inet6->sock; | |
1208 | sock->class = class; | |
1209 | sock->inode = (ino_t)inode; | |
1210 | sock->netns_inode = netns_inode; | |
1211 | for (int i = 0; i < 4; i++) { | |
1212 | inet6->local_addr.s6_addr32[i] = kernel32_to_cpu(byteorder, local_addr[i]); | |
1213 | inet6->remote_addr.s6_addr32[i] = kernel32_to_cpu(byteorder, remote_addr[i]); | |
1214 | } | |
1215 | raw->protocol = protocol; | |
1216 | raw->l4.st = st; | |
1217 | ||
1218 | return sock; | |
1219 | } | |
1220 | ||
1221 | static bool raw6_fill_column(struct proc *proc __attribute__((__unused__)), | |
1222 | struct sock_xinfo *sock_xinfo, | |
1223 | struct sock *sock __attribute__((__unused__)), | |
1224 | struct libscols_line *ln __attribute__((__unused__)), | |
1225 | int column_id, | |
1226 | size_t column_index __attribute__((__unused__)), | |
1227 | char **str) | |
1228 | { | |
1229 | struct raw_xinfo *raw; | |
1230 | ||
1231 | if (l3_fill_column_handler(INET6, sock_xinfo, column_id, str)) | |
1232 | return true; | |
1233 | ||
1234 | raw = (struct raw_xinfo *)sock_xinfo; | |
1235 | if (column_id == COL_RAW_PROTOCOL) { | |
1236 | xasprintf(str, "%"PRIu16, raw->protocol); | |
1237 | return true; | |
1238 | } | |
1239 | ||
1240 | return false; | |
1241 | } | |
1242 | ||
1243 | static const struct l4_xinfo_class raw6_xinfo_class = { | |
1244 | .sock = { | |
1245 | .get_name = raw_get_name, | |
1246 | .get_type = raw_get_type, | |
1247 | .get_state = tcp_get_state, | |
1248 | .get_listening = NULL, | |
1249 | .fill_column = raw6_fill_column, | |
1250 | .free = NULL, | |
1251 | }, | |
1252 | .scan_line = raw6_xinfo_scan_line, | |
1253 | .get_addr = tcp6_xinfo_get_addr, | |
1254 | .is_any_addr = tcp6_xinfo_is_any_addr, | |
1255 | .family = AF_INET6, | |
1256 | .l3_decorator = {"[", "]"}, | |
1257 | }; | |
1258 | ||
1259 | static void load_xinfo_from_proc_raw6(ino_t netns_inode) | |
1260 | { | |
1261 | load_xinfo_from_proc_inet_L4(netns_inode, | |
1262 | "/proc/net/raw6", | |
1263 | &raw6_xinfo_class); | |
1264 | } |