1/* vi: set sw=4 ts=4: */ 2/* 3 * Mini netstat implementation(s) for busybox 4 * based in part on the netstat implementation from net-tools. 5 * 6 * Copyright (C) 2002 by Bart Visscher <magick@linux-fan.com> 7 * 8 * 2002-04-20 9 * IPV6 support added by Bart Visscher <magick@linux-fan.com> 10 * 11 * 2008-07-10 12 * optional '-p' flag support ported from net-tools by G. Somlo <somlo@cmu.edu> 13 * 14 * Licensed under GPLv2 or later, see file LICENSE in this tarball for details. 15 */ 16 17#include "libbb.h" 18#include "inet_common.h" 19 20#define NETSTAT_OPTS "laentuwx" \ 21 IF_ROUTE( "r") \ 22 IF_FEATURE_NETSTAT_WIDE("W") \ 23 IF_FEATURE_NETSTAT_PRG( "p") 24 25enum { 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 31 OPT_sock_all = 1 << 1, // a 32 OPT_extended = 1 << 2, // e 33 OPT_noresolve = 1 << 3, // n 34 OPT_sock_tcp = 1 << 4, // t 35 OPT_sock_udp = 1 << 5, // u 36 OPT_sock_raw = 1 << 6, // w 37 OPT_sock_unix = 1 << 7, // x 38 OPT_route = IF_ROUTE( (1 << OPTBIT_ROUTE)) + 0, // r 39 OPT_wide = IF_FEATURE_NETSTAT_WIDE((1 << OPTBIT_WIDE )) + 0, // W 40 OPT_prg = IF_FEATURE_NETSTAT_PRG( (1 << OPTBIT_PRG )) + 0, // p 41}; 42 43#define NETSTAT_CONNECTED 0x01 44#define NETSTAT_LISTENING 0x02 45#define NETSTAT_NUMERIC 0x04 46/* Must match getopt32 option string */ 47#define NETSTAT_TCP 0x10 48#define NETSTAT_UDP 0x20 49#define NETSTAT_RAW 0x40 50#define NETSTAT_UNIX 0x80 51#define NETSTAT_ALLPROTO (NETSTAT_TCP|NETSTAT_UDP|NETSTAT_RAW|NETSTAT_UNIX) 52 53 54enum { 55 TCP_ESTABLISHED = 1, 56 TCP_SYN_SENT, 57 TCP_SYN_RECV, 58 TCP_FIN_WAIT1, 59 TCP_FIN_WAIT2, 60 TCP_TIME_WAIT, 61 TCP_CLOSE, 62 TCP_CLOSE_WAIT, 63 TCP_LAST_ACK, 64 TCP_LISTEN, 65 TCP_CLOSING, /* now a valid state */ 66}; 67 68static const char *const tcp_state[] = { 69 "", 70 "ESTABLISHED", 71 "SYN_SENT", 72 "SYN_RECV", 73 "FIN_WAIT1", 74 "FIN_WAIT2", 75 "TIME_WAIT", 76 "CLOSE", 77 "CLOSE_WAIT", 78 "LAST_ACK", 79 "LISTEN", 80 "CLOSING" 81}; 82 83typedef enum { 84 SS_FREE = 0, /* not allocated */ 85 SS_UNCONNECTED, /* unconnected to any socket */ 86 SS_CONNECTING, /* in process of connecting */ 87 SS_CONNECTED, /* connected to socket */ 88 SS_DISCONNECTING /* in process of disconnecting */ 89} socket_state; 90 91#define SO_ACCEPTCON (1<<16) /* performed a listen */ 92#define SO_WAITDATA (1<<17) /* wait data to read */ 93#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 100/* When there are IPv6 connections the IPv6 addresses will be 101 * truncated to none-recognition. The '-W' option makes the 102 * address columns wide enough to accomodate for longest possible 103 * IPv6 addresses, i.e. addresses of the form 104 * xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:ddd.ddd.ddd.ddd 105 */ 106#define PRINT_IP_MAX_SIZE_WIDE 51 /* INET6_ADDRSTRLEN + 5 for the port number */ 107#define PRINT_NET_CONN_WIDE "%s %6ld %6ld %-51s %-51s %-12s" 108#define PRINT_NET_CONN_HEADER_WIDE "\nProto Recv-Q Send-Q %-51s %-51s State " 109 110#define PROGNAME_WIDTH 20 111#define PROGNAME_WIDTH_STR "20" 112/* PROGNAME_WIDTH chars: 12345678901234567890 */ 113#define PROGNAME_BANNER "PID/Program name " 114 115struct prg_node { 116 struct prg_node *next; 117 long inode; 118 char name[PROGNAME_WIDTH]; 119}; 120 121#define PRG_HASH_SIZE 211 122 123struct globals { 124 const char *net_conn_line; 125 smallint flags; 126#if ENABLE_FEATURE_NETSTAT_PRG 127 smallint prg_cache_loaded; 128 struct prg_node *prg_hash[PRG_HASH_SIZE]; 129#endif 130}; 131#define G (*ptr_to_globals) 132#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) 136#define INIT_G() do { \ 137 SET_PTR_TO_GLOBALS(xzalloc(sizeof(G))); \ 138 flags = NETSTAT_CONNECTED | NETSTAT_ALLPROTO; \ 139 net_conn_line = PRINT_NET_CONN; \ 140} while (0) 141 142 143#if ENABLE_FEATURE_NETSTAT_PRG 144 145/* Deliberately truncating long to unsigned *int* */ 146#define PRG_HASHIT(x) ((unsigned)(x) % PRG_HASH_SIZE) 147 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) 153{ 154 unsigned hi = PRG_HASHIT(inode); 155 struct prg_node **pnp, *pn; 156 157 prg_cache_loaded = 2; 158 for (pnp = prg_hash + hi; (pn = *pnp) != NULL; pnp = &pn->next) { 159 if (pn->inode == inode) { 160 /* Some warning should be appropriate here 161 as we got multiple processes for one i-node */ 162 return; 163 } 164 } 165 *pnp = xzalloc(sizeof(struct prg_node)); 166 pn = *pnp; 167 pn->inode = inode; 168 safe_strncpy(pn->name, name, PROGNAME_WIDTH); 169} 170 171static const char *prg_cache_get(long inode) 172{ 173 unsigned hi = PRG_HASHIT(inode); 174 struct prg_node *pn; 175 176 for (pn = prg_hash[hi]; pn; pn = pn->next) 177 if (pn->inode == inode) 178 return pn->name; 179 return "-"; 180} 181 182#if ENABLE_FEATURE_CLEAN_UP 183static void prg_cache_clear(void) 184{ 185 struct prg_node **pnp, *pn; 186 187 for (pnp = prg_hash; pnp < prg_hash + PRG_HASH_SIZE; pnp++) { 188 while ((pn = *pnp) != NULL) { 189 *pnp = pn->next; 190 free(pn); 191 } 192 } 193} 194#else 195#define prg_cache_clear() ((void)0) 196#endif 197 198static long extract_socket_inode(const char *lname) 199{ 200 long inode = -1; 201 202 if (strncmp(lname, "socket:[", sizeof("socket:[")-1) == 0) { 203 /* "socket:[12345]", extract the "12345" as inode */ 204 inode = bb_strtol(lname + sizeof("socket:[")-1, (char**)&lname, 0); 205 if (*lname != ']') 206 inode = -1; 207 } else if (strncmp(lname, "[0000]:", sizeof("[0000]:")-1) == 0) { 208 /* "[0000]:12345", extract the "12345" as inode */ 209 inode = bb_strtol(lname + sizeof("[0000]:")-1, NULL, 0); 210 if (errno) /* not NUL terminated? */ 211 inode = -1; 212 } 213 214#if 0 /* bb_strtol returns all-ones bit pattern on ERANGE anyway */ 215 if (errno == ERANGE) 216 inode = -1; 217#endif 218 return inode; 219} 220 221static int FAST_FUNC file_act(const char *fileName, 222 struct stat *statbuf UNUSED_PARAM, 223 void *userData, 224 int depth UNUSED_PARAM) 225{ 226 char *linkname; 227 long inode; 228 229 linkname = xmalloc_readlink(fileName); 230 if (linkname != NULL) { 231 inode = extract_socket_inode(linkname); 232 free(linkname); 233 if (inode >= 0) 234 prg_cache_add(inode, (char *)userData); 235 } 236 return TRUE; 237} 238 239static int FAST_FUNC dir_act(const char *fileName, 240 struct stat *statbuf UNUSED_PARAM, 241 void *userData UNUSED_PARAM, 242 int depth) 243{ 244 const char *shortName; 245 char *p, *q; 246 char cmdline_buf[512]; 247 int i; 248 249 if (depth == 0) /* "/proc" itself */ 250 return TRUE; /* continue looking one level below /proc */ 251 252 shortName = fileName + sizeof("/proc/")-1; /* point after "/proc/" */ 253 if (!isdigit(shortName[0])) /* skip /proc entries whic aren't processes */ 254 return SKIP; 255 256 p = concat_path_file(fileName, "cmdline"); /* "/proc/PID/cmdline" */ 257 i = open_read_close(p, cmdline_buf, sizeof(cmdline_buf) - 1); 258 free(p); 259 if (i < 0) 260 return FALSE; 261 cmdline_buf[i] = '\0'; 262 q = concat_path_file(shortName, bb_basename(cmdline_buf)); /* "PID/argv0" */ 263 264 /* go through all files in /proc/PID/fd */ 265 p = concat_path_file(fileName, "fd"); 266 i = recursive_action(p, ACTION_RECURSE | ACTION_QUIET, 267 file_act, NULL, (void *)q, 0); 268 269 free(p); 270 free(q); 271 272 if (!i) 273 return FALSE; /* signal permissions error to caller */ 274 275 return SKIP; /* caller should not recurse further into this dir. */ 276} 277 278static void prg_cache_load(void) 279{ 280 int load_ok; 281 282 prg_cache_loaded = 1; 283 load_ok = recursive_action("/proc", ACTION_RECURSE | ACTION_QUIET, 284 NULL, dir_act, NULL, 0); 285 if (load_ok) 286 return; 287 288 if (prg_cache_loaded == 1) 289 bb_error_msg("can't scan /proc - are you root?"); 290 else 291 bb_error_msg("showing only processes with your user ID"); 292} 293 294#else 295 296#define prg_cache_clear() ((void)0) 297#define print_progname_banner() ((void)0) 298 299#endif //ENABLE_FEATURE_NETSTAT_PRG 300 301 302#if ENABLE_FEATURE_IPV6 303static void build_ipv6_addr(char* local_addr, struct sockaddr_in6* localaddr) 304{ 305 char addr6[INET6_ADDRSTRLEN]; 306 struct in6_addr in6; 307 308 sscanf(local_addr, "%08X%08X%08X%08X", 309 &in6.s6_addr32[0], &in6.s6_addr32[1], 310 &in6.s6_addr32[2], &in6.s6_addr32[3]); 311 inet_ntop(AF_INET6, &in6, addr6, sizeof(addr6)); 312 inet_pton(AF_INET6, addr6, &localaddr->sin6_addr); 313 314 localaddr->sin6_family = AF_INET6; 315} 316#endif 317 318static void build_ipv4_addr(char* local_addr, struct sockaddr_in* localaddr) 319{ 320 sscanf(local_addr, "%X", &localaddr->sin_addr.s_addr); 321 localaddr->sin_family = AF_INET; 322} 323 324static const char *get_sname(int port, const char *proto, int numeric) 325{ 326 if (!port) 327 return "*"; 328 if (!numeric) { 329 struct servent *se = getservbyport(port, proto); 330 if (se) 331 return se->s_name; 332 } 333 /* hummm, we may return static buffer here!! */ 334 return itoa(ntohs(port)); 335} 336 337static char *ip_port_str(struct sockaddr *addr, int port, const char *proto, int numeric) 338{ 339 char *host, *host_port; 340 341 /* Code which used "*" for INADDR_ANY is removed: it's ambiguous 342 * in IPv6, while "0.0.0.0" is not. */ 343 344 host = numeric ? xmalloc_sockaddr2dotted_noport(addr) 345 : xmalloc_sockaddr2host_noport(addr); 346 347 host_port = xasprintf("%s:%s", host, get_sname(htons(port), proto, numeric)); 348 free(host); 349 return host_port; 350} 351 352struct inet_params { 353 int local_port, rem_port, state, uid; 354 union { 355 struct sockaddr sa; 356 struct sockaddr_in sin; 357#if ENABLE_FEATURE_IPV6 358 struct sockaddr_in6 sin6; 359#endif 360 } localaddr, remaddr; 361 unsigned long rxq, txq, inode; 362}; 363 364static int scan_inet_proc_line(struct inet_params *param, char *line) 365{ 366 int num; 367 char local_addr[64], rem_addr[64]; 368 369 num = sscanf(line, 370 "%*d: %64[0-9A-Fa-f]:%X " 371 "%64[0-9A-Fa-f]:%X %X " 372 "%lX:%lX %*X:%*X " 373 "%*X %d %*d %ld ", 374 local_addr, ¶m->local_port, 375 rem_addr, ¶m->rem_port, ¶m->state, 376 ¶m->txq, ¶m->rxq, 377 ¶m->uid, ¶m->inode); 378 if (num < 9) { 379 return 1; /* error */ 380 } 381 382 if (strlen(local_addr) > 8) { 383#if ENABLE_FEATURE_IPV6 384 build_ipv6_addr(local_addr, ¶m->localaddr.sin6); 385 build_ipv6_addr(rem_addr, ¶m->remaddr.sin6); 386#endif 387 } else { 388 build_ipv4_addr(local_addr, ¶m->localaddr.sin); 389 build_ipv4_addr(rem_addr, ¶m->remaddr.sin); 390 } 391 return 0; 392} 393 394static void print_inet_line(struct inet_params *param, 395 const char *state_str, const char *proto, int is_connected) 396{ 397 if ((is_connected && (flags & NETSTAT_CONNECTED)) 398 || (!is_connected && (flags & NETSTAT_LISTENING)) 399 ) { 400 char *l = ip_port_str( 401 ¶m->localaddr.sa, param->local_port, 402 proto, flags & NETSTAT_NUMERIC); 403 char *r = ip_port_str( 404 ¶m->remaddr.sa, param->rem_port, 405 proto, flags & NETSTAT_NUMERIC); 406 printf(net_conn_line, 407 proto, param->rxq, param->txq, l, r, state_str); 408#if ENABLE_FEATURE_NETSTAT_PRG 409 if (option_mask32 & OPT_prg) 410 printf("%."PROGNAME_WIDTH_STR"s", prg_cache_get(param->inode)); 411#endif 412 bb_putchar('\n'); 413 free(l); 414 free(r); 415 } 416} 417 418static int FAST_FUNC tcp_do_one(char *line) 419{ 420 struct inet_params param; 421 422 if (scan_inet_proc_line(¶m, line)) 423 return 1; 424 425 print_inet_line(¶m, tcp_state[param.state], "tcp", param.rem_port); 426 return 0; 427} 428 429#if ENABLE_FEATURE_IPV6 430# define NOT_NULL_ADDR(A) ( \ 431 ( (A.sa.sa_family == AF_INET6) \ 432 && (A.sin6.sin6_addr.s6_addr32[0] | A.sin6.sin6_addr.s6_addr32[1] | \ 433 A.sin6.sin6_addr.s6_addr32[2] | A.sin6.sin6_addr.s6_addr32[3]) \ 434 ) || ( \ 435 (A.sa.sa_family == AF_INET) \ 436 && A.sin.sin_addr.s_addr != 0 \ 437 ) \ 438) 439#else 440# define NOT_NULL_ADDR(A) (A.sin.sin_addr.s_addr) 441#endif 442 443static int FAST_FUNC udp_do_one(char *line) 444{ 445 int have_remaddr; 446 const char *state_str; 447 struct inet_params param; 448 449 if (scan_inet_proc_line(¶m, line)) 450 return 1; 451 452 state_str = "UNKNOWN"; 453 switch (param.state) { 454 case TCP_ESTABLISHED: 455 state_str = "ESTABLISHED"; 456 break; 457 case TCP_CLOSE: 458 state_str = ""; 459 break; 460 } 461 462 have_remaddr = NOT_NULL_ADDR(param.remaddr); 463 print_inet_line(¶m, state_str, "udp", have_remaddr); 464 return 0; 465} 466 467static int FAST_FUNC raw_do_one(char *line) 468{ 469 int have_remaddr; 470 struct inet_params param; 471 472 if (scan_inet_proc_line(¶m, line)) 473 return 1; 474 475 have_remaddr = NOT_NULL_ADDR(param.remaddr); 476 print_inet_line(¶m, itoa(param.state), "raw", have_remaddr); 477 return 0; 478} 479 480static int FAST_FUNC unix_do_one(char *line) 481{ 482 unsigned long refcnt, proto, unix_flags; 483 unsigned long inode; 484 int type, state; 485 int num, path_ofs; 486 const char *ss_proto, *ss_state, *ss_type; 487 char ss_flags[32]; 488 489 /* 2.6.15 may report lines like "... @/tmp/fam-user-^@^@^@^@^@^@^@..." 490 * Other users report long lines filled by NUL bytes. 491 * (those ^@ are NUL bytes too). We see them as empty lines. */ 492 if (!line[0]) 493 return 0; 494 495 path_ofs = 0; /* paranoia */ 496 num = sscanf(line, "%*p: %lX %lX %lX %X %X %lu %n", 497 &refcnt, &proto, &unix_flags, &type, &state, &inode, &path_ofs); 498 if (num < 6) { 499 return 1; /* error */ 500 } 501 if ((flags & (NETSTAT_LISTENING|NETSTAT_CONNECTED)) != (NETSTAT_LISTENING|NETSTAT_CONNECTED)) { 502 if ((state == SS_UNCONNECTED) && (unix_flags & SO_ACCEPTCON)) { 503 if (!(flags & NETSTAT_LISTENING)) 504 return 0; 505 } else { 506 if (!(flags & NETSTAT_CONNECTED)) 507 return 0; 508 } 509 } 510 511 switch (proto) { 512 case 0: 513 ss_proto = "unix"; 514 break; 515 default: 516 ss_proto = "??"; 517 } 518 519 switch (type) { 520 case SOCK_STREAM: 521 ss_type = "STREAM"; 522 break; 523 case SOCK_DGRAM: 524 ss_type = "DGRAM"; 525 break; 526 case SOCK_RAW: 527 ss_type = "RAW"; 528 break; 529 case SOCK_RDM: 530 ss_type = "RDM"; 531 break; 532 case SOCK_SEQPACKET: 533 ss_type = "SEQPACKET"; 534 break; 535 default: 536 ss_type = "UNKNOWN"; 537 } 538 539 switch (state) { 540 case SS_FREE: 541 ss_state = "FREE"; 542 break; 543 case SS_UNCONNECTED: 544 /* 545 * Unconnected sockets may be listening 546 * for something. 547 */ 548 if (unix_flags & SO_ACCEPTCON) { 549 ss_state = "LISTENING"; 550 } else { 551 ss_state = ""; 552 } 553 break; 554 case SS_CONNECTING: 555 ss_state = "CONNECTING"; 556 break; 557 case SS_CONNECTED: 558 ss_state = "CONNECTED"; 559 break; 560 case SS_DISCONNECTING: 561 ss_state = "DISCONNECTING"; 562 break; 563 default: 564 ss_state = "UNKNOWN"; 565 } 566 567 strcpy(ss_flags, "[ "); 568 if (unix_flags & SO_ACCEPTCON) 569 strcat(ss_flags, "ACC "); 570 if (unix_flags & SO_WAITDATA) 571 strcat(ss_flags, "W "); 572 if (unix_flags & SO_NOSPACE) 573 strcat(ss_flags, "N "); 574 strcat(ss_flags, "]"); 575 576 printf("%-5s %-6ld %-11s %-10s %-13s %6lu ", 577 ss_proto, refcnt, ss_flags, ss_type, ss_state, inode 578 ); 579 580#if ENABLE_FEATURE_NETSTAT_PRG 581 if (option_mask32 & OPT_prg) 582 printf("%-"PROGNAME_WIDTH_STR"s", prg_cache_get(inode)); 583#endif 584 585 /* TODO: currently we stop at first NUL byte. Is it a problem? */ 586 line += path_ofs; 587 *strchrnul(line, '\n') = '\0'; 588 while (*line) 589 fputc_printable(*line++, stdout); 590 bb_putchar('\n'); 591 return 0; 592} 593 594static void do_info(const char *file, int FAST_FUNC (*proc)(char *)) 595{ 596 int lnr; 597 FILE *procinfo; 598 char *buffer; 599 600 /* _stdin is just to save "r" param */ 601 procinfo = fopen_or_warn_stdin(file); 602 if (procinfo == NULL) { 603 return; 604 } 605 lnr = 0; 606 /* Why xmalloc_fgets_str? because it doesn't stop on NULs */ 607 while ((buffer = xmalloc_fgets_str(procinfo, "\n")) != NULL) { 608 /* line 0 is skipped */ 609 if (lnr && proc(buffer)) 610 bb_error_msg("%s: bogus data on line %d", file, lnr + 1); 611 lnr++; 612 free(buffer); 613 } 614 fclose(procinfo); 615} 616 617int netstat_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; 618int netstat_main(int argc UNUSED_PARAM, char **argv) 619{ 620 const char *net_conn_line_header = PRINT_NET_CONN_HEADER; 621 unsigned opt; 622 623 INIT_G(); 624 625 /* Option string must match NETSTAT_xxx constants */ 626 opt = getopt32(argv, NETSTAT_OPTS); 627 if (opt & 0x1) { // -l 628 flags &= ~NETSTAT_CONNECTED; 629 flags |= NETSTAT_LISTENING; 630 } 631 if (opt & 0x2) flags |= NETSTAT_LISTENING | NETSTAT_CONNECTED; // -a 632 //if (opt & 0x4) // -e 633 if (opt & 0x8) flags |= NETSTAT_NUMERIC; // -n 634 //if (opt & 0x10) // -t: NETSTAT_TCP 635 //if (opt & 0x20) // -u: NETSTAT_UDP 636 //if (opt & 0x40) // -w: NETSTAT_RAW 637 //if (opt & 0x80) // -x: NETSTAT_UNIX 638 if (opt & OPT_route) { // -r 639#if ENABLE_ROUTE 640 bb_displayroutes(flags & NETSTAT_NUMERIC, !(opt & OPT_extended)); 641 return 0; 642#else 643 bb_show_usage(); 644#endif 645 } 646 if (opt & OPT_wide) { // -W 647 net_conn_line = PRINT_NET_CONN_WIDE; 648 net_conn_line_header = PRINT_NET_CONN_HEADER_WIDE; 649 } 650#if ENABLE_FEATURE_NETSTAT_PRG 651 if (opt & OPT_prg) { // -p 652 prg_cache_load(); 653 } 654#endif 655 656 opt &= NETSTAT_ALLPROTO; 657 if (opt) { 658 flags &= ~NETSTAT_ALLPROTO; 659 flags |= opt; 660 } 661 if (flags & (NETSTAT_TCP|NETSTAT_UDP|NETSTAT_RAW)) { 662 printf("Active Internet connections "); /* xxx */ 663 664 if ((flags & (NETSTAT_LISTENING|NETSTAT_CONNECTED)) == (NETSTAT_LISTENING|NETSTAT_CONNECTED)) 665 printf("(servers and established)"); 666 else if (flags & NETSTAT_LISTENING) 667 printf("(only servers)"); 668 else 669 printf("(w/o servers)"); 670 printf(net_conn_line_header, "Local Address", "Foreign Address"); 671 print_progname_banner(); 672 bb_putchar('\n'); 673 } 674 if (flags & NETSTAT_TCP) { 675 do_info("/proc/net/tcp", tcp_do_one); 676#if ENABLE_FEATURE_IPV6 677 do_info("/proc/net/tcp6", tcp_do_one); 678#endif 679 } 680 if (flags & NETSTAT_UDP) { 681 do_info("/proc/net/udp", udp_do_one); 682#if ENABLE_FEATURE_IPV6 683 do_info("/proc/net/udp6", udp_do_one); 684#endif 685 } 686 if (flags & NETSTAT_RAW) { 687 do_info("/proc/net/raw", raw_do_one); 688#if ENABLE_FEATURE_IPV6 689 do_info("/proc/net/raw6", raw_do_one); 690#endif 691 } 692 if (flags & NETSTAT_UNIX) { 693 printf("Active UNIX domain sockets "); 694 if ((flags & (NETSTAT_LISTENING|NETSTAT_CONNECTED)) == (NETSTAT_LISTENING|NETSTAT_CONNECTED)) 695 printf("(servers and established)"); 696 else if (flags & NETSTAT_LISTENING) 697 printf("(only servers)"); 698 else 699 printf("(w/o servers)"); 700 printf("\nProto RefCnt Flags Type State I-Node "); 701 print_progname_banner(); 702 printf("Path\n"); 703 do_info("/proc/net/unix", unix_do_one); 704 } 705 prg_cache_clear(); 706 return 0; 707} 708