Magellan Linux

Annotation of /tags/mkinitrd-6_2_0/networking/netstat.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 994 - (hide annotations) (download)
Sun May 30 11:53:18 2010 UTC (14 years ago) by niro
File MIME type: text/plain
File size: 18069 byte(s)
tagged 'mkinitrd-6_2_0'
1 niro 532 /* 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 niro 816 * 2008-07-10
12     * optional '-p' flag support ported from net-tools by G. Somlo <somlo@cmu.edu>
13     *
14 niro 532 * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
15     */
16    
17 niro 816 #include "libbb.h"
18 niro 532 #include "inet_common.h"
19    
20 niro 816 #define NETSTAT_OPTS "laentuwx" \
21 niro 984 IF_ROUTE( "r") \
22     IF_FEATURE_NETSTAT_WIDE("W") \
23     IF_FEATURE_NETSTAT_PRG( "p")
24 niro 532
25 niro 816 enum {
26     OPTBIT_KEEP_OLD = 7,
27 niro 984 IF_ROUTE( OPTBIT_ROUTE,)
28     IF_FEATURE_NETSTAT_WIDE(OPTBIT_WIDE ,)
29     IF_FEATURE_NETSTAT_PRG( OPTBIT_PRG ,)
30 niro 816 OPT_sock_listen = 1 << 0, // l
31     OPT_sock_all = 1 << 1, // a
32     OPT_extended = 1 << 2, // e
33     OPT_noresolve = 1 << 3, // n
34     OPT_sock_tcp = 1 << 4, // t
35     OPT_sock_udp = 1 << 5, // u
36     OPT_sock_raw = 1 << 6, // w
37     OPT_sock_unix = 1 << 7, // x
38 niro 984 OPT_route = IF_ROUTE( (1 << OPTBIT_ROUTE)) + 0, // r
39     OPT_wide = IF_FEATURE_NETSTAT_WIDE((1 << OPTBIT_WIDE )) + 0, // W
40     OPT_prg = IF_FEATURE_NETSTAT_PRG( (1 << OPTBIT_PRG )) + 0, // p
41 niro 816 };
42    
43     #define NETSTAT_CONNECTED 0x01
44     #define NETSTAT_LISTENING 0x02
45     #define NETSTAT_NUMERIC 0x04
46 niro 532 /* Must match getopt32 option string */
47 niro 816 #define NETSTAT_TCP 0x10
48     #define NETSTAT_UDP 0x20
49     #define NETSTAT_RAW 0x40
50     #define NETSTAT_UNIX 0x80
51     #define NETSTAT_ALLPROTO (NETSTAT_TCP|NETSTAT_UDP|NETSTAT_RAW|NETSTAT_UNIX)
52 niro 532
53    
54     enum {
55 niro 816 TCP_ESTABLISHED = 1,
56     TCP_SYN_SENT,
57     TCP_SYN_RECV,
58     TCP_FIN_WAIT1,
59     TCP_FIN_WAIT2,
60     TCP_TIME_WAIT,
61     TCP_CLOSE,
62     TCP_CLOSE_WAIT,
63     TCP_LAST_ACK,
64     TCP_LISTEN,
65     TCP_CLOSING, /* now a valid state */
66 niro 532 };
67    
68 niro 816 static const char *const tcp_state[] = {
69     "",
70     "ESTABLISHED",
71     "SYN_SENT",
72     "SYN_RECV",
73     "FIN_WAIT1",
74     "FIN_WAIT2",
75     "TIME_WAIT",
76     "CLOSE",
77     "CLOSE_WAIT",
78     "LAST_ACK",
79     "LISTEN",
80     "CLOSING"
81 niro 532 };
82    
83     typedef enum {
84 niro 816 SS_FREE = 0, /* not allocated */
85     SS_UNCONNECTED, /* unconnected to any socket */
86     SS_CONNECTING, /* in process of connecting */
87     SS_CONNECTED, /* connected to socket */
88     SS_DISCONNECTING /* in process of disconnecting */
89 niro 532 } socket_state;
90    
91 niro 816 #define SO_ACCEPTCON (1<<16) /* performed a listen */
92     #define SO_WAITDATA (1<<17) /* wait data to read */
93     #define SO_NOSPACE (1<<18) /* no space to write */
94 niro 532
95 niro 816 /* Standard printout size */
96     #define PRINT_IP_MAX_SIZE 23
97     #define PRINT_NET_CONN "%s %6ld %6ld %-23s %-23s %-12s"
98     #define PRINT_NET_CONN_HEADER "\nProto Recv-Q Send-Q %-23s %-23s State "
99    
100     /* When there are IPv6 connections the IPv6 addresses will be
101     * truncated to none-recognition. The '-W' option makes the
102     * address columns wide enough to accomodate for longest possible
103     * IPv6 addresses, i.e. addresses of the form
104     * xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:ddd.ddd.ddd.ddd
105     */
106     #define PRINT_IP_MAX_SIZE_WIDE 51 /* INET6_ADDRSTRLEN + 5 for the port number */
107     #define PRINT_NET_CONN_WIDE "%s %6ld %6ld %-51s %-51s %-12s"
108     #define PRINT_NET_CONN_HEADER_WIDE "\nProto Recv-Q Send-Q %-51s %-51s State "
109    
110    
111     #define PROGNAME_WIDTH 20
112     #define PROGNAME_WIDTH_STR "20"
113     /* PROGNAME_WIDTH chars: 12345678901234567890 */
114     #define PROGNAME_BANNER "PID/Program name "
115    
116     struct prg_node {
117     struct prg_node *next;
118     long inode;
119     char name[PROGNAME_WIDTH];
120     };
121    
122     #define PRG_HASH_SIZE 211
123    
124    
125     struct globals {
126     const char *net_conn_line;
127     smallint flags;
128     #if ENABLE_FEATURE_NETSTAT_PRG
129     smallint prg_cache_loaded;
130     struct prg_node *prg_hash[PRG_HASH_SIZE];
131     #endif
132     };
133     #define G (*ptr_to_globals)
134     #define flags (G.flags )
135     #define net_conn_line (G.net_conn_line )
136     #define prg_hash (G.prg_hash )
137     #define prg_cache_loaded (G.prg_cache_loaded)
138     #define INIT_G() do { \
139     SET_PTR_TO_GLOBALS(xzalloc(sizeof(G))); \
140     flags = NETSTAT_CONNECTED | NETSTAT_ALLPROTO; \
141     net_conn_line = PRINT_NET_CONN; \
142     } while (0)
143    
144    
145     #if ENABLE_FEATURE_NETSTAT_PRG
146    
147     /* Deliberately truncating long to unsigned *int* */
148     #define PRG_HASHIT(x) ((unsigned)(x) % PRG_HASH_SIZE)
149    
150     #define print_progname_banner() do { \
151     if (option_mask32 & OPT_prg) printf(PROGNAME_BANNER); \
152     } while (0)
153    
154     static void prg_cache_add(long inode, char *name)
155 niro 532 {
156 niro 816 unsigned hi = PRG_HASHIT(inode);
157     struct prg_node **pnp, *pn;
158    
159     prg_cache_loaded = 2;
160     for (pnp = prg_hash + hi; (pn = *pnp) != NULL; pnp = &pn->next) {
161     if (pn->inode == inode) {
162     /* Some warning should be appropriate here
163     as we got multiple processes for one i-node */
164     return;
165     }
166 niro 532 }
167 niro 816 *pnp = xzalloc(sizeof(struct prg_node));
168     pn = *pnp;
169     pn->inode = inode;
170     safe_strncpy(pn->name, name, PROGNAME_WIDTH);
171 niro 532 }
172    
173 niro 816 static const char *prg_cache_get(long inode)
174 niro 532 {
175 niro 816 unsigned hi = PRG_HASHIT(inode);
176     struct prg_node *pn;
177 niro 532
178 niro 816 for (pn = prg_hash[hi]; pn; pn = pn->next)
179     if (pn->inode == inode)
180     return pn->name;
181     return "-";
182 niro 532 }
183    
184 niro 816 #if ENABLE_FEATURE_CLEAN_UP
185     static void prg_cache_clear(void)
186 niro 532 {
187 niro 816 struct prg_node **pnp, *pn;
188    
189     for (pnp = prg_hash; pnp < prg_hash + PRG_HASH_SIZE; pnp++) {
190     while ((pn = *pnp) != NULL) {
191     *pnp = pn->next;
192     free(pn);
193     }
194     }
195     }
196 niro 532 #else
197 niro 816 #define prg_cache_clear() ((void)0)
198 niro 532 #endif
199    
200 niro 816 static long extract_socket_inode(const char *lname)
201     {
202     long inode = -1;
203 niro 532
204 niro 816 if (strncmp(lname, "socket:[", sizeof("socket:[")-1) == 0) {
205     /* "socket:[12345]", extract the "12345" as inode */
206     inode = bb_strtol(lname + sizeof("socket:[")-1, (char**)&lname, 0);
207     if (*lname != ']')
208     inode = -1;
209     } else if (strncmp(lname, "[0000]:", sizeof("[0000]:")-1) == 0) {
210     /* "[0000]:12345", extract the "12345" as inode */
211     inode = bb_strtol(lname + sizeof("[0000]:")-1, NULL, 0);
212     if (errno) /* not NUL terminated? */
213     inode = -1;
214     }
215 niro 532
216 niro 816 #if 0 /* bb_strtol returns all-ones bit pattern on ERANGE anyway */
217     if (errno == ERANGE)
218     inode = -1;
219 niro 532 #endif
220 niro 816 return inode;
221     }
222 niro 532
223 niro 816 static int FAST_FUNC file_act(const char *fileName,
224     struct stat *statbuf UNUSED_PARAM,
225     void *userData,
226     int depth UNUSED_PARAM)
227     {
228     char *linkname;
229     long inode;
230    
231     linkname = xmalloc_readlink(fileName);
232     if (linkname != NULL) {
233     inode = extract_socket_inode(linkname);
234     free(linkname);
235     if (inode >= 0)
236     prg_cache_add(inode, (char *)userData);
237 niro 532 }
238 niro 816 return TRUE;
239     }
240 niro 532
241 niro 816 static int FAST_FUNC dir_act(const char *fileName,
242     struct stat *statbuf UNUSED_PARAM,
243     void *userData UNUSED_PARAM,
244     int depth)
245     {
246     const char *shortName;
247     char *p, *q;
248     char cmdline_buf[512];
249     int i;
250 niro 532
251 niro 816 if (depth == 0) /* "/proc" itself */
252     return TRUE; /* continue looking one level below /proc */
253 niro 532
254 niro 816 shortName = fileName + sizeof("/proc/")-1; /* point after "/proc/" */
255     if (!isdigit(shortName[0])) /* skip /proc entries whic aren't processes */
256     return SKIP;
257    
258     p = concat_path_file(fileName, "cmdline"); /* "/proc/PID/cmdline" */
259     i = open_read_close(p, cmdline_buf, sizeof(cmdline_buf) - 1);
260     free(p);
261     if (i < 0)
262     return FALSE;
263     cmdline_buf[i] = '\0';
264     q = concat_path_file(shortName, bb_basename(cmdline_buf)); /* "PID/argv0" */
265    
266     /* go through all files in /proc/PID/fd */
267     p = concat_path_file(fileName, "fd");
268     i = recursive_action(p, ACTION_RECURSE | ACTION_QUIET,
269     file_act, NULL, (void *)q, 0);
270    
271     free(p);
272     free(q);
273    
274     if (!i)
275     return FALSE; /* signal permissions error to caller */
276    
277     return SKIP; /* caller should not recurse further into this dir. */
278 niro 532 }
279    
280 niro 816 static void prg_cache_load(void)
281 niro 532 {
282 niro 816 int load_ok;
283 niro 532
284 niro 816 prg_cache_loaded = 1;
285     load_ok = recursive_action("/proc", ACTION_RECURSE | ACTION_QUIET,
286     NULL, dir_act, NULL, 0);
287     if (load_ok)
288 niro 532 return;
289    
290 niro 816 if (prg_cache_loaded == 1)
291     bb_error_msg("can't scan /proc - are you root?");
292     else
293     bb_error_msg("showing only processes with your user ID");
294     }
295 niro 532
296 niro 816 #else
297 niro 532
298 niro 816 #define prg_cache_clear() ((void)0)
299     #define print_progname_banner() ((void)0)
300 niro 532
301 niro 816 #endif //ENABLE_FEATURE_NETSTAT_PRG
302 niro 532
303    
304 niro 816 #if ENABLE_FEATURE_IPV6
305     static void build_ipv6_addr(char* local_addr, struct sockaddr_in6* localaddr)
306     {
307     char addr6[INET6_ADDRSTRLEN];
308     struct in6_addr in6;
309    
310     sscanf(local_addr, "%08X%08X%08X%08X",
311     &in6.s6_addr32[0], &in6.s6_addr32[1],
312     &in6.s6_addr32[2], &in6.s6_addr32[3]);
313     inet_ntop(AF_INET6, &in6, addr6, sizeof(addr6));
314     inet_pton(AF_INET6, addr6, (struct sockaddr *) &localaddr->sin6_addr);
315    
316     localaddr->sin6_family = AF_INET6;
317     }
318     #endif
319    
320     #if ENABLE_FEATURE_IPV6
321     static void build_ipv4_addr(char* local_addr, struct sockaddr_in6* localaddr)
322 niro 532 #else
323 niro 816 static void build_ipv4_addr(char* local_addr, struct sockaddr_in* localaddr)
324 niro 532 #endif
325 niro 816 {
326     sscanf(local_addr, "%X",
327     &((struct sockaddr_in *) localaddr)->sin_addr.s_addr);
328     ((struct sockaddr *) localaddr)->sa_family = AF_INET;
329     }
330 niro 532
331 niro 816 static const char *get_sname(int port, const char *proto, int numeric)
332     {
333     if (!port)
334     return "*";
335     if (!numeric) {
336     struct servent *se = getservbyport(port, proto);
337     if (se)
338     return se->s_name;
339     }
340     /* hummm, we may return static buffer here!! */
341     return itoa(ntohs(port));
342     }
343 niro 532
344 niro 816 static char *ip_port_str(struct sockaddr *addr, int port, const char *proto, int numeric)
345     {
346     char *host, *host_port;
347 niro 532
348 niro 816 /* Code which used "*" for INADDR_ANY is removed: it's ambiguous
349     * in IPv6, while "0.0.0.0" is not. */
350    
351     host = numeric ? xmalloc_sockaddr2dotted_noport(addr)
352     : xmalloc_sockaddr2host_noport(addr);
353    
354     host_port = xasprintf("%s:%s", host, get_sname(htons(port), proto, numeric));
355     free(host);
356     return host_port;
357 niro 532 }
358    
359 niro 816 struct inet_params {
360     int local_port, rem_port, state, uid;
361     #if ENABLE_FEATURE_IPV6
362 niro 532 struct sockaddr_in6 localaddr, remaddr;
363     #else
364     struct sockaddr_in localaddr, remaddr;
365     #endif
366 niro 816 unsigned long rxq, txq, inode;
367     };
368 niro 532
369 niro 816 static int scan_inet_proc_line(struct inet_params *param, char *line)
370     {
371     int num;
372     char local_addr[64], rem_addr[64];
373 niro 532
374     num = sscanf(line,
375 niro 816 "%*d: %64[0-9A-Fa-f]:%X "
376     "%64[0-9A-Fa-f]:%X %X "
377     "%lX:%lX %*X:%*X "
378     "%*X %d %*d %ld ",
379     local_addr, &param->local_port,
380     rem_addr, &param->rem_port, &param->state,
381     &param->txq, &param->rxq,
382     &param->uid, &param->inode);
383     if (num < 9) {
384     return 1; /* error */
385     }
386 niro 532
387     if (strlen(local_addr) > 8) {
388 niro 816 #if ENABLE_FEATURE_IPV6
389     build_ipv6_addr(local_addr, &param->localaddr);
390     build_ipv6_addr(rem_addr, &param->remaddr);
391 niro 532 #endif
392     } else {
393 niro 816 build_ipv4_addr(local_addr, &param->localaddr);
394     build_ipv4_addr(rem_addr, &param->remaddr);
395 niro 532 }
396 niro 816 return 0;
397     }
398 niro 532
399 niro 816 static void print_inet_line(struct inet_params *param,
400     const char *state_str, const char *proto, int is_connected)
401     {
402     if ((is_connected && (flags & NETSTAT_CONNECTED))
403     || (!is_connected && (flags & NETSTAT_LISTENING))
404     ) {
405     char *l = ip_port_str(
406     (struct sockaddr *) &param->localaddr, param->local_port,
407     proto, flags & NETSTAT_NUMERIC);
408     char *r = ip_port_str(
409     (struct sockaddr *) &param->remaddr, param->rem_port,
410     proto, flags & NETSTAT_NUMERIC);
411     printf(net_conn_line,
412     proto, param->rxq, param->txq, l, r, state_str);
413     #if ENABLE_FEATURE_NETSTAT_PRG
414     if (option_mask32 & OPT_prg)
415     printf("%."PROGNAME_WIDTH_STR"s", prg_cache_get(param->inode));
416     #endif
417     bb_putchar('\n');
418     free(l);
419     free(r);
420 niro 532 }
421 niro 816 }
422 niro 532
423 niro 816 static int FAST_FUNC tcp_do_one(char *line)
424     {
425     struct inet_params param;
426    
427     if (scan_inet_proc_line(&param, line))
428     return 1;
429    
430     print_inet_line(&param, tcp_state[param.state], "tcp", param.rem_port);
431     return 0;
432     }
433    
434     #if ENABLE_FEATURE_IPV6
435     # define notnull(A) ( \
436     ( (A.sin6_family == AF_INET6) \
437     && (A.sin6_addr.s6_addr32[0] | A.sin6_addr.s6_addr32[1] | \
438     A.sin6_addr.s6_addr32[2] | A.sin6_addr.s6_addr32[3]) \
439     ) || ( \
440     (A.sin6_family == AF_INET) \
441     && ((struct sockaddr_in*)&A)->sin_addr.s_addr \
442     ) \
443     )
444 niro 532 #else
445     # define notnull(A) (A.sin_addr.s_addr)
446     #endif
447    
448 niro 816 static int FAST_FUNC udp_do_one(char *line)
449     {
450     int have_remaddr;
451     const char *state_str;
452     struct inet_params param;
453 niro 532
454 niro 816 if (scan_inet_proc_line(&param, line))
455     return 1;
456 niro 532
457 niro 816 state_str = "UNKNOWN";
458     switch (param.state) {
459     case TCP_ESTABLISHED:
460     state_str = "ESTABLISHED";
461     break;
462     case TCP_CLOSE:
463     state_str = "";
464     break;
465 niro 532 }
466 niro 816
467     have_remaddr = notnull(param.remaddr);
468     print_inet_line(&param, state_str, "udp", have_remaddr);
469     return 0;
470 niro 532 }
471    
472 niro 816 static int FAST_FUNC raw_do_one(char *line)
473     {
474     int have_remaddr;
475     struct inet_params param;
476 niro 532
477 niro 816 if (scan_inet_proc_line(&param, line))
478     return 1;
479    
480     have_remaddr = notnull(param.remaddr);
481     print_inet_line(&param, itoa(param.state), "raw", have_remaddr);
482     return 0;
483     }
484    
485     static int FAST_FUNC unix_do_one(char *line)
486 niro 532 {
487     unsigned long refcnt, proto, unix_flags;
488 niro 816 unsigned long inode;
489     int type, state;
490     int num, path_ofs;
491     const char *ss_proto, *ss_state, *ss_type;
492     char ss_flags[32];
493 niro 532
494 niro 816 /* 2.6.15 may report lines like "... @/tmp/fam-user-^@^@^@^@^@^@^@..."
495     * Other users report long lines filled by NUL bytes.
496     * (those ^@ are NUL bytes too). We see them as empty lines. */
497     if (!line[0])
498     return 0;
499    
500     path_ofs = 0; /* paranoia */
501     num = sscanf(line, "%*p: %lX %lX %lX %X %X %lu %n",
502     &refcnt, &proto, &unix_flags, &type, &state, &inode, &path_ofs);
503 niro 532 if (num < 6) {
504 niro 816 return 1; /* error */
505 niro 532 }
506 niro 816 if ((flags & (NETSTAT_LISTENING|NETSTAT_CONNECTED)) != (NETSTAT_LISTENING|NETSTAT_CONNECTED)) {
507 niro 532 if ((state == SS_UNCONNECTED) && (unix_flags & SO_ACCEPTCON)) {
508 niro 816 if (!(flags & NETSTAT_LISTENING))
509     return 0;
510 niro 532 } else {
511 niro 816 if (!(flags & NETSTAT_CONNECTED))
512     return 0;
513 niro 532 }
514     }
515    
516     switch (proto) {
517     case 0:
518     ss_proto = "unix";
519     break;
520     default:
521     ss_proto = "??";
522     }
523    
524     switch (type) {
525     case SOCK_STREAM:
526     ss_type = "STREAM";
527     break;
528     case SOCK_DGRAM:
529     ss_type = "DGRAM";
530     break;
531     case SOCK_RAW:
532     ss_type = "RAW";
533     break;
534     case SOCK_RDM:
535     ss_type = "RDM";
536     break;
537     case SOCK_SEQPACKET:
538     ss_type = "SEQPACKET";
539     break;
540     default:
541     ss_type = "UNKNOWN";
542     }
543    
544     switch (state) {
545     case SS_FREE:
546     ss_state = "FREE";
547     break;
548     case SS_UNCONNECTED:
549     /*
550     * Unconnected sockets may be listening
551     * for something.
552     */
553     if (unix_flags & SO_ACCEPTCON) {
554     ss_state = "LISTENING";
555     } else {
556     ss_state = "";
557     }
558     break;
559     case SS_CONNECTING:
560     ss_state = "CONNECTING";
561     break;
562     case SS_CONNECTED:
563     ss_state = "CONNECTED";
564     break;
565     case SS_DISCONNECTING:
566     ss_state = "DISCONNECTING";
567     break;
568     default:
569     ss_state = "UNKNOWN";
570     }
571    
572     strcpy(ss_flags, "[ ");
573     if (unix_flags & SO_ACCEPTCON)
574     strcat(ss_flags, "ACC ");
575     if (unix_flags & SO_WAITDATA)
576     strcat(ss_flags, "W ");
577     if (unix_flags & SO_NOSPACE)
578     strcat(ss_flags, "N ");
579     strcat(ss_flags, "]");
580    
581 niro 816 printf("%-5s %-6ld %-11s %-10s %-13s %6lu ",
582     ss_proto, refcnt, ss_flags, ss_type, ss_state, inode
583     );
584    
585     #if ENABLE_FEATURE_NETSTAT_PRG
586     if (option_mask32 & OPT_prg)
587     printf("%-"PROGNAME_WIDTH_STR"s", prg_cache_get(inode));
588     #endif
589    
590     /* TODO: currently we stop at first NUL byte. Is it a problem? */
591     line += path_ofs;
592     *strchrnul(line, '\n') = '\0';
593     while (*line)
594     fputc_printable(*line++, stdout);
595     bb_putchar('\n');
596     return 0;
597 niro 532 }
598    
599 niro 816 static void do_info(const char *file, int FAST_FUNC (*proc)(char *))
600 niro 532 {
601 niro 816 int lnr;
602 niro 532 FILE *procinfo;
603 niro 816 char *buffer;
604 niro 532
605 niro 816 /* _stdin is just to save "r" param */
606     procinfo = fopen_or_warn_stdin(file);
607 niro 532 if (procinfo == NULL) {
608 niro 816 return;
609 niro 532 }
610 niro 816 lnr = 0;
611     /* Why xmalloc_fgets_str? because it doesn't stop on NULs */
612     while ((buffer = xmalloc_fgets_str(procinfo, "\n")) != NULL) {
613     /* line 0 is skipped */
614     if (lnr && proc(buffer))
615     bb_error_msg("%s: bogus data on line %d", file, lnr + 1);
616     lnr++;
617     free(buffer);
618     }
619     fclose(procinfo);
620 niro 532 }
621    
622 niro 816 int netstat_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
623     int netstat_main(int argc UNUSED_PARAM, char **argv)
624 niro 532 {
625 niro 816 const char *net_conn_line_header = PRINT_NET_CONN_HEADER;
626 niro 532 unsigned opt;
627    
628 niro 816 INIT_G();
629    
630 niro 532 /* Option string must match NETSTAT_xxx constants */
631 niro 816 opt = getopt32(argv, NETSTAT_OPTS);
632 niro 532 if (opt & 0x1) { // -l
633     flags &= ~NETSTAT_CONNECTED;
634     flags |= NETSTAT_LISTENING;
635     }
636     if (opt & 0x2) flags |= NETSTAT_LISTENING | NETSTAT_CONNECTED; // -a
637     //if (opt & 0x4) // -e
638     if (opt & 0x8) flags |= NETSTAT_NUMERIC; // -n
639     //if (opt & 0x10) // -t: NETSTAT_TCP
640     //if (opt & 0x20) // -u: NETSTAT_UDP
641     //if (opt & 0x40) // -w: NETSTAT_RAW
642     //if (opt & 0x80) // -x: NETSTAT_UNIX
643 niro 816 if (opt & OPT_route) { // -r
644     #if ENABLE_ROUTE
645     bb_displayroutes(flags & NETSTAT_NUMERIC, !(opt & OPT_extended));
646 niro 532 return 0;
647     #else
648 niro 816 bb_show_usage();
649 niro 532 #endif
650     }
651 niro 816 if (opt & OPT_wide) { // -W
652     net_conn_line = PRINT_NET_CONN_WIDE;
653     net_conn_line_header = PRINT_NET_CONN_HEADER_WIDE;
654     }
655     #if ENABLE_FEATURE_NETSTAT_PRG
656     if (opt & OPT_prg) { // -p
657     prg_cache_load();
658     }
659     #endif
660 niro 532
661     opt &= NETSTAT_ALLPROTO;
662     if (opt) {
663     flags &= ~NETSTAT_ALLPROTO;
664     flags |= opt;
665     }
666     if (flags & (NETSTAT_TCP|NETSTAT_UDP|NETSTAT_RAW)) {
667     printf("Active Internet connections "); /* xxx */
668    
669 niro 816 if ((flags & (NETSTAT_LISTENING|NETSTAT_CONNECTED)) == (NETSTAT_LISTENING|NETSTAT_CONNECTED))
670 niro 532 printf("(servers and established)");
671 niro 816 else if (flags & NETSTAT_LISTENING)
672     printf("(only servers)");
673     else
674     printf("(w/o servers)");
675     printf(net_conn_line_header, "Local Address", "Foreign Address");
676     print_progname_banner();
677     bb_putchar('\n');
678 niro 532 }
679 niro 816 if (flags & NETSTAT_TCP) {
680     do_info("/proc/net/tcp", tcp_do_one);
681     #if ENABLE_FEATURE_IPV6
682     do_info("/proc/net/tcp6", tcp_do_one);
683 niro 532 #endif
684 niro 816 }
685     if (flags & NETSTAT_UDP) {
686     do_info("/proc/net/udp", udp_do_one);
687     #if ENABLE_FEATURE_IPV6
688     do_info("/proc/net/udp6", udp_do_one);
689 niro 532 #endif
690 niro 816 }
691     if (flags & NETSTAT_RAW) {
692     do_info("/proc/net/raw", raw_do_one);
693     #if ENABLE_FEATURE_IPV6
694     do_info("/proc/net/raw6", raw_do_one);
695 niro 532 #endif
696 niro 816 }
697     if (flags & NETSTAT_UNIX) {
698 niro 532 printf("Active UNIX domain sockets ");
699 niro 816 if ((flags & (NETSTAT_LISTENING|NETSTAT_CONNECTED)) == (NETSTAT_LISTENING|NETSTAT_CONNECTED))
700 niro 532 printf("(servers and established)");
701 niro 816 else if (flags & NETSTAT_LISTENING)
702     printf("(only servers)");
703     else
704     printf("(w/o servers)");
705     printf("\nProto RefCnt Flags Type State I-Node ");
706     print_progname_banner();
707     printf("Path\n");
708     do_info("/proc/net/unix", unix_do_one);
709 niro 532 }
710 niro 816 prg_cache_clear();
711 niro 532 return 0;
712     }