blob: dcbcee21d87310b5be4ac3ce585a407520ea13f8 [file] [log] [blame]
Eric Andersen31a0ece2001-10-31 11:00:46 +00001/* 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 *
Eric Andersen51b8bd62002-07-03 11:46:38 +00006 * Copyright (C) 2002 by Bart Visscher <magick@linux-fan.com>
Eric Andersen31a0ece2001-10-31 11:00:46 +00007 *
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.
12 *
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.
17 *
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
Eric Andersen51b8bd62002-07-03 11:46:38 +000021 *
22 * 2002-04-20
23 * IPV6 support added by Bart Visscher <magick@linux-fan.com>
Eric Andersen31a0ece2001-10-31 11:00:46 +000024 */
25
26#include <stdio.h>
27#include <stdlib.h>
28#include <string.h>
29#include <stdarg.h>
30#include <signal.h>
31#include <errno.h>
32#include <sys/stat.h>
33#include <dirent.h>
34#include <unistd.h>
Eric Andersencd8c4362001-11-10 11:22:46 +000035#include "inet_common.h"
Eric Andersen31a0ece2001-10-31 11:00:46 +000036#include "busybox.h"
Eric Andersen887ca792002-07-03 23:19:26 +000037#include "pwd_.h"
Eric Andersen31a0ece2001-10-31 11:00:46 +000038
Robert Griebl820098f2002-05-14 23:03:23 +000039#ifdef CONFIG_ROUTE
40extern void displayroutes(int noresolve, int netstatfmt);
41#endif
42
Eric Andersen31a0ece2001-10-31 11:00:46 +000043#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
50
Eric Andersenb0c39a82002-06-22 17:32:58 +000051static int flags = NETSTAT_CONNECTED |
Eric Andersen31a0ece2001-10-31 11:00:46 +000052 NETSTAT_TCP | NETSTAT_UDP | NETSTAT_RAW | NETSTAT_UNIX;
53
54#define PROGNAME_WIDTHs PROGNAME_WIDTH1(PROGNAME_WIDTH)
55#define PROGNAME_WIDTH1(s) PROGNAME_WIDTH2(s)
56#define PROGNAME_WIDTH2(s) #s
57
58#define PRG_HASH_SIZE 211
59
60enum {
61 TCP_ESTABLISHED = 1,
62 TCP_SYN_SENT,
63 TCP_SYN_RECV,
64 TCP_FIN_WAIT1,
65 TCP_FIN_WAIT2,
66 TCP_TIME_WAIT,
67 TCP_CLOSE,
68 TCP_CLOSE_WAIT,
69 TCP_LAST_ACK,
70 TCP_LISTEN,
71 TCP_CLOSING /* now a valid state */
72};
73
Eric Andersenb0c39a82002-06-22 17:32:58 +000074static const char * const tcp_state[] =
Eric Andersen31a0ece2001-10-31 11:00:46 +000075{
76 "",
77 "ESTABLISHED",
78 "SYN_SENT",
79 "SYN_RECV",
80 "FIN_WAIT1",
81 "FIN_WAIT2",
82 "TIME_WAIT",
83 "CLOSE",
84 "CLOSE_WAIT",
85 "LAST_ACK",
86 "LISTEN",
87 "CLOSING"
88};
89
90typedef enum {
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 */
96} socket_state;
97
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 */
101
Eric Andersencd8c4362001-11-10 11:22:46 +0000102static char *itoa(unsigned int i)
Eric Andersen31a0ece2001-10-31 11:00:46 +0000103{
104 /* 21 digits plus null terminator, good for 64-bit or smaller ints */
105 static char local[22];
106 char *p = &local[21];
107 *p-- = '\0';
108 do {
109 *p-- = '0' + i % 10;
110 i /= 10;
111 } while (i > 0);
112 return p + 1;
113}
114
Eric Andersencd8c4362001-11-10 11:22:46 +0000115static char *get_sname(int port, const char *proto, int num)
Eric Andersen31a0ece2001-10-31 11:00:46 +0000116{
117 char *str=itoa(ntohs(port));
118 if (num) {
119 } else {
120 struct servent *se=getservbyport(port,proto);
121 if (se)
122 str=se->s_name;
123 }
124 if (!port) {
125 str="*";
126 }
127 return str;
128}
129
Eric Andersencd8c4362001-11-10 11:22:46 +0000130static void snprint_ip_port(char *ip_port, int size, struct sockaddr *addr, int port, char *proto, int numeric)
Eric Andersen31a0ece2001-10-31 11:00:46 +0000131{
132 char *port_name;
Eric Andersencd8c4362001-11-10 11:22:46 +0000133
Eric Andersen51b8bd62002-07-03 11:46:38 +0000134#if 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);
138 } else
139#endif
140 {
Eric Andersencd8c4362001-11-10 11:22:46 +0000141 INET_rresolve(ip_port, size, (struct sockaddr_in *)addr,
142 0x4000 | ((numeric&NETSTAT_NUMERIC) ? 0x0fff : 0),
143 0xffffffff);
Eric Andersen51b8bd62002-07-03 11:46:38 +0000144 }
Eric Andersen31a0ece2001-10-31 11:00:46 +0000145 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);
151}
152
153static void tcp_do_one(int lnr, const char *line)
154{
155 char local_addr[64], rem_addr[64];
Eric Andersenb0c39a82002-06-22 17:32:58 +0000156 const char *state_str;
157 char more[512];
Eric Andersen31a0ece2001-10-31 11:00:46 +0000158 int num, local_port, rem_port, d, state, timer_run, uid, timeout;
Eric Andersen51b8bd62002-07-03 11:46:38 +0000159#if CONFIG_FEATURE_IPV6
160 struct sockaddr_in6 localaddr, remaddr;
161 char addr6[INET6_ADDRSTRLEN];
162 struct in6_addr in6;
163#else
Eric Andersen31a0ece2001-10-31 11:00:46 +0000164 struct sockaddr_in localaddr, remaddr;
Eric Andersen51b8bd62002-07-03 11:46:38 +0000165#endif
Eric Andersen31a0ece2001-10-31 11:00:46 +0000166 unsigned long rxq, txq, time_len, retr, inode;
167
168 if (lnr == 0)
169 return;
170
171 more[0] = '\0';
172 num = sscanf(line,
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);
177
178 if (strlen(local_addr) > 8) {
Eric Andersen51b8bd62002-07-03 11:46:38 +0000179#if 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;
192#endif
Eric Andersen31a0ece2001-10-31 11:00:46 +0000193 } else {
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;
200 }
201
202 if (num < 10) {
Eric Andersenb0c39a82002-06-22 17:32:58 +0000203 error_msg("warning, got bogus tcp line.");
Eric Andersen31a0ece2001-10-31 11:00:46 +0000204 return;
205 }
Eric Andersenb0c39a82002-06-22 17:32:58 +0000206 state_str = tcp_state[state];
Eric Andersen31a0ece2001-10-31 11:00:46 +0000207 if ((rem_port && (flags&NETSTAT_CONNECTED)) ||
208 (!rem_port && (flags&NETSTAT_LISTENING)))
209 {
210 snprint_ip_port(local_addr, sizeof(local_addr),
211 (struct sockaddr *) &localaddr, local_port,
212 "tcp", flags&NETSTAT_NUMERIC);
213
214 snprint_ip_port(rem_addr, sizeof(rem_addr),
215 (struct sockaddr *) &remaddr, rem_port,
216 "tcp", flags&NETSTAT_NUMERIC);
217
218 printf("tcp %6ld %6ld %-23s %-23s %-12s\n",
219 rxq, txq, local_addr, rem_addr, state_str);
220
221 }
222}
223
224static void udp_do_one(int lnr, const char *line)
225{
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;
Eric Andersen51b8bd62002-07-03 11:46:38 +0000229#if CONFIG_FEATURE_IPV6
230 struct sockaddr_in6 localaddr, remaddr;
231 char addr6[INET6_ADDRSTRLEN];
232 struct in6_addr in6;
233#else
Eric Andersen31a0ece2001-10-31 11:00:46 +0000234 struct sockaddr_in localaddr, remaddr;
Eric Andersen51b8bd62002-07-03 11:46:38 +0000235#endif
Eric Andersen31a0ece2001-10-31 11:00:46 +0000236 unsigned long rxq, txq, time_len, retr, inode;
237
238 if (lnr == 0)
239 return;
240
241 more[0] = '\0';
242 num = sscanf(line,
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);
247
248 if (strlen(local_addr) > 8) {
Eric Andersen51b8bd62002-07-03 11:46:38 +0000249#if 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;
263#endif
Eric Andersen31a0ece2001-10-31 11:00:46 +0000264 } else {
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;
271 }
272
273 if (num < 10) {
Eric Andersenb0c39a82002-06-22 17:32:58 +0000274 error_msg("warning, got bogus udp line.");
Eric Andersen31a0ece2001-10-31 11:00:46 +0000275 return;
276 }
277 switch (state) {
278 case TCP_ESTABLISHED:
279 state_str = "ESTABLISHED";
280 break;
281
282 case TCP_CLOSE:
283 state_str = "";
284 break;
285
286 default:
287 state_str = "UNKNOWN";
288 break;
289 }
290
Eric Andersen51b8bd62002-07-03 11:46:38 +0000291#if 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))
299#else
Eric Andersen31a0ece2001-10-31 11:00:46 +0000300#define notnull(A) (A.sin_addr.s_addr)
Eric Andersen51b8bd62002-07-03 11:46:38 +0000301#endif
Eric Andersen31a0ece2001-10-31 11:00:46 +0000302 if ((notnull(remaddr) && (flags&NETSTAT_CONNECTED)) ||
303 (!notnull(remaddr) && (flags&NETSTAT_LISTENING)))
304 {
305 snprint_ip_port(local_addr, sizeof(local_addr),
306 (struct sockaddr *) &localaddr, local_port,
307 "udp", flags&NETSTAT_NUMERIC);
308
309 snprint_ip_port(rem_addr, sizeof(rem_addr),
310 (struct sockaddr *) &remaddr, rem_port,
311 "udp", flags&NETSTAT_NUMERIC);
312
313 printf("udp %6ld %6ld %-23s %-23s %-12s\n",
314 rxq, txq, local_addr, rem_addr, state_str);
315
316 }
317}
318
319static void raw_do_one(int lnr, const char *line)
320{
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;
Eric Andersen51b8bd62002-07-03 11:46:38 +0000324#if CONFIG_FEATURE_IPV6
325 struct sockaddr_in6 localaddr, remaddr;
326 char addr6[INET6_ADDRSTRLEN];
327 struct in6_addr in6;
328#else
Eric Andersen31a0ece2001-10-31 11:00:46 +0000329 struct sockaddr_in localaddr, remaddr;
Eric Andersen51b8bd62002-07-03 11:46:38 +0000330#endif
Eric Andersen31a0ece2001-10-31 11:00:46 +0000331 unsigned long rxq, txq, time_len, retr, inode;
332
333 if (lnr == 0)
334 return;
335
336 more[0] = '\0';
337 num = sscanf(line,
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);
342
343 if (strlen(local_addr) > 8) {
Eric Andersen51b8bd62002-07-03 11:46:38 +0000344#if 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;
357#endif
Eric Andersen31a0ece2001-10-31 11:00:46 +0000358 } else {
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;
365 }
366
367 if (num < 10) {
Eric Andersenb0c39a82002-06-22 17:32:58 +0000368 error_msg("warning, got bogus raw line.");
Eric Andersen31a0ece2001-10-31 11:00:46 +0000369 return;
370 }
371 state_str=itoa(state);
372
Eric Andersen51b8bd62002-07-03 11:46:38 +0000373#if 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))
381#else
Eric Andersen31a0ece2001-10-31 11:00:46 +0000382#define notnull(A) (A.sin_addr.s_addr)
Eric Andersen51b8bd62002-07-03 11:46:38 +0000383#endif
Eric Andersen31a0ece2001-10-31 11:00:46 +0000384 if ((notnull(remaddr) && (flags&NETSTAT_CONNECTED)) ||
385 (!notnull(remaddr) && (flags&NETSTAT_LISTENING)))
386 {
387 snprint_ip_port(local_addr, sizeof(local_addr),
388 (struct sockaddr *) &localaddr, local_port,
389 "raw", flags&NETSTAT_NUMERIC);
390
391 snprint_ip_port(rem_addr, sizeof(rem_addr),
392 (struct sockaddr *) &remaddr, rem_port,
393 "raw", flags&NETSTAT_NUMERIC);
394
395 printf("raw %6ld %6ld %-23s %-23s %-12s\n",
396 rxq, txq, local_addr, rem_addr, state_str);
397
398 }
399}
400
401#define HAS_INODE 1
402
403static void unix_do_one(int nr, const char *line)
404{
405 static int has = 0;
406 char path[PATH_MAX], ss_flags[32];
407 char *ss_proto, *ss_state, *ss_type;
408 int num, state, type, inode;
409 void *d;
410 unsigned long refcnt, proto, unix_flags;
411
412 if (nr == 0) {
413 if (strstr(line, "Inode"))
414 has |= HAS_INODE;
415 return;
416 }
417 path[0] = '\0';
418 num = sscanf(line, "%p: %lX %lX %lX %X %X %d %s",
419 &d, &refcnt, &proto, &unix_flags, &type, &state, &inode, path);
420 if (num < 6) {
Eric Andersenb0c39a82002-06-22 17:32:58 +0000421 error_msg("warning, got bogus unix line.");
Eric Andersen31a0ece2001-10-31 11:00:46 +0000422 return;
423 }
424 if (!(has & HAS_INODE))
425 snprintf(path,sizeof(path),"%d",inode);
426
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))
430 return;
431 } else {
432 if (!(flags&NETSTAT_CONNECTED))
433 return;
434 }
435 }
436
437 switch (proto) {
438 case 0:
439 ss_proto = "unix";
440 break;
441
442 default:
443 ss_proto = "??";
444 }
445
446 switch (type) {
447 case SOCK_STREAM:
448 ss_type = "STREAM";
449 break;
450
451 case SOCK_DGRAM:
452 ss_type = "DGRAM";
453 break;
454
455 case SOCK_RAW:
456 ss_type = "RAW";
457 break;
458
459 case SOCK_RDM:
460 ss_type = "RDM";
461 break;
462
463 case SOCK_SEQPACKET:
464 ss_type = "SEQPACKET";
465 break;
466
467 default:
468 ss_type = "UNKNOWN";
469 }
470
471 switch (state) {
472 case SS_FREE:
473 ss_state = "FREE";
474 break;
475
476 case SS_UNCONNECTED:
477 /*
478 * Unconnected sockets may be listening
479 * for something.
480 */
481 if (unix_flags & SO_ACCEPTCON) {
482 ss_state = "LISTENING";
483 } else {
484 ss_state = "";
485 }
486 break;
487
488 case SS_CONNECTING:
489 ss_state = "CONNECTING";
490 break;
491
492 case SS_CONNECTED:
493 ss_state = "CONNECTED";
494 break;
495
496 case SS_DISCONNECTING:
497 ss_state = "DISCONNECTING";
498 break;
499
500 default:
501 ss_state = "UNKNOWN";
502 }
503
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 ");
511
512 strcat(ss_flags, "]");
513
514 printf("%-5s %-6ld %-11s %-10s %-13s ",
515 ss_proto, refcnt, ss_flags, ss_type, ss_state);
516 if (has & HAS_INODE)
517 printf("%-6d ",inode);
518 else
519 printf("- ");
520 puts(path);
521}
522
523#define _PATH_PROCNET_UDP "/proc/net/udp"
Eric Andersen51b8bd62002-07-03 11:46:38 +0000524#define _PATH_PROCNET_UDP6 "/proc/net/udp6"
Eric Andersen31a0ece2001-10-31 11:00:46 +0000525#define _PATH_PROCNET_TCP "/proc/net/tcp"
Eric Andersen51b8bd62002-07-03 11:46:38 +0000526#define _PATH_PROCNET_TCP6 "/proc/net/tcp6"
Eric Andersen31a0ece2001-10-31 11:00:46 +0000527#define _PATH_PROCNET_RAW "/proc/net/raw"
Eric Andersen51b8bd62002-07-03 11:46:38 +0000528#define _PATH_PROCNET_RAW6 "/proc/net/raw6"
Eric Andersen31a0ece2001-10-31 11:00:46 +0000529#define _PATH_PROCNET_UNIX "/proc/net/unix"
530
Eric Andersenb0c39a82002-06-22 17:32:58 +0000531static void do_info(const char *file, const char *name, void (*proc)(int, const char *))
Eric Andersen31a0ece2001-10-31 11:00:46 +0000532{
533 char buffer[8192];
Eric Andersen31a0ece2001-10-31 11:00:46 +0000534 int lnr = 0;
535 FILE *procinfo;
536
Eric Andersenb0c39a82002-06-22 17:32:58 +0000537 procinfo = fopen(file, "r");
Eric Andersen31a0ece2001-10-31 11:00:46 +0000538 if (procinfo == NULL) {
539 if (errno != ENOENT) {
Eric Andersenb0c39a82002-06-22 17:32:58 +0000540 perror(file);
541 } else {
542 error_msg("no support for `%s' on this system.", name);
Eric Andersen31a0ece2001-10-31 11:00:46 +0000543 }
Eric Andersen31a0ece2001-10-31 11:00:46 +0000544 } else {
545 do {
546 if (fgets(buffer, sizeof(buffer), procinfo))
547 (proc)(lnr++, buffer);
548 } while (!feof(procinfo));
549 fclose(procinfo);
550 }
Eric Andersen31a0ece2001-10-31 11:00:46 +0000551}
552
553/*
554 * Our main function.
555 */
556
557int netstat_main(int argc, char **argv)
558{
559 int opt;
560 int new_flags=0;
Robert Griebl820098f2002-05-14 23:03:23 +0000561 int showroute = 0, extended = 0;
Eric Andersen51b8bd62002-07-03 11:46:38 +0000562#if CONFIG_FEATURE_IPV6
563 int inet=1;
564 int inet6=1;
565#else
566#define inet 1
567#define inet6 0
568#endif
Robert Griebl820098f2002-05-14 23:03:23 +0000569 while ((opt = getopt(argc, argv, "laenrtuwx")) != -1)
Eric Andersen31a0ece2001-10-31 11:00:46 +0000570 switch (opt) {
571 case 'l':
572 flags &= ~NETSTAT_CONNECTED;
573 flags |= NETSTAT_LISTENING;
574 break;
575 case 'a':
576 flags |= NETSTAT_LISTENING | NETSTAT_CONNECTED;
577 break;
578 case 'n':
579 flags |= NETSTAT_NUMERIC;
580 break;
Robert Griebl820098f2002-05-14 23:03:23 +0000581 case 'r':
582 showroute = 1;
583 break;
584 case 'e':
585 extended = 1;
586 break;
Eric Andersen31a0ece2001-10-31 11:00:46 +0000587 case 't':
588 new_flags |= NETSTAT_TCP;
589 break;
590 case 'u':
591 new_flags |= NETSTAT_UDP;
592 break;
593 case 'w':
594 new_flags |= NETSTAT_RAW;
595 break;
596 case 'x':
597 new_flags |= NETSTAT_UNIX;
598 break;
599 default:
600 show_usage();
601 }
Robert Griebl820098f2002-05-14 23:03:23 +0000602 if ( showroute ) {
603#ifdef CONFIG_ROUTE
604 displayroutes ( flags & NETSTAT_NUMERIC, !extended );
605 return 0;
606#else
Eric Andersenb0c39a82002-06-22 17:32:58 +0000607 error_msg_and_die( "-r (display routing table) is not compiled in." );
Robert Griebl820098f2002-05-14 23:03:23 +0000608#endif
609 }
610
Eric Andersen31a0ece2001-10-31 11:00:46 +0000611 if (new_flags) {
612 flags &= ~(NETSTAT_TCP|NETSTAT_UDP|NETSTAT_RAW|NETSTAT_UNIX);
613 flags |= new_flags;
614 }
615 if (flags&(NETSTAT_TCP|NETSTAT_UDP|NETSTAT_RAW)) {
616 printf("Active Internet connections "); /* xxx */
617
618 if ((flags&(NETSTAT_LISTENING|NETSTAT_CONNECTED))==(NETSTAT_LISTENING|NETSTAT_CONNECTED))
619 printf("(servers and established)");
620 else {
621 if (flags&NETSTAT_LISTENING)
622 printf("(only servers)");
623 else
624 printf("(w/o servers)");
625 }
626 printf("\nProto Recv-Q Send-Q Local Address Foreign Address State \n");
627 }
Eric Andersen51b8bd62002-07-03 11:46:38 +0000628 if (inet && flags&NETSTAT_TCP)
Eric Andersen31a0ece2001-10-31 11:00:46 +0000629 do_info(_PATH_PROCNET_TCP,"AF INET (tcp)",tcp_do_one);
Eric Andersen51b8bd62002-07-03 11:46:38 +0000630#if CONFIG_FEATURE_IPV6
631 if (inet6 && flags&NETSTAT_TCP)
632 do_info(_PATH_PROCNET_TCP6,"AF INET6 (tcp)",tcp_do_one);
633#endif
634 if (inet && flags&NETSTAT_UDP)
Eric Andersen31a0ece2001-10-31 11:00:46 +0000635 do_info(_PATH_PROCNET_UDP,"AF INET (udp)",udp_do_one);
Eric Andersen51b8bd62002-07-03 11:46:38 +0000636#if CONFIG_FEATURE_IPV6
637 if (inet6 && flags&NETSTAT_UDP)
638 do_info(_PATH_PROCNET_UDP6,"AF INET6 (udp)",udp_do_one);
639#endif
640 if (inet && flags&NETSTAT_RAW)
Eric Andersen31a0ece2001-10-31 11:00:46 +0000641 do_info(_PATH_PROCNET_RAW,"AF INET (raw)",raw_do_one);
Eric Andersen51b8bd62002-07-03 11:46:38 +0000642#if CONFIG_FEATURE_IPV6
643 if (inet6 && flags&NETSTAT_RAW)
644 do_info(_PATH_PROCNET_RAW6,"AF INET6 (raw)",raw_do_one);
645#endif
Eric Andersen31a0ece2001-10-31 11:00:46 +0000646 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)");
650 else {
651 if (flags&NETSTAT_LISTENING)
652 printf("(only servers)");
653 else
654 printf("(w/o servers)");
655 }
656
657 printf("\nProto RefCnt Flags Type State I-Node Path\n");
658 do_info(_PATH_PROCNET_UNIX,"AF UNIX",unix_do_one);
659 }
660 return 0;
661}