]> git.proxmox.com Git - mirror_iproute2.git/blobdiff - misc/ss.c
ss: add fastopen support
[mirror_iproute2.git] / misc / ss.c
index 890e2056b2f35e92a82d89bc850451df267a2349..54936308484fe430315b69284322f6e1716a064f 100644 (file)
--- a/misc/ss.c
+++ b/misc/ss.c
@@ -1,4 +1,4 @@
-//*
+/*
  * ss.c                "sockstat", socket statistics
  *
  *             This program is free software; you can redistribute it and/or
@@ -22,7 +22,6 @@
 #include <errno.h>
 #include <netdb.h>
 #include <arpa/inet.h>
-#include <resolv.h>
 #include <dirent.h>
 #include <fnmatch.h>
 #include <getopt.h>
@@ -34,7 +33,9 @@
 #include "SNAPSHOT.h"
 
 #include <linux/tcp.h>
-#include <linux/tcp_diag.h>
+#include <linux/sock_diag.h>
+#include <linux/inet_diag.h>
+#include <linux/unix_diag.h>
 
 int resolve_hosts = 0;
 int resolve_services = 1;
@@ -60,6 +61,7 @@ static const char *dg_proto = NULL;
 enum
 {
        TCP_DB,
+       DCCP_DB,
        UDP_DB,
        RAW_DB,
        UNIX_DG_DB,
@@ -103,184 +105,244 @@ struct filter
 };
 
 struct filter default_filter = {
-       dbs: (1<<TCP_DB),
-       states: SS_ALL & ~((1<<SS_LISTEN)|(1<<SS_CLOSE)|(1<<SS_TIME_WAIT)|(1<<SS_SYN_RECV)),
-       families: (1<<AF_INET)|(1<<AF_INET6),
+       .dbs    =  ~0,
+       .states = SS_ALL & ~((1<<SS_LISTEN)|(1<<SS_CLOSE)|(1<<SS_TIME_WAIT)|(1<<SS_SYN_RECV)),
+       .families= (1<<AF_INET)|(1<<AF_INET6),
 };
 
 struct filter current_filter;
 
-int generic_proc_open(char *env, char *name)
+static FILE *generic_proc_open(const char *env, const char *name)
 {
+       const char *p = getenv(env);
        char store[128];
-       char *p = getenv(env);
+
        if (!p) {
                p = getenv("PROC_ROOT") ? : "/proc";
                snprintf(store, sizeof(store)-1, "%s/%s", p, name);
                p = store;
        }
-       return open(store, O_RDONLY);
+
+       return fopen(p, "r");
 }
 
-int net_tcp_open(void)
+static FILE *net_tcp_open(void)
 {
        return generic_proc_open("PROC_NET_TCP", "net/tcp");
 }
 
-int net_tcp6_open(void)
+static FILE *net_tcp6_open(void)
 {
        return generic_proc_open("PROC_NET_TCP6", "net/tcp6");
 }
 
-int net_udp_open(void)
+static FILE *net_udp_open(void)
 {
        return generic_proc_open("PROC_NET_UDP", "net/udp");
 }
 
-int net_udp6_open(void)
+static FILE *net_udp6_open(void)
 {
        return generic_proc_open("PROC_NET_UDP6", "net/udp6");
 }
 
-int net_raw_open(void)
+static FILE *net_raw_open(void)
 {
        return generic_proc_open("PROC_NET_RAW", "net/raw");
 }
 
-int net_raw6_open(void)
+static FILE *net_raw6_open(void)
 {
        return generic_proc_open("PROC_NET_RAW6", "net/raw6");
 }
 
-int net_unix_open(void)
+static FILE *net_unix_open(void)
 {
        return generic_proc_open("PROC_NET_UNIX", "net/unix");
 }
 
-int net_packet_open(void)
+static FILE *net_packet_open(void)
 {
        return generic_proc_open("PROC_NET_PACKET", "net/packet");
 }
 
-int net_netlink_open(void)
+static FILE *net_netlink_open(void)
 {
        return generic_proc_open("PROC_NET_NETLINK", "net/netlink");
 }
 
-int slabinfo_open(void)
+static FILE *slabinfo_open(void)
 {
        return generic_proc_open("PROC_SLABINFO", "slabinfo");
 }
 
-int net_sockstat_open(void)
+static FILE *net_sockstat_open(void)
 {
        return generic_proc_open("PROC_NET_SOCKSTAT", "net/sockstat");
 }
 
-int net_sockstat6_open(void)
+static FILE *net_sockstat6_open(void)
 {
        return generic_proc_open("PROC_NET_SOCKSTAT6", "net/sockstat6");
 }
 
-int net_snmp_open(void)
+static FILE *net_snmp_open(void)
 {
        return generic_proc_open("PROC_NET_SNMP", "net/snmp");
 }
 
-int net_netstat_open(void)
+static FILE *ephemeral_ports_open(void)
 {
-       return generic_proc_open("PROC_NET_NETSTAT", "net/netstat");
+       return generic_proc_open("PROC_IP_LOCAL_PORT_RANGE", "sys/net/ipv4/ip_local_port_range");
 }
 
-int ephemeral_ports_open(void)
+struct user_ent {
+       struct user_ent *next;
+       unsigned int    ino;
+       int             pid;
+       int             fd;
+       char            process[0];
+};
+
+#define USER_ENT_HASH_SIZE     256
+struct user_ent *user_ent_hash[USER_ENT_HASH_SIZE];
+
+static int user_ent_hashfn(unsigned int ino)
 {
-       return generic_proc_open("PROC_IP_LOCAL_PORT_RANGE", "sys/net/ipv4/ip_local_port_range");
+       int val = (ino >> 24) ^ (ino >> 16) ^ (ino >> 8) ^ ino;
+
+       return val & (USER_ENT_HASH_SIZE - 1);
 }
 
-int find_users(int ino, char *buf, int buflen)
+static void user_ent_add(unsigned int ino, const char *process, int pid, int fd)
 {
-       char pattern[64];
-       int  pattern_len;
-       char *ptr = buf;
-       char name[1024];
-       DIR *dir;
-       struct dirent *d;
-       int cnt = 0;
-       int nameoff;
+       struct user_ent *p, **pp;
+       int str_len;
 
-       if (!ino)
-               return 0;
+       str_len = strlen(process) + 1;
+       p = malloc(sizeof(struct user_ent) + str_len);
+       if (!p)
+               abort();
+       p->next = NULL;
+       p->ino = ino;
+       p->pid = pid;
+       p->fd = fd;
+       strcpy(p->process, process);
+
+       pp = &user_ent_hash[user_ent_hashfn(ino)];
+       p->next = *pp;
+       *pp = p;
+}
 
-       sprintf(pattern, "socket:[%d]", ino);
-       pattern_len = strlen(pattern);
+static void user_ent_hash_build(void)
+{
+       const char *root = getenv("PROC_ROOT") ? : "/proc/";
+       struct dirent *d;
+       char name[1024];
+       int nameoff;
+       DIR *dir;
 
-       strncpy(name, getenv("PROC_ROOT") ? : "/proc/", sizeof(name)/2);
-       name[sizeof(name)/2] = 0;
-       if (strlen(name) == 0 ||
-           name[strlen(name)-1] != '/')
+       strcpy(name, root);
+       if (strlen(name) == 0 || name[strlen(name)-1] != '/')
                strcat(name, "/");
+
        nameoff = strlen(name);
-       if ((dir = opendir(name)) == NULL)
-               return 0;
+
+       dir = opendir(name);
+       if (!dir)
+               return;
 
        while ((d = readdir(dir)) != NULL) {
-               DIR *dir1;
                struct dirent *d1;
-               int pid;
-               int pos;
-               char crap;
                char process[16];
+               int pid, pos;
+               DIR *dir1;
+               char crap;
 
                if (sscanf(d->d_name, "%d%c", &pid, &crap) != 1)
                        continue;
 
-               sprintf(name+nameoff, "%d/fd/", pid);
+               sprintf(name + nameoff, "%d/fd/", pid);
                pos = strlen(name);
                if ((dir1 = opendir(name)) == NULL)
                        continue;
 
-               process[0] = 0;
+               process[0] = '\0';
 
                while ((d1 = readdir(dir1)) != NULL) {
-                       int fd, n;
+                       const char *pattern = "socket:[";
+                       unsigned int ino;
                        char lnk[64];
+                       int fd;
+                       ssize_t link_len;
 
                        if (sscanf(d1->d_name, "%d%c", &fd, &crap) != 1)
                                continue;
 
                        sprintf(name+pos, "%d", fd);
-                       n = readlink(name, lnk, sizeof(lnk)-1);
-                       if (n != pattern_len ||
-                           memcmp(lnk, pattern, n))
+
+                       link_len = readlink(name, lnk, sizeof(lnk)-1);
+                       if (link_len == -1)
                                continue;
+                       lnk[link_len] = '\0';
 
-                       if (ptr-buf >= buflen-1)
-                               break;
+                       if (strncmp(lnk, pattern, strlen(pattern)))
+                               continue;
 
-                       if (process[0] == 0) {
+                       sscanf(lnk, "socket:[%u]", &ino);
+
+                       if (process[0] == '\0') {
                                char tmp[1024];
                                FILE *fp;
-                               snprintf(tmp, sizeof(tmp), "%s/%d/stat",
-                                        getenv("PROC_ROOT") ? : "/proc", pid);
+
+                               snprintf(tmp, sizeof(tmp), "%s/%d/stat", root, pid);
                                if ((fp = fopen(tmp, "r")) != NULL) {
                                        fscanf(fp, "%*d (%[^)])", process);
                                        fclose(fp);
                                }
                        }
 
-                       snprintf(ptr, buflen-(ptr-buf), "(\"%s\",%d,%d),", process, pid, fd);
-                       ptr += strlen(ptr);
-                       cnt++;
+                       user_ent_add(ino, process, pid, fd);
                }
                closedir(dir1);
        }
        closedir(dir);
+}
+
+static int find_users(unsigned ino, char *buf, int buflen)
+{
+       struct user_ent *p;
+       int cnt = 0;
+       char *ptr;
+
+       if (!ino)
+               return 0;
+
+       p = user_ent_hash[user_ent_hashfn(ino)];
+       ptr = buf;
+       while (p) {
+               if (p->ino != ino)
+                       goto next;
+
+               if (ptr - buf >= buflen - 1)
+                       break;
+
+               snprintf(ptr, buflen - (ptr - buf),
+                        "(\"%s\",%d,%d),",
+                        p->process, p->pid, p->fd);
+               ptr += strlen(ptr);
+               cnt++;
+
+       next:
+               p = p->next;
+       }
+
        if (ptr != buf)
-               ptr[-1] = 0;
+               ptr[-1] = '\0';
+
        return cnt;
 }
 
-
 /* Get stats from slab */
 
 struct slabstat
@@ -294,7 +356,7 @@ struct slabstat
 
 struct slabstat slabstat;
 
-static const char *slabstat_ids[] = 
+static const char *slabstat_ids[] =
 {
        "sock",
        "tcp_bind_bucket",
@@ -303,7 +365,7 @@ static const char *slabstat_ids[] =
        "skbuff_head_cache",
 };
 
-int get_slabstat(struct slabstat *s)
+static int get_slabstat(struct slabstat *s)
 {
        char buf[256];
        FILE *fp;
@@ -311,7 +373,8 @@ int get_slabstat(struct slabstat *s)
 
        memset(s, 0, sizeof(*s));
 
-       if ((fp = fdopen(slabinfo_open(), "r")) == NULL)
+       fp = slabinfo_open();
+       if (!fp)
                return -1;
 
        cnt = sizeof(*s)/sizeof(int);
@@ -336,32 +399,32 @@ int get_slabstat(struct slabstat *s)
 
 static const char *sstate_name[] = {
        "UNKNOWN",
-       [TCP_ESTABLISHED] = "ESTAB",
-       [TCP_SYN_SENT] = "SYN-SENT",
-       [TCP_SYN_RECV] = "SYN-RECV",
-       [TCP_FIN_WAIT1] = "FIN-WAIT-1",
-       [TCP_FIN_WAIT2] = "FIN-WAIT-2",
-       [TCP_TIME_WAIT] = "TIME-WAIT",
-       [TCP_CLOSE] = "UNCONN",
-       [TCP_CLOSE_WAIT] = "CLOSE-WAIT",
-       [TCP_LAST_ACK] = "LAST-ACK",
-       [TCP_LISTEN] =  "LISTEN",
-       [TCP_CLOSING] = "CLOSING",
+       [SS_ESTABLISHED] = "ESTAB",
+       [SS_SYN_SENT] = "SYN-SENT",
+       [SS_SYN_RECV] = "SYN-RECV",
+       [SS_FIN_WAIT1] = "FIN-WAIT-1",
+       [SS_FIN_WAIT2] = "FIN-WAIT-2",
+       [SS_TIME_WAIT] = "TIME-WAIT",
+       [SS_CLOSE] = "UNCONN",
+       [SS_CLOSE_WAIT] = "CLOSE-WAIT",
+       [SS_LAST_ACK] = "LAST-ACK",
+       [SS_LISTEN] =   "LISTEN",
+       [SS_CLOSING] = "CLOSING",
 };
 
 static const char *sstate_namel[] = {
        "UNKNOWN",
-       [TCP_ESTABLISHED] = "established",
-       [TCP_SYN_SENT] = "syn-sent",
-       [TCP_SYN_RECV] = "syn-recv",
-       [TCP_FIN_WAIT1] = "fin-wait-1",
-       [TCP_FIN_WAIT2] = "fin-wait-2",
-       [TCP_TIME_WAIT] = "time-wait",
-       [TCP_CLOSE] = "unconnected",
-       [TCP_CLOSE_WAIT] = "close-wait",
-       [TCP_LAST_ACK] = "last-ack",
-       [TCP_LISTEN] =  "listening",
-       [TCP_CLOSING] = "closing",
+       [SS_ESTABLISHED] = "established",
+       [SS_SYN_SENT] = "syn-sent",
+       [SS_SYN_RECV] = "syn-recv",
+       [SS_FIN_WAIT1] = "fin-wait-1",
+       [SS_FIN_WAIT2] = "fin-wait-2",
+       [SS_TIME_WAIT] = "time-wait",
+       [SS_CLOSE] = "unconnected",
+       [SS_CLOSE_WAIT] = "close-wait",
+       [SS_LAST_ACK] = "last-ack",
+       [SS_LISTEN] =   "listening",
+       [SS_CLOSING] = "closing",
 };
 
 struct tcpstat
@@ -375,9 +438,9 @@ struct tcpstat
        int             timer;
        int             timeout;
        int             retrs;
-       int             ino;
+       unsigned        ino;
        int             probes;
-       int             uid;
+       unsigned        uid;
        int             refcnt;
        unsigned long long sk;
        int             rto, ato, qack, cwnd, ssthresh;
@@ -392,7 +455,7 @@ static const char *tmr_name[] = {
        "unknown"
 };
 
-const char *print_ms_timer(int timeout)
+static const char *print_ms_timer(int timeout)
 {
        static char buf[64];
        int secs, msecs, minutes;
@@ -417,13 +480,13 @@ const char *print_ms_timer(int timeout)
        if (msecs)
                sprintf(buf+strlen(buf), "%03dms", msecs);
        return buf;
-};
+}
 
-const char *print_hz_timer(int timeout)
+static const char *print_hz_timer(int timeout)
 {
-       int hz = get_hz();
+       int hz = get_user_hz();
        return print_ms_timer(((timeout*1000) + hz-1)/hz);
-};
+}
 
 struct scache
 {
@@ -435,7 +498,7 @@ struct scache
 
 struct scache *rlist;
 
-void init_service_resolver(void)
+static void init_service_resolver(void)
 {
        char buf[128];
        FILE *fp = popen("/usr/sbin/rpcinfo -p 2>/dev/null", "r");
@@ -462,6 +525,7 @@ void init_service_resolver(void)
                                }
                        }
                }
+               pclose(fp);
        }
 }
 
@@ -476,9 +540,9 @@ static int ip_local_port_min, ip_local_port_max;
 static int is_ephemeral(int port)
 {
        if (!ip_local_port_min) {
-               FILE *f = fdopen(ephemeral_ports_open(), "r");
+               FILE *f = ephemeral_ports_open();
                if (f) {
-                       fscanf(f, "%d %d", 
+                       fscanf(f, "%d %d",
                               &ip_local_port_min, &ip_local_port_max);
                        fclose(f);
                } else {
@@ -491,7 +555,7 @@ static int is_ephemeral(int port)
 }
 
 
-const char *__resolve_service(int port)
+static const char *__resolve_service(int port)
 {
        struct scache *c;
 
@@ -506,7 +570,7 @@ const char *__resolve_service(int port)
                if (!notfirst) {
                        setservent(1);
                        notfirst = 1;
-               } 
+               }
                se = getservbyport(htons(port), dg_proto);
                if (se)
                        return se->s_name;
@@ -516,7 +580,7 @@ const char *__resolve_service(int port)
 }
 
 
-const char *resolve_service(int port)
+static const char *resolve_service(int port)
 {
        static char buf[128];
        static struct scache cache[256];
@@ -535,7 +599,7 @@ const char *resolve_service(int port)
                        const char *res;
                        int hash = (port^(((unsigned long)dg_proto)>>2))&255;
 
-                       for (c = &cache[hash]; c; c = c->next) { 
+                       for (c = &cache[hash]; c; c = c->next) {
                                if (c->port == port &&
                                    c->proto == dg_proto) {
                                        if (c->name)
@@ -570,7 +634,7 @@ const char *resolve_service(int port)
        return buf;
 }
 
-void formatted_print(const inet_prefix *a, int port)
+static void formatted_print(const inet_prefix *a, int port)
 {
        char buf[1024];
        const char *ap = buf;
@@ -603,7 +667,8 @@ struct aafilter
        struct aafilter *next;
 };
 
-int inet2_addr_match(const inet_prefix *a, const inet_prefix *p, int plen)
+static int inet2_addr_match(const inet_prefix *a, const inet_prefix *p,
+                           int plen)
 {
        if (!inet_addr_match(a, p, plen))
                return 0;
@@ -622,7 +687,7 @@ int inet2_addr_match(const inet_prefix *a, const inet_prefix *p, int plen)
        return 1;
 }
 
-int unix_match(const inet_prefix *a, const inet_prefix *p)
+static int unix_match(const inet_prefix *a, const inet_prefix *p)
 {
        char *addr, *pattern;
        memcpy(&addr, a->data, sizeof(addr));
@@ -634,7 +699,7 @@ int unix_match(const inet_prefix *a, const inet_prefix *p)
        return !fnmatch(pattern, addr, 0);
 }
 
-int run_ssfilter(struct ssfilter *f, struct tcpstat *s)
+static int run_ssfilter(struct ssfilter *f, struct tcpstat *s)
 {
        switch (f->type) {
                case SSF_S_AUTO:
@@ -645,7 +710,7 @@ int run_ssfilter(struct ssfilter *f, struct tcpstat *s)
                        char *p;
                        memcpy(&p, s->local.data, sizeof(p));
                        return p == NULL || (p[0] == '@' && strlen(p) == 6 &&
-                                            strspn(p+1, "0123456789abcdef") == 5); 
+                                            strspn(p+1, "0123456789abcdef") == 5);
                }
                if (s->local.family == AF_PACKET)
                        return s->lport == 0 && s->local.data == 0;
@@ -653,7 +718,7 @@ int run_ssfilter(struct ssfilter *f, struct tcpstat *s)
                        return s->lport < 0;
 
                 if (!low) {
-                       FILE *fp = fdopen(ephemeral_ports_open(), "r");
+                       FILE *fp = ephemeral_ports_open();
                        if (fp) {
                                fscanf(fp, "%d%d", &low, &high);
                                fclose(fp);
@@ -688,7 +753,7 @@ int run_ssfilter(struct ssfilter *f, struct tcpstat *s)
                        do {
                                if (!inet2_addr_match(&s->local, &a->addr, a->addr.bitlen))
                                        return 1;
-                       } while ((a = a->next) != NULL); 
+                       } while ((a = a->next) != NULL);
                        return 0;
                }
                return 1;
@@ -726,11 +791,11 @@ int run_ssfilter(struct ssfilter *f, struct tcpstat *s)
        }
 }
 
-/* Relocate external jumps by reloc. */ 
+/* Relocate external jumps by reloc. */
 static void ssfilter_patch(char *a, int len, int reloc)
 {
        while (len > 0) {
-               struct tcpdiag_bc_op *op = (struct tcpdiag_bc_op*)a;
+               struct inet_diag_bc_op *op = (struct inet_diag_bc_op*)a;
                if (op->no == len+4)
                        op->no += reloc;
                len -= op->yes;
@@ -746,8 +811,8 @@ static int ssfilter_bytecompile(struct ssfilter *f, char **bytecode)
                case SSF_S_AUTO:
        {
                if (!(*bytecode=malloc(4))) abort();
-               ((struct tcpdiag_bc_op*)*bytecode)[0] = (struct tcpdiag_bc_op){ TCPDIAG_BC_AUTO, 4, 8 };
-               return 8;
+               ((struct inet_diag_bc_op*)*bytecode)[0] = (struct inet_diag_bc_op){ INET_DIAG_BC_AUTO, 4, 8 };
+               return 4;
        }
                case SSF_DCOND:
                case SSF_SCOND:
@@ -755,11 +820,11 @@ static int ssfilter_bytecompile(struct ssfilter *f, char **bytecode)
                struct aafilter *a = (void*)f->pred;
                struct aafilter *b;
                char *ptr;
-               int  code = (f->type == SSF_DCOND ? TCPDIAG_BC_D_COND : TCPDIAG_BC_S_COND);
+               int  code = (f->type == SSF_DCOND ? INET_DIAG_BC_D_COND : INET_DIAG_BC_S_COND);
                int len = 0;
 
                for (b=a; b; b=b->next) {
-                       len += 4 + sizeof(struct tcpdiag_hostcond);
+                       len += 4 + sizeof(struct inet_diag_hostcond);
                        if (a->addr.family == AF_INET6)
                                len += 16;
                        else
@@ -770,20 +835,20 @@ static int ssfilter_bytecompile(struct ssfilter *f, char **bytecode)
                if (!(ptr = malloc(len))) abort();
                *bytecode = ptr;
                for (b=a; b; b=b->next) {
-                       struct tcpdiag_bc_op *op = (struct tcpdiag_bc_op *)ptr;
+                       struct inet_diag_bc_op *op = (struct inet_diag_bc_op *)ptr;
                        int alen = (a->addr.family == AF_INET6 ? 16 : 4);
-                       int oplen = alen + 4 + sizeof(struct tcpdiag_hostcond);
-                       struct tcpdiag_hostcond *cond = (struct tcpdiag_hostcond*)(ptr+4);
+                       int oplen = alen + 4 + sizeof(struct inet_diag_hostcond);
+                       struct inet_diag_hostcond *cond = (struct inet_diag_hostcond*)(ptr+4);
 
-                       *op = (struct tcpdiag_bc_op){ code, oplen, oplen+4 };
+                       *op = (struct inet_diag_bc_op){ code, oplen, oplen+4 };
                        cond->family = a->addr.family;
                        cond->port = a->port;
                        cond->prefix_len = a->addr.bitlen;
                        memcpy(cond->addr, a->addr.data, alen);
                        ptr += oplen;
                        if (b->next) {
-                               op = (struct tcpdiag_bc_op *)ptr;
-                               *op = (struct tcpdiag_bc_op){ TCPDIAG_BC_JMP, 4, len - (ptr-*bytecode)};
+                               op = (struct inet_diag_bc_op *)ptr;
+                               *op = (struct inet_diag_bc_op){ INET_DIAG_BC_JMP, 4, len - (ptr-*bytecode)};
                                ptr += 4;
                        }
                }
@@ -793,32 +858,32 @@ static int ssfilter_bytecompile(struct ssfilter *f, char **bytecode)
        {
                struct aafilter *x = (void*)f->pred;
                if (!(*bytecode=malloc(8))) abort();
-               ((struct tcpdiag_bc_op*)*bytecode)[0] = (struct tcpdiag_bc_op){ TCPDIAG_BC_D_GE, 8, 12 };
-               ((struct tcpdiag_bc_op*)*bytecode)[1] = (struct tcpdiag_bc_op){ 0, 0, x->port };
+               ((struct inet_diag_bc_op*)*bytecode)[0] = (struct inet_diag_bc_op){ INET_DIAG_BC_D_GE, 8, 12 };
+               ((struct inet_diag_bc_op*)*bytecode)[1] = (struct inet_diag_bc_op){ 0, 0, x->port };
                return 8;
        }
                case SSF_D_LE:
        {
                struct aafilter *x = (void*)f->pred;
                if (!(*bytecode=malloc(8))) abort();
-               ((struct tcpdiag_bc_op*)*bytecode)[0] = (struct tcpdiag_bc_op){ TCPDIAG_BC_D_LE, 8, 12 };
-               ((struct tcpdiag_bc_op*)*bytecode)[1] = (struct tcpdiag_bc_op){ 0, 0, x->port };
+               ((struct inet_diag_bc_op*)*bytecode)[0] = (struct inet_diag_bc_op){ INET_DIAG_BC_D_LE, 8, 12 };
+               ((struct inet_diag_bc_op*)*bytecode)[1] = (struct inet_diag_bc_op){ 0, 0, x->port };
                return 8;
        }
                case SSF_S_GE:
        {
                struct aafilter *x = (void*)f->pred;
                if (!(*bytecode=malloc(8))) abort();
-               ((struct tcpdiag_bc_op*)*bytecode)[0] = (struct tcpdiag_bc_op){ TCPDIAG_BC_S_GE, 8, 12 };
-               ((struct tcpdiag_bc_op*)*bytecode)[1] = (struct tcpdiag_bc_op){ 0, 0, x->port };
+               ((struct inet_diag_bc_op*)*bytecode)[0] = (struct inet_diag_bc_op){ INET_DIAG_BC_S_GE, 8, 12 };
+               ((struct inet_diag_bc_op*)*bytecode)[1] = (struct inet_diag_bc_op){ 0, 0, x->port };
                return 8;
        }
                case SSF_S_LE:
        {
                struct aafilter *x = (void*)f->pred;
                if (!(*bytecode=malloc(8))) abort();
-               ((struct tcpdiag_bc_op*)*bytecode)[0] = (struct tcpdiag_bc_op){ TCPDIAG_BC_S_LE, 8, 12 };
-               ((struct tcpdiag_bc_op*)*bytecode)[1] = (struct tcpdiag_bc_op){ 0, 0, x->port };
+               ((struct inet_diag_bc_op*)*bytecode)[0] = (struct inet_diag_bc_op){ INET_DIAG_BC_S_LE, 8, 12 };
+               ((struct inet_diag_bc_op*)*bytecode)[1] = (struct inet_diag_bc_op){ 0, 0, x->port };
                return 8;
        }
 
@@ -844,7 +909,7 @@ static int ssfilter_bytecompile(struct ssfilter *f, char **bytecode)
                memcpy(a, a1, l1);
                memcpy(a+l1+4, a2, l2);
                free(a1); free(a2);
-               *(struct tcpdiag_bc_op*)(a+l1) = (struct tcpdiag_bc_op){ TCPDIAG_BC_JMP, 4, l2+4 };
+               *(struct inet_diag_bc_op*)(a+l1) = (struct inet_diag_bc_op){ INET_DIAG_BC_JMP, 4, l2+4 };
                *bytecode = a;
                return l1+l2+4;
        }
@@ -855,7 +920,7 @@ static int ssfilter_bytecompile(struct ssfilter *f, char **bytecode)
                if (!(a = malloc(l1+4))) abort();
                memcpy(a, a1, l1);
                free(a1);
-               *(struct tcpdiag_bc_op*)(a+l1) = (struct tcpdiag_bc_op){ TCPDIAG_BC_JMP, 4, 8 };
+               *(struct inet_diag_bc_op*)(a+l1) = (struct inet_diag_bc_op){ INET_DIAG_BC_JMP, 4, 8 };
                *bytecode = a;
                return l1+4;
        }
@@ -866,7 +931,7 @@ static int ssfilter_bytecompile(struct ssfilter *f, char **bytecode)
 
 static int remember_he(struct aafilter *a, struct hostent *he)
 {
-       char **ptr = he->h_addr_list; 
+       char **ptr = he->h_addr_list;
        int cnt = 0;
        int len;
 
@@ -1101,35 +1166,35 @@ void *parse_hostcond(char *addr)
        return res;
 }
 
-static int tcp_show_line(char *line, struct filter *f, int family)
+static int tcp_show_line(char *line, const struct filter *f, int family)
 {
        struct tcpstat s;
        char *loc, *rem, *data;
        char opt[256];
        int n;
        char *p;
-       
+
        if ((p = strchr(line, ':')) == NULL)
                return -1;
        loc = p+2;
-       
+
        if ((p = strchr(loc, ':')) == NULL)
                return -1;
        p[5] = 0;
        rem = p+6;
-       
+
        if ((p = strchr(rem, ':')) == NULL)
                return -1;
        p[5] = 0;
        data = p+6;
-       
+
        do {
                int state = (data[1] >= 'A') ? (data[1] - 'A' + 10) : (data[1] - '0');
 
                if (!(f->states & (1<<state)))
                        return 0;
        } while (0);
-       
+
        s.local.family = s.remote.family = family;
        if (family == AF_INET) {
                sscanf(loc, "%x:%x", s.local.data, (unsigned*)&s.lport);
@@ -1150,37 +1215,37 @@ static int tcp_show_line(char *line, struct filter *f, int family)
                       &s.rport);
                s.local.bytelen = s.remote.bytelen = 16;
        }
-       
+
        if (f->f && run_ssfilter(f->f, &s) == 0)
                return 0;
-       
+
        opt[0] = 0;
-       n = sscanf(data, "%x %x:%x %x:%x %x %d %d %d %d %llx %d %d %d %d %d %[^\n]\n",
+       n = sscanf(data, "%x %x:%x %x:%x %x %d %d %u %d %llx %d %d %d %d %d %[^\n]\n",
                   &s.state, &s.wq, &s.rq,
                   &s.timer, &s.timeout, &s.retrs, &s.uid, &s.probes, &s.ino,
                   &s.refcnt, &s.sk, &s.rto, &s.ato, &s.qack,
                   &s.cwnd, &s.ssthresh, opt);
-       
+
        if (n < 17)
                opt[0] = 0;
-       
+
        if (n < 12) {
                s.rto = 0;
                s.cwnd = 2;
                s.ssthresh = -1;
                s.ato = s.qack = 0;
        }
-       
+
        if (netid_width)
                printf("%-*s ", netid_width, "tcp");
        if (state_width)
                printf("%-*s ", state_width, sstate_name[s.state]);
-       
+
        printf("%-6d %-6d ", s.rq, s.wq);
-       
+
        formatted_print(&s.local, s.lport);
        formatted_print(&s.remote, s.rport);
-       
+
        if (show_options) {
                if (s.timer) {
                        if (s.timer > 4)
@@ -1192,10 +1257,11 @@ static int tcp_show_line(char *line, struct filter *f, int family)
                }
        }
        if (show_tcpinfo) {
-               if (s.rto && s.rto != 3*get_hz())
-                       printf(" rto:%g", (double)s.rto/get_hz());
+               int hz = get_user_hz();
+               if (s.rto && s.rto != 3*hz)
+                       printf(" rto:%g", (double)s.rto/hz);
                if (s.ato)
-                       printf(" ato:%g", (double)s.ato/get_hz());
+                       printf(" ato:%g", (double)s.ato/hz);
                if (s.cwnd != 2)
                        printf(" cwnd:%d", s.cwnd);
                if (s.ssthresh != -1)
@@ -1213,7 +1279,7 @@ static int tcp_show_line(char *line, struct filter *f, int family)
        if (show_details) {
                if (s.uid)
                        printf(" uid:%u", (unsigned)s.uid);
-               printf(" ino:%u", (unsigned)s.ino);
+               printf(" ino:%u", s.ino);
                printf(" sk:%llx", s.sk);
                if (opt[0])
                        printf(" opt:\"%s\"", opt);
@@ -1223,73 +1289,35 @@ static int tcp_show_line(char *line, struct filter *f, int family)
        return 0;
 }
 
-static int generic_record_read(int fd, char *buf, int bufsize,
-                       int (*worker)(char*, struct filter *, int),
-                       struct filter *f, int fam)
+static int generic_record_read(FILE *fp,
+                              int (*worker)(char*, const struct filter *, int),
+                              const struct filter *f, int fam)
 {
-       int n;
-       int recsize;
-       int eof = 0;
-       char *p;
+       char line[256];
 
-       /* Load the first chunk and calculate record length from it. */
-       n = read(fd, buf, bufsize);
-       if (n < 0)
+       /* skip header */
+       if (fgets(line, sizeof(line), fp) == NULL)
                goto outerr;
-       /* I _know_ that this is wrong, do not remind. :-)
-        * But this works nowadays. */
-       if (n < bufsize)
-               eof = 1;
-       p = memchr(buf, '\n', n);
-       if (p == NULL || (p-buf) >= n)
-               goto outwrongformat;
-       recsize = (p-buf)+1;
-       p = buf+recsize;
-
-       for (;;) {
-               while ((p+recsize) - buf <= n) {
-                       if (p[recsize-1] != '\n')
-                               goto outwrongformat;
-                       p[recsize-1] = 0;
-                       if (worker(p, f, fam) < 0)
-                               goto done;
-                       p += recsize;
-               }
-               if (!eof) {
-                       int remains = (buf+bufsize) - p;
-                       memcpy(buf, p, remains);
-                       p = buf+remains;
-                       n = read(fd, p, (buf+bufsize) - p);
-                       if (n < 0)
-                               goto outerr;
-                       if (n < (buf+bufsize) - p) {
-                               eof = 1;
-                               if (n == 0) {
-                                       if (remains)
-                                               goto outwrongformat;
-                                       goto done;
-                               }
-                       }
-                       n += remains;
-                       p = buf;
-               } else {
-                       if (p != buf+n)
-                               goto outwrongformat;
-                       goto done;
+
+       while (fgets(line, sizeof(line), fp) != NULL) {
+               int n = strlen(line);
+               if (n == 0 || line[n-1] != '\n') {
+                       errno = -EINVAL;
+                       return -1;
                }
-       }
-done:
-       return 0;
+               line[n-1] = 0;
 
-outwrongformat:
-       errno = EINVAL;
+               if (worker(line, f, fam) < 0)
+                       return 0;
+       }
 outerr:
-       return -1;
+
+       return ferror(fp) ? -1 : 0;
 }
-                       
+
 static char *sprint_bw(char *buf, double bw)
 {
-       if (bw > 1000000.) 
+       if (bw > 1000000.)
                sprintf(buf,"%.1fM", bw / 1000000.);
        else if (bw > 1000.)
                sprintf(buf,"%.1fK", bw / 1000.);
@@ -1299,36 +1327,61 @@ static char *sprint_bw(char *buf, double bw)
        return buf;
 }
 
-static void tcp_show_info(const struct nlmsghdr *nlh, struct tcpdiagmsg *r)
+static void print_skmeminfo(struct rtattr *tb[], int attrtype)
+{
+       const __u32 *skmeminfo;
+       if (!tb[attrtype])
+               return;
+       skmeminfo = RTA_DATA(tb[attrtype]);
+
+       printf(" skmem:(r%u,rb%u,t%u,tb%u,f%u,w%u,o%u",
+              skmeminfo[SK_MEMINFO_RMEM_ALLOC],
+              skmeminfo[SK_MEMINFO_RCVBUF],
+              skmeminfo[SK_MEMINFO_WMEM_ALLOC],
+              skmeminfo[SK_MEMINFO_SNDBUF],
+              skmeminfo[SK_MEMINFO_FWD_ALLOC],
+              skmeminfo[SK_MEMINFO_WMEM_QUEUED],
+              skmeminfo[SK_MEMINFO_OPTMEM]);
+
+       if (RTA_PAYLOAD(tb[attrtype]) >=
+               (SK_MEMINFO_BACKLOG + 1) * sizeof(__u32))
+               printf(",bl%u", skmeminfo[SK_MEMINFO_BACKLOG]);
+
+       printf(")");
+}
+
+static void tcp_show_info(const struct nlmsghdr *nlh, struct inet_diag_msg *r)
 {
-       struct rtattr * tb[TCPDIAG_MAX+1];
+       struct rtattr * tb[INET_DIAG_MAX+1];
        char b1[64];
        double rtt = 0;
 
-       parse_rtattr(tb, TCPDIAG_MAX, (struct rtattr*)(r+1),
+       parse_rtattr(tb, INET_DIAG_MAX, (struct rtattr*)(r+1),
                     nlh->nlmsg_len - NLMSG_LENGTH(sizeof(*r)));
 
-       if (tb[TCPDIAG_MEMINFO]) {
-               const struct tcpdiag_meminfo *minfo
-                       = RTA_DATA(tb[TCPDIAG_MEMINFO]);
+       if (tb[INET_DIAG_SKMEMINFO]) {
+               print_skmeminfo(tb, INET_DIAG_SKMEMINFO);
+       } else if (tb[INET_DIAG_MEMINFO]) {
+               const struct inet_diag_meminfo *minfo
+                       = RTA_DATA(tb[INET_DIAG_MEMINFO]);
                printf(" mem:(r%u,w%u,f%u,t%u)",
-                      minfo->tcpdiag_rmem,
-                      minfo->tcpdiag_wmem,
-                      minfo->tcpdiag_fmem,
-                      minfo->tcpdiag_tmem);
+                      minfo->idiag_rmem,
+                      minfo->idiag_wmem,
+                      minfo->idiag_fmem,
+                      minfo->idiag_tmem);
        }
 
-       if (tb[TCPDIAG_INFO]) {
+       if (tb[INET_DIAG_INFO]) {
                struct tcp_info *info;
-               int len = RTA_PAYLOAD(tb[TCPDIAG_INFO]);
+               int len = RTA_PAYLOAD(tb[INET_DIAG_INFO]);
 
                /* workaround for older kernels with less fields */
                if (len < sizeof(*info)) {
                        info = alloca(sizeof(*info));
                        memset(info, 0, sizeof(*info));
-                       memcpy(info, RTA_DATA(tb[TCPDIAG_INFO]), len);
+                       memcpy(info, RTA_DATA(tb[INET_DIAG_INFO]), len);
                } else
-                       info = RTA_DATA(tb[TCPDIAG_INFO]);
+                       info = RTA_DATA(tb[INET_DIAG_INFO]);
 
                if (show_options) {
                        if (info->tcpi_options & TCPI_OPT_TIMESTAMPS)
@@ -1337,8 +1390,16 @@ static void tcp_show_info(const struct nlmsghdr *nlh, struct tcpdiagmsg *r)
                                printf(" sack");
                        if (info->tcpi_options & TCPI_OPT_ECN)
                                printf(" ecn");
+                       if (info->tcpi_options & TCPI_OPT_ECN_SEEN)
+                               printf(" ecnseen");
+                       if (info->tcpi_options & TCPI_OPT_SYN_DATA)
+                               printf(" fastopen");
                }
-               if (info->tcpi_options & TCPI_OPT_WSCALE) 
+
+               if (tb[INET_DIAG_CONG])
+                       printf(" %s", rta_getattr_str(tb[INET_DIAG_CONG]));
+
+               if (info->tcpi_options & TCPI_OPT_WSCALE)
                        printf(" wscale:%d,%d", info->tcpi_snd_wscale,
                               info->tcpi_rcv_wscale);
                if (info->tcpi_rto && info->tcpi_rto != 3000000)
@@ -1348,22 +1409,21 @@ static void tcp_show_info(const struct nlmsghdr *nlh, struct tcpdiagmsg *r)
                               (double)info->tcpi_rttvar/1000);
                if (info->tcpi_ato)
                        printf(" ato:%g", (double)info->tcpi_ato/1000);
+               if (info->tcpi_snd_mss)
+                       printf(" mss:%d", info->tcpi_snd_mss);
                if (info->tcpi_snd_cwnd != 2)
                        printf(" cwnd:%d", info->tcpi_snd_cwnd);
                if (info->tcpi_snd_ssthresh < 0xFFFF)
                        printf(" ssthresh:%d", info->tcpi_snd_ssthresh);
-               
+
                rtt = (double) info->tcpi_rtt;
-               if (tb[TCPDIAG_VEGASINFO]) {
+               if (tb[INET_DIAG_VEGASINFO]) {
                        const struct tcpvegas_info *vinfo
-                               = RTA_DATA(tb[TCPDIAG_VEGASINFO]);
-
-                       if (vinfo->tcpv_enabled)
-                               printf(" vegas");
+                               = RTA_DATA(tb[INET_DIAG_VEGASINFO]);
 
-                       if (vinfo->tcpv_rtt && 
-                           vinfo->tcpv_rtt != 0x7fffffff)
-                           rtt =  vinfo->tcpv_rtt;
+                       if (vinfo->tcpv_enabled &&
+                           vinfo->tcpv_rtt && vinfo->tcpv_rtt != 0x7fffffff)
+                               rtt =  vinfo->tcpv_rtt;
                }
 
                if (rtt > 0 && info->tcpi_snd_mss && info->tcpi_snd_cwnd) {
@@ -1381,22 +1441,22 @@ static void tcp_show_info(const struct nlmsghdr *nlh, struct tcpdiagmsg *r)
        }
 }
 
-int tcp_show_sock(struct nlmsghdr *nlh, struct filter *f)
+static int inet_show_sock(struct nlmsghdr *nlh, struct filter *f)
 {
-       struct tcpdiagmsg *r = NLMSG_DATA(nlh);
+       struct inet_diag_msg *r = NLMSG_DATA(nlh);
        struct tcpstat s;
 
-       s.state = r->tcpdiag_state;
-       s.local.family = s.remote.family = r->tcpdiag_family;
-       s.lport = ntohs(r->id.tcpdiag_sport);
-       s.rport = ntohs(r->id.tcpdiag_dport);
+       s.state = r->idiag_state;
+       s.local.family = s.remote.family = r->idiag_family;
+       s.lport = ntohs(r->id.idiag_sport);
+       s.rport = ntohs(r->id.idiag_dport);
        if (s.local.family == AF_INET) {
                s.local.bytelen = s.remote.bytelen = 4;
        } else {
                s.local.bytelen = s.remote.bytelen = 16;
        }
-       memcpy(s.local.data, r->id.tcpdiag_src, s.local.bytelen);
-       memcpy(s.remote.data, r->id.tcpdiag_dst, s.local.bytelen);
+       memcpy(s.local.data, r->id.idiag_src, s.local.bytelen);
+       memcpy(s.remote.data, r->id.idiag_dst, s.local.bytelen);
 
        if (f && f->f && run_ssfilter(f->f, &s) == 0)
                return 0;
@@ -1406,33 +1466,34 @@ int tcp_show_sock(struct nlmsghdr *nlh, struct filter *f)
        if (state_width)
                printf("%-*s ", state_width, sstate_name[s.state]);
 
-       printf("%-6d %-6d ", r->tcpdiag_rqueue, r->tcpdiag_wqueue);
+       printf("%-6d %-6d ", r->idiag_rqueue, r->idiag_wqueue);
 
        formatted_print(&s.local, s.lport);
        formatted_print(&s.remote, s.rport);
 
        if (show_options) {
-               if (r->tcpdiag_timer) {
-                       if (r->tcpdiag_timer > 4)
-                               r->tcpdiag_timer = 5;
+               if (r->idiag_timer) {
+                       if (r->idiag_timer > 4)
+                               r->idiag_timer = 5;
                        printf(" timer:(%s,%s,%d)",
-                              tmr_name[r->tcpdiag_timer],
-                              print_ms_timer(r->tcpdiag_expires),
-                              r->tcpdiag_retrans);
+                              tmr_name[r->idiag_timer],
+                              print_ms_timer(r->idiag_expires),
+                              r->idiag_retrans);
                }
        }
        if (show_users) {
                char ubuf[4096];
-               if (find_users(r->tcpdiag_inode, ubuf, sizeof(ubuf)) > 0)
+               if (find_users(r->idiag_inode, ubuf, sizeof(ubuf)) > 0)
                        printf(" users:(%s)", ubuf);
        }
        if (show_details) {
-               if (r->tcpdiag_uid)
-                       printf(" uid:%u", (unsigned)r->tcpdiag_uid);
-               printf(" ino:%u", (unsigned)r->tcpdiag_inode);
-               printf(" sk:%08x", r->id.tcpdiag_cookie[0]);
-               if (r->id.tcpdiag_cookie[1] != 0)
-                       printf("%08x", r->id.tcpdiag_cookie[1]);
+               if (r->idiag_uid)
+                       printf(" uid:%u", (unsigned)r->idiag_uid);
+               printf(" ino:%u", r->idiag_inode);
+               printf(" sk:");
+               if (r->id.idiag_cookie[1] != 0)
+                       printf("%08x", r->id.idiag_cookie[1]);
+               printf("%08x", r->id.idiag_cookie[0]);
        }
        if (show_mem || show_tcpinfo) {
                printf("\n\t");
@@ -1442,50 +1503,123 @@ int tcp_show_sock(struct nlmsghdr *nlh, struct filter *f)
        printf("\n");
 
        return 0;
-
 }
 
-int tcp_show_netlink(struct filter *f, FILE *dump_fp)
+static int tcpdiag_send(int fd, int protocol, struct filter *f)
 {
-       int fd;
        struct sockaddr_nl nladdr;
        struct {
                struct nlmsghdr nlh;
-               struct tcpdiagreq r;
+               struct inet_diag_req r;
        } req;
        char    *bc = NULL;
        int     bclen;
        struct msghdr msg;
        struct rtattr rta;
-       char    buf[8192];
        struct iovec iov[3];
 
-       if ((fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_TCPDIAG)) < 0)
+       if (protocol == IPPROTO_UDP)
                return -1;
 
        memset(&nladdr, 0, sizeof(nladdr));
        nladdr.nl_family = AF_NETLINK;
 
        req.nlh.nlmsg_len = sizeof(req);
-       req.nlh.nlmsg_type = TCPDIAG_GETSOCK;
+       if (protocol == IPPROTO_TCP)
+               req.nlh.nlmsg_type = TCPDIAG_GETSOCK;
+       else
+               req.nlh.nlmsg_type = DCCPDIAG_GETSOCK;
        req.nlh.nlmsg_flags = NLM_F_ROOT|NLM_F_MATCH|NLM_F_REQUEST;
        req.nlh.nlmsg_pid = 0;
        req.nlh.nlmsg_seq = 123456;
        memset(&req.r, 0, sizeof(req.r));
-       req.r.tcpdiag_family = AF_INET;
-       req.r.tcpdiag_states = f->states;
-       if (show_mem)
-               req.r.tcpdiag_ext |= (1<<(TCPDIAG_MEMINFO-1)); 
+       req.r.idiag_family = AF_INET;
+       req.r.idiag_states = f->states;
+       if (show_mem) {
+               req.r.idiag_ext |= (1<<(INET_DIAG_MEMINFO-1));
+               req.r.idiag_ext |= (1<<(INET_DIAG_SKMEMINFO-1));
+       }
+
+       if (show_tcpinfo) {
+               req.r.idiag_ext |= (1<<(INET_DIAG_INFO-1));
+               req.r.idiag_ext |= (1<<(INET_DIAG_VEGASINFO-1));
+               req.r.idiag_ext |= (1<<(INET_DIAG_CONG-1));
+       }
+
+       iov[0] = (struct iovec){
+               .iov_base = &req,
+               .iov_len = sizeof(req)
+       };
+       if (f->f) {
+               bclen = ssfilter_bytecompile(f->f, &bc);
+               rta.rta_type = INET_DIAG_REQ_BYTECODE;
+               rta.rta_len = RTA_LENGTH(bclen);
+               iov[1] = (struct iovec){ &rta, sizeof(rta) };
+               iov[2] = (struct iovec){ bc, bclen };
+               req.nlh.nlmsg_len += RTA_LENGTH(bclen);
+       }
+
+       msg = (struct msghdr) {
+               .msg_name = (void*)&nladdr,
+               .msg_namelen = sizeof(nladdr),
+               .msg_iov = iov,
+               .msg_iovlen = f->f ? 3 : 1,
+       };
+
+       if (sendmsg(fd, &msg, 0) < 0) {
+               close(fd);
+               return -1;
+       }
+
+       return 0;
+}
+
+static int sockdiag_send(int family, int fd, int protocol, struct filter *f)
+{
+       struct sockaddr_nl nladdr;
+       struct {
+               struct nlmsghdr nlh;
+               struct inet_diag_req_v2 r;
+       } req;
+       char    *bc = NULL;
+       int     bclen;
+       struct msghdr msg;
+       struct rtattr rta;
+       struct iovec iov[3];
+
+       if (family == PF_UNSPEC)
+               return tcpdiag_send(fd, protocol, f);
+
+       memset(&nladdr, 0, sizeof(nladdr));
+       nladdr.nl_family = AF_NETLINK;
+
+       req.nlh.nlmsg_len = sizeof(req);
+       req.nlh.nlmsg_type = SOCK_DIAG_BY_FAMILY;
+       req.nlh.nlmsg_flags = NLM_F_ROOT|NLM_F_MATCH|NLM_F_REQUEST;
+       req.nlh.nlmsg_pid = 0;
+       req.nlh.nlmsg_seq = 123456;
+       memset(&req.r, 0, sizeof(req.r));
+       req.r.sdiag_family = family;
+       req.r.sdiag_protocol = protocol;
+       req.r.idiag_states = f->states;
+       if (show_mem) {
+               req.r.idiag_ext |= (1<<(INET_DIAG_MEMINFO-1));
+               req.r.idiag_ext |= (1<<(INET_DIAG_SKMEMINFO-1));
+       }
 
        if (show_tcpinfo) {
-               req.r.tcpdiag_ext |= (1<<(TCPDIAG_INFO-1));
-               req.r.tcpdiag_ext |= (1<<(TCPDIAG_VEGASINFO-1));
+               req.r.idiag_ext |= (1<<(INET_DIAG_INFO-1));
+               req.r.idiag_ext |= (1<<(INET_DIAG_VEGASINFO-1));
+               req.r.idiag_ext |= (1<<(INET_DIAG_CONG-1));
        }
 
-       iov[0] = (struct iovec){ &req, sizeof(req) };
+       iov[0] = (struct iovec){
+               .iov_base = &req,
+               .iov_len = sizeof(req)
+       };
        if (f->f) {
                bclen = ssfilter_bytecompile(f->f, &bc);
-               rta.rta_type = TCPDIAG_REQ_BYTECODE;
+               rta.rta_type = INET_DIAG_REQ_BYTECODE;
                rta.rta_len = RTA_LENGTH(bclen);
                iov[1] = (struct iovec){ &rta, sizeof(rta) };
                iov[2] = (struct iovec){ bc, bclen };
@@ -1493,17 +1627,43 @@ int tcp_show_netlink(struct filter *f, FILE *dump_fp)
        }
 
        msg = (struct msghdr) {
-               (void*)&nladdr, sizeof(nladdr),
-               iov,    f->f ? 3 : 1,
-               NULL,   0,
-               0
+               .msg_name = (void*)&nladdr,
+               .msg_namelen = sizeof(nladdr),
+               .msg_iov = iov,
+               .msg_iovlen = f->f ? 3 : 1,
        };
 
-       if (sendmsg(fd, &msg, 0) < 0)
+       if (sendmsg(fd, &msg, 0) < 0) {
+               close(fd);
                return -1;
+       }
 
+       return 0;
+}
 
-       iov[0] = (struct iovec){ buf, sizeof(buf) };
+static int inet_show_netlink(struct filter *f, FILE *dump_fp, int protocol)
+{
+       int fd, family;
+       struct sockaddr_nl nladdr;
+       struct msghdr msg;
+       char    buf[8192];
+       struct iovec iov[3];
+
+       if ((fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_INET_DIAG)) < 0)
+               return -1;
+
+       family = PF_INET;
+again:
+       if (sockdiag_send(family, fd, protocol, f))
+               return -1;
+
+       memset(&nladdr, 0, sizeof(nladdr));
+       nladdr.nl_family = AF_NETLINK;
+
+       iov[0] = (struct iovec){
+               .iov_base = buf,
+               .iov_len = sizeof(buf)
+       };
 
        while (1) {
                int status;
@@ -1526,6 +1686,7 @@ int tcp_show_netlink(struct filter *f, FILE *dump_fp)
                }
                if (status == 0) {
                        fprintf(stderr, "EOF on netlink\n");
+                       close(fd);
                        return 0;
                }
 
@@ -1535,27 +1696,45 @@ int tcp_show_netlink(struct filter *f, FILE *dump_fp)
                h = (struct nlmsghdr*)buf;
                while (NLMSG_OK(h, status)) {
                        int err;
+                       struct inet_diag_msg *r = NLMSG_DATA(h);
 
                        if (/*h->nlmsg_pid != rth->local.nl_pid ||*/
                            h->nlmsg_seq != 123456)
                                goto skip_it;
 
                        if (h->nlmsg_type == NLMSG_DONE)
-                               return 0;
+                               goto done;
+
                        if (h->nlmsg_type == NLMSG_ERROR) {
                                struct nlmsgerr *err = (struct nlmsgerr*)NLMSG_DATA(h);
                                if (h->nlmsg_len < NLMSG_LENGTH(sizeof(struct nlmsgerr))) {
                                        fprintf(stderr, "ERROR truncated\n");
                                } else {
+                                       if (family != PF_UNSPEC) {
+                                               family = PF_UNSPEC;
+                                               goto again;
+                                       }
+
                                        errno = -err->error;
+                                       if (errno == EOPNOTSUPP) {
+                                               close(fd);
+                                               return -1;
+                                       }
                                        perror("TCPDIAG answers");
                                }
-                               return 0;
+
+                               goto done;
                        }
                        if (!dump_fp) {
-                               err = tcp_show_sock(h, NULL);
-                               if (err < 0)
+                               if (!(f->families & (1<<r->idiag_family))) {
+                                       h = NLMSG_NEXT(h, status);
+                                       continue;
+                               }
+                               err = inet_show_sock(h, NULL);
+                               if (err < 0) {
+                                       close(fd);
                                        return err;
+                               }
                        }
 
 skip_it:
@@ -1570,10 +1749,17 @@ skip_it:
                        exit(1);
                }
        }
+done:
+       if (family == PF_INET) {
+               family = PF_INET6;
+               goto again;
+       }
+
+       close(fd);
        return 0;
 }
 
-int tcp_show_netlink_file(struct filter *f)
+static int tcp_show_netlink_file(struct filter *f)
 {
        FILE    *fp;
        char    buf[8192];
@@ -1623,15 +1809,15 @@ int tcp_show_netlink_file(struct filter *f)
                        return -1;
                }
 
-               err = tcp_show_sock(h, f);
+               err = inet_show_sock(h, f);
                if (err < 0)
                        return err;
        }
 }
 
-int tcp_show(struct filter *f)
+static int tcp_show(struct filter *f, int socktype)
 {
-       int fd = -1;
+       FILE *fp = NULL;
        char *buf = NULL;
        int bufsize = 64*1024;
 
@@ -1641,11 +1827,12 @@ int tcp_show(struct filter *f)
                return tcp_show_netlink_file(f);
 
        if (!getenv("PROC_NET_TCP") && !getenv("PROC_ROOT")
-           && tcp_show_netlink(f, NULL) == 0)
+           && inet_show_netlink(f, NULL, socktype) == 0)
                return 0;
 
        /* Sigh... We have to parse /proc/net/tcp... */
 
+
        /* Estimate amount of sockets and try to allocate
         * huge buffer to read all the table at one read.
         * Limit it by 16MB though. The assumption is: as soon as
@@ -1673,18 +1860,21 @@ int tcp_show(struct filter *f)
        }
 
        if (f->families & (1<<AF_INET)) {
-               if ((fd = net_tcp_open()) < 0)
+               if ((fp = net_tcp_open()) == NULL)
                        goto outerr;
-               if (generic_record_read(fd, buf, bufsize, tcp_show_line, f, AF_INET))
+
+               setbuffer(fp, buf, bufsize);
+               if (generic_record_read(fp, tcp_show_line, f, AF_INET))
                        goto outerr;
-               close(fd);
+               fclose(fp);
        }
 
        if ((f->families & (1<<AF_INET6)) &&
-           (fd = net_tcp6_open()) >= 0) {
-               if (generic_record_read(fd, buf, bufsize, tcp_show_line, f, AF_INET6))
+           (fp = net_tcp6_open()) != NULL) {
+               setbuffer(fp, buf, bufsize);
+               if (generic_record_read(fp, tcp_show_line, f, AF_INET6))
                        goto outerr;
-               close(fd);
+               fclose(fp);
        }
 
        free(buf);
@@ -1695,15 +1885,15 @@ outerr:
                int saved_errno = errno;
                if (buf)
                        free(buf);
-               if (fd >= 0)
-                       close(fd);
+               if (fp)
+                       fclose(fp);
                errno = saved_errno;
                return -1;
        } while (0);
 }
 
 
-int dgram_show_line(char *line, struct filter *f, int family)
+static int dgram_show_line(char *line, const struct filter *f, int family)
 {
        struct tcpstat s;
        char *loc, *rem, *data;
@@ -1757,7 +1947,7 @@ int dgram_show_line(char *line, struct filter *f, int family)
                return 0;
 
        opt[0] = 0;
-       n = sscanf(data, "%x %x:%x %*x:%*x %*x %d %*d %d %d %llx %[^\n]\n",
+       n = sscanf(data, "%x %x:%x %*x:%*x %*x %d %*d %u %d %llx %[^\n]\n",
               &s.state, &s.wq, &s.rq,
               &s.uid, &s.ino,
               &s.refcnt, &s.sk, opt);
@@ -1784,7 +1974,7 @@ int dgram_show_line(char *line, struct filter *f, int family)
        if (show_details) {
                if (s.uid)
                        printf(" uid=%u", (unsigned)s.uid);
-               printf(" ino=%u", (unsigned)s.ino);
+               printf(" ino=%u", s.ino);
                printf(" sk=%llx", s.sk);
                if (opt[0])
                        printf(" opt:\"%s\"", opt);
@@ -1795,69 +1985,69 @@ int dgram_show_line(char *line, struct filter *f, int family)
 }
 
 
-int udp_show(struct filter *f)
+static int udp_show(struct filter *f)
 {
-       int fd = -1;
-       char buf[8192];
-       int  bufsize = sizeof(buf);
+       FILE *fp = NULL;
+
+       if (!getenv("PROC_NET_UDP") && !getenv("PROC_ROOT")
+           && inet_show_netlink(f, NULL, IPPROTO_UDP) == 0)
+               return 0;
 
        dg_proto = UDP_PROTO;
 
        if (f->families&(1<<AF_INET)) {
-               if ((fd = net_udp_open()) < 0)
+               if ((fp = net_udp_open()) == NULL)
                        goto outerr;
-               if (generic_record_read(fd, buf, bufsize, dgram_show_line, f, AF_INET))
+               if (generic_record_read(fp, dgram_show_line, f, AF_INET))
                        goto outerr;
-               close(fd);
+               fclose(fp);
        }
 
        if ((f->families&(1<<AF_INET6)) &&
-           (fd = net_udp6_open()) >= 0) {
-               if (generic_record_read(fd, buf, bufsize, dgram_show_line, f, AF_INET6))
+           (fp = net_udp6_open()) != NULL) {
+               if (generic_record_read(fp, dgram_show_line, f, AF_INET6))
                        goto outerr;
-               close(fd);
+               fclose(fp);
        }
        return 0;
 
 outerr:
        do {
                int saved_errno = errno;
-               if (fd >= 0)
-                       close(fd);
+               if (fp)
+                       fclose(fp);
                errno = saved_errno;
                return -1;
        } while (0);
 }
 
-int raw_show(struct filter *f)
+static int raw_show(struct filter *f)
 {
-       int fd = -1;
-       char buf[8192];
-       int  bufsize = sizeof(buf);
+       FILE *fp = NULL;
 
        dg_proto = RAW_PROTO;
 
        if (f->families&(1<<AF_INET)) {
-               if ((fd = net_raw_open()) < 0)
+               if ((fp = net_raw_open()) == NULL)
                        goto outerr;
-               if (generic_record_read(fd, buf, bufsize, dgram_show_line, f, AF_INET))
+               if (generic_record_read(fp, dgram_show_line, f, AF_INET))
                        goto outerr;
-               close(fd);
+               fclose(fp);
        }
 
        if ((f->families&(1<<AF_INET6)) &&
-           (fd = net_raw6_open()) >= 0) {
-               if (generic_record_read(fd, buf, bufsize, dgram_show_line, f, AF_INET6))
+           (fp = net_raw6_open()) != NULL) {
+               if (generic_record_read(fp, dgram_show_line, f, AF_INET6))
                        goto outerr;
-               close(fd);
+               fclose(fp);
        }
        return 0;
 
 outerr:
        do {
                int saved_errno = errno;
-               if (fd >= 0)
-                       close(fd);
+               if (fp)
+                       fclose(fp);
                errno = saved_errno;
                return -1;
        } while (0);
@@ -1884,7 +2074,7 @@ int unix_state_map[] = { SS_CLOSE, SS_SYN_SENT,
 
 #define MAX_UNIX_REMEMBER (1024*1024/sizeof(struct unixstat))
 
-void unix_list_free(struct unixstat *list)
+static void unix_list_free(struct unixstat *list)
 {
        while (list) {
                struct unixstat *s = list;
@@ -1895,7 +2085,7 @@ void unix_list_free(struct unixstat *list)
        }
 }
 
-void unix_list_print(struct unixstat *list, struct filter *f)
+static void unix_list_print(struct unixstat *list, struct filter *f)
 {
        struct unixstat *s;
        char *peer;
@@ -1930,13 +2120,13 @@ void unix_list_print(struct unixstat *list, struct filter *f)
                        if (strcmp(peer, "*") == 0)
                                memset(tst.remote.data, 0, sizeof(peer));
                        else
-                               memcpy(tst.remote.data, &peer, sizeof(peer));  
+                               memcpy(tst.remote.data, &peer, sizeof(peer));
                        if (run_ssfilter(f->f, &tst) == 0)
                                continue;
                }
 
                if (netid_width)
-                       printf("%-*s ", netid_width, 
+                       printf("%-*s ", netid_width,
                               s->type == SOCK_STREAM ? "u_str" : "u_dgr");
                if (state_width)
                        printf("%-*s ", state_width, sstate_name[s->state]);
@@ -1953,7 +2143,169 @@ void unix_list_print(struct unixstat *list, struct filter *f)
        }
 }
 
-int unix_show(struct filter *f)
+static int unix_show_sock(struct nlmsghdr *nlh, struct filter *f)
+{
+       struct unix_diag_msg *r = NLMSG_DATA(nlh);
+       struct rtattr *tb[UNIX_DIAG_MAX+1];
+       char name[128];
+       int peer_ino;
+       __u32 rqlen, wqlen;
+
+       parse_rtattr(tb, UNIX_DIAG_MAX, (struct rtattr*)(r+1),
+                    nlh->nlmsg_len - NLMSG_LENGTH(sizeof(*r)));
+
+       if (netid_width)
+               printf("%-*s ", netid_width,
+                               r->udiag_type == SOCK_STREAM ? "u_str" : "u_dgr");
+       if (state_width)
+               printf("%-*s ", state_width, sstate_name[r->udiag_state]);
+
+       if (tb[UNIX_DIAG_RQLEN]) {
+               struct unix_diag_rqlen *rql = RTA_DATA(tb[UNIX_DIAG_RQLEN]);
+               rqlen = rql->udiag_rqueue;
+               wqlen = rql->udiag_wqueue;
+       } else {
+               rqlen = 0;
+               wqlen = 0;
+       }
+
+       printf("%-6u %-6u ", rqlen, wqlen);
+
+       if (tb[UNIX_DIAG_NAME]) {
+               int len = RTA_PAYLOAD(tb[UNIX_DIAG_NAME]);
+
+               memcpy(name, RTA_DATA(tb[UNIX_DIAG_NAME]), len);
+               name[len] = '\0';
+               if (name[0] == '\0')
+                       name[0] = '@';
+       } else
+               sprintf(name, "*");
+
+       if (tb[UNIX_DIAG_PEER])
+               peer_ino = rta_getattr_u32(tb[UNIX_DIAG_PEER]);
+       else
+               peer_ino = 0;
+
+       printf("%*s %-*d %*s %-*d",
+                       addr_width, name,
+                       serv_width, r->udiag_ino,
+                       addr_width, "*", /* FIXME */
+                       serv_width, peer_ino);
+
+       if (show_users) {
+               char ubuf[4096];
+               if (find_users(r->udiag_ino, ubuf, sizeof(ubuf)) > 0)
+                       printf(" users:(%s)", ubuf);
+       }
+
+       if (show_mem) {
+               printf("\n\t");
+               print_skmeminfo(tb, UNIX_DIAG_MEMINFO);
+       }
+
+       printf("\n");
+
+       return 0;
+}
+
+static int unix_show_netlink(struct filter *f, FILE *dump_fp)
+{
+       int fd;
+       struct {
+               struct nlmsghdr nlh;
+               struct unix_diag_req r;
+       } req;
+       char    buf[8192];
+
+       if ((fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_INET_DIAG)) < 0)
+               return -1;
+
+       memset(&req, 0, sizeof(req));
+       req.nlh.nlmsg_len = sizeof(req);
+       req.nlh.nlmsg_type = SOCK_DIAG_BY_FAMILY;
+       req.nlh.nlmsg_flags = NLM_F_ROOT|NLM_F_MATCH|NLM_F_REQUEST;
+       req.nlh.nlmsg_seq = 123456;
+
+       req.r.sdiag_family = AF_UNIX;
+       req.r.udiag_states = f->states;
+       req.r.udiag_show = UDIAG_SHOW_NAME | UDIAG_SHOW_PEER | UDIAG_SHOW_RQLEN;
+       if (show_mem)
+               req.r.udiag_show |= UDIAG_SHOW_MEMINFO;
+
+       if (send(fd, &req, sizeof(req), 0) < 0) {
+               close(fd);
+               return -1;
+       }
+
+       while (1) {
+               ssize_t status;
+               struct nlmsghdr *h;
+               struct sockaddr_nl nladdr;
+               socklen_t slen = sizeof(nladdr);
+
+               status = recvfrom(fd, buf, sizeof(buf), 0,
+                                 (struct sockaddr *) &nladdr, &slen);
+               if (status < 0) {
+                       if (errno == EINTR)
+                               continue;
+                       perror("OVERRUN");
+                       continue;
+               }
+               if (status == 0) {
+                       fprintf(stderr, "EOF on netlink\n");
+                       goto close_it;
+               }
+
+               if (dump_fp)
+                       fwrite(buf, 1, NLMSG_ALIGN(status), dump_fp);
+
+               h = (struct nlmsghdr*)buf;
+               while (NLMSG_OK(h, status)) {
+                       int err;
+
+                       if (/*h->nlmsg_pid != rth->local.nl_pid ||*/
+                           h->nlmsg_seq != 123456)
+                               goto skip_it;
+
+                       if (h->nlmsg_type == NLMSG_DONE)
+                               goto close_it;
+
+                       if (h->nlmsg_type == NLMSG_ERROR) {
+                               struct nlmsgerr *err = (struct nlmsgerr*)NLMSG_DATA(h);
+                               if (h->nlmsg_len < NLMSG_LENGTH(sizeof(struct nlmsgerr))) {
+                                       fprintf(stderr, "ERROR truncated\n");
+                               } else {
+                                       errno = -err->error;
+                                       if (errno != ENOENT)
+                                               fprintf(stderr, "UDIAG answers %d\n", errno);
+                               }
+                               close(fd);
+                               return -1;
+                       }
+                       if (!dump_fp) {
+                               err = unix_show_sock(h, f);
+                               if (err < 0) {
+                                       close(fd);
+                                       return err;
+                               }
+                       }
+
+skip_it:
+                       h = NLMSG_NEXT(h, status);
+               }
+
+               if (status) {
+                       fprintf(stderr, "!!!Remnant of size %zd\n", status);
+                       exit(1);
+               }
+       }
+
+close_it:
+       close(fd);
+       return 0;
+}
+
+static int unix_show(struct filter *f)
 {
        FILE *fp;
        char buf[256];
@@ -1962,11 +2314,15 @@ int unix_show(struct filter *f)
        int  cnt;
        struct unixstat *list = NULL;
 
-       if ((fp = fdopen(net_unix_open(), "r")) == NULL)
+       if (!getenv("PROC_NET_UNIX") && !getenv("PROC_ROOT")
+           && unix_show_netlink(f, NULL) == 0)
+               return 0;
+
+       if ((fp = net_unix_open()) == NULL)
                return -1;
        fgets(buf, sizeof(buf)-1, fp);
 
-       if (memcmp(buf, "Peer", 4) == 0) 
+       if (memcmp(buf, "Peer", 4) == 0)
                newformat = 1;
        cnt = 0;
 
@@ -2022,7 +2378,7 @@ int unix_show(struct filter *f)
                        cnt = 0;
                }
        }
-
+       fclose(fp);
        if (list) {
                unix_list_print(list, f);
                unix_list_free(list);
@@ -2034,7 +2390,7 @@ int unix_show(struct filter *f)
 }
 
 
-int packet_show(struct filter *f)
+static int packet_show(struct filter *f)
 {
        FILE *fp;
        char buf[256];
@@ -2050,7 +2406,7 @@ int packet_show(struct filter *f)
        if (!(f->states & (1<<SS_CLOSE)))
                return 0;
 
-       if ((fp = fdopen(net_packet_open(), "r")) == NULL)
+       if ((fp = net_packet_open()) == NULL)
                return -1;
        fgets(buf, sizeof(buf)-1, fp);
 
@@ -2077,7 +2433,7 @@ int packet_show(struct filter *f)
                }
 
                if (netid_width)
-                       printf("%-*s ", netid_width, 
+                       printf("%-*s ", netid_width,
                               type == SOCK_RAW ? "p_raw" : "p_dgr");
                if (state_width)
                        printf("%-*s ", state_width, "UNCONN");
@@ -2086,7 +2442,7 @@ int packet_show(struct filter *f)
                        printf("%*s:", addr_width, "*");
                } else {
                        char tb[16];
-                       printf("%*s:", addr_width, 
+                       printf("%*s:", addr_width,
                               ll_proto_n2a(htons(prot), tb, sizeof(tb)));
                }
                if (iface == 0) {
@@ -2111,7 +2467,7 @@ int packet_show(struct filter *f)
        return 0;
 }
 
-int netlink_show(struct filter *f)
+static int netlink_show(struct filter *f)
 {
        FILE *fp;
        char buf[256];
@@ -2123,7 +2479,7 @@ int netlink_show(struct filter *f)
        if (!(f->states & (1<<SS_CLOSE)))
                return 0;
 
-       if ((fp = fdopen(net_netlink_open(), "r")) == NULL)
+       if ((fp = net_netlink_open()) == NULL)
                return -1;
        fgets(buf, sizeof(buf)-1, fp);
 
@@ -2145,7 +2501,7 @@ int netlink_show(struct filter *f)
                }
 
                if (netid_width)
-                       printf("%-*s ", netid_width, "nl"); 
+                       printf("%-*s ", netid_width, "nl");
                if (state_width)
                        printf("%-*s ", state_width, "UNCONN");
                printf("%-6d %-6d ", rq, wq);
@@ -2171,7 +2527,7 @@ int netlink_show(struct filter *f)
                                        getenv("PROC_ROOT") ? : "/proc", pid);
                                if ((fp = fopen(procname, "r")) != NULL) {
                                        if (fscanf(fp, "%*d (%[^)])", procname) == 1) {
-                                               sprintf(procname+strlen(procname), "/%d", pid);  
+                                               sprintf(procname+strlen(procname), "/%d", pid);
                                                printf("%-*s ", serv_width, procname);
                                                done = 1;
                                        }
@@ -2200,7 +2556,7 @@ struct snmpstat
        int tcp_estab;
 };
 
-int get_snmp_int(char *proto, char *key, int *result)
+static int get_snmp_int(char *proto, char *key, int *result)
 {
        char buf[1024];
        FILE *fp;
@@ -2209,7 +2565,7 @@ int get_snmp_int(char *proto, char *key, int *result)
 
        *result = 0;
 
-       if ((fp = fdopen(net_snmp_open(), "r")) == NULL)
+       if ((fp = net_snmp_open()) == NULL)
                return -1;
 
        while (fgets(buf, sizeof(buf), fp) != NULL) {
@@ -2295,20 +2651,20 @@ static void get_sockstat_line(char *line, struct sockstat *s)
                       &s->tcp_orphans, &s->tcp_tws, &s->tcp_total, &s->tcp_mem);
 }
 
-int get_sockstat(struct sockstat *s)
+static int get_sockstat(struct sockstat *s)
 {
        char buf[256];
        FILE *fp;
 
        memset(s, 0, sizeof(*s));
 
-       if ((fp = fdopen(net_sockstat_open(), "r")) == NULL)
+       if ((fp = net_sockstat_open()) == NULL)
                return -1;
        while(fgets(buf, sizeof(buf), fp) != NULL)
                get_sockstat_line(buf, s);
        fclose(fp);
 
-       if ((fp = fdopen(net_sockstat6_open(), "r")) == NULL)
+       if ((fp = net_sockstat6_open()) == NULL)
                return 0;
        while(fgets(buf, sizeof(buf), fp) != NULL)
                get_sockstat_line(buf, s);
@@ -2317,7 +2673,7 @@ int get_sockstat(struct sockstat *s)
        return 0;
 }
 
-int print_summary(void)
+static int print_summary(void)
 {
        struct sockstat s;
        struct snmpstat sn;
@@ -2345,7 +2701,7 @@ int print_summary(void)
        printf("RAW       %-9d %-9d %-9d\n", s.raw4+s.raw6, s.raw4, s.raw6);
        printf("UDP       %-9d %-9d %-9d\n", s.udp4+s.udp6, s.udp4, s.udp6);
        printf("TCP       %-9d %-9d %-9d\n", s.tcp4_hashed+s.tcp6_hashed, s.tcp4_hashed, s.tcp6_hashed);
-       printf("INET      %-9d %-9d %-9d\n", 
+       printf("INET      %-9d %-9d %-9d\n",
               s.raw4+s.udp4+s.tcp4_hashed+
               s.raw6+s.udp6+s.tcp6_hashed,
               s.raw4+s.udp4+s.tcp4_hashed,
@@ -2357,12 +2713,9 @@ int print_summary(void)
        return 0;
 }
 
-
-static void usage(void) __attribute__((noreturn));
-
-static void usage(void)
+static void _usage(FILE *dest)
 {
-       fprintf(stderr,
+       fprintf(dest,
 "Usage: ss [ OPTIONS ]\n"
 "       ss [ OPTIONS ] [ FILTER ]\n"
 "   -h, --help         this message\n"
@@ -2383,21 +2736,36 @@ static void usage(void)
 "   -0, --packet       display PACKET sockets\n"
 "   -t, --tcp          display only TCP sockets\n"
 "   -u, --udp          display only UDP sockets\n"
+"   -d, --dccp         display only DCCP sockets\n"
 "   -w, --raw          display only RAW sockets\n"
 "   -x, --unix         display only Unix domain sockets\n"
 "   -f, --family=FAMILY display sockets of type FAMILY\n"
 "\n"
-"   -A, --query=QUERY\n"
+"   -A, --query=QUERY, --socket=QUERY\n"
 "       QUERY := {all|inet|tcp|udp|raw|unix|packet|netlink}[,QUERY]\n"
 "\n"
+"   -D, --diag=FILE     Dump raw information about TCP sockets to FILE\n"
 "   -F, --filter=FILE   read filter information from FILE\n"
 "       FILTER := [ state TCP-STATE ] [ EXPRESSION ]\n"
                );
+}
+
+static void help(void) __attribute__((noreturn));
+static void help(void)
+{
+       _usage(stdout);
+       exit(0);
+}
+
+static void usage(void) __attribute__((noreturn));
+static void usage(void)
+{
+       _usage(stderr);
        exit(-1);
 }
 
 
-int scan_state(const char *state)
+static int scan_state(const char *state)
 {
        int i;
        if (strcasecmp(state, "close") == 0 ||
@@ -2432,6 +2800,7 @@ static const struct option long_opts[] = {
        { "memory", 0, 0, 'm' },
        { "info", 0, 0, 'i' },
        { "processes", 0, 0, 'p' },
+       { "dccp", 0, 0, 'd' },
        { "tcp", 0, 0, 't' },
        { "udp", 0, 0, 'u' },
        { "raw", 0, 0, 'w' },
@@ -2443,13 +2812,14 @@ static const struct option long_opts[] = {
        { "packet", 0, 0, '0' },
        { "family", 1, 0, 'f' },
        { "socket", 1, 0, 'A' },
+       { "query", 1, 0, 'A' },
        { "summary", 0, 0, 's' },
-       { "diag", 0, 0, 'D' },
+       { "diag", 1, 0, 'D' },
        { "filter", 1, 0, 'F' },
        { "version", 0, 0, 'V' },
        { "help", 0, 0, 'h' },
        { 0 }
-       
+
 };
 
 int main(int argc, char *argv[])
@@ -2466,7 +2836,7 @@ int main(int argc, char *argv[])
 
        current_filter.states = default_filter.states;
 
-       while ((ch = getopt_long(argc, argv, "haletuwxnro460spf:miA:D:F:vV",
+       while ((ch = getopt_long(argc, argv, "dhaletuwxnro460spf:miA:D:F:vV",
                                 long_opts, NULL)) != EOF) {
                switch(ch) {
                case 'n':
@@ -2490,6 +2860,11 @@ int main(int argc, char *argv[])
                        break;
                case 'p':
                        show_users++;
+                       user_ent_hash_build();
+                       break;
+               case 'd':
+                       current_filter.dbs |= (1<<DCCP_DB);
+                       do_default = 0;
                        break;
                case 't':
                        current_filter.dbs |= (1<<TCP_DB);
@@ -2511,7 +2886,7 @@ int main(int argc, char *argv[])
                        current_filter.states = SS_ALL;
                        break;
                case 'l':
-                       current_filter.states = (1<<SS_LISTEN);
+                       current_filter.states = (1<<SS_LISTEN) | (1<<SS_CLOSE);
                        break;
                case '4':
                        preferred_family = AF_INET;
@@ -2534,7 +2909,7 @@ int main(int argc, char *argv[])
                        else if (strcmp(optarg, "netlink") == 0)
                                preferred_family = AF_NETLINK;
                        else if (strcmp(optarg, "help") == 0)
-                               usage();
+                               help();
                        else {
                                fprintf(stderr, "ss: \"%s\" is invalid family\n", optarg);
                                usage();
@@ -2551,13 +2926,15 @@ int main(int argc, char *argv[])
                        p = p1 = optarg;
                        do {
                                if ((p1 = strchr(p, ',')) != NULL)
-                                       *p1 = 0; 
+                                       *p1 = 0;
                                if (strcmp(p, "all") == 0) {
                                        current_filter.dbs = ALL_DB;
                                } else if (strcmp(p, "inet") == 0) {
-                                       current_filter.dbs |= (1<<TCP_DB)|(1<<UDP_DB)|(1<<RAW_DB);
+                                       current_filter.dbs |= (1<<TCP_DB)|(1<<DCCP_DB)|(1<<UDP_DB)|(1<<RAW_DB);
                                } else if (strcmp(p, "udp") == 0) {
                                        current_filter.dbs |= (1<<UDP_DB);
+                               } else if (strcmp(p, "dccp") == 0) {
+                                       current_filter.dbs |= (1<<DCCP_DB);
                                } else if (strcmp(p, "tcp") == 0) {
                                        current_filter.dbs |= (1<<TCP_DB);
                                } else if (strcmp(p, "raw") == 0) {
@@ -2614,6 +2991,7 @@ int main(int argc, char *argv[])
                        exit(0);
                case 'h':
                case '?':
+                       help();
                default:
                        usage();
                }
@@ -2646,9 +3024,7 @@ int main(int argc, char *argv[])
                int mask2;
                if (preferred_family == AF_INET ||
                    preferred_family == AF_INET6) {
-                       mask2= (1<<TCP_DB);
-                       if (!do_default)
-                               mask2 = (1<<UDP_DB)|(1<<RAW_DB);
+                       mask2= current_filter.dbs;
                } else if (preferred_family == AF_PACKET) {
                        mask2 = PACKET_DBM;
                } else if (preferred_family == AF_UNIX) {
@@ -2680,7 +3056,7 @@ int main(int argc, char *argv[])
        }
 
        if (resolve_services && resolve_hosts &&
-           (current_filter.dbs&(UNIX_DBM|(1<<TCP_DB)|(1<<UDP_DB))))
+           (current_filter.dbs&(UNIX_DBM|(1<<TCP_DB)|(1<<UDP_DB)|(1<<DCCP_DB))))
                init_service_resolver();
 
        /* Now parse filter... */
@@ -2729,7 +3105,7 @@ int main(int argc, char *argv[])
                                exit(-1);
                        }
                }
-               tcp_show_netlink(&current_filter, dump_fp);
+               inet_show_netlink(&current_filter, dump_fp, IPPROTO_TCP);
                fflush(dump_fp);
                exit(0);
        }
@@ -2772,7 +3148,7 @@ int main(int argc, char *argv[])
        if (addrp_width < 15+serv_width+1)
                addrp_width = 15+serv_width+1;
 
-       addr_width = addrp_width - serv_width - 1; 
+       addr_width = addrp_width - serv_width - 1;
 
        if (netid_width)
                printf("%-*s ", netid_width, "Netid");
@@ -2784,7 +3160,6 @@ int main(int argc, char *argv[])
               addr_width, "Local Address", serv_width, "Port",
               addr_width, "Peer Address", serv_width, "Port");
 
-//printf("%08x %08x %08x\n", current_filter.dbs, current_filter.states, current_filter.families);
        fflush(stdout);
 
        if (current_filter.dbs & (1<<NETLINK_DB))
@@ -2798,6 +3173,8 @@ int main(int argc, char *argv[])
        if (current_filter.dbs & (1<<UDP_DB))
                udp_show(&current_filter);
        if (current_filter.dbs & (1<<TCP_DB))
-               tcp_show(&current_filter);
+               tcp_show(&current_filter, IPPROTO_TCP);
+       if (current_filter.dbs & (1<<DCCP_DB))
+               tcp_show(&current_filter, IPPROTO_DCCP);
        return 0;
 }