blob: 00b58228e5da5a216440a66c3c6b8202d2bffe6a [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>
Eric Andersencd8c4362001-11-10 11:22:46 +000032#include "inet_common.h"
Eric Andersen31a0ece2001-10-31 11:00:46 +000033#include "busybox.h"
Robert Grieblea1a63a2002-06-04 20:10:23 +000034#include "pwd.h"
Eric Andersen31a0ece2001-10-31 11:00:46 +000035
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
Eric Andersenb0c39a82002-06-22 17:32:58 +000048static int flags = NETSTAT_CONNECTED |
Eric Andersen31a0ece2001-10-31 11:00:46 +000049 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
Eric Andersenb0c39a82002-06-22 17:32:58 +000071static const char * const tcp_state[] =
Eric Andersen31a0ece2001-10-31 11:00:46 +000072{
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];
Eric Andersenb0c39a82002-06-22 17:32:58 +0000145 const char *state_str;
146 char more[512];
Eric Andersen31a0ece2001-10-31 11:00:46 +0000147 int num, local_port, rem_port, d, state, timer_run, uid, timeout;
148 struct sockaddr_in localaddr, remaddr;
149 unsigned long rxq, txq, time_len, retr, inode;
150
151 if (lnr == 0)
152 return;
153
154 more[0] = '\0';
155 num = sscanf(line,
156 "%d: %64[0-9A-Fa-f]:%X %64[0-9A-Fa-f]:%X %X %lX:%lX %X:%lX %lX %d %d %ld %512s\n",
157 &d, local_addr, &local_port,
158 rem_addr, &rem_port, &state,
159 &txq, &rxq, &timer_run, &time_len, &retr, &uid, &timeout, &inode, more);
160
161 if (strlen(local_addr) > 8) {
162 } else {
163 sscanf(local_addr, "%X",
164 &((struct sockaddr_in *) &localaddr)->sin_addr.s_addr);
165 sscanf(rem_addr, "%X",
166 &((struct sockaddr_in *) &remaddr)->sin_addr.s_addr);
167 ((struct sockaddr *) &localaddr)->sa_family = AF_INET;
168 ((struct sockaddr *) &remaddr)->sa_family = AF_INET;
169 }
170
171 if (num < 10) {
Eric Andersenb0c39a82002-06-22 17:32:58 +0000172 error_msg("warning, got bogus tcp line.");
Eric Andersen31a0ece2001-10-31 11:00:46 +0000173 return;
174 }
Eric Andersenb0c39a82002-06-22 17:32:58 +0000175 state_str = tcp_state[state];
Eric Andersen31a0ece2001-10-31 11:00:46 +0000176 if ((rem_port && (flags&NETSTAT_CONNECTED)) ||
177 (!rem_port && (flags&NETSTAT_LISTENING)))
178 {
179 snprint_ip_port(local_addr, sizeof(local_addr),
180 (struct sockaddr *) &localaddr, local_port,
181 "tcp", flags&NETSTAT_NUMERIC);
182
183 snprint_ip_port(rem_addr, sizeof(rem_addr),
184 (struct sockaddr *) &remaddr, rem_port,
185 "tcp", flags&NETSTAT_NUMERIC);
186
187 printf("tcp %6ld %6ld %-23s %-23s %-12s\n",
188 rxq, txq, local_addr, rem_addr, state_str);
189
190 }
191}
192
193static void udp_do_one(int lnr, const char *line)
194{
195 char local_addr[64], rem_addr[64];
196 char *state_str, more[512];
197 int num, local_port, rem_port, d, state, timer_run, uid, timeout;
198 struct sockaddr_in localaddr, remaddr;
199 unsigned long rxq, txq, time_len, retr, inode;
200
201 if (lnr == 0)
202 return;
203
204 more[0] = '\0';
205 num = sscanf(line,
206 "%d: %64[0-9A-Fa-f]:%X %64[0-9A-Fa-f]:%X %X %lX:%lX %X:%lX %lX %d %d %ld %512s\n",
207 &d, local_addr, &local_port,
208 rem_addr, &rem_port, &state,
209 &txq, &rxq, &timer_run, &time_len, &retr, &uid, &timeout, &inode, more);
210
211 if (strlen(local_addr) > 8) {
212 } else {
213 sscanf(local_addr, "%X",
214 &((struct sockaddr_in *) &localaddr)->sin_addr.s_addr);
215 sscanf(rem_addr, "%X",
216 &((struct sockaddr_in *) &remaddr)->sin_addr.s_addr);
217 ((struct sockaddr *) &localaddr)->sa_family = AF_INET;
218 ((struct sockaddr *) &remaddr)->sa_family = AF_INET;
219 }
220
221 if (num < 10) {
Eric Andersenb0c39a82002-06-22 17:32:58 +0000222 error_msg("warning, got bogus udp line.");
Eric Andersen31a0ece2001-10-31 11:00:46 +0000223 return;
224 }
225 switch (state) {
226 case TCP_ESTABLISHED:
227 state_str = "ESTABLISHED";
228 break;
229
230 case TCP_CLOSE:
231 state_str = "";
232 break;
233
234 default:
235 state_str = "UNKNOWN";
236 break;
237 }
238
239#define notnull(A) (A.sin_addr.s_addr)
240 if ((notnull(remaddr) && (flags&NETSTAT_CONNECTED)) ||
241 (!notnull(remaddr) && (flags&NETSTAT_LISTENING)))
242 {
243 snprint_ip_port(local_addr, sizeof(local_addr),
244 (struct sockaddr *) &localaddr, local_port,
245 "udp", flags&NETSTAT_NUMERIC);
246
247 snprint_ip_port(rem_addr, sizeof(rem_addr),
248 (struct sockaddr *) &remaddr, rem_port,
249 "udp", flags&NETSTAT_NUMERIC);
250
251 printf("udp %6ld %6ld %-23s %-23s %-12s\n",
252 rxq, txq, local_addr, rem_addr, state_str);
253
254 }
255}
256
257static void raw_do_one(int lnr, const char *line)
258{
259 char local_addr[64], rem_addr[64];
260 char *state_str, more[512];
261 int num, local_port, rem_port, d, state, timer_run, uid, timeout;
262 struct sockaddr_in localaddr, remaddr;
263 unsigned long rxq, txq, time_len, retr, inode;
264
265 if (lnr == 0)
266 return;
267
268 more[0] = '\0';
269 num = sscanf(line,
270 "%d: %64[0-9A-Fa-f]:%X %64[0-9A-Fa-f]:%X %X %lX:%lX %X:%lX %lX %d %d %ld %512s\n",
271 &d, local_addr, &local_port,
272 rem_addr, &rem_port, &state,
273 &txq, &rxq, &timer_run, &time_len, &retr, &uid, &timeout, &inode, more);
274
275 if (strlen(local_addr) > 8) {
276 } else {
277 sscanf(local_addr, "%X",
278 &((struct sockaddr_in *) &localaddr)->sin_addr.s_addr);
279 sscanf(rem_addr, "%X",
280 &((struct sockaddr_in *) &remaddr)->sin_addr.s_addr);
281 ((struct sockaddr *) &localaddr)->sa_family = AF_INET;
282 ((struct sockaddr *) &remaddr)->sa_family = AF_INET;
283 }
284
285 if (num < 10) {
Eric Andersenb0c39a82002-06-22 17:32:58 +0000286 error_msg("warning, got bogus raw line.");
Eric Andersen31a0ece2001-10-31 11:00:46 +0000287 return;
288 }
289 state_str=itoa(state);
290
291#define notnull(A) (A.sin_addr.s_addr)
292 if ((notnull(remaddr) && (flags&NETSTAT_CONNECTED)) ||
293 (!notnull(remaddr) && (flags&NETSTAT_LISTENING)))
294 {
295 snprint_ip_port(local_addr, sizeof(local_addr),
296 (struct sockaddr *) &localaddr, local_port,
297 "raw", flags&NETSTAT_NUMERIC);
298
299 snprint_ip_port(rem_addr, sizeof(rem_addr),
300 (struct sockaddr *) &remaddr, rem_port,
301 "raw", flags&NETSTAT_NUMERIC);
302
303 printf("raw %6ld %6ld %-23s %-23s %-12s\n",
304 rxq, txq, local_addr, rem_addr, state_str);
305
306 }
307}
308
309#define HAS_INODE 1
310
311static void unix_do_one(int nr, const char *line)
312{
313 static int has = 0;
314 char path[PATH_MAX], ss_flags[32];
315 char *ss_proto, *ss_state, *ss_type;
316 int num, state, type, inode;
317 void *d;
318 unsigned long refcnt, proto, unix_flags;
319
320 if (nr == 0) {
321 if (strstr(line, "Inode"))
322 has |= HAS_INODE;
323 return;
324 }
325 path[0] = '\0';
326 num = sscanf(line, "%p: %lX %lX %lX %X %X %d %s",
327 &d, &refcnt, &proto, &unix_flags, &type, &state, &inode, path);
328 if (num < 6) {
Eric Andersenb0c39a82002-06-22 17:32:58 +0000329 error_msg("warning, got bogus unix line.");
Eric Andersen31a0ece2001-10-31 11:00:46 +0000330 return;
331 }
332 if (!(has & HAS_INODE))
333 snprintf(path,sizeof(path),"%d",inode);
334
335 if ((flags&(NETSTAT_LISTENING|NETSTAT_CONNECTED))!=(NETSTAT_LISTENING|NETSTAT_CONNECTED)) {
336 if ((state == SS_UNCONNECTED) && (unix_flags & SO_ACCEPTCON)) {
337 if (!(flags&NETSTAT_LISTENING))
338 return;
339 } else {
340 if (!(flags&NETSTAT_CONNECTED))
341 return;
342 }
343 }
344
345 switch (proto) {
346 case 0:
347 ss_proto = "unix";
348 break;
349
350 default:
351 ss_proto = "??";
352 }
353
354 switch (type) {
355 case SOCK_STREAM:
356 ss_type = "STREAM";
357 break;
358
359 case SOCK_DGRAM:
360 ss_type = "DGRAM";
361 break;
362
363 case SOCK_RAW:
364 ss_type = "RAW";
365 break;
366
367 case SOCK_RDM:
368 ss_type = "RDM";
369 break;
370
371 case SOCK_SEQPACKET:
372 ss_type = "SEQPACKET";
373 break;
374
375 default:
376 ss_type = "UNKNOWN";
377 }
378
379 switch (state) {
380 case SS_FREE:
381 ss_state = "FREE";
382 break;
383
384 case SS_UNCONNECTED:
385 /*
386 * Unconnected sockets may be listening
387 * for something.
388 */
389 if (unix_flags & SO_ACCEPTCON) {
390 ss_state = "LISTENING";
391 } else {
392 ss_state = "";
393 }
394 break;
395
396 case SS_CONNECTING:
397 ss_state = "CONNECTING";
398 break;
399
400 case SS_CONNECTED:
401 ss_state = "CONNECTED";
402 break;
403
404 case SS_DISCONNECTING:
405 ss_state = "DISCONNECTING";
406 break;
407
408 default:
409 ss_state = "UNKNOWN";
410 }
411
412 strcpy(ss_flags, "[ ");
413 if (unix_flags & SO_ACCEPTCON)
414 strcat(ss_flags, "ACC ");
415 if (unix_flags & SO_WAITDATA)
416 strcat(ss_flags, "W ");
417 if (unix_flags & SO_NOSPACE)
418 strcat(ss_flags, "N ");
419
420 strcat(ss_flags, "]");
421
422 printf("%-5s %-6ld %-11s %-10s %-13s ",
423 ss_proto, refcnt, ss_flags, ss_type, ss_state);
424 if (has & HAS_INODE)
425 printf("%-6d ",inode);
426 else
427 printf("- ");
428 puts(path);
429}
430
431#define _PATH_PROCNET_UDP "/proc/net/udp"
432#define _PATH_PROCNET_TCP "/proc/net/tcp"
433#define _PATH_PROCNET_RAW "/proc/net/raw"
434#define _PATH_PROCNET_UNIX "/proc/net/unix"
435
Eric Andersenb0c39a82002-06-22 17:32:58 +0000436static void do_info(const char *file, const char *name, void (*proc)(int, const char *))
Eric Andersen31a0ece2001-10-31 11:00:46 +0000437{
438 char buffer[8192];
Eric Andersen31a0ece2001-10-31 11:00:46 +0000439 int lnr = 0;
440 FILE *procinfo;
441
Eric Andersenb0c39a82002-06-22 17:32:58 +0000442 procinfo = fopen(file, "r");
Eric Andersen31a0ece2001-10-31 11:00:46 +0000443 if (procinfo == NULL) {
444 if (errno != ENOENT) {
Eric Andersenb0c39a82002-06-22 17:32:58 +0000445 perror(file);
446 } else {
447 error_msg("no support for `%s' on this system.", name);
Eric Andersen31a0ece2001-10-31 11:00:46 +0000448 }
Eric Andersen31a0ece2001-10-31 11:00:46 +0000449 } else {
450 do {
451 if (fgets(buffer, sizeof(buffer), procinfo))
452 (proc)(lnr++, buffer);
453 } while (!feof(procinfo));
454 fclose(procinfo);
455 }
Eric Andersen31a0ece2001-10-31 11:00:46 +0000456}
457
458/*
459 * Our main function.
460 */
461
462int netstat_main(int argc, char **argv)
463{
464 int opt;
465 int new_flags=0;
Robert Griebl820098f2002-05-14 23:03:23 +0000466 int showroute = 0, extended = 0;
467 while ((opt = getopt(argc, argv, "laenrtuwx")) != -1)
Eric Andersen31a0ece2001-10-31 11:00:46 +0000468 switch (opt) {
469 case 'l':
470 flags &= ~NETSTAT_CONNECTED;
471 flags |= NETSTAT_LISTENING;
472 break;
473 case 'a':
474 flags |= NETSTAT_LISTENING | NETSTAT_CONNECTED;
475 break;
476 case 'n':
477 flags |= NETSTAT_NUMERIC;
478 break;
Robert Griebl820098f2002-05-14 23:03:23 +0000479 case 'r':
480 showroute = 1;
481 break;
482 case 'e':
483 extended = 1;
484 break;
Eric Andersen31a0ece2001-10-31 11:00:46 +0000485 case 't':
486 new_flags |= NETSTAT_TCP;
487 break;
488 case 'u':
489 new_flags |= NETSTAT_UDP;
490 break;
491 case 'w':
492 new_flags |= NETSTAT_RAW;
493 break;
494 case 'x':
495 new_flags |= NETSTAT_UNIX;
496 break;
497 default:
498 show_usage();
499 }
Robert Griebl820098f2002-05-14 23:03:23 +0000500 if ( showroute ) {
501#ifdef CONFIG_ROUTE
502 displayroutes ( flags & NETSTAT_NUMERIC, !extended );
503 return 0;
504#else
Eric Andersenb0c39a82002-06-22 17:32:58 +0000505 error_msg_and_die( "-r (display routing table) is not compiled in." );
Robert Griebl820098f2002-05-14 23:03:23 +0000506#endif
507 }
508
Eric Andersen31a0ece2001-10-31 11:00:46 +0000509 if (new_flags) {
510 flags &= ~(NETSTAT_TCP|NETSTAT_UDP|NETSTAT_RAW|NETSTAT_UNIX);
511 flags |= new_flags;
512 }
513 if (flags&(NETSTAT_TCP|NETSTAT_UDP|NETSTAT_RAW)) {
514 printf("Active Internet connections "); /* xxx */
515
516 if ((flags&(NETSTAT_LISTENING|NETSTAT_CONNECTED))==(NETSTAT_LISTENING|NETSTAT_CONNECTED))
517 printf("(servers and established)");
518 else {
519 if (flags&NETSTAT_LISTENING)
520 printf("(only servers)");
521 else
522 printf("(w/o servers)");
523 }
524 printf("\nProto Recv-Q Send-Q Local Address Foreign Address State \n");
525 }
526 if (flags&NETSTAT_TCP)
527 do_info(_PATH_PROCNET_TCP,"AF INET (tcp)",tcp_do_one);
528 if (flags&NETSTAT_UDP)
529 do_info(_PATH_PROCNET_UDP,"AF INET (udp)",udp_do_one);
530 if (flags&NETSTAT_RAW)
531 do_info(_PATH_PROCNET_RAW,"AF INET (raw)",raw_do_one);
532 if (flags&NETSTAT_UNIX) {
533 printf("Active UNIX domain sockets ");
534 if ((flags&(NETSTAT_LISTENING|NETSTAT_CONNECTED))==(NETSTAT_LISTENING|NETSTAT_CONNECTED))
535 printf("(servers and established)");
536 else {
537 if (flags&NETSTAT_LISTENING)
538 printf("(only servers)");
539 else
540 printf("(w/o servers)");
541 }
542
543 printf("\nProto RefCnt Flags Type State I-Node Path\n");
544 do_info(_PATH_PROCNET_UNIX,"AF UNIX",unix_do_one);
545 }
546 return 0;
547}