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 * Licensed under GPLv2 or later, see file LICENSE in this tarball for details. 12 */ 13 14#include "libbb.h" 15#include "inet_common.h" 16 17enum { 18 OPT_extended = 0x4, 19 OPT_showroute = 0x100, 20 OPT_widedisplay = 0x200 * ENABLE_FEATURE_NETSTAT_WIDE, 21}; 22# define NETSTAT_OPTS "laentuwxr"USE_FEATURE_NETSTAT_WIDE("W") 23 24#define NETSTAT_CONNECTED 0x01 25#define NETSTAT_LISTENING 0x02 26#define NETSTAT_NUMERIC 0x04 27/* Must match getopt32 option string */ 28#define NETSTAT_TCP 0x10 29#define NETSTAT_UDP 0x20 30#define NETSTAT_RAW 0x40 31#define NETSTAT_UNIX 0x80 32#define NETSTAT_ALLPROTO (NETSTAT_TCP|NETSTAT_UDP|NETSTAT_RAW|NETSTAT_UNIX) 33 34static smallint flags = NETSTAT_CONNECTED | NETSTAT_ALLPROTO; 35 36enum { 37 TCP_ESTABLISHED = 1, 38 TCP_SYN_SENT, 39 TCP_SYN_RECV, 40 TCP_FIN_WAIT1, 41 TCP_FIN_WAIT2, 42 TCP_TIME_WAIT, 43 TCP_CLOSE, 44 TCP_CLOSE_WAIT, 45 TCP_LAST_ACK, 46 TCP_LISTEN, 47 TCP_CLOSING /* now a valid state */ 48}; 49 50static const char *const tcp_state[] = { 51 "", 52 "ESTABLISHED", 53 "SYN_SENT", 54 "SYN_RECV", 55 "FIN_WAIT1", 56 "FIN_WAIT2", 57 "TIME_WAIT", 58 "CLOSE", 59 "CLOSE_WAIT", 60 "LAST_ACK", 61 "LISTEN", 62 "CLOSING" 63}; 64 65typedef enum { 66 SS_FREE = 0, /* not allocated */ 67 SS_UNCONNECTED, /* unconnected to any socket */ 68 SS_CONNECTING, /* in process of connecting */ 69 SS_CONNECTED, /* connected to socket */ 70 SS_DISCONNECTING /* in process of disconnecting */ 71} socket_state; 72 73#define SO_ACCEPTCON (1<<16) /* performed a listen */ 74#define SO_WAITDATA (1<<17) /* wait data to read */ 75#define SO_NOSPACE (1<<18) /* no space to write */ 76 77/* Standard printout size */ 78#define PRINT_IP_MAX_SIZE 23 79#define PRINT_NET_CONN "%s %6ld %6ld %-23s %-23s %-12s\n" 80#define PRINT_NET_CONN_HEADER "\nProto Recv-Q Send-Q %-23s %-23s State\n" 81 82#define PRINT_IP_MAX_SIZE_WIDE 51 /* INET6_ADDRSTRLEN + 5 for the port number */ 83#define PRINT_NET_CONN_WIDE "%s %6ld %6ld %-51s %-51s %-12s\n" 84#define PRINT_NET_CONN_HEADER_WIDE "\nProto Recv-Q Send-Q %-51s %-51s State\n" 85 86static const char *net_conn_line = PRINT_NET_CONN; 87 88 89#if ENABLE_FEATURE_IPV6 90static void build_ipv6_addr(char* local_addr, struct sockaddr_in6* localaddr) 91{ 92 char addr6[INET6_ADDRSTRLEN]; 93 struct in6_addr in6; 94 95 sscanf(local_addr, "%08X%08X%08X%08X", 96 &in6.s6_addr32[0], &in6.s6_addr32[1], 97 &in6.s6_addr32[2], &in6.s6_addr32[3]); 98 inet_ntop(AF_INET6, &in6, addr6, sizeof(addr6)); 99 inet_pton(AF_INET6, addr6, (struct sockaddr *) &localaddr->sin6_addr); 100 101 localaddr->sin6_family = AF_INET6; 102} 103#endif 104 105#if ENABLE_FEATURE_IPV6 106static void build_ipv4_addr(char* local_addr, struct sockaddr_in6* localaddr) 107#else 108static void build_ipv4_addr(char* local_addr, struct sockaddr_in* localaddr) 109#endif 110{ 111 sscanf(local_addr, "%X", 112 &((struct sockaddr_in *) localaddr)->sin_addr.s_addr); 113 ((struct sockaddr *) localaddr)->sa_family = AF_INET; 114} 115 116static const char *get_sname(int port, const char *proto, int numeric) 117{ 118 if (!port) 119 return "*"; 120 if (!numeric) { 121 struct servent *se = getservbyport(port, proto); 122 if (se) 123 return se->s_name; 124 } 125 /* hummm, we may return static buffer here!! */ 126 return itoa(ntohs(port)); 127} 128 129static char *ip_port_str(struct sockaddr *addr, int port, const char *proto, int numeric) 130{ 131 enum { salen = USE_FEATURE_IPV6(sizeof(struct sockaddr_in6)) SKIP_FEATURE_IPV6(sizeof(struct sockaddr_in)) }; 132 char *host, *host_port; 133 134 /* Code which used "*" for INADDR_ANY is removed: it's ambiguous in IPv6, 135 * while "0.0.0.0" is not. */ 136 137 host = numeric ? xmalloc_sockaddr2dotted_noport(addr) 138 : xmalloc_sockaddr2host_noport(addr); 139 140 host_port = xasprintf("%s:%s", host, get_sname(htons(port), proto, numeric)); 141 free(host); 142 return host_port; 143} 144 145static void tcp_do_one(int lnr, const char *line) 146{ 147 char local_addr[64], rem_addr[64]; 148 char more[512]; 149 int num, local_port, rem_port, d, state, timer_run, uid, timeout; 150#if ENABLE_FEATURE_IPV6 151 struct sockaddr_in6 localaddr, remaddr; 152#else 153 struct sockaddr_in localaddr, remaddr; 154#endif 155 unsigned long rxq, txq, time_len, retr, inode; 156 157 if (lnr == 0) 158 return; 159 160 more[0] = '\0'; 161 num = sscanf(line, 162 "%d: %64[0-9A-Fa-f]:%X %64[0-9A-Fa-f]:%X %X %lX:%lX %X:%lX %lX %d %d %ld %512s\n", 163 &d, local_addr, &local_port, 164 rem_addr, &rem_port, &state, 165 &txq, &rxq, &timer_run, &time_len, &retr, &uid, &timeout, &inode, more); 166 167 if (strlen(local_addr) > 8) { 168#if ENABLE_FEATURE_IPV6 169 build_ipv6_addr(local_addr, &localaddr); 170 build_ipv6_addr(rem_addr, &remaddr); 171#endif 172 } else { 173 build_ipv4_addr(local_addr, &localaddr); 174 build_ipv4_addr(rem_addr, &remaddr); 175 } 176 177 if (num < 10) { 178 bb_error_msg("warning, got bogus tcp line"); 179 return; 180 } 181 182 if ((rem_port && (flags & NETSTAT_CONNECTED)) 183 || (!rem_port && (flags & NETSTAT_LISTENING)) 184 ) { 185 char *l = ip_port_str( 186 (struct sockaddr *) &localaddr, local_port, 187 "tcp", flags & NETSTAT_NUMERIC); 188 char *r = ip_port_str( 189 (struct sockaddr *) &remaddr, rem_port, 190 "tcp", flags & NETSTAT_NUMERIC); 191 printf(net_conn_line, 192 "tcp", rxq, txq, l, r, tcp_state[state]); 193 free(l); 194 free(r); 195 } 196} 197 198static void udp_do_one(int lnr, const char *line) 199{ 200 char local_addr[64], rem_addr[64]; 201 const char *state_str; 202 char more[512]; 203 int num, local_port, rem_port, d, state, timer_run, uid, timeout; 204#if ENABLE_FEATURE_IPV6 205 struct sockaddr_in6 localaddr, remaddr; 206#else 207 struct sockaddr_in localaddr, remaddr; 208#endif 209 unsigned long rxq, txq, time_len, retr, inode; 210 211 if (lnr == 0) 212 return; 213 214 more[0] = '\0'; 215 num = sscanf(line, 216 "%d: %64[0-9A-Fa-f]:%X %64[0-9A-Fa-f]:%X %X %lX:%lX %X:%lX %lX %d %d %ld %512s\n", 217 &d, local_addr, &local_port, 218 rem_addr, &rem_port, &state, 219 &txq, &rxq, &timer_run, &time_len, &retr, &uid, &timeout, &inode, more); 220 221 if (strlen(local_addr) > 8) { 222#if ENABLE_FEATURE_IPV6 223 /* Demangle what the kernel gives us */ 224 build_ipv6_addr(local_addr, &localaddr); 225 build_ipv6_addr(rem_addr, &remaddr); 226#endif 227 } else { 228 build_ipv4_addr(local_addr, &localaddr); 229 build_ipv4_addr(rem_addr, &remaddr); 230 } 231 232 if (num < 10) { 233 bb_error_msg("warning, got bogus udp line"); 234 return; 235 } 236 switch (state) { 237 case TCP_ESTABLISHED: 238 state_str = "ESTABLISHED"; 239 break; 240 case TCP_CLOSE: 241 state_str = ""; 242 break; 243 default: 244 state_str = "UNKNOWN"; 245 break; 246 } 247 248#if ENABLE_FEATURE_IPV6 249# define notnull(A) ( \ 250 ( (A.sin6_family == AF_INET6) \ 251 && (A.sin6_addr.s6_addr32[0] | A.sin6_addr.s6_addr32[1] | \ 252 A.sin6_addr.s6_addr32[2] | A.sin6_addr.s6_addr32[3]) \ 253 ) || ( \ 254 (A.sin6_family == AF_INET) \ 255 && ((struct sockaddr_in*)&A)->sin_addr.s_addr \ 256 ) \ 257) 258#else 259# define notnull(A) (A.sin_addr.s_addr) 260#endif 261 { 262 int have_remaddr = notnull(remaddr); 263 if ((have_remaddr && (flags & NETSTAT_CONNECTED)) 264 || (!have_remaddr && (flags & NETSTAT_LISTENING)) 265 ) { 266 char *l = ip_port_str( 267 (struct sockaddr *) &localaddr, local_port, 268 "udp", flags & NETSTAT_NUMERIC); 269 char *r = ip_port_str( 270 (struct sockaddr *) &remaddr, rem_port, 271 "udp", flags & NETSTAT_NUMERIC); 272 printf(net_conn_line, 273 "udp", rxq, txq, l, r, state_str); 274 free(l); 275 free(r); 276 } 277 } 278} 279 280static void raw_do_one(int lnr, const char *line) 281{ 282 char local_addr[64], rem_addr[64]; 283 char more[512]; 284 int num, local_port, rem_port, d, state, timer_run, uid, timeout; 285#if ENABLE_FEATURE_IPV6 286 struct sockaddr_in6 localaddr, remaddr; 287#else 288 struct sockaddr_in localaddr, remaddr; 289#endif 290 unsigned long rxq, txq, time_len, retr, inode; 291 292 if (lnr == 0) 293 return; 294 295 more[0] = '\0'; 296 num = sscanf(line, 297 "%d: %64[0-9A-Fa-f]:%X %64[0-9A-Fa-f]:%X %X %lX:%lX %X:%lX %lX %d %d %ld %512s\n", 298 &d, local_addr, &local_port, 299 rem_addr, &rem_port, &state, 300 &txq, &rxq, &timer_run, &time_len, &retr, &uid, &timeout, &inode, more); 301 302 if (strlen(local_addr) > 8) { 303#if ENABLE_FEATURE_IPV6 304 build_ipv6_addr(local_addr, &localaddr); 305 build_ipv6_addr(rem_addr, &remaddr); 306#endif 307 } else { 308 build_ipv4_addr(local_addr, &localaddr); 309 build_ipv4_addr(rem_addr, &remaddr); 310 } 311 312 if (num < 10) { 313 bb_error_msg("warning, got bogus raw line"); 314 return; 315 } 316 317 { 318 int have_remaddr = notnull(remaddr); 319 if ((have_remaddr && (flags & NETSTAT_CONNECTED)) 320 || (!have_remaddr && (flags & NETSTAT_LISTENING)) 321 ) { 322 char *l = ip_port_str( 323 (struct sockaddr *) &localaddr, local_port, 324 "raw", flags & NETSTAT_NUMERIC); 325 char *r = ip_port_str( 326 (struct sockaddr *) &remaddr, rem_port, 327 "raw", flags & NETSTAT_NUMERIC); 328 printf(net_conn_line, 329 "raw", rxq, txq, l, r, itoa(state)); 330 free(l); 331 free(r); 332 } 333 } 334} 335 336static void unix_do_one(int nr, const char *line) 337{ 338 static smallint has_inode = 0; 339 340 char path[PATH_MAX], ss_flags[32]; 341 const char *ss_proto, *ss_state, *ss_type; 342 int num, state, type, inode; 343 void *d; 344 unsigned long refcnt, proto, unix_flags; 345 346 if (nr == 0) { 347 if (strstr(line, "Inode")) 348 has_inode = 1; 349 return; 350 } 351 path[0] = '\0'; 352 num = sscanf(line, "%p: %lX %lX %lX %X %X %d %s", 353 &d, &refcnt, &proto, &unix_flags, &type, &state, &inode, path); 354 if (num < 6) { 355 bb_error_msg("warning, got bogus unix line"); 356 return; 357 } 358 if (!has_inode) 359 sprintf(path, "%d", inode); 360 361 if ((flags & (NETSTAT_LISTENING|NETSTAT_CONNECTED)) != (NETSTAT_LISTENING|NETSTAT_CONNECTED)) { 362 if ((state == SS_UNCONNECTED) && (unix_flags & SO_ACCEPTCON)) { 363 if (!(flags & NETSTAT_LISTENING)) 364 return; 365 } else { 366 if (!(flags & NETSTAT_CONNECTED)) 367 return; 368 } 369 } 370 371 switch (proto) { 372 case 0: 373 ss_proto = "unix"; 374 break; 375 default: 376 ss_proto = "??"; 377 } 378 379 switch (type) { 380 case SOCK_STREAM: 381 ss_type = "STREAM"; 382 break; 383 case SOCK_DGRAM: 384 ss_type = "DGRAM"; 385 break; 386 case SOCK_RAW: 387 ss_type = "RAW"; 388 break; 389 case SOCK_RDM: 390 ss_type = "RDM"; 391 break; 392 case SOCK_SEQPACKET: 393 ss_type = "SEQPACKET"; 394 break; 395 default: 396 ss_type = "UNKNOWN"; 397 } 398 399 switch (state) { 400 case SS_FREE: 401 ss_state = "FREE"; 402 break; 403 case SS_UNCONNECTED: 404 /* 405 * Unconnected sockets may be listening 406 * for something. 407 */ 408 if (unix_flags & SO_ACCEPTCON) { 409 ss_state = "LISTENING"; 410 } else { 411 ss_state = ""; 412 } 413 break; 414 case SS_CONNECTING: 415 ss_state = "CONNECTING"; 416 break; 417 case SS_CONNECTED: 418 ss_state = "CONNECTED"; 419 break; 420 case SS_DISCONNECTING: 421 ss_state = "DISCONNECTING"; 422 break; 423 default: 424 ss_state = "UNKNOWN"; 425 } 426 427 strcpy(ss_flags, "[ "); 428 if (unix_flags & SO_ACCEPTCON) 429 strcat(ss_flags, "ACC "); 430 if (unix_flags & SO_WAITDATA) 431 strcat(ss_flags, "W "); 432 if (unix_flags & SO_NOSPACE) 433 strcat(ss_flags, "N "); 434 strcat(ss_flags, "]"); 435 436 printf("%-5s %-6ld %-11s %-10s %-13s ", 437 ss_proto, refcnt, ss_flags, ss_type, ss_state); 438 if (has_inode) 439 printf("%-6d ", inode); 440 else 441 printf("- "); 442 puts(path); 443} 444 445#define _PATH_PROCNET_UDP "/proc/net/udp" 446#define _PATH_PROCNET_UDP6 "/proc/net/udp6" 447#define _PATH_PROCNET_TCP "/proc/net/tcp" 448#define _PATH_PROCNET_TCP6 "/proc/net/tcp6" 449#define _PATH_PROCNET_RAW "/proc/net/raw" 450#define _PATH_PROCNET_RAW6 "/proc/net/raw6" 451#define _PATH_PROCNET_UNIX "/proc/net/unix" 452 453static void do_info(const char *file, const char *name, void (*proc)(int, const char *)) 454{ 455 int lnr = 0; 456 FILE *procinfo; 457 458 procinfo = fopen(file, "r"); 459 if (procinfo == NULL) { 460 if (errno != ENOENT) { 461 bb_perror_msg("%s", file); 462 } else { 463 bb_error_msg("no support for '%s' on this system", name); 464 } 465 return; 466 } 467 do { 468 char *buffer = xmalloc_fgets(procinfo); 469 if (buffer) { 470 (proc)(lnr++, buffer); 471 free(buffer); 472 } 473 } while (!feof(procinfo)); 474 fclose(procinfo); 475} 476 477/* 478 * Our main function. 479 */ 480 481int netstat_main(int argc, char **argv); 482int netstat_main(int argc, char **argv) 483{ 484 const char *net_conn_line_header = PRINT_NET_CONN_HEADER; 485 unsigned opt; 486#if ENABLE_FEATURE_IPV6 487 smallint inet = 1; 488 smallint inet6 = 1; 489#else 490 enum { inet = 1, inet6 = 0 }; 491#endif 492 493 /* Option string must match NETSTAT_xxx constants */ 494 opt = getopt32(argv, NETSTAT_OPTS); 495 if (opt & 0x1) { // -l 496 flags &= ~NETSTAT_CONNECTED; 497 flags |= NETSTAT_LISTENING; 498 } 499 if (opt & 0x2) flags |= NETSTAT_LISTENING | NETSTAT_CONNECTED; // -a 500 //if (opt & 0x4) // -e 501 if (opt & 0x8) flags |= NETSTAT_NUMERIC; // -n 502 //if (opt & 0x10) // -t: NETSTAT_TCP 503 //if (opt & 0x20) // -u: NETSTAT_UDP 504 //if (opt & 0x40) // -w: NETSTAT_RAW 505 //if (opt & 0x80) // -x: NETSTAT_UNIX 506 if (opt & OPT_showroute) { // -r 507#if ENABLE_ROUTE 508 bb_displayroutes(flags & NETSTAT_NUMERIC, !(opt & OPT_extended)); 509 return 0; 510#else 511 bb_show_usage(); 512#endif 513 } 514 515 if (opt & OPT_widedisplay) { // -W 516 net_conn_line = PRINT_NET_CONN_WIDE; 517 net_conn_line_header = PRINT_NET_CONN_HEADER_WIDE; 518 } 519 520 opt &= NETSTAT_ALLPROTO; 521 if (opt) { 522 flags &= ~NETSTAT_ALLPROTO; 523 flags |= opt; 524 } 525 if (flags & (NETSTAT_TCP|NETSTAT_UDP|NETSTAT_RAW)) { 526 printf("Active Internet connections "); 527 528 if ((flags & (NETSTAT_LISTENING|NETSTAT_CONNECTED)) == (NETSTAT_LISTENING|NETSTAT_CONNECTED)) 529 printf("(servers and established)"); 530 else if (flags & NETSTAT_LISTENING) 531 printf("(only servers)"); 532 else 533 printf("(w/o servers)"); 534 printf(net_conn_line_header, "Local Address", "Foreign Address"); 535 } 536 if (inet && flags & NETSTAT_TCP) 537 do_info(_PATH_PROCNET_TCP, "AF INET (tcp)", tcp_do_one); 538#if ENABLE_FEATURE_IPV6 539 if (inet6 && flags & NETSTAT_TCP) 540 do_info(_PATH_PROCNET_TCP6, "AF INET6 (tcp)", tcp_do_one); 541#endif 542 if (inet && flags & NETSTAT_UDP) 543 do_info(_PATH_PROCNET_UDP, "AF INET (udp)", udp_do_one); 544#if ENABLE_FEATURE_IPV6 545 if (inet6 && flags & NETSTAT_UDP) 546 do_info(_PATH_PROCNET_UDP6, "AF INET6 (udp)", udp_do_one); 547#endif 548 if (inet && flags & NETSTAT_RAW) 549 do_info(_PATH_PROCNET_RAW, "AF INET (raw)", raw_do_one); 550#if ENABLE_FEATURE_IPV6 551 if (inet6 && flags & NETSTAT_RAW) 552 do_info(_PATH_PROCNET_RAW6, "AF INET6 (raw)", raw_do_one); 553#endif 554 if (flags & NETSTAT_UNIX) { 555 printf("Active UNIX domain sockets "); 556 if ((flags & (NETSTAT_LISTENING|NETSTAT_CONNECTED)) == (NETSTAT_LISTENING|NETSTAT_CONNECTED)) 557 printf("(servers and established)"); 558 else if (flags & NETSTAT_LISTENING) 559 printf("(only servers)"); 560 else 561 printf("(w/o servers)"); 562 printf("\nProto RefCnt Flags Type State I-Node Path\n"); 563 do_info(_PATH_PROCNET_UNIX, "AF UNIX", unix_do_one); 564 } 565 return 0; 566} 567