1 /* vi: set sw=4 ts=4: */
3 * Mini netstat implementation(s) for busybox
4 * based in part on the netstat implementation from net-tools.
6 * Copyright (C) 2002 by Bart Visscher <magick@linux-fan.com>
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * General Public License for more details.
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
23 * IPV6 support added by Bart Visscher <magick@linux-fan.com>
35 #include "inet_common.h"
40 extern void displayroutes(int noresolve, int netstatfmt);
43 #define NETSTAT_CONNECTED 0x01
44 #define NETSTAT_LISTENING 0x02
45 #define NETSTAT_NUMERIC 0x04
46 #define NETSTAT_TCP 0x10
47 #define NETSTAT_UDP 0x20
48 #define NETSTAT_RAW 0x40
49 #define NETSTAT_UNIX 0x80
51 static int flags = NETSTAT_CONNECTED |
52 NETSTAT_TCP | NETSTAT_UDP | NETSTAT_RAW | NETSTAT_UNIX;
54 #define PROGNAME_WIDTHs PROGNAME_WIDTH1(PROGNAME_WIDTH)
55 #define PROGNAME_WIDTH1(s) PROGNAME_WIDTH2(s)
56 #define PROGNAME_WIDTH2(s) #s
58 #define PRG_HASH_SIZE 211
71 TCP_CLOSING /* now a valid state */
74 static const char * const tcp_state[] =
91 SS_FREE = 0, /* not allocated */
92 SS_UNCONNECTED, /* unconnected to any socket */
93 SS_CONNECTING, /* in process of connecting */
94 SS_CONNECTED, /* connected to socket */
95 SS_DISCONNECTING /* in process of disconnecting */
98 #define SO_ACCEPTCON (1<<16) /* performed a listen */
99 #define SO_WAITDATA (1<<17) /* wait data to read */
100 #define SO_NOSPACE (1<<18) /* no space to write */
102 static char *itoa(unsigned int i)
104 /* 21 digits plus null terminator, good for 64-bit or smaller ints */
105 static char local[22];
106 char *p = &local[21];
115 static char *get_sname(int port, const char *proto, int num)
117 char *str=itoa(ntohs(port));
120 struct servent *se=getservbyport(port,proto);
130 static void snprint_ip_port(char *ip_port, int size, struct sockaddr *addr, int port, char *proto, int numeric)
134 #ifdef CONFIG_FEATURE_IPV6
135 if (addr->sa_family == AF_INET6) {
136 INET6_rresolve(ip_port, size, (struct sockaddr_in6 *)addr,
137 (numeric&NETSTAT_NUMERIC) ? 0x0fff : 0);
141 INET_rresolve(ip_port, size, (struct sockaddr_in *)addr,
142 0x4000 | ((numeric&NETSTAT_NUMERIC) ? 0x0fff : 0),
145 port_name=get_sname(htons(port), proto, numeric);
146 if ((strlen(ip_port) + strlen(port_name)) > 22)
147 ip_port[22 - strlen(port_name)] = '\0';
148 ip_port+=strlen(ip_port);
149 strcat(ip_port, ":");
150 strcat(ip_port, port_name);
153 static void tcp_do_one(int lnr, const char *line)
155 char local_addr[64], rem_addr[64];
156 const char *state_str;
158 int num, local_port, rem_port, d, state, timer_run, uid, timeout;
159 #ifdef CONFIG_FEATURE_IPV6
160 struct sockaddr_in6 localaddr, remaddr;
161 char addr6[INET6_ADDRSTRLEN];
164 struct sockaddr_in localaddr, remaddr;
166 unsigned long rxq, txq, time_len, retr, inode;
173 "%d: %64[0-9A-Fa-f]:%X %64[0-9A-Fa-f]:%X %X %lX:%lX %X:%lX %lX %d %d %ld %512s\n",
174 &d, local_addr, &local_port,
175 rem_addr, &rem_port, &state,
176 &txq, &rxq, &timer_run, &time_len, &retr, &uid, &timeout, &inode, more);
178 if (strlen(local_addr) > 8) {
179 #ifdef CONFIG_FEATURE_IPV6
180 sscanf(local_addr, "%08X%08X%08X%08X",
181 &in6.s6_addr32[0], &in6.s6_addr32[1],
182 &in6.s6_addr32[2], &in6.s6_addr32[3]);
183 inet_ntop(AF_INET6, &in6, addr6, sizeof(addr6));
184 inet_pton(AF_INET6, addr6, (struct sockaddr *) &localaddr.sin6_addr);
185 sscanf(rem_addr, "%08X%08X%08X%08X",
186 &in6.s6_addr32[0], &in6.s6_addr32[1],
187 &in6.s6_addr32[2], &in6.s6_addr32[3]);
188 inet_ntop(AF_INET6, &in6, addr6, sizeof(addr6));
189 inet_pton(AF_INET6, addr6, (struct sockaddr *) &remaddr.sin6_addr);
190 localaddr.sin6_family = AF_INET6;
191 remaddr.sin6_family = AF_INET6;
194 sscanf(local_addr, "%X",
195 &((struct sockaddr_in *) &localaddr)->sin_addr.s_addr);
196 sscanf(rem_addr, "%X",
197 &((struct sockaddr_in *) &remaddr)->sin_addr.s_addr);
198 ((struct sockaddr *) &localaddr)->sa_family = AF_INET;
199 ((struct sockaddr *) &remaddr)->sa_family = AF_INET;
203 bb_error_msg("warning, got bogus tcp line.");
206 state_str = tcp_state[state];
207 if ((rem_port && (flags&NETSTAT_CONNECTED)) ||
208 (!rem_port && (flags&NETSTAT_LISTENING)))
210 snprint_ip_port(local_addr, sizeof(local_addr),
211 (struct sockaddr *) &localaddr, local_port,
212 "tcp", flags&NETSTAT_NUMERIC);
214 snprint_ip_port(rem_addr, sizeof(rem_addr),
215 (struct sockaddr *) &remaddr, rem_port,
216 "tcp", flags&NETSTAT_NUMERIC);
218 printf("tcp %6ld %6ld %-23s %-23s %-12s\n",
219 rxq, txq, local_addr, rem_addr, state_str);
224 static void udp_do_one(int lnr, const char *line)
226 char local_addr[64], rem_addr[64];
227 char *state_str, more[512];
228 int num, local_port, rem_port, d, state, timer_run, uid, timeout;
229 #ifdef CONFIG_FEATURE_IPV6
230 struct sockaddr_in6 localaddr, remaddr;
231 char addr6[INET6_ADDRSTRLEN];
234 struct sockaddr_in localaddr, remaddr;
236 unsigned long rxq, txq, time_len, retr, inode;
243 "%d: %64[0-9A-Fa-f]:%X %64[0-9A-Fa-f]:%X %X %lX:%lX %X:%lX %lX %d %d %ld %512s\n",
244 &d, local_addr, &local_port,
245 rem_addr, &rem_port, &state,
246 &txq, &rxq, &timer_run, &time_len, &retr, &uid, &timeout, &inode, more);
248 if (strlen(local_addr) > 8) {
249 #ifdef CONFIG_FEATURE_IPV6
250 /* Demangle what the kernel gives us */
251 sscanf(local_addr, "%08X%08X%08X%08X",
252 &in6.s6_addr32[0], &in6.s6_addr32[1],
253 &in6.s6_addr32[2], &in6.s6_addr32[3]);
254 inet_ntop(AF_INET6, &in6, addr6, sizeof(addr6));
255 inet_pton(AF_INET6, addr6, (struct sockaddr *) &localaddr.sin6_addr);
256 sscanf(rem_addr, "%08X%08X%08X%08X",
257 &in6.s6_addr32[0], &in6.s6_addr32[1],
258 &in6.s6_addr32[2], &in6.s6_addr32[3]);
259 inet_ntop(AF_INET6, &in6, addr6, sizeof(addr6));
260 inet_pton(AF_INET6, addr6, (struct sockaddr *) &remaddr.sin6_addr);
261 localaddr.sin6_family = AF_INET6;
262 remaddr.sin6_family = AF_INET6;
265 sscanf(local_addr, "%X",
266 &((struct sockaddr_in *) &localaddr)->sin_addr.s_addr);
267 sscanf(rem_addr, "%X",
268 &((struct sockaddr_in *) &remaddr)->sin_addr.s_addr);
269 ((struct sockaddr *) &localaddr)->sa_family = AF_INET;
270 ((struct sockaddr *) &remaddr)->sa_family = AF_INET;
274 bb_error_msg("warning, got bogus udp line.");
278 case TCP_ESTABLISHED:
279 state_str = "ESTABLISHED";
287 state_str = "UNKNOWN";
291 #ifdef CONFIG_FEATURE_IPV6
292 # define notnull(A) (((A.sin6_family == AF_INET6) && \
293 ((A.sin6_addr.s6_addr32[0]) || \
294 (A.sin6_addr.s6_addr32[1]) || \
295 (A.sin6_addr.s6_addr32[2]) || \
296 (A.sin6_addr.s6_addr32[3]))) || \
297 ((A.sin6_family == AF_INET) && \
298 ((struct sockaddr_in *) &A)->sin_addr.s_addr))
300 # define notnull(A) (A.sin_addr.s_addr)
302 if ((notnull(remaddr) && (flags&NETSTAT_CONNECTED)) ||
303 (!notnull(remaddr) && (flags&NETSTAT_LISTENING)))
305 snprint_ip_port(local_addr, sizeof(local_addr),
306 (struct sockaddr *) &localaddr, local_port,
307 "udp", flags&NETSTAT_NUMERIC);
309 snprint_ip_port(rem_addr, sizeof(rem_addr),
310 (struct sockaddr *) &remaddr, rem_port,
311 "udp", flags&NETSTAT_NUMERIC);
313 printf("udp %6ld %6ld %-23s %-23s %-12s\n",
314 rxq, txq, local_addr, rem_addr, state_str);
319 static void raw_do_one(int lnr, const char *line)
321 char local_addr[64], rem_addr[64];
322 char *state_str, more[512];
323 int num, local_port, rem_port, d, state, timer_run, uid, timeout;
324 #ifdef CONFIG_FEATURE_IPV6
325 struct sockaddr_in6 localaddr, remaddr;
326 char addr6[INET6_ADDRSTRLEN];
329 struct sockaddr_in localaddr, remaddr;
331 unsigned long rxq, txq, time_len, retr, inode;
338 "%d: %64[0-9A-Fa-f]:%X %64[0-9A-Fa-f]:%X %X %lX:%lX %X:%lX %lX %d %d %ld %512s\n",
339 &d, local_addr, &local_port,
340 rem_addr, &rem_port, &state,
341 &txq, &rxq, &timer_run, &time_len, &retr, &uid, &timeout, &inode, more);
343 if (strlen(local_addr) > 8) {
344 #ifdef CONFIG_FEATURE_IPV6
345 sscanf(local_addr, "%08X%08X%08X%08X",
346 &in6.s6_addr32[0], &in6.s6_addr32[1],
347 &in6.s6_addr32[2], &in6.s6_addr32[3]);
348 inet_ntop(AF_INET6, &in6, addr6, sizeof(addr6));
349 inet_pton(AF_INET6, addr6, (struct sockaddr *) &localaddr.sin6_addr);
350 sscanf(rem_addr, "%08X%08X%08X%08X",
351 &in6.s6_addr32[0], &in6.s6_addr32[1],
352 &in6.s6_addr32[2], &in6.s6_addr32[3]);
353 inet_ntop(AF_INET6, &in6, addr6, sizeof(addr6));
354 inet_pton(AF_INET6, addr6, (struct sockaddr *) &remaddr.sin6_addr);
355 localaddr.sin6_family = AF_INET6;
356 remaddr.sin6_family = AF_INET6;
359 sscanf(local_addr, "%X",
360 &((struct sockaddr_in *) &localaddr)->sin_addr.s_addr);
361 sscanf(rem_addr, "%X",
362 &((struct sockaddr_in *) &remaddr)->sin_addr.s_addr);
363 ((struct sockaddr *) &localaddr)->sa_family = AF_INET;
364 ((struct sockaddr *) &remaddr)->sa_family = AF_INET;
368 bb_error_msg("warning, got bogus raw line.");
371 state_str=itoa(state);
373 #ifdef CONFIG_FEATURE_IPV6
374 # define notnull(A) (((A.sin6_family == AF_INET6) && \
375 ((A.sin6_addr.s6_addr32[0]) || \
376 (A.sin6_addr.s6_addr32[1]) || \
377 (A.sin6_addr.s6_addr32[2]) || \
378 (A.sin6_addr.s6_addr32[3]))) || \
379 ((A.sin6_family == AF_INET) && \
380 ((struct sockaddr_in *) &A)->sin_addr.s_addr))
382 # define notnull(A) (A.sin_addr.s_addr)
384 if ((notnull(remaddr) && (flags&NETSTAT_CONNECTED)) ||
385 (!notnull(remaddr) && (flags&NETSTAT_LISTENING)))
387 snprint_ip_port(local_addr, sizeof(local_addr),
388 (struct sockaddr *) &localaddr, local_port,
389 "raw", flags&NETSTAT_NUMERIC);
391 snprint_ip_port(rem_addr, sizeof(rem_addr),
392 (struct sockaddr *) &remaddr, rem_port,
393 "raw", flags&NETSTAT_NUMERIC);
395 printf("raw %6ld %6ld %-23s %-23s %-12s\n",
396 rxq, txq, local_addr, rem_addr, state_str);
403 static void unix_do_one(int nr, const char *line)
406 char path[PATH_MAX], ss_flags[32];
407 char *ss_proto, *ss_state, *ss_type;
408 int num, state, type, inode;
410 unsigned long refcnt, proto, unix_flags;
413 if (strstr(line, "Inode"))
418 num = sscanf(line, "%p: %lX %lX %lX %X %X %d %s",
419 &d, &refcnt, &proto, &unix_flags, &type, &state, &inode, path);
421 bb_error_msg("warning, got bogus unix line.");
424 if (!(has & HAS_INODE))
425 snprintf(path,sizeof(path),"%d",inode);
427 if ((flags&(NETSTAT_LISTENING|NETSTAT_CONNECTED))!=(NETSTAT_LISTENING|NETSTAT_CONNECTED)) {
428 if ((state == SS_UNCONNECTED) && (unix_flags & SO_ACCEPTCON)) {
429 if (!(flags&NETSTAT_LISTENING))
432 if (!(flags&NETSTAT_CONNECTED))
464 ss_type = "SEQPACKET";
478 * Unconnected sockets may be listening
481 if (unix_flags & SO_ACCEPTCON) {
482 ss_state = "LISTENING";
489 ss_state = "CONNECTING";
493 ss_state = "CONNECTED";
496 case SS_DISCONNECTING:
497 ss_state = "DISCONNECTING";
501 ss_state = "UNKNOWN";
504 strcpy(ss_flags, "[ ");
505 if (unix_flags & SO_ACCEPTCON)
506 strcat(ss_flags, "ACC ");
507 if (unix_flags & SO_WAITDATA)
508 strcat(ss_flags, "W ");
509 if (unix_flags & SO_NOSPACE)
510 strcat(ss_flags, "N ");
512 strcat(ss_flags, "]");
514 printf("%-5s %-6ld %-11s %-10s %-13s ",
515 ss_proto, refcnt, ss_flags, ss_type, ss_state);
517 printf("%-6d ",inode);
523 #define _PATH_PROCNET_UDP "/proc/net/udp"
524 #define _PATH_PROCNET_UDP6 "/proc/net/udp6"
525 #define _PATH_PROCNET_TCP "/proc/net/tcp"
526 #define _PATH_PROCNET_TCP6 "/proc/net/tcp6"
527 #define _PATH_PROCNET_RAW "/proc/net/raw"
528 #define _PATH_PROCNET_RAW6 "/proc/net/raw6"
529 #define _PATH_PROCNET_UNIX "/proc/net/unix"
531 static void do_info(const char *file, const char *name, void (*proc)(int, const char *))
537 procinfo = fopen(file, "r");
538 if (procinfo == NULL) {
539 if (errno != ENOENT) {
542 bb_error_msg("no support for `%s' on this system.", name);
546 if (fgets(buffer, sizeof(buffer), procinfo))
547 (proc)(lnr++, buffer);
548 } while (!feof(procinfo));
557 int netstat_main(int argc, char **argv)
561 int showroute = 0, extended = 0;
562 #ifdef CONFIG_FEATURE_IPV6
569 while ((opt = getopt(argc, argv, "laenrtuwx")) != -1)
572 flags &= ~NETSTAT_CONNECTED;
573 flags |= NETSTAT_LISTENING;
576 flags |= NETSTAT_LISTENING | NETSTAT_CONNECTED;
579 flags |= NETSTAT_NUMERIC;
588 new_flags |= NETSTAT_TCP;
591 new_flags |= NETSTAT_UDP;
594 new_flags |= NETSTAT_RAW;
597 new_flags |= NETSTAT_UNIX;
604 displayroutes ( flags & NETSTAT_NUMERIC, !extended );
607 bb_error_msg_and_die( "-r (display routing table) is not compiled in." );
612 flags &= ~(NETSTAT_TCP|NETSTAT_UDP|NETSTAT_RAW|NETSTAT_UNIX);
615 if (flags&(NETSTAT_TCP|NETSTAT_UDP|NETSTAT_RAW)) {
616 printf("Active Internet connections "); /* xxx */
618 if ((flags&(NETSTAT_LISTENING|NETSTAT_CONNECTED))==(NETSTAT_LISTENING|NETSTAT_CONNECTED))
619 printf("(servers and established)");
621 if (flags&NETSTAT_LISTENING)
622 printf("(only servers)");
624 printf("(w/o servers)");
626 printf("\nProto Recv-Q Send-Q Local Address Foreign Address State \n");
628 if (inet && flags&NETSTAT_TCP)
629 do_info(_PATH_PROCNET_TCP,"AF INET (tcp)",tcp_do_one);
630 #ifdef CONFIG_FEATURE_IPV6
631 if (inet6 && flags&NETSTAT_TCP)
632 do_info(_PATH_PROCNET_TCP6,"AF INET6 (tcp)",tcp_do_one);
634 if (inet && flags&NETSTAT_UDP)
635 do_info(_PATH_PROCNET_UDP,"AF INET (udp)",udp_do_one);
636 #ifdef CONFIG_FEATURE_IPV6
637 if (inet6 && flags&NETSTAT_UDP)
638 do_info(_PATH_PROCNET_UDP6,"AF INET6 (udp)",udp_do_one);
640 if (inet && flags&NETSTAT_RAW)
641 do_info(_PATH_PROCNET_RAW,"AF INET (raw)",raw_do_one);
642 #ifdef CONFIG_FEATURE_IPV6
643 if (inet6 && flags&NETSTAT_RAW)
644 do_info(_PATH_PROCNET_RAW6,"AF INET6 (raw)",raw_do_one);
646 if (flags&NETSTAT_UNIX) {
647 printf("Active UNIX domain sockets ");
648 if ((flags&(NETSTAT_LISTENING|NETSTAT_CONNECTED))==(NETSTAT_LISTENING|NETSTAT_CONNECTED))
649 printf("(servers and established)");
651 if (flags&NETSTAT_LISTENING)
652 printf("(only servers)");
654 printf("(w/o servers)");
657 printf("\nProto RefCnt Flags Type State I-Node Path\n");
658 do_info(_PATH_PROCNET_UNIX,"AF UNIX",unix_do_one);