blob: b083b0dd4da526a9d11af8a6aa18eafb0407093d [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 *
6 * Copyright (C) 2001 by Bart Visscher <magick@linux-fan.com>
7 *
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
21 */
22
23#include <stdio.h>
24#include <stdlib.h>
25#include <string.h>
26#include <stdarg.h>
27#include <signal.h>
28#include <errno.h>
29#include <sys/stat.h>
30#include <dirent.h>
31#include <unistd.h>
32#include <pwd.h>
Eric Andersencd8c4362001-11-10 11:22:46 +000033#include "inet_common.h"
Eric Andersen31a0ece2001-10-31 11:00:46 +000034#include "busybox.h"
35
Robert Griebl820098f2002-05-14 23:03:23 +000036#ifdef CONFIG_ROUTE
37extern void displayroutes(int noresolve, int netstatfmt);
38#endif
39
Eric Andersen31a0ece2001-10-31 11:00:46 +000040#define NETSTAT_CONNECTED 0x01
41#define NETSTAT_LISTENING 0x02
42#define NETSTAT_NUMERIC 0x04
43#define NETSTAT_TCP 0x10
44#define NETSTAT_UDP 0x20
45#define NETSTAT_RAW 0x40
46#define NETSTAT_UNIX 0x80
47
48int flags = NETSTAT_CONNECTED |
49 NETSTAT_TCP | NETSTAT_UDP | NETSTAT_RAW | NETSTAT_UNIX;
50
51#define PROGNAME_WIDTHs PROGNAME_WIDTH1(PROGNAME_WIDTH)
52#define PROGNAME_WIDTH1(s) PROGNAME_WIDTH2(s)
53#define PROGNAME_WIDTH2(s) #s
54
55#define PRG_HASH_SIZE 211
56
57enum {
58 TCP_ESTABLISHED = 1,
59 TCP_SYN_SENT,
60 TCP_SYN_RECV,
61 TCP_FIN_WAIT1,
62 TCP_FIN_WAIT2,
63 TCP_TIME_WAIT,
64 TCP_CLOSE,
65 TCP_CLOSE_WAIT,
66 TCP_LAST_ACK,
67 TCP_LISTEN,
68 TCP_CLOSING /* now a valid state */
69};
70
71static const char *tcp_state[] =
72{
73 "",
74 "ESTABLISHED",
75 "SYN_SENT",
76 "SYN_RECV",
77 "FIN_WAIT1",
78 "FIN_WAIT2",
79 "TIME_WAIT",
80 "CLOSE",
81 "CLOSE_WAIT",
82 "LAST_ACK",
83 "LISTEN",
84 "CLOSING"
85};
86
87typedef enum {
88 SS_FREE = 0, /* not allocated */
89 SS_UNCONNECTED, /* unconnected to any socket */
90 SS_CONNECTING, /* in process of connecting */
91 SS_CONNECTED, /* connected to socket */
92 SS_DISCONNECTING /* in process of disconnecting */
93} socket_state;
94
95#define SO_ACCEPTCON (1<<16) /* performed a listen */
96#define SO_WAITDATA (1<<17) /* wait data to read */
97#define SO_NOSPACE (1<<18) /* no space to write */
98
Eric Andersencd8c4362001-11-10 11:22:46 +000099static char *itoa(unsigned int i)
Eric Andersen31a0ece2001-10-31 11:00:46 +0000100{
101 /* 21 digits plus null terminator, good for 64-bit or smaller ints */
102 static char local[22];
103 char *p = &local[21];
104 *p-- = '\0';
105 do {
106 *p-- = '0' + i % 10;
107 i /= 10;
108 } while (i > 0);
109 return p + 1;
110}
111
Eric Andersencd8c4362001-11-10 11:22:46 +0000112static char *get_sname(int port, const char *proto, int num)
Eric Andersen31a0ece2001-10-31 11:00:46 +0000113{
114 char *str=itoa(ntohs(port));
115 if (num) {
116 } else {
117 struct servent *se=getservbyport(port,proto);
118 if (se)
119 str=se->s_name;
120 }
121 if (!port) {
122 str="*";
123 }
124 return str;
125}
126
Eric Andersencd8c4362001-11-10 11:22:46 +0000127static 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 +0000128{
129 char *port_name;
Eric Andersencd8c4362001-11-10 11:22:46 +0000130
131 INET_rresolve(ip_port, size, (struct sockaddr_in *)addr,
132 0x4000 | ((numeric&NETSTAT_NUMERIC) ? 0x0fff : 0),
133 0xffffffff);
Eric Andersen31a0ece2001-10-31 11:00:46 +0000134 port_name=get_sname(htons(port), proto, numeric);
135 if ((strlen(ip_port) + strlen(port_name)) > 22)
136 ip_port[22 - strlen(port_name)] = '\0';
137 ip_port+=strlen(ip_port);
138 strcat(ip_port, ":");
139 strcat(ip_port, port_name);
140}
141
142static void tcp_do_one(int lnr, const char *line)
143{
144 char local_addr[64], rem_addr[64];
145 char *state_str, more[512];
146 int num, local_port, rem_port, d, state, timer_run, uid, timeout;
147 struct sockaddr_in localaddr, remaddr;
148 unsigned long rxq, txq, time_len, retr, inode;
149
150 if (lnr == 0)
151 return;
152
153 more[0] = '\0';
154 num = sscanf(line,
155 "%d: %64[0-9A-Fa-f]:%X %64[0-9A-Fa-f]:%X %X %lX:%lX %X:%lX %lX %d %d %ld %512s\n",
156 &d, local_addr, &local_port,
157 rem_addr, &rem_port, &state,
158 &txq, &rxq, &timer_run, &time_len, &retr, &uid, &timeout, &inode, more);
159
160 if (strlen(local_addr) > 8) {
161 } else {
162 sscanf(local_addr, "%X",
163 &((struct sockaddr_in *) &localaddr)->sin_addr.s_addr);
164 sscanf(rem_addr, "%X",
165 &((struct sockaddr_in *) &remaddr)->sin_addr.s_addr);
166 ((struct sockaddr *) &localaddr)->sa_family = AF_INET;
167 ((struct sockaddr *) &remaddr)->sa_family = AF_INET;
168 }
169
170 if (num < 10) {
171 error_msg("warning, got bogus tcp line.\n");
172 return;
173 }
174 state_str=(char*)tcp_state[state];
175 if ((rem_port && (flags&NETSTAT_CONNECTED)) ||
176 (!rem_port && (flags&NETSTAT_LISTENING)))
177 {
178 snprint_ip_port(local_addr, sizeof(local_addr),
179 (struct sockaddr *) &localaddr, local_port,
180 "tcp", flags&NETSTAT_NUMERIC);
181
182 snprint_ip_port(rem_addr, sizeof(rem_addr),
183 (struct sockaddr *) &remaddr, rem_port,
184 "tcp", flags&NETSTAT_NUMERIC);
185
186 printf("tcp %6ld %6ld %-23s %-23s %-12s\n",
187 rxq, txq, local_addr, rem_addr, state_str);
188
189 }
190}
191
192static void udp_do_one(int lnr, const char *line)
193{
194 char local_addr[64], rem_addr[64];
195 char *state_str, more[512];
196 int num, local_port, rem_port, d, state, timer_run, uid, timeout;
197 struct sockaddr_in localaddr, remaddr;
198 unsigned long rxq, txq, time_len, retr, inode;
199
200 if (lnr == 0)
201 return;
202
203 more[0] = '\0';
204 num = sscanf(line,
205 "%d: %64[0-9A-Fa-f]:%X %64[0-9A-Fa-f]:%X %X %lX:%lX %X:%lX %lX %d %d %ld %512s\n",
206 &d, local_addr, &local_port,
207 rem_addr, &rem_port, &state,
208 &txq, &rxq, &timer_run, &time_len, &retr, &uid, &timeout, &inode, more);
209
210 if (strlen(local_addr) > 8) {
211 } else {
212 sscanf(local_addr, "%X",
213 &((struct sockaddr_in *) &localaddr)->sin_addr.s_addr);
214 sscanf(rem_addr, "%X",
215 &((struct sockaddr_in *) &remaddr)->sin_addr.s_addr);
216 ((struct sockaddr *) &localaddr)->sa_family = AF_INET;
217 ((struct sockaddr *) &remaddr)->sa_family = AF_INET;
218 }
219
220 if (num < 10) {
221 error_msg("warning, got bogus udp line.\n");
222 return;
223 }
224 switch (state) {
225 case TCP_ESTABLISHED:
226 state_str = "ESTABLISHED";
227 break;
228
229 case TCP_CLOSE:
230 state_str = "";
231 break;
232
233 default:
234 state_str = "UNKNOWN";
235 break;
236 }
237
238#define notnull(A) (A.sin_addr.s_addr)
239 if ((notnull(remaddr) && (flags&NETSTAT_CONNECTED)) ||
240 (!notnull(remaddr) && (flags&NETSTAT_LISTENING)))
241 {
242 snprint_ip_port(local_addr, sizeof(local_addr),
243 (struct sockaddr *) &localaddr, local_port,
244 "udp", flags&NETSTAT_NUMERIC);
245
246 snprint_ip_port(rem_addr, sizeof(rem_addr),
247 (struct sockaddr *) &remaddr, rem_port,
248 "udp", flags&NETSTAT_NUMERIC);
249
250 printf("udp %6ld %6ld %-23s %-23s %-12s\n",
251 rxq, txq, local_addr, rem_addr, state_str);
252
253 }
254}
255
256static void raw_do_one(int lnr, const char *line)
257{
258 char local_addr[64], rem_addr[64];
259 char *state_str, more[512];
260 int num, local_port, rem_port, d, state, timer_run, uid, timeout;
261 struct sockaddr_in localaddr, remaddr;
262 unsigned long rxq, txq, time_len, retr, inode;
263
264 if (lnr == 0)
265 return;
266
267 more[0] = '\0';
268 num = sscanf(line,
269 "%d: %64[0-9A-Fa-f]:%X %64[0-9A-Fa-f]:%X %X %lX:%lX %X:%lX %lX %d %d %ld %512s\n",
270 &d, local_addr, &local_port,
271 rem_addr, &rem_port, &state,
272 &txq, &rxq, &timer_run, &time_len, &retr, &uid, &timeout, &inode, more);
273
274 if (strlen(local_addr) > 8) {
275 } else {
276 sscanf(local_addr, "%X",
277 &((struct sockaddr_in *) &localaddr)->sin_addr.s_addr);
278 sscanf(rem_addr, "%X",
279 &((struct sockaddr_in *) &remaddr)->sin_addr.s_addr);
280 ((struct sockaddr *) &localaddr)->sa_family = AF_INET;
281 ((struct sockaddr *) &remaddr)->sa_family = AF_INET;
282 }
283
284 if (num < 10) {
285 error_msg("warning, got bogus raw line.\n");
286 return;
287 }
288 state_str=itoa(state);
289
290#define notnull(A) (A.sin_addr.s_addr)
291 if ((notnull(remaddr) && (flags&NETSTAT_CONNECTED)) ||
292 (!notnull(remaddr) && (flags&NETSTAT_LISTENING)))
293 {
294 snprint_ip_port(local_addr, sizeof(local_addr),
295 (struct sockaddr *) &localaddr, local_port,
296 "raw", flags&NETSTAT_NUMERIC);
297
298 snprint_ip_port(rem_addr, sizeof(rem_addr),
299 (struct sockaddr *) &remaddr, rem_port,
300 "raw", flags&NETSTAT_NUMERIC);
301
302 printf("raw %6ld %6ld %-23s %-23s %-12s\n",
303 rxq, txq, local_addr, rem_addr, state_str);
304
305 }
306}
307
308#define HAS_INODE 1
309
310static void unix_do_one(int nr, const char *line)
311{
312 static int has = 0;
313 char path[PATH_MAX], ss_flags[32];
314 char *ss_proto, *ss_state, *ss_type;
315 int num, state, type, inode;
316 void *d;
317 unsigned long refcnt, proto, unix_flags;
318
319 if (nr == 0) {
320 if (strstr(line, "Inode"))
321 has |= HAS_INODE;
322 return;
323 }
324 path[0] = '\0';
325 num = sscanf(line, "%p: %lX %lX %lX %X %X %d %s",
326 &d, &refcnt, &proto, &unix_flags, &type, &state, &inode, path);
327 if (num < 6) {
328 error_msg("warning, got bogus unix line.\n");
329 return;
330 }
331 if (!(has & HAS_INODE))
332 snprintf(path,sizeof(path),"%d",inode);
333
334 if ((flags&(NETSTAT_LISTENING|NETSTAT_CONNECTED))!=(NETSTAT_LISTENING|NETSTAT_CONNECTED)) {
335 if ((state == SS_UNCONNECTED) && (unix_flags & SO_ACCEPTCON)) {
336 if (!(flags&NETSTAT_LISTENING))
337 return;
338 } else {
339 if (!(flags&NETSTAT_CONNECTED))
340 return;
341 }
342 }
343
344 switch (proto) {
345 case 0:
346 ss_proto = "unix";
347 break;
348
349 default:
350 ss_proto = "??";
351 }
352
353 switch (type) {
354 case SOCK_STREAM:
355 ss_type = "STREAM";
356 break;
357
358 case SOCK_DGRAM:
359 ss_type = "DGRAM";
360 break;
361
362 case SOCK_RAW:
363 ss_type = "RAW";
364 break;
365
366 case SOCK_RDM:
367 ss_type = "RDM";
368 break;
369
370 case SOCK_SEQPACKET:
371 ss_type = "SEQPACKET";
372 break;
373
374 default:
375 ss_type = "UNKNOWN";
376 }
377
378 switch (state) {
379 case SS_FREE:
380 ss_state = "FREE";
381 break;
382
383 case SS_UNCONNECTED:
384 /*
385 * Unconnected sockets may be listening
386 * for something.
387 */
388 if (unix_flags & SO_ACCEPTCON) {
389 ss_state = "LISTENING";
390 } else {
391 ss_state = "";
392 }
393 break;
394
395 case SS_CONNECTING:
396 ss_state = "CONNECTING";
397 break;
398
399 case SS_CONNECTED:
400 ss_state = "CONNECTED";
401 break;
402
403 case SS_DISCONNECTING:
404 ss_state = "DISCONNECTING";
405 break;
406
407 default:
408 ss_state = "UNKNOWN";
409 }
410
411 strcpy(ss_flags, "[ ");
412 if (unix_flags & SO_ACCEPTCON)
413 strcat(ss_flags, "ACC ");
414 if (unix_flags & SO_WAITDATA)
415 strcat(ss_flags, "W ");
416 if (unix_flags & SO_NOSPACE)
417 strcat(ss_flags, "N ");
418
419 strcat(ss_flags, "]");
420
421 printf("%-5s %-6ld %-11s %-10s %-13s ",
422 ss_proto, refcnt, ss_flags, ss_type, ss_state);
423 if (has & HAS_INODE)
424 printf("%-6d ",inode);
425 else
426 printf("- ");
427 puts(path);
428}
429
430#define _PATH_PROCNET_UDP "/proc/net/udp"
431#define _PATH_PROCNET_TCP "/proc/net/tcp"
432#define _PATH_PROCNET_RAW "/proc/net/raw"
433#define _PATH_PROCNET_UNIX "/proc/net/unix"
434
435static int do_info(char *file, char *name, void (*proc)(int, const char *))
436{
437 char buffer[8192];
438 int rc = 0;
439 int lnr = 0;
440 FILE *procinfo;
441
442 procinfo = fopen((file), "r");
443 if (procinfo == NULL) {
444 if (errno != ENOENT) {
445 perror((file));
446 return -1;
447 }
448 error_msg("%s: no support for `%s' on this system.\n",
449 "netstat", (name));
450 rc = 1;
451 } else {
452 do {
453 if (fgets(buffer, sizeof(buffer), procinfo))
454 (proc)(lnr++, buffer);
455 } while (!feof(procinfo));
456 fclose(procinfo);
457 }
458 return rc;
459}
460
461/*
462 * Our main function.
463 */
464
465int netstat_main(int argc, char **argv)
466{
467 int opt;
468 int new_flags=0;
Robert Griebl820098f2002-05-14 23:03:23 +0000469 int showroute = 0, extended = 0;
470 while ((opt = getopt(argc, argv, "laenrtuwx")) != -1)
Eric Andersen31a0ece2001-10-31 11:00:46 +0000471 switch (opt) {
472 case 'l':
473 flags &= ~NETSTAT_CONNECTED;
474 flags |= NETSTAT_LISTENING;
475 break;
476 case 'a':
477 flags |= NETSTAT_LISTENING | NETSTAT_CONNECTED;
478 break;
479 case 'n':
480 flags |= NETSTAT_NUMERIC;
481 break;
Robert Griebl820098f2002-05-14 23:03:23 +0000482 case 'r':
483 showroute = 1;
484 break;
485 case 'e':
486 extended = 1;
487 break;
Eric Andersen31a0ece2001-10-31 11:00:46 +0000488 case 't':
489 new_flags |= NETSTAT_TCP;
490 break;
491 case 'u':
492 new_flags |= NETSTAT_UDP;
493 break;
494 case 'w':
495 new_flags |= NETSTAT_RAW;
496 break;
497 case 'x':
498 new_flags |= NETSTAT_UNIX;
499 break;
500 default:
501 show_usage();
502 }
Robert Griebl820098f2002-05-14 23:03:23 +0000503 if ( showroute ) {
504#ifdef CONFIG_ROUTE
505 displayroutes ( flags & NETSTAT_NUMERIC, !extended );
506 return 0;
507#else
508 printf( "-r (display routing table) is not compiled in.\n" );
509 return 1;
510#endif
511 }
512
Eric Andersen31a0ece2001-10-31 11:00:46 +0000513 if (new_flags) {
514 flags &= ~(NETSTAT_TCP|NETSTAT_UDP|NETSTAT_RAW|NETSTAT_UNIX);
515 flags |= new_flags;
516 }
517 if (flags&(NETSTAT_TCP|NETSTAT_UDP|NETSTAT_RAW)) {
518 printf("Active Internet connections "); /* xxx */
519
520 if ((flags&(NETSTAT_LISTENING|NETSTAT_CONNECTED))==(NETSTAT_LISTENING|NETSTAT_CONNECTED))
521 printf("(servers and established)");
522 else {
523 if (flags&NETSTAT_LISTENING)
524 printf("(only servers)");
525 else
526 printf("(w/o servers)");
527 }
528 printf("\nProto Recv-Q Send-Q Local Address Foreign Address State \n");
529 }
530 if (flags&NETSTAT_TCP)
531 do_info(_PATH_PROCNET_TCP,"AF INET (tcp)",tcp_do_one);
532 if (flags&NETSTAT_UDP)
533 do_info(_PATH_PROCNET_UDP,"AF INET (udp)",udp_do_one);
534 if (flags&NETSTAT_RAW)
535 do_info(_PATH_PROCNET_RAW,"AF INET (raw)",raw_do_one);
536 if (flags&NETSTAT_UNIX) {
537 printf("Active UNIX domain sockets ");
538 if ((flags&(NETSTAT_LISTENING|NETSTAT_CONNECTED))==(NETSTAT_LISTENING|NETSTAT_CONNECTED))
539 printf("(servers and established)");
540 else {
541 if (flags&NETSTAT_LISTENING)
542 printf("(only servers)");
543 else
544 printf("(w/o servers)");
545 }
546
547 printf("\nProto RefCnt Flags Type State I-Node Path\n");
548 do_info(_PATH_PROCNET_UNIX,"AF UNIX",unix_do_one);
549 }
550 return 0;
551}