aboutsummaryrefslogtreecommitdiff
path: root/networking/netstat.c
diff options
context:
space:
mode:
Diffstat (limited to 'networking/netstat.c')
-rw-r--r--networking/netstat.c198
1 files changed, 119 insertions, 79 deletions
diff --git a/networking/netstat.c b/networking/netstat.c
index 3114a3902..2a83af3ac 100644
--- a/networking/netstat.c
+++ b/networking/netstat.c
@@ -17,16 +17,37 @@
17#include "libbb.h" 17#include "libbb.h"
18#include "inet_common.h" 18#include "inet_common.h"
19 19
20//usage:#define netstat_trivial_usage
21//usage: "[-"IF_ROUTE("r")"al] [-tuwx] [-en"IF_FEATURE_NETSTAT_WIDE("W")IF_FEATURE_NETSTAT_PRG("p")"]"
22//usage:#define netstat_full_usage "\n\n"
23//usage: "Display networking information\n"
24//usage: "\nOptions:"
25//usage: IF_ROUTE(
26//usage: "\n -r Routing table"
27//usage: )
28//usage: "\n -a All sockets"
29//usage: "\n -l Listening sockets"
30//usage: "\n Else: connected sockets"
31//usage: "\n -t TCP sockets"
32//usage: "\n -u UDP sockets"
33//usage: "\n -w Raw sockets"
34//usage: "\n -x Unix sockets"
35//usage: "\n Else: all socket types"
36//usage: "\n -e Other/more information"
37//usage: "\n -n Don't resolve names"
38//usage: IF_FEATURE_NETSTAT_WIDE(
39//usage: "\n -W Wide display"
40//usage: )
41//usage: IF_FEATURE_NETSTAT_PRG(
42//usage: "\n -p Show PID/program name for sockets"
43//usage: )
44
20#define NETSTAT_OPTS "laentuwx" \ 45#define NETSTAT_OPTS "laentuwx" \
21 IF_ROUTE( "r") \ 46 IF_ROUTE( "r") \
22 IF_FEATURE_NETSTAT_WIDE("W") \ 47 IF_FEATURE_NETSTAT_WIDE("W") \
23 IF_FEATURE_NETSTAT_PRG( "p") 48 IF_FEATURE_NETSTAT_PRG( "p")
24 49
25enum { 50enum {
26 OPTBIT_KEEP_OLD = 7,
27 IF_ROUTE( OPTBIT_ROUTE,)
28 IF_FEATURE_NETSTAT_WIDE(OPTBIT_WIDE ,)
29 IF_FEATURE_NETSTAT_PRG( OPTBIT_PRG ,)
30 OPT_sock_listen = 1 << 0, // l 51 OPT_sock_listen = 1 << 0, // l
31 OPT_sock_all = 1 << 1, // a 52 OPT_sock_all = 1 << 1, // a
32 OPT_extended = 1 << 2, // e 53 OPT_extended = 1 << 2, // e
@@ -35,6 +56,10 @@ enum {
35 OPT_sock_udp = 1 << 5, // u 56 OPT_sock_udp = 1 << 5, // u
36 OPT_sock_raw = 1 << 6, // w 57 OPT_sock_raw = 1 << 6, // w
37 OPT_sock_unix = 1 << 7, // x 58 OPT_sock_unix = 1 << 7, // x
59 OPTBIT_x = 7,
60 IF_ROUTE( OPTBIT_ROUTE,)
61 IF_FEATURE_NETSTAT_WIDE(OPTBIT_WIDE ,)
62 IF_FEATURE_NETSTAT_PRG( OPTBIT_PRG ,)
38 OPT_route = IF_ROUTE( (1 << OPTBIT_ROUTE)) + 0, // r 63 OPT_route = IF_ROUTE( (1 << OPTBIT_ROUTE)) + 0, // r
39 OPT_wide = IF_FEATURE_NETSTAT_WIDE((1 << OPTBIT_WIDE )) + 0, // W 64 OPT_wide = IF_FEATURE_NETSTAT_WIDE((1 << OPTBIT_WIDE )) + 0, // W
40 OPT_prg = IF_FEATURE_NETSTAT_PRG( (1 << OPTBIT_PRG )) + 0, // p 65 OPT_prg = IF_FEATURE_NETSTAT_PRG( (1 << OPTBIT_PRG )) + 0, // p
@@ -88,24 +113,25 @@ typedef enum {
88 SS_DISCONNECTING /* in process of disconnecting */ 113 SS_DISCONNECTING /* in process of disconnecting */
89} socket_state; 114} socket_state;
90 115
91#define SO_ACCEPTCON (1<<16) /* performed a listen */ 116#define SO_ACCEPTCON (1<<16) /* performed a listen */
92#define SO_WAITDATA (1<<17) /* wait data to read */ 117#define SO_WAITDATA (1<<17) /* wait data to read */
93#define SO_NOSPACE (1<<18) /* no space to write */ 118#define SO_NOSPACE (1<<18) /* no space to write */
94
95/* Standard printout size */
96#define PRINT_IP_MAX_SIZE 23
97#define PRINT_NET_CONN "%s %6ld %6ld %-23s %-23s %-12s"
98#define PRINT_NET_CONN_HEADER "\nProto Recv-Q Send-Q %-23s %-23s State "
99 119
120#define ADDR_NORMAL_WIDTH 23
100/* When there are IPv6 connections the IPv6 addresses will be 121/* When there are IPv6 connections the IPv6 addresses will be
101 * truncated to none-recognition. The '-W' option makes the 122 * truncated to none-recognition. The '-W' option makes the
102 * address columns wide enough to accomodate for longest possible 123 * address columns wide enough to accomodate for longest possible
103 * IPv6 addresses, i.e. addresses of the form 124 * IPv6 addresses, i.e. addresses of the form
104 * xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:ddd.ddd.ddd.ddd 125 * xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:ddd.ddd.ddd.ddd
105 */ 126 */
106#define PRINT_IP_MAX_SIZE_WIDE 51 /* INET6_ADDRSTRLEN + 5 for the port number */ 127#define ADDR_WIDE 51 /* INET6_ADDRSTRLEN + 5 for the port number */
107#define PRINT_NET_CONN_WIDE "%s %6ld %6ld %-51s %-51s %-12s" 128#if ENABLE_FEATURE_NETSTAT_WIDE
108#define PRINT_NET_CONN_HEADER_WIDE "\nProto Recv-Q Send-Q %-51s %-51s State " 129# define FMT_NET_CONN_DATA "%s %6ld %6ld %-*s %-*s %-12s"
130# define FMT_NET_CONN_HEADER "\nProto Recv-Q Send-Q %-*s %-*s State %s\n"
131#else
132# define FMT_NET_CONN_DATA "%s %6ld %6ld %-23s %-23s %-12s"
133# define FMT_NET_CONN_HEADER "\nProto Recv-Q Send-Q %-23s %-23s State %s\n"
134#endif
109 135
110#define PROGNAME_WIDTH 20 136#define PROGNAME_WIDTH 20
111#define PROGNAME_WIDTH_STR "20" 137#define PROGNAME_WIDTH_STR "20"
@@ -121,22 +147,30 @@ struct prg_node {
121#define PRG_HASH_SIZE 211 147#define PRG_HASH_SIZE 211
122 148
123struct globals { 149struct globals {
124 const char *net_conn_line;
125 smallint flags; 150 smallint flags;
126#if ENABLE_FEATURE_NETSTAT_PRG 151#if ENABLE_FEATURE_NETSTAT_PRG
127 smallint prg_cache_loaded; 152 smallint prg_cache_loaded;
128 struct prg_node *prg_hash[PRG_HASH_SIZE]; 153 struct prg_node *prg_hash[PRG_HASH_SIZE];
129#endif 154#endif
155#if ENABLE_FEATURE_NETSTAT_PRG
156 const char *progname_banner;
157#endif
158#if ENABLE_FEATURE_NETSTAT_WIDE
159 unsigned addr_width;
160#endif
130}; 161};
131#define G (*ptr_to_globals) 162#define G (*ptr_to_globals)
132#define flags (G.flags ) 163#define flags (G.flags )
133#define net_conn_line (G.net_conn_line )
134#define prg_hash (G.prg_hash )
135#define prg_cache_loaded (G.prg_cache_loaded) 164#define prg_cache_loaded (G.prg_cache_loaded)
165#define prg_hash (G.prg_hash )
166#if ENABLE_FEATURE_NETSTAT_PRG
167# define progname_banner (G.progname_banner )
168#else
169# define progname_banner ""
170#endif
136#define INIT_G() do { \ 171#define INIT_G() do { \
137 SET_PTR_TO_GLOBALS(xzalloc(sizeof(G))); \ 172 SET_PTR_TO_GLOBALS(xzalloc(sizeof(G))); \
138 flags = NETSTAT_CONNECTED | NETSTAT_ALLPROTO; \ 173 flags = NETSTAT_CONNECTED | NETSTAT_ALLPROTO; \
139 net_conn_line = PRINT_NET_CONN; \
140} while (0) 174} while (0)
141 175
142 176
@@ -145,10 +179,6 @@ struct globals {
145/* Deliberately truncating long to unsigned *int* */ 179/* Deliberately truncating long to unsigned *int* */
146#define PRG_HASHIT(x) ((unsigned)(x) % PRG_HASH_SIZE) 180#define PRG_HASHIT(x) ((unsigned)(x) % PRG_HASH_SIZE)
147 181
148#define print_progname_banner() do { \
149 if (option_mask32 & OPT_prg) printf(PROGNAME_BANNER); \
150} while (0)
151
152static void prg_cache_add(long inode, char *name) 182static void prg_cache_add(long inode, char *name)
153{ 183{
154 unsigned hi = PRG_HASHIT(inode); 184 unsigned hi = PRG_HASHIT(inode);
@@ -201,12 +231,12 @@ static long extract_socket_inode(const char *lname)
201 231
202 if (strncmp(lname, "socket:[", sizeof("socket:[")-1) == 0) { 232 if (strncmp(lname, "socket:[", sizeof("socket:[")-1) == 0) {
203 /* "socket:[12345]", extract the "12345" as inode */ 233 /* "socket:[12345]", extract the "12345" as inode */
204 inode = bb_strtol(lname + sizeof("socket:[")-1, (char**)&lname, 0); 234 inode = bb_strtoul(lname + sizeof("socket:[")-1, (char**)&lname, 0);
205 if (*lname != ']') 235 if (*lname != ']')
206 inode = -1; 236 inode = -1;
207 } else if (strncmp(lname, "[0000]:", sizeof("[0000]:")-1) == 0) { 237 } else if (strncmp(lname, "[0000]:", sizeof("[0000]:")-1) == 0) {
208 /* "[0000]:12345", extract the "12345" as inode */ 238 /* "[0000]:12345", extract the "12345" as inode */
209 inode = bb_strtol(lname + sizeof("[0000]:")-1, NULL, 0); 239 inode = bb_strtoul(lname + sizeof("[0000]:")-1, NULL, 0);
210 if (errno) /* not NUL terminated? */ 240 if (errno) /* not NUL terminated? */
211 inode = -1; 241 inode = -1;
212 } 242 }
@@ -218,9 +248,9 @@ static long extract_socket_inode(const char *lname)
218 return inode; 248 return inode;
219} 249}
220 250
221static int FAST_FUNC file_act(const char *fileName, 251static int FAST_FUNC add_to_prg_cache_if_socket(const char *fileName,
222 struct stat *statbuf UNUSED_PARAM, 252 struct stat *statbuf UNUSED_PARAM,
223 void *userData, 253 void *pid_slash_progname,
224 int depth UNUSED_PARAM) 254 int depth UNUSED_PARAM)
225{ 255{
226 char *linkname; 256 char *linkname;
@@ -231,7 +261,7 @@ static int FAST_FUNC file_act(const char *fileName,
231 inode = extract_socket_inode(linkname); 261 inode = extract_socket_inode(linkname);
232 free(linkname); 262 free(linkname);
233 if (inode >= 0) 263 if (inode >= 0)
234 prg_cache_add(inode, (char *)userData); 264 prg_cache_add(inode, (char *)pid_slash_progname);
235 } 265 }
236 return TRUE; 266 return TRUE;
237} 267}
@@ -241,38 +271,40 @@ static int FAST_FUNC dir_act(const char *fileName,
241 void *userData UNUSED_PARAM, 271 void *userData UNUSED_PARAM,
242 int depth) 272 int depth)
243{ 273{
244 const char *shortName; 274 const char *pid;
245 char *p, *q; 275 char *pid_slash_progname;
276 char proc_pid_fname[sizeof("/proc/%u/cmdline") + sizeof(long)*3];
246 char cmdline_buf[512]; 277 char cmdline_buf[512];
247 int i; 278 int n, len;
248 279
249 if (depth == 0) /* "/proc" itself */ 280 if (depth == 0) /* "/proc" itself */
250 return TRUE; /* continue looking one level below /proc */ 281 return TRUE; /* continue looking one level below /proc */
251 282
252 shortName = fileName + sizeof("/proc/")-1; /* point after "/proc/" */ 283 pid = fileName + sizeof("/proc/")-1; /* point after "/proc/" */
253 if (!isdigit(shortName[0])) /* skip /proc entries whic aren't processes */ 284 if (!isdigit(pid[0])) /* skip /proc entries which aren't processes */
254 return SKIP; 285 return SKIP;
255 286
256 p = concat_path_file(fileName, "cmdline"); /* "/proc/PID/cmdline" */ 287 len = snprintf(proc_pid_fname, sizeof(proc_pid_fname), "%s/cmdline", fileName);
257 i = open_read_close(p, cmdline_buf, sizeof(cmdline_buf) - 1); 288 n = open_read_close(proc_pid_fname, cmdline_buf, sizeof(cmdline_buf) - 1);
258 free(p); 289 if (n < 0)
259 if (i < 0)
260 return FALSE; 290 return FALSE;
261 cmdline_buf[i] = '\0'; 291 cmdline_buf[n] = '\0';
262 q = concat_path_file(shortName, bb_basename(cmdline_buf)); /* "PID/argv0" */ 292
263 293 /* go through all files in /proc/PID/fd and check whether they are sockets */
264 /* go through all files in /proc/PID/fd */ 294 strcpy(proc_pid_fname + len - (sizeof("cmdline")-1), "fd");
265 p = concat_path_file(fileName, "fd"); 295 pid_slash_progname = concat_path_file(pid, bb_basename(cmdline_buf)); /* "PID/argv0" */
266 i = recursive_action(p, ACTION_RECURSE | ACTION_QUIET, 296 n = recursive_action(proc_pid_fname,
267 file_act, NULL, (void *)q, 0); 297 ACTION_RECURSE | ACTION_QUIET,
268 298 add_to_prg_cache_if_socket,
269 free(p); 299 NULL,
270 free(q); 300 (void *)pid_slash_progname,
271 301 0);
272 if (!i) 302 free(pid_slash_progname);
273 return FALSE; /* signal permissions error to caller */ 303
274 304 if (!n)
275 return SKIP; /* caller should not recurse further into this dir. */ 305 return FALSE; /* signal permissions error to caller */
306
307 return SKIP; /* caller should not recurse further into this dir */
276} 308}
277 309
278static void prg_cache_load(void) 310static void prg_cache_load(void)
@@ -294,7 +326,6 @@ static void prg_cache_load(void)
294#else 326#else
295 327
296#define prg_cache_clear() ((void)0) 328#define prg_cache_clear() ((void)0)
297#define print_progname_banner() ((void)0)
298 329
299#endif //ENABLE_FEATURE_NETSTAT_PRG 330#endif //ENABLE_FEATURE_NETSTAT_PRG
300 331
@@ -364,11 +395,14 @@ struct inet_params {
364static int scan_inet_proc_line(struct inet_params *param, char *line) 395static int scan_inet_proc_line(struct inet_params *param, char *line)
365{ 396{
366 int num; 397 int num;
367 char local_addr[64], rem_addr[64]; 398 /* IPv6 /proc files use 32-char hex representation
399 * of IPv6 address, followed by :PORT_IN_HEX
400 */
401 char local_addr[33], rem_addr[33]; /* 32 + 1 for NUL */
368 402
369 num = sscanf(line, 403 num = sscanf(line,
370 "%*d: %64[0-9A-Fa-f]:%X " 404 "%*d: %32[0-9A-Fa-f]:%X "
371 "%64[0-9A-Fa-f]:%X %X " 405 "%32[0-9A-Fa-f]:%X %X "
372 "%lX:%lX %*X:%*X " 406 "%lX:%lX %*X:%*X "
373 "%*X %d %*d %ld ", 407 "%*X %d %*d %ld ",
374 local_addr, &param->local_port, 408 local_addr, &param->local_port,
@@ -403,8 +437,11 @@ static void print_inet_line(struct inet_params *param,
403 char *r = ip_port_str( 437 char *r = ip_port_str(
404 &param->remaddr.sa, param->rem_port, 438 &param->remaddr.sa, param->rem_port,
405 proto, flags & NETSTAT_NUMERIC); 439 proto, flags & NETSTAT_NUMERIC);
406 printf(net_conn_line, 440 printf(FMT_NET_CONN_DATA,
407 proto, param->rxq, param->txq, l, r, state_str); 441 proto, param->rxq, param->txq,
442 IF_FEATURE_NETSTAT_WIDE(G.addr_width,) l,
443 IF_FEATURE_NETSTAT_WIDE(G.addr_width,) r,
444 state_str);
408#if ENABLE_FEATURE_NETSTAT_PRG 445#if ENABLE_FEATURE_NETSTAT_PRG
409 if (option_mask32 & OPT_prg) 446 if (option_mask32 & OPT_prg)
410 printf("%."PROGNAME_WIDTH_STR"s", prg_cache_get(param->inode)); 447 printf("%."PROGNAME_WIDTH_STR"s", prg_cache_get(param->inode));
@@ -419,6 +456,7 @@ static int FAST_FUNC tcp_do_one(char *line)
419{ 456{
420 struct inet_params param; 457 struct inet_params param;
421 458
459 memset(&param, 0, sizeof(param));
422 if (scan_inet_proc_line(&param, line)) 460 if (scan_inet_proc_line(&param, line))
423 return 1; 461 return 1;
424 462
@@ -446,6 +484,7 @@ static int FAST_FUNC udp_do_one(char *line)
446 const char *state_str; 484 const char *state_str;
447 struct inet_params param; 485 struct inet_params param;
448 486
487 memset(&param, 0, sizeof(param)); /* otherwise we display garbage IPv6 scope_ids */
449 if (scan_inet_proc_line(&param, line)) 488 if (scan_inet_proc_line(&param, line))
450 return 1; 489 return 1;
451 490
@@ -617,38 +656,39 @@ static void do_info(const char *file, int FAST_FUNC (*proc)(char *))
617int netstat_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; 656int netstat_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
618int netstat_main(int argc UNUSED_PARAM, char **argv) 657int netstat_main(int argc UNUSED_PARAM, char **argv)
619{ 658{
620 const char *net_conn_line_header = PRINT_NET_CONN_HEADER;
621 unsigned opt; 659 unsigned opt;
622 660
623 INIT_G(); 661 INIT_G();
624 662
625 /* Option string must match NETSTAT_xxx constants */ 663 /* Option string must match NETSTAT_xxx constants */
626 opt = getopt32(argv, NETSTAT_OPTS); 664 opt = getopt32(argv, NETSTAT_OPTS);
627 if (opt & 0x1) { // -l 665 if (opt & OPT_sock_listen) { // -l
628 flags &= ~NETSTAT_CONNECTED; 666 flags &= ~NETSTAT_CONNECTED;
629 flags |= NETSTAT_LISTENING; 667 flags |= NETSTAT_LISTENING;
630 } 668 }
631 if (opt & 0x2) flags |= NETSTAT_LISTENING | NETSTAT_CONNECTED; // -a 669 if (opt & OPT_sock_all) flags |= NETSTAT_LISTENING | NETSTAT_CONNECTED; // -a
632 //if (opt & 0x4) // -e 670 //if (opt & OPT_extended) // -e
633 if (opt & 0x8) flags |= NETSTAT_NUMERIC; // -n 671 if (opt & OPT_noresolve) flags |= NETSTAT_NUMERIC; // -n
634 //if (opt & 0x10) // -t: NETSTAT_TCP 672 //if (opt & OPT_sock_tcp) // -t: NETSTAT_TCP
635 //if (opt & 0x20) // -u: NETSTAT_UDP 673 //if (opt & OPT_sock_udp) // -u: NETSTAT_UDP
636 //if (opt & 0x40) // -w: NETSTAT_RAW 674 //if (opt & OPT_sock_raw) // -w: NETSTAT_RAW
637 //if (opt & 0x80) // -x: NETSTAT_UNIX 675 //if (opt & OPT_sock_unix) // -x: NETSTAT_UNIX
638 if (opt & OPT_route) { // -r
639#if ENABLE_ROUTE 676#if ENABLE_ROUTE
677 if (opt & OPT_route) { // -r
640 bb_displayroutes(flags & NETSTAT_NUMERIC, !(opt & OPT_extended)); 678 bb_displayroutes(flags & NETSTAT_NUMERIC, !(opt & OPT_extended));
641 return 0; 679 return 0;
642#else
643 bb_show_usage();
644#endif
645 } 680 }
681#endif
682#if ENABLE_FEATURE_NETSTAT_WIDE
683 G.addr_width = ADDR_NORMAL_WIDTH;
646 if (opt & OPT_wide) { // -W 684 if (opt & OPT_wide) { // -W
647 net_conn_line = PRINT_NET_CONN_WIDE; 685 G.addr_width = ADDR_WIDE;
648 net_conn_line_header = PRINT_NET_CONN_HEADER_WIDE;
649 } 686 }
687#endif
650#if ENABLE_FEATURE_NETSTAT_PRG 688#if ENABLE_FEATURE_NETSTAT_PRG
689 progname_banner = "";
651 if (opt & OPT_prg) { // -p 690 if (opt & OPT_prg) { // -p
691 progname_banner = PROGNAME_BANNER;
652 prg_cache_load(); 692 prg_cache_load();
653 } 693 }
654#endif 694#endif
@@ -667,9 +707,11 @@ int netstat_main(int argc UNUSED_PARAM, char **argv)
667 printf("(only servers)"); 707 printf("(only servers)");
668 else 708 else
669 printf("(w/o servers)"); 709 printf("(w/o servers)");
670 printf(net_conn_line_header, "Local Address", "Foreign Address"); 710 printf(FMT_NET_CONN_HEADER,
671 print_progname_banner(); 711 IF_FEATURE_NETSTAT_WIDE(G.addr_width,) "Local Address",
672 bb_putchar('\n'); 712 IF_FEATURE_NETSTAT_WIDE(G.addr_width,) "Foreign Address",
713 progname_banner
714 );
673 } 715 }
674 if (flags & NETSTAT_TCP) { 716 if (flags & NETSTAT_TCP) {
675 do_info("/proc/net/tcp", tcp_do_one); 717 do_info("/proc/net/tcp", tcp_do_one);
@@ -697,9 +739,7 @@ int netstat_main(int argc UNUSED_PARAM, char **argv)
697 printf("(only servers)"); 739 printf("(only servers)");
698 else 740 else
699 printf("(w/o servers)"); 741 printf("(w/o servers)");
700 printf("\nProto RefCnt Flags Type State I-Node "); 742 printf("\nProto RefCnt Flags Type State I-Node %sPath\n", progname_banner);
701 print_progname_banner();
702 printf("Path\n");
703 do_info("/proc/net/unix", unix_do_one); 743 do_info("/proc/net/unix", unix_do_one);
704 } 744 }
705 prg_cache_clear(); 745 prg_cache_clear();