]> git.ipfire.org Git - thirdparty/util-linux.git/blame - misc-utils/lsfd-sock-xinfo.c
lsfd: (man) write more about TCP scokets
[thirdparty/util-linux.git] / misc-utils / lsfd-sock-xinfo.c
CommitLineData
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 */
2e38e61c
MY
22#include <fcntl.h> /* open(2) */
23#include <linux/net.h> /* SS_* */
24#include <linux/un.h> /* UNIX_PATH_MAX */
0ee16e43
MY
25#include <sched.h> /* for setns(2) */
26#include <search.h>
2e38e61c
MY
27#include <stdint.h>
28#include <string.h>
29#include <sys/socket.h> /* SOCK_* */
0ee16e43
MY
30
31#include "xalloc.h"
32#include "nls.h"
33#include "libsmartcols.h"
34
35#include "lsfd.h"
36#include "lsfd-sock.h"
37
2e38e61c 38static void load_xinfo_from_proc_unix(ino_t netns_inode);
7b2a9be0 39static void load_xinfo_from_proc_tcp(ino_t netns_inode);
2e38e61c 40
0ee16e43
MY
41static int self_netns_fd = -1;
42struct stat self_netns_sb;
43
44static void *xinfo_tree; /* for tsearch/tfind */
45static void *netns_tree;
46
47static int netns_compare(const void *a, const void *b)
48{
49 if (*(ino_t *)a < *(ino_t *)b)
50 return -1;
51 else if (*(ino_t *)a > *(ino_t *)b)
52 return 1;
53 else
54 return 0;
55}
56
57static bool is_sock_xinfo_loaded(ino_t netns)
58{
59 return tfind(&netns, &netns_tree, netns_compare)? true: false;
60}
61
62static void mark_sock_xinfo_loaded(ino_t ino)
63{
64 ino_t *netns = xmalloc(sizeof(ino));
65 ino_t **tmp;
66
67 *netns = ino;
68 tmp = tsearch(netns, &netns_tree, netns_compare);
69 if (tmp == NULL)
70 errx(EXIT_FAILURE, _("failed to allocate memory"));
71}
72
2e38e61c 73static void load_sock_xinfo_no_nsswitch(ino_t netns)
0ee16e43 74{
2e38e61c 75 load_xinfo_from_proc_unix(netns);
7b2a9be0 76 load_xinfo_from_proc_tcp(netns);
0ee16e43
MY
77}
78
79static void load_sock_xinfo_with_fd(int fd, ino_t netns)
80{
b3649945 81 if (setns(fd, CLONE_NEWNET) == 0) {
0ee16e43 82 load_sock_xinfo_no_nsswitch(netns);
b3649945 83 setns(self_netns_fd, CLONE_NEWNET);
0ee16e43
MY
84 }
85}
86
87void load_sock_xinfo(struct path_cxt *pc, const char *name, ino_t netns)
88{
89 if (self_netns_fd == -1)
90 return;
91
92 if (!is_sock_xinfo_loaded(netns)) {
93 int fd;
94
95 mark_sock_xinfo_loaded(netns);
96 fd = ul_path_open(pc, O_RDONLY, name);
97 if (fd < 0)
98 return;
99
100 load_sock_xinfo_with_fd(fd, netns);
101 close(fd);
102 }
103}
104
105void initialize_sock_xinfos(void)
106{
107 struct path_cxt *pc;
108 DIR *dir;
109 struct dirent *d;
110
111 self_netns_fd = open("/proc/self/ns/net", O_RDONLY);
112
113 if (self_netns_fd < 0)
114 load_sock_xinfo_no_nsswitch(0);
115 else {
116 if (fstat(self_netns_fd, &self_netns_sb) == 0) {
117 mark_sock_xinfo_loaded(self_netns_sb.st_ino);
118 load_sock_xinfo_no_nsswitch(self_netns_sb.st_ino);
119 }
120 }
121
122 /* Load /proc/net/{unix,...} of the network namespace
123 * specified with netns files under /var/run/netns/.
124 *
125 * `ip netns' command pins a network namespace on
126 * /var/run/netns.
127 */
128 pc = ul_new_path("/var/run/netns");
129 if (!pc)
130 err(EXIT_FAILURE, _("failed to alloc path context for /var/run/netns"));
131 dir = ul_path_opendir(pc, NULL);
132 if (dir == NULL) {
133 ul_unref_path(pc);
134 return;
135 }
136 while ((d = readdir(dir))) {
137 struct stat sb;
138 int fd;
139 if (ul_path_stat(pc, &sb, 0, d->d_name) < 0)
140 continue;
141 if (is_sock_xinfo_loaded(sb.st_ino))
142 continue;
143 mark_sock_xinfo_loaded(sb.st_ino);
144 fd = ul_path_open(pc, O_RDONLY, d->d_name);
145 if (fd < 0)
146 continue;
147 load_sock_xinfo_with_fd(fd, sb.st_ino);
148 close(fd);
149 }
150 closedir(dir);
151 ul_unref_path(pc);
152}
153
b3649945 154static void free_sock_xinfo(void *node)
0ee16e43
MY
155{
156 struct sock_xinfo *xinfo = node;
157 if (xinfo->class->free)
b3649945 158 xinfo->class->free(xinfo);
0ee16e43
MY
159 free(node);
160}
161
162void finalize_sock_xinfos(void)
163{
164 if (self_netns_fd != -1)
165 close(self_netns_fd);
166 tdestroy(netns_tree, free);
167 tdestroy(xinfo_tree, free_sock_xinfo);
168}
169
170static int xinfo_compare(const void *a, const void *b)
171{
172 if (((struct sock_xinfo *)a)->inode < ((struct sock_xinfo *)b)->inode)
173 return -1;
174 if (((struct sock_xinfo *)a)->inode > ((struct sock_xinfo *)b)->inode)
175 return 1;
176 return 0;
177}
178
2e38e61c
MY
179static void add_sock_info(struct sock_xinfo *xinfo)
180{
181 struct sock_xinfo **tmp = tsearch(xinfo, &xinfo_tree, xinfo_compare);
182
183 if (tmp == NULL)
184 errx(EXIT_FAILURE, _("failed to allocate memory"));
185}
186
0ee16e43
MY
187struct sock_xinfo *get_sock_xinfo(ino_t netns_inode)
188{
189 struct sock_xinfo **xinfo = tfind(&netns_inode, &xinfo_tree, xinfo_compare);
190
191 if (xinfo)
192 return *xinfo;
193 return NULL;
194}
195
196bool is_nsfs_dev(dev_t dev)
197{
198 return (dev == self_netns_sb.st_dev);
199}
2e38e61c
MY
200
201static const char *sock_decode_type(uint16_t type)
202{
203 switch (type) {
204 case SOCK_STREAM:
205 return "stream";
206 case SOCK_DGRAM:
207 return "dgram";
208 case SOCK_RAW:
209 return "raw";
210 case SOCK_RDM:
211 return "rdm";
212 case SOCK_SEQPACKET:
213 return "seqpacket";
214 case SOCK_DCCP:
215 return "dccp";
216 case SOCK_PACKET:
217 return "packet";
218 default:
219 return "unknown";
220 }
221}
222
223/*
224 * Protocol specific code
225 */
226
227/*
228 * UNIX
229 */
230struct unix_xinfo {
231 struct sock_xinfo sock;
232 int acceptcon; /* flags */
233 uint16_t type;
234 uint8_t st;
235 char path[
236 UNIX_PATH_MAX
237 + 1 /* for @ */
238 + 1 /* \0? */
239 ];
240};
241
242static const char *unix_decode_state(uint8_t st)
243{
244 switch (st) {
245 case SS_FREE:
246 return "free";
247 case SS_UNCONNECTED:
248 return "unconnected";
249 case SS_CONNECTING:
250 return "connecting";
251 case SS_CONNECTED:
252 return "connected";
253 case SS_DISCONNECTING:
254 return "disconnecting";
255 default:
256 return "unknown";
257 }
258}
259
260static char *unix_get_name(struct sock_xinfo *sock_xinfo,
261 struct sock *sock)
262{
263 struct unix_xinfo *ux = (struct unix_xinfo *)sock_xinfo;
264 const char *state = unix_decode_state(ux->st);
265 char *str = NULL;
266
267 if (sock->protoname && (strcmp(sock->protoname, "UNIX-STREAM") == 0))
268 xasprintf(&str, "state=%s%s%s",
269 (ux->acceptcon)? "listen": state,
270 *(ux->path)? " path=": "",
271 *(ux->path)? ux->path: "");
272 else
273 xasprintf(&str, "state=%s%s%s type=%s",
274 (ux->acceptcon)? "listen": state,
275 *(ux->path)? " path=": "",
276 *(ux->path)? ux->path: "",
277 sock_decode_type(ux->type));
278 return str;
279}
280
281static char *unix_get_type(struct sock_xinfo *sock_xinfo,
282 struct sock *sock __attribute__((__unused__)))
283{
284 const char *str;
285 struct unix_xinfo *ux = (struct unix_xinfo *)sock_xinfo;
286
287 str = sock_decode_type(ux->type);
288 return strdup(str);
289}
290
291static char *unix_get_state(struct sock_xinfo *sock_xinfo,
292 struct sock *sock __attribute__((__unused__)))
293{
294 const char *str;
295 struct unix_xinfo *ux = (struct unix_xinfo *)sock_xinfo;
296
297 if (ux->acceptcon)
298 return strdup("listen");
299
300 str = unix_decode_state(ux->st);
301 return strdup(str);
302}
303
45d61bff
MY
304static bool unix_get_listening(struct sock_xinfo *sock_xinfo,
305 struct sock *sock __attribute__((__unused__)))
306{
307 struct unix_xinfo *ux = (struct unix_xinfo *)sock_xinfo;
308
309 return ux->acceptcon;
310}
311
2e38e61c
MY
312static bool unix_fill_column(struct proc *proc __attribute__((__unused__)),
313 struct sock_xinfo *sock_xinfo,
314 struct sock *sock __attribute__((__unused__)),
315 struct libscols_line *ln __attribute__((__unused__)),
316 int column_id,
317 size_t column_index __attribute__((__unused__)),
318 char **str)
319{
320 struct unix_xinfo *ux = (struct unix_xinfo *)sock_xinfo;
321
322 switch(column_id) {
323 case COL_UNIX_PATH:
324 if (*ux->path) {
325 *str = strdup(ux->path);
326 return true;
327 }
328 break;
329 }
330
331 return false;
332}
333
334static struct sock_xinfo_class unix_xinfo_class = {
335 .class = "unix",
336 .get_name = unix_get_name,
337 .get_type = unix_get_type,
338 .get_state = unix_get_state,
45d61bff 339 .get_listening = unix_get_listening,
2e38e61c
MY
340 .fill_column = unix_fill_column,
341 .free = NULL,
342};
343
344/* #define UNIX_LINE_LEN 54 + 21 + UNIX_LINE_LEN + 1 */
345#define UNIX_LINE_LEN 256
346static void load_xinfo_from_proc_unix(ino_t netns_inode)
347{
348 char line[UNIX_LINE_LEN];
349 FILE *unix_fp;
350
351 unix_fp = fopen("/proc/net/unix", "r");
352 if (!unix_fp)
353 return;
354
355 if (fgets(line, sizeof(line), unix_fp) == NULL)
356 goto out;
b3649945 357 if (!(line[0] == 'N' && line[1] == 'u' && line[2] == 'm'))
2e38e61c
MY
358 /* Unexpected line */
359 goto out;
360
361 while (fgets(line, sizeof(line), unix_fp)) {
362 uint64_t flags;
363 uint32_t type;
364 unsigned int st;
365 unsigned long inode;
366 char path[1 + UNIX_PATH_MAX +1];
367 struct unix_xinfo *ux;
368
369 memset(path, 0, sizeof(path));
370 if (sscanf(line, "%*x: %*x %*x %lx %x %x %lu %s",
371 &flags, &type, &st, &inode, path) < 4)
372 continue;
373
374 if (inode == 0)
375 continue;
376
377 ux = xmalloc(sizeof(struct unix_xinfo));
378 ux->sock.class = &unix_xinfo_class;
379 ux->sock.inode = (ino_t)inode;
380 ux->sock.netns_inode = netns_inode;
381
382 ux->acceptcon = !!flags;
383 ux->type = type;
384 ux->st = st;
385 strcpy(ux->path, path);
386
387 add_sock_info((struct sock_xinfo *)ux);
388 }
389
390 out:
391 fclose(unix_fp);
392}
7b2a9be0
MY
393
394/*
395 * AF_INET
396 */
397struct inet_xinfo {
398 struct sock_xinfo sock;
399 uint32_t local_addr;
400 uint32_t remote_addr;
401};
402
403static bool inet_fill_column(struct proc *proc __attribute__((__unused__)),
404 struct inet_xinfo *inet,
405 struct sock *sock __attribute__((__unused__)),
406 struct libscols_line *ln __attribute__((__unused__)),
407 int column_id,
408 size_t column_index __attribute__((__unused__)),
409 char **str)
410{
411 struct in_addr n;
412 struct in_addr *nptr = NULL;
413 char s[INET_ADDRSTRLEN];
414
415 switch(column_id) {
416 case COL_INET_LADDR:
417 n.s_addr = inet->local_addr;
418 nptr = &n;
419 break;
420 case COL_INET_RADDR:
421 n.s_addr = inet->remote_addr;
422 nptr = &n;
423 break;
424 default:
425 return false;
426 }
427
428 if (nptr && inet_ntop(AF_INET, &n, s, sizeof(s))) {
429 *str = strdup(s);
430 return true;
431 }
432 return false;
433}
434
435/*
436 * TCP
437 */
438struct tcp_xinfo {
439 struct inet_xinfo inet;
440 uint16_t local_port;
441 uint16_t remote_port;
442 unsigned int st;
443};
444
445enum tcp_state {
446 /*
447 * Taken from linux/include/net/tcp_states.h.
448 * (GPL-2.0-or-later)
449 */
450 TCP_ESTABLISHED = 1,
451 TCP_SYN_SENT,
452 TCP_SYN_RECV,
453 TCP_FIN_WAIT1,
454 TCP_FIN_WAIT2,
455 TCP_TIME_WAIT,
456 TCP_CLOSE,
457 TCP_CLOSE_WAIT,
458 TCP_LAST_ACK,
459 TCP_LISTEN,
460 TCP_CLOSING,
461 TCP_NEW_SYN_RECV,
462
463 TCP_MAX_STATES /* Leave at the end! */
464};
465
466static const char *tcp_decode_state(unsigned int st)
467{
468 const char * table [] = {
469 [TCP_ESTABLISHED] = "established",
470 [TCP_SYN_SENT] = "syn-sent",
471 [TCP_SYN_RECV] = "syn-recv",
472 [TCP_FIN_WAIT1] = "fin-wait1",
473 [TCP_FIN_WAIT2] = "fin-wait2",
474 [TCP_TIME_WAIT] = "time-wait",
475 [TCP_CLOSE] = "close",
476 [TCP_CLOSE_WAIT] = "close-wait",
477 [TCP_LAST_ACK] = "last-ack",
478 [TCP_LISTEN] = "listen",
479 [TCP_CLOSING] = "closing",
480 [TCP_NEW_SYN_RECV] = "new-syn-recv",
481 };
482
483 if (st < TCP_MAX_STATES)
484 return table[st];
485 return "unknown";
486}
487
488static char *tcp_get_name(struct sock_xinfo *sock_xinfo,
489 struct sock *sock __attribute__((__unused__)))
490{
491 char *str = NULL;
492 struct inet_xinfo *inet = ((struct inet_xinfo *)sock_xinfo);
493 struct tcp_xinfo *tcp = ((struct tcp_xinfo *)sock_xinfo);
494 struct in_addr local_n, remote_n;
495 char local_s[INET_ADDRSTRLEN], remote_s[INET_ADDRSTRLEN];
496
497 local_n.s_addr = inet->local_addr;
498 remote_n.s_addr = inet->remote_addr;
499 if (!inet_ntop(AF_INET, &local_n, local_s, sizeof(local_s)))
500 xasprintf(&str, "state=%s", tcp_decode_state(tcp->st));
501 else if (tcp->st == TCP_LISTEN
502 || !inet_ntop(AF_INET, &remote_n, remote_s, sizeof(remote_s)))
503 xasprintf(&str, "state=%s laddr=%s:%u",
504 tcp_decode_state(tcp->st),
505 local_s, tcp->local_port);
506 else
507 xasprintf(&str, "state=%s laddr=%s:%u raddr=%s:%u",
508 tcp_decode_state(tcp->st),
509 local_s, tcp->local_port,
510 remote_s, tcp->remote_port);
511 return str;
512}
513
514static char *tcp_get_type(struct sock_xinfo *sock_xinfo __attribute__((__unused__)),
515 struct sock *sock __attribute__((__unused__)))
516{
517 return strdup("stream");
518}
519
520static char *tcp_get_state(struct sock_xinfo *sock_xinfo,
521 struct sock *sock __attribute__((__unused__)))
522{
523 struct tcp_xinfo *tcp = (struct tcp_xinfo *)sock_xinfo;
524
525 return strdup(tcp_decode_state(tcp->st));
526}
527
528static bool tcp_get_listening(struct sock_xinfo *sock_xinfo,
529 struct sock *sock __attribute__((__unused__)))
530{
531 struct tcp_xinfo *tcp = (struct tcp_xinfo *)sock_xinfo;
532 return tcp->st == TCP_LISTEN;
533}
534
535static bool tcp_fill_column(struct proc *proc,
536 struct sock_xinfo *sock_xinfo,
537 struct sock *sock,
538 struct libscols_line *ln,
539 int column_id,
540 size_t column_index,
541 char **str)
542{
543 struct tcp_xinfo *tcp = (struct tcp_xinfo *)sock_xinfo;
544 struct inet_xinfo *inet = (struct inet_xinfo *)sock_xinfo;
545 struct in_addr n;
546 bool has_laddr = false;
547 char s[INET_ADDRSTRLEN];
548 unsigned int p;
549 bool has_lport = false;
550
551 if (inet_fill_column(proc, (struct inet_xinfo *)sock_xinfo, sock, ln,
552 column_id, column_index, str))
553 return true;
554
555 switch(column_id) {
556 case COL_TCP_LADDR:
557 n.s_addr = inet->local_addr;
558 has_laddr = true;
559 p = (unsigned int)tcp->local_port;
560 /* FALL THROUGH */
561 case COL_TCP_RADDR:
562 if (!has_laddr) {
563 n.s_addr = inet->remote_addr;
564 p = (unsigned int)tcp->remote_port;
565 }
566 if (inet_ntop(AF_INET, &n, s, sizeof(s)))
567 xasprintf(str, "%s:%u", s, p);
568 break;
569 case COL_TCP_LPORT:
570 p = (unsigned int)tcp->local_port;
571 has_lport = true;
572 /* FALL THROUGH */
573 case COL_TCP_RPORT:
574 if (!has_lport)
575 p = (unsigned int)tcp->remote_port;
576 xasprintf(str, "%u", p);
577 break;
578 default:
579 return false;
580 }
581
582 return true;
583}
584
585static struct sock_xinfo_class tcp_xinfo_class = {
586 .class = "tcp",
587 .get_name = tcp_get_name,
588 .get_type = tcp_get_type,
589 .get_state = tcp_get_state,
590 .get_listening = tcp_get_listening,
591 .fill_column = tcp_fill_column,
592 .free = NULL,
593};
594
595#define TCP_LINE_LEN 256
596static void load_xinfo_from_proc_tcp(ino_t netns_inode)
597{
598 char line[TCP_LINE_LEN];
599 FILE *tcp_fp;
600
601 tcp_fp = fopen("/proc/net/tcp", "r");
602 if (!tcp_fp)
603 return;
604
605 if (fgets(line, sizeof(line), tcp_fp) == NULL)
606 goto out;
607 if (!(line[0] == ' ' && line[1] == ' '
608 && line[2] == 's' && line[3] == 'l'))
609 /* Unexpected line */
610 goto out;
611
612 while (fgets(line, sizeof(line), tcp_fp)) {
613 unsigned long local_addr;
614 unsigned long local_port;
615 unsigned long remote_addr;
616 unsigned long remote_port;
617 unsigned long st;
618 unsigned long long inode;
619 struct tcp_xinfo *tcp;
620 struct inet_xinfo *inet;
621 struct sock_xinfo *sock;
622
623 if (sscanf(line, "%*d: %lx:%lx %lx:%lx %lx %*x:%*x %*x:%*x %*x %*u %*u %lld",
624 &local_addr, &local_port, &remote_addr, &remote_port,
625 &st, &inode) != 6)
626 continue;
627
628 if (inode == 0)
629 continue;
630
631 tcp = xmalloc(sizeof(struct tcp_xinfo));
632 inet = (struct inet_xinfo *)tcp;
633 sock = (struct sock_xinfo *)inet;
634 sock->class = &tcp_xinfo_class;
635 sock->inode = (ino_t)inode;
636 sock->netns_inode = netns_inode;
637 inet->local_addr = local_addr;
638 tcp->local_port = local_port;
639 inet->remote_addr = remote_addr;
640 tcp->remote_port = remote_port;
641 tcp->st = st;
642
643 add_sock_info(sock);
644 }
645
646 out:
647 fclose(tcp_fp);
648}