source: branches/3.2/mindi-busybox/networking/netstat.c @ 3232

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