diff options
Diffstat (limited to 'misc/ss.c')
-rw-r--r-- | misc/ss.c | 1474 |
1 files changed, 937 insertions, 537 deletions
@@ -26,6 +26,7 @@ #include <fnmatch.h> #include <getopt.h> #include <stdbool.h> +#include <limits.h> #include "utils.h" #include "rt_names.h" @@ -42,6 +43,7 @@ #include <linux/filter.h> #include <linux/packet_diag.h> #include <linux/netlink_diag.h> +#include <linux/sctp.h> #define MAGIC_SEQ 123456 @@ -86,35 +88,33 @@ static int security_get_initial_context(char *name, char **context) } #endif -int resolve_hosts = 0; int resolve_services = 1; int preferred_family = AF_UNSPEC; -int show_options = 0; -int show_details = 0; -int show_users = 0; -int show_mem = 0; -int show_tcpinfo = 0; -int show_bpf = 0; -int show_proc_ctx = 0; -int show_sock_ctx = 0; -/* If show_users & show_proc_ctx only do user_ent_hash_build() once */ -int user_ent_hash_build_init = 0; -int follow_events = 0; +int show_options; +int show_details; +int show_users; +int show_mem; +int show_tcpinfo; +int show_bpf; +int show_proc_ctx; +int show_sock_ctx; +int show_header = 1; +int follow_events; +int sctp_ino; int netid_width; int state_width; -int addrp_width; int addr_width; int serv_width; -int screen_width; +char *odd_width_pad = ""; static const char *TCP_PROTO = "tcp"; +static const char *SCTP_PROTO = "sctp"; static const char *UDP_PROTO = "udp"; static const char *RAW_PROTO = "raw"; -static const char *dg_proto = NULL; +static const char *dg_proto; -enum -{ +enum { TCP_DB, DCCP_DB, UDP_DB, @@ -125,13 +125,15 @@ enum PACKET_DG_DB, PACKET_R_DB, NETLINK_DB, + SCTP_DB, MAX_DB }; #define PACKET_DBM ((1<<PACKET_DG_DB)|(1<<PACKET_R_DB)) #define UNIX_DBM ((1<<UNIX_DG_DB)|(1<<UNIX_ST_DB)|(1<<UNIX_SQ_DB)) #define ALL_DB ((1<<MAX_DB)-1) -#define INET_DBM ((1<<TCP_DB)|(1<<UDP_DB)|(1<<DCCP_DB)|(1<<RAW_DB)) +#define INET_L4_DBM ((1<<TCP_DB)|(1<<UDP_DB)|(1<<DCCP_DB)|(1<<SCTP_DB)) +#define INET_DBM (INET_L4_DBM | (1<<RAW_DB)) enum { SS_UNKNOWN, @@ -149,13 +151,23 @@ enum { SS_MAX }; +enum { + SCTP_STATE_CLOSED = 0, + SCTP_STATE_COOKIE_WAIT = 1, + SCTP_STATE_COOKIE_ECHOED = 2, + SCTP_STATE_ESTABLISHED = 3, + SCTP_STATE_SHUTDOWN_PENDING = 4, + SCTP_STATE_SHUTDOWN_SENT = 5, + SCTP_STATE_SHUTDOWN_RECEIVED = 6, + SCTP_STATE_SHUTDOWN_ACK_SENT = 7, +}; + #define SS_ALL ((1 << SS_MAX) - 1) #define SS_CONN (SS_ALL & ~((1<<SS_LISTEN)|(1<<SS_CLOSE)|(1<<SS_TIME_WAIT)|(1<<SS_SYN_RECV))) #include "ssfilter.h" -struct filter -{ +struct filter { int dbs; int states; int families; @@ -204,6 +216,10 @@ static const struct filter default_dbs[MAX_DB] = { .states = (1 << SS_CLOSE), .families = (1 << AF_NETLINK), }, + [SCTP_DB] = { + .states = SS_CONN, + .families = (1 << AF_INET) | (1 << AF_INET6), + }, }; static const struct filter default_afs[AF_MAX] = { @@ -264,12 +280,13 @@ static void filter_default_dbs(struct filter *f) filter_db_set(f, PACKET_R_DB); filter_db_set(f, PACKET_DG_DB); filter_db_set(f, NETLINK_DB); + filter_db_set(f, SCTP_DB); } static void filter_states_set(struct filter *f, int states) { if (states) - f->states = (f->states | states) & states; + f->states = states; } static void filter_merge_defaults(struct filter *f) @@ -306,76 +323,25 @@ static FILE *generic_proc_open(const char *env, const char *name) return fopen(p, "r"); } - -static FILE *net_tcp_open(void) -{ - return generic_proc_open("PROC_NET_TCP", "net/tcp"); -} - -static FILE *net_tcp6_open(void) -{ - return generic_proc_open("PROC_NET_TCP6", "net/tcp6"); -} - -static FILE *net_udp_open(void) -{ - return generic_proc_open("PROC_NET_UDP", "net/udp"); -} - -static FILE *net_udp6_open(void) -{ - return generic_proc_open("PROC_NET_UDP6", "net/udp6"); -} - -static FILE *net_raw_open(void) -{ - return generic_proc_open("PROC_NET_RAW", "net/raw"); -} - -static FILE *net_raw6_open(void) -{ - return generic_proc_open("PROC_NET_RAW6", "net/raw6"); -} - -static FILE *net_unix_open(void) -{ - return generic_proc_open("PROC_NET_UNIX", "net/unix"); -} - -static FILE *net_packet_open(void) -{ - return generic_proc_open("PROC_NET_PACKET", "net/packet"); -} - -static FILE *net_netlink_open(void) -{ - return generic_proc_open("PROC_NET_NETLINK", "net/netlink"); -} - -static FILE *slabinfo_open(void) -{ - return generic_proc_open("PROC_SLABINFO", "slabinfo"); -} - -static FILE *net_sockstat_open(void) -{ - return generic_proc_open("PROC_NET_SOCKSTAT", "net/sockstat"); -} - -static FILE *net_sockstat6_open(void) -{ - return generic_proc_open("PROC_NET_SOCKSTAT6", "net/sockstat6"); -} - -static FILE *net_snmp_open(void) -{ - return generic_proc_open("PROC_NET_SNMP", "net/snmp"); -} - -static FILE *ephemeral_ports_open(void) -{ - return generic_proc_open("PROC_IP_LOCAL_PORT_RANGE", "sys/net/ipv4/ip_local_port_range"); -} +#define net_tcp_open() generic_proc_open("PROC_NET_TCP", "net/tcp") +#define net_tcp6_open() generic_proc_open("PROC_NET_TCP6", "net/tcp6") +#define net_udp_open() generic_proc_open("PROC_NET_UDP", "net/udp") +#define net_udp6_open() generic_proc_open("PROC_NET_UDP6", "net/udp6") +#define net_raw_open() generic_proc_open("PROC_NET_RAW", "net/raw") +#define net_raw6_open() generic_proc_open("PROC_NET_RAW6", "net/raw6") +#define net_unix_open() generic_proc_open("PROC_NET_UNIX", "net/unix") +#define net_packet_open() generic_proc_open("PROC_NET_PACKET", \ + "net/packet") +#define net_netlink_open() generic_proc_open("PROC_NET_NETLINK", \ + "net/netlink") +#define slabinfo_open() generic_proc_open("PROC_SLABINFO", "slabinfo") +#define net_sockstat_open() generic_proc_open("PROC_NET_SOCKSTAT", \ + "net/sockstat") +#define net_sockstat6_open() generic_proc_open("PROC_NET_SOCKSTAT6", \ + "net/sockstat6") +#define net_snmp_open() generic_proc_open("PROC_NET_SNMP", "net/snmp") +#define ephemeral_ports_open() generic_proc_open("PROC_IP_LOCAL_PORT_RANGE", \ + "sys/net/ipv4/ip_local_port_range") struct user_ent { struct user_ent *next; @@ -451,6 +417,7 @@ static void user_ent_hash_build(void) char *pid_context; char *sock_context; const char *no_ctx = "unavailable"; + static int user_ent_hash_build_init; /* If show_users & show_proc_ctx set only do this once */ if (user_ent_hash_build_init != 0) @@ -458,8 +425,7 @@ static void user_ent_hash_build(void) user_ent_hash_build_init = 1; - strncpy(name, root, sizeof(name)-1); - name[sizeof(name)-1] = 0; + strlcpy(name, root, sizeof(name)); if (strlen(name) == 0 || name[strlen(name)-1] != '/') strcat(name, "/"); @@ -551,7 +517,7 @@ enum entry_types { }; #define ENTRY_BUF_SIZE 512 -static int find_entry(unsigned ino, char **buf, int type) +static int find_entry(unsigned int ino, char **buf, int type) { struct user_ent *p; int cnt = 0; @@ -624,8 +590,7 @@ next: /* Get stats from slab */ -struct slabstat -{ +struct slabstat { int socks; int tcp_ports; int tcp_tws; @@ -635,21 +600,19 @@ struct slabstat static struct slabstat slabstat; -static const char *slabstat_ids[] = -{ - "sock", - "tcp_bind_bucket", - "tcp_tw_bucket", - "tcp_open_request", - "skbuff_head_cache", -}; - static int get_slabstat(struct slabstat *s) { char buf[256]; FILE *fp; int cnt; static int slabstat_valid; + static const char * const slabstat_ids[] = { + "sock", + "tcp_bind_bucket", + "tcp_tw_bucket", + "tcp_open_request", + "skbuff_head_cache", + }; if (slabstat_valid) return 0; @@ -666,9 +629,10 @@ static int get_slabstat(struct slabstat *s) fclose(fp); return -1; } - while(fgets(buf, sizeof(buf), fp) != NULL) { + while (fgets(buf, sizeof(buf), fp) != NULL) { int i; - for (i=0; i<sizeof(slabstat_ids)/sizeof(slabstat_ids[0]); i++) { + + for (i = 0; i < ARRAY_SIZE(slabstat_ids); i++) { if (memcmp(buf, slabstat_ids[i], strlen(slabstat_ids[i])) == 0) { sscanf(buf, "%*s%d", ((int *)s) + i); cnt--; @@ -690,49 +654,30 @@ static unsigned long long cookie_sk_get(const uint32_t *cookie) return (((unsigned long long)cookie[1] << 31) << 1) | cookie[0]; } -static const char *sstate_name[] = { - "UNKNOWN", - [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 *sctp_sstate_name[] = { + [SCTP_STATE_CLOSED] = "CLOSED", + [SCTP_STATE_COOKIE_WAIT] = "COOKIE_WAIT", + [SCTP_STATE_COOKIE_ECHOED] = "COOKIE_ECHOED", + [SCTP_STATE_ESTABLISHED] = "ESTAB", + [SCTP_STATE_SHUTDOWN_PENDING] = "SHUTDOWN_PENDING", + [SCTP_STATE_SHUTDOWN_SENT] = "SHUTDOWN_SENT", + [SCTP_STATE_SHUTDOWN_RECEIVED] = "SHUTDOWN_RECEIVED", + [SCTP_STATE_SHUTDOWN_ACK_SENT] = "ACK_SENT", }; -static const char *sstate_namel[] = { - "UNKNOWN", - [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 sockstat -{ +struct sockstat { struct sockstat *next; unsigned int type; uint16_t prot; + uint16_t raw_prot; inet_prefix local; inet_prefix remote; int lport; int rport; int state; int rq, wq; - unsigned ino; - unsigned uid; + unsigned int ino; + unsigned int uid; int refcnt; unsigned int iface; unsigned long long sk; @@ -741,8 +686,7 @@ struct sockstat __u32 mark; }; -struct dctcpstat -{ +struct dctcpstat { unsigned int ce_state; unsigned int alpha; unsigned int ab_ecn; @@ -750,28 +694,33 @@ struct dctcpstat bool enabled; }; -struct tcpstat -{ +struct tcpstat { struct sockstat ss; - int timer; - int timeout; + unsigned int timer; + unsigned int timeout; int probes; char cong_alg[16]; double rto, ato, rtt, rttvar; - int qack, cwnd, ssthresh, backoff; + int qack, ssthresh, backoff; double send_bps; int snd_wscale; int rcv_wscale; int mss; + int rcv_mss; + int advmss; + unsigned int cwnd; unsigned int lastsnd; unsigned int lastrcv; unsigned int lastack; double pacing_rate; double pacing_rate_max; + double delivery_rate; unsigned long long bytes_acked; unsigned long long bytes_received; unsigned int segs_out; unsigned int segs_in; + unsigned int data_segs_out; + unsigned int data_segs_in; unsigned int unacked; unsigned int retrans; unsigned int retrans_total; @@ -779,25 +728,117 @@ struct tcpstat unsigned int sacked; unsigned int fackets; unsigned int reordering; + unsigned int not_sent; double rcv_rtt; + double min_rtt; int rcv_space; + unsigned long long busy_time; + unsigned long long rwnd_limited; + unsigned long long sndbuf_limited; bool has_ts_opt; bool has_sack_opt; bool has_ecn_opt; bool has_ecnseen_opt; bool has_fastopen_opt; bool has_wscale_opt; + bool app_limited; struct dctcpstat *dctcp; + struct tcp_bbr_info *bbr_info; }; -static void sock_state_print(struct sockstat *s, const char *sock_name) +/* SCTP assocs share the same inode number with their parent endpoint. So if we + * have seen the inode number before, it must be an assoc instead of the next + * endpoint. */ +static bool is_sctp_assoc(struct sockstat *s, const char *sock_name) +{ + if (strcmp(sock_name, "sctp")) + return false; + if (!sctp_ino || sctp_ino != s->ino) + return false; + return true; +} + +static const char *unix_netid_name(int type) +{ + switch (type) { + case SOCK_STREAM: + return "u_str"; + case SOCK_SEQPACKET: + return "u_seq"; + case SOCK_DGRAM: + default: + return "u_dgr"; + } +} + +static const char *proto_name(int protocol) +{ + switch (protocol) { + case 0: + return "raw"; + case IPPROTO_UDP: + return "udp"; + case IPPROTO_TCP: + return "tcp"; + case IPPROTO_SCTP: + return "sctp"; + case IPPROTO_DCCP: + return "dccp"; + case IPPROTO_ICMPV6: + return "icmp6"; + } + + return "???"; +} + +static void sock_state_print(struct sockstat *s) { + const char *sock_name; + static const char * const sstate_name[] = { + "UNKNOWN", + [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", + }; + + switch (s->local.family) { + case AF_UNIX: + sock_name = unix_netid_name(s->type); + break; + case AF_INET: + case AF_INET6: + sock_name = proto_name(s->type); + break; + case AF_PACKET: + sock_name = s->type == SOCK_RAW ? "p_raw" : "p_dgr"; + break; + case AF_NETLINK: + sock_name = "nl"; + break; + default: + sock_name = "unknown"; + } + if (netid_width) - printf("%-*s ", netid_width, sock_name); - if (state_width) - printf("%-*s ", state_width, sstate_name[s->state]); + printf("%-*s ", netid_width, + is_sctp_assoc(s, sock_name) ? "" : sock_name); + if (state_width) { + if (is_sctp_assoc(s, sock_name)) + printf("`- %-*s ", state_width - 3, + sctp_sstate_name[s->state]); + else + printf("%-*s ", state_width, sstate_name[s->state]); + } - printf("%-6d %-6d ", s->rq, s->wq); + printf("%-6d %-6d %s", s->rq, s->wq, odd_width_pad); } static void sock_details_print(struct sockstat *s) @@ -818,8 +859,7 @@ static void sock_addr_print_width(int addr_len, const char *addr, char *delim, if (ifname) { printf("%*s%%%s%s%-*s ", addr_len, addr, ifname, delim, port_len, port); - } - else { + } else { printf("%*s%s%-*s ", addr_len, addr, delim, port_len, port); } } @@ -830,21 +870,11 @@ static void sock_addr_print(const char *addr, char *delim, const char *port, sock_addr_print_width(addr_width, addr, delim, serv_width, port, ifname); } -static const char *tmr_name[] = { - "off", - "on", - "keepalive", - "timewait", - "persist", - "unknown" -}; - -static const char *print_ms_timer(int timeout) +static const char *print_ms_timer(unsigned int timeout) { static char buf[64]; int secs, msecs, minutes; - if (timeout < 0) - timeout = 0; + secs = timeout/1000; minutes = secs/60; secs = secs%60; @@ -893,7 +923,7 @@ static void init_service_resolver(void) struct scache *c; if (sscanf(buf, "%u %*d %s %u %s", - &progn, proto, &port, prog+4) != 4) + &progn, proto, &port, prog+4) != 4) continue; if (!(c = malloc(sizeof(*c)))) @@ -905,6 +935,8 @@ static void init_service_resolver(void) c->proto = TCP_PROTO; else if (strcmp(proto, UDP_PROTO) == 0) c->proto = UDP_PROTO; + else if (strcmp(proto, SCTP_PROTO) == 0) + c->proto = SCTP_PROTO; else c->proto = NULL; c->next = rlist; @@ -921,10 +953,11 @@ static void init_service_resolver(void) */ static int is_ephemeral(int port) { - static int min = 0, max = 0; + static int min = 0, max; if (!min) { FILE *f = ephemeral_ports_open(); + if (!f || fscanf(f, "%d %d", &min, &max) < 2) { min = 1024; max = 4999; @@ -948,6 +981,7 @@ static const char *__resolve_service(int port) if (!is_ephemeral(port)) { static int notfirst; struct servent *se; + if (!notfirst) { setservent(1); notfirst = 1; @@ -1009,7 +1043,8 @@ do_numeric: return buf; } -static void inet_addr_print(const inet_prefix *a, int port, unsigned int ifindex) +static void inet_addr_print(const inet_prefix *a, int port, + unsigned int ifindex, bool v6only) { char buf[1024]; const char *ap = buf; @@ -1017,19 +1052,28 @@ static void inet_addr_print(const inet_prefix *a, int port, unsigned int ifindex const char *ifname = NULL; if (a->family == AF_INET) { - if (a->data[0] == 0) { + ap = format_host(AF_INET, 4, a->data); + } else { + if (!v6only && + !memcmp(a->data, &in6addr_any, sizeof(in6addr_any))) { buf[0] = '*'; buf[1] = 0; } else { - ap = format_host(AF_INET, 4, a->data, buf, sizeof(buf)); + ap = format_host(a->family, 16, a->data); + + /* Numeric IPv6 addresses should be bracketed */ + if (strchr(ap, ':')) { + snprintf(buf, sizeof(buf), + "[%s]", ap); + ap = buf; + } + + est_len = strlen(ap); + if (est_len <= addr_width) + est_len = addr_width; + else + est_len = addr_width + ((est_len-addr_width+3)/4)*4; } - } else { - ap = format_host(a->family, 16, a->data, buf, sizeof(buf)); - est_len = strlen(ap); - if (est_len <= addr_width) - est_len = addr_width; - else - est_len = addr_width + ((est_len-addr_width+3)/4)*4; } if (ifindex) { @@ -1043,10 +1087,10 @@ static void inet_addr_print(const inet_prefix *a, int port, unsigned int ifindex ifname); } -struct aafilter -{ +struct aafilter { inet_prefix addr; int port; + unsigned int iface; __u32 mark; __u32 mask; struct aafilter *next; @@ -1065,6 +1109,7 @@ static int inet2_addr_match(const inet_prefix *a, const inet_prefix *p, if (a->data[0] == 0 && a->data[1] == 0 && a->data[2] == htonl(0xffff)) { inet_prefix tmp = *a; + tmp.data[0] = a->data[3]; return inet_addr_match(&tmp, p, plen); } @@ -1075,6 +1120,7 @@ static int inet2_addr_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)); memcpy(&pattern, p->data, sizeof(pattern)); if (pattern == NULL) @@ -1091,6 +1137,7 @@ static int run_ssfilter(struct ssfilter *f, struct sockstat *s) { if (s->local.family == AF_UNIX) { char *p; + memcpy(&p, s->local.data, sizeof(p)); return p == NULL || (p[0] == '@' && strlen(p) == 6 && strspn(p+1, "0123456789abcdef") == 5); @@ -1104,7 +1151,8 @@ static int run_ssfilter(struct ssfilter *f, struct sockstat *s) } case SSF_DCOND: { - struct aafilter *a = (void*)f->pred; + struct aafilter *a = (void *)f->pred; + if (a->addr.family == AF_UNIX) return unix_match(&s->remote, &a->addr); if (a->port != -1 && a->port != s->rport) @@ -1120,7 +1168,8 @@ static int run_ssfilter(struct ssfilter *f, struct sockstat *s) } case SSF_SCOND: { - struct aafilter *a = (void*)f->pred; + struct aafilter *a = (void *)f->pred; + if (a->addr.family == AF_UNIX) return unix_match(&s->local, &a->addr); if (a->port != -1 && a->port != s->lport) @@ -1136,24 +1185,34 @@ static int run_ssfilter(struct ssfilter *f, struct sockstat *s) } case SSF_D_GE: { - struct aafilter *a = (void*)f->pred; + struct aafilter *a = (void *)f->pred; + return s->rport >= a->port; } case SSF_D_LE: { - struct aafilter *a = (void*)f->pred; + struct aafilter *a = (void *)f->pred; + return s->rport <= a->port; } case SSF_S_GE: { - struct aafilter *a = (void*)f->pred; + struct aafilter *a = (void *)f->pred; + return s->lport >= a->port; } case SSF_S_LE: { - struct aafilter *a = (void*)f->pred; + struct aafilter *a = (void *)f->pred; + return s->lport <= a->port; } + case SSF_DEVCOND: + { + struct aafilter *a = (void *)f->pred; + + return s->iface == a->iface; + } case SSF_MARKMASK: { struct aafilter *a = (void *)f->pred; @@ -1176,7 +1235,8 @@ static int run_ssfilter(struct ssfilter *f, struct sockstat *s) static void ssfilter_patch(char *a, int len, int reloc) { while (len > 0) { - struct inet_diag_bc_op *op = (struct inet_diag_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; @@ -1191,20 +1251,20 @@ static int ssfilter_bytecompile(struct ssfilter *f, char **bytecode) switch (f->type) { case SSF_S_AUTO: { - if (!(*bytecode=malloc(4))) abort(); - ((struct inet_diag_bc_op*)*bytecode)[0] = (struct inet_diag_bc_op){ INET_DIAG_BC_AUTO, 4, 8 }; + if (!(*bytecode = malloc(4))) abort(); + ((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: { - struct aafilter *a = (void*)f->pred; + struct aafilter *a = (void *)f->pred; struct aafilter *b; char *ptr; 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) { + for (b = a; b; b = b->next) { len += 4 + sizeof(struct inet_diag_hostcond); if (a->addr.family == AF_INET6) len += 16; @@ -1215,11 +1275,11 @@ static int ssfilter_bytecompile(struct ssfilter *f, char **bytecode) } if (!(ptr = malloc(len))) abort(); *bytecode = ptr; - for (b=a; b; b=b->next) { + for (b = a; b; b = b->next) { 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 inet_diag_hostcond); - struct inet_diag_hostcond *cond = (struct inet_diag_hostcond*)(ptr+4); + struct inet_diag_hostcond *cond = (struct inet_diag_hostcond *)(ptr+4); *op = (struct inet_diag_bc_op){ code, oplen, oplen+4 }; cond->family = a->addr.family; @@ -1237,43 +1297,53 @@ static int ssfilter_bytecompile(struct ssfilter *f, char **bytecode) } case SSF_D_GE: { - struct aafilter *x = (void*)f->pred; - if (!(*bytecode=malloc(8))) abort(); - ((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 }; + struct aafilter *x = (void *)f->pred; + + if (!(*bytecode = malloc(8))) abort(); + ((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 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 }; + struct aafilter *x = (void *)f->pred; + + if (!(*bytecode = malloc(8))) abort(); + ((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 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 }; + struct aafilter *x = (void *)f->pred; + + if (!(*bytecode = malloc(8))) abort(); + ((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 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 }; + struct aafilter *x = (void *)f->pred; + + if (!(*bytecode = malloc(8))) abort(); + ((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; } case SSF_AND: { - char *a1, *a2, *a; + char *a1 = NULL, *a2 = NULL, *a; int l1, l2; + l1 = ssfilter_bytecompile(f->pred, &a1); l2 = ssfilter_bytecompile(f->post, &a2); + if (!l1 || !l2) { + free(a1); + free(a2); + return 0; + } if (!(a = malloc(l1+l2))) abort(); memcpy(a, a1, l1); memcpy(a+l1, a2, l2); @@ -1284,30 +1354,46 @@ static int ssfilter_bytecompile(struct ssfilter *f, char **bytecode) } case SSF_OR: { - char *a1, *a2, *a; + char *a1 = NULL, *a2 = NULL, *a; int l1, l2; + l1 = ssfilter_bytecompile(f->pred, &a1); l2 = ssfilter_bytecompile(f->post, &a2); + if (!l1 || !l2) { + free(a1); + free(a2); + return 0; + } if (!(a = malloc(l1+l2+4))) abort(); memcpy(a, a1, l1); memcpy(a+l1+4, a2, l2); free(a1); free(a2); - *(struct inet_diag_bc_op*)(a+l1) = (struct inet_diag_bc_op){ INET_DIAG_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; } case SSF_NOT: { - char *a1, *a; + char *a1 = NULL, *a; int l1; + l1 = ssfilter_bytecompile(f->pred, &a1); + if (!l1) { + free(a1); + return 0; + } if (!(a = malloc(l1+4))) abort(); memcpy(a, a1, l1); free(a1); - *(struct inet_diag_bc_op*)(a+l1) = (struct inet_diag_bc_op){ INET_DIAG_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; } + case SSF_DEVCOND: + { + /* bytecompile for SSF_DEVCOND not supported yet */ + return 0; + } case SSF_MARKMASK: { struct aafilter *a = (void *)f->pred; @@ -1318,7 +1404,7 @@ static int ssfilter_bytecompile(struct ssfilter *f, char **bytecode) int inslen = sizeof(struct instr); if (!(*bytecode = malloc(inslen))) abort(); - ((struct instr *)*bytecode)[0] = (struct instr) { + ((struct instr *)*bytecode)[0] = (struct instr) { { INET_DIAG_BC_MARK_COND, inslen, inslen + 4 }, { a->mark, a->mask}, }; @@ -1345,11 +1431,11 @@ static int remember_he(struct aafilter *a, struct hostent *he) while (*ptr) { struct aafilter *b = a; + if (a->addr.bitlen) { if ((b = malloc(sizeof(*b))) == NULL) return cnt; *b = *a; - b->next = a->next; a->next = b; } memcpy(b->addr.data, *ptr, len); @@ -1384,11 +1470,12 @@ static int get_dns_host(struct aafilter *a, const char *addr, int fam) return !cnt; } -static int xll_initted = 0; +static int xll_initted; static void xll_init(void) { struct rtnl_handle rth; + if (rtnl_open(&rth, 0) < 0) exit(1); @@ -1411,6 +1498,29 @@ static int xll_name_to_index(const char *dev) return ll_name_to_index(dev); } +void *parse_devcond(char *name) +{ + struct aafilter a = { .iface = 0 }; + struct aafilter *res; + + a.iface = xll_name_to_index(name); + if (a.iface == 0) { + char *end; + unsigned long n; + + n = strtoul(name, &end, 0); + if (!end || end == name || *end || n > UINT_MAX) + return NULL; + + a.iface = n; + } + + res = malloc(sizeof(*res)); + *res = a; + + return res; +} + void *parse_hostcond(char *addr, bool is_port) { char *port = NULL; @@ -1421,9 +1531,10 @@ void *parse_hostcond(char *addr, bool is_port) if (fam == AF_UNIX || strncmp(addr, "unix:", 5) == 0) { char *p; + a.addr.family = AF_UNIX; if (strncmp(addr, "unix:", 5) == 0) - addr+=5; + addr += 5; p = strdup(addr); a.addr.bitlen = 8*strlen(p); memcpy(a.addr.data, &p, sizeof(p)); @@ -1435,7 +1546,7 @@ void *parse_hostcond(char *addr, bool is_port) a.addr.family = AF_PACKET; a.addr.bitlen = 0; if (strncmp(addr, "link:", 5) == 0) - addr+=5; + addr += 5; port = strchr(addr, ':'); if (port) { *port = 0; @@ -1448,6 +1559,7 @@ void *parse_hostcond(char *addr, bool is_port) } if (addr[0] && strcmp(addr, "*")) { unsigned short tmp; + a.addr.bitlen = 32; if (ll_proto_a2n(&tmp, addr)) return NULL; @@ -1461,7 +1573,7 @@ void *parse_hostcond(char *addr, bool is_port) a.addr.family = AF_NETLINK; a.addr.bitlen = 0; if (strncmp(addr, "netlink:", 8) == 0) - addr+=8; + addr += 8; port = strchr(addr, ':'); if (port) { *port = 0; @@ -1516,6 +1628,7 @@ void *parse_hostcond(char *addr, bool is_port) if (get_integer(&a.port, port, 0)) { struct servent *se1 = NULL; struct servent *se2 = NULL; + if (current_filter.dbs&(1<<UDP_DB)) se1 = getservbyname(port, UDP_PROTO); if (current_filter.dbs&(1<<TCP_DB)) @@ -1530,6 +1643,7 @@ void *parse_hostcond(char *addr, bool is_port) a.port = ntohs(se1->s_port); } else { struct scache *s; + for (s = rlist; s; s = s->next) { if ((s->proto == UDP_PROTO && (current_filter.dbs&(1<<UDP_DB))) || @@ -1552,7 +1666,7 @@ void *parse_hostcond(char *addr, bool is_port) } } } - if (!is_port && addr && *addr && *addr != '*') { + if (!is_port && *addr && *addr != '*') { if (get_prefix_1(&a.addr, addr, fam)) { if (get_dns_host(&a, addr, fam)) { fprintf(stderr, "Error: an inet prefix is expected rather than \"%s\".\n", addr); @@ -1563,9 +1677,10 @@ void *parse_hostcond(char *addr, bool is_port) out: if (fam != AF_UNSPEC) { + int states = f->states; f->families = 0; filter_af_set(f, fam); - filter_states_set(f, 0); + filter_states_set(f, states); } res = malloc(sizeof(*res)); @@ -1593,30 +1708,9 @@ void *parse_markmask(const char *markmask) return res; } -static char *proto_name(int protocol) +static void proc_ctx_print(struct sockstat *s) { - switch (protocol) { - case 0: - return "raw"; - case IPPROTO_UDP: - return "udp"; - case IPPROTO_TCP: - return "tcp"; - case IPPROTO_DCCP: - return "dccp"; - } - - return "???"; -} - -static void inet_stats_print(struct sockstat *s, int protocol) -{ - char *buf = NULL; - - sock_state_print(s, proto_name(protocol)); - - inet_addr_print(&s->local, s->lport, s->iface); - inet_addr_print(&s->remote, s->rport, 0); + char *buf; if (show_proc_ctx || show_sock_ctx) { if (find_entry(s->ino, &buf, @@ -1633,13 +1727,23 @@ static void inet_stats_print(struct sockstat *s, int protocol) } } +static void inet_stats_print(struct sockstat *s, bool v6only) +{ + sock_state_print(s); + + inet_addr_print(&s->local, s->lport, s->iface, v6only); + inet_addr_print(&s->remote, s->rport, 0, v6only); + + proc_ctx_print(s); +} + static int proc_parse_inet_addr(char *loc, char *rem, int family, struct - sockstat *s) + sockstat * s) { s->local.family = s->remote.family = family; if (family == AF_INET) { - sscanf(loc, "%x:%x", s->local.data, (unsigned*)&s->lport); - sscanf(rem, "%x:%x", s->remote.data, (unsigned*)&s->rport); + sscanf(loc, "%x:%x", s->local.data, (unsigned *)&s->lport); + sscanf(rem, "%x:%x", s->remote.data, (unsigned *)&s->rport); s->local.bytelen = s->remote.bytelen = 4; return 0; } else { @@ -1685,15 +1789,65 @@ static int proc_inet_split_line(char *line, char **loc, char **rem, char **data) static char *sprint_bw(char *buf, double bw) { if (bw > 1000000.) - sprintf(buf,"%.1fM", bw / 1000000.); + sprintf(buf, "%.1fM", bw / 1000000.); else if (bw > 1000.) - sprintf(buf,"%.1fK", bw / 1000.); + sprintf(buf, "%.1fK", bw / 1000.); else sprintf(buf, "%g", bw); return buf; } +static void sctp_stats_print(struct sctp_info *s) +{ + if (s->sctpi_tag) + printf(" tag:%x", s->sctpi_tag); + if (s->sctpi_state) + printf(" state:%s", sctp_sstate_name[s->sctpi_state]); + if (s->sctpi_rwnd) + printf(" rwnd:%d", s->sctpi_rwnd); + if (s->sctpi_unackdata) + printf(" unackdata:%d", s->sctpi_unackdata); + if (s->sctpi_penddata) + printf(" penddata:%d", s->sctpi_penddata); + if (s->sctpi_instrms) + printf(" instrms:%d", s->sctpi_instrms); + if (s->sctpi_outstrms) + printf(" outstrms:%d", s->sctpi_outstrms); + if (s->sctpi_inqueue) + printf(" inqueue:%d", s->sctpi_inqueue); + if (s->sctpi_outqueue) + printf(" outqueue:%d", s->sctpi_outqueue); + if (s->sctpi_overall_error) + printf(" overerr:%d", s->sctpi_overall_error); + if (s->sctpi_max_burst) + printf(" maxburst:%d", s->sctpi_max_burst); + if (s->sctpi_maxseg) + printf(" maxseg:%d", s->sctpi_maxseg); + if (s->sctpi_peer_rwnd) + printf(" prwnd:%d", s->sctpi_peer_rwnd); + if (s->sctpi_peer_tag) + printf(" ptag:%x", s->sctpi_peer_tag); + if (s->sctpi_peer_capable) + printf(" pcapable:%d", s->sctpi_peer_capable); + if (s->sctpi_peer_sack) + printf(" psack:%d", s->sctpi_peer_sack); + if (s->sctpi_s_autoclose) + printf(" autoclose:%d", s->sctpi_s_autoclose); + if (s->sctpi_s_adaptation_ind) + printf(" adapind:%d", s->sctpi_s_adaptation_ind); + if (s->sctpi_s_pd_point) + printf(" pdpoint:%d", s->sctpi_s_pd_point); + if (s->sctpi_s_nodelay) + printf(" nodealy:%d", s->sctpi_s_nodelay); + if (s->sctpi_s_disable_fragments) + printf(" nofrag:%d", s->sctpi_s_disable_fragments); + if (s->sctpi_s_v4mapped) + printf(" v4mapped:%d", s->sctpi_s_v4mapped); + if (s->sctpi_s_frag_interleave) + printf(" fraginl:%d", s->sctpi_s_frag_interleave); +} + static void tcp_stats_print(struct tcpstat *s) { char b1[64]; @@ -1728,8 +1882,12 @@ static void tcp_stats_print(struct tcpstat *s) if (s->mss) printf(" mss:%d", s->mss); + if (s->rcv_mss) + printf(" rcvmss:%d", s->rcv_mss); + if (s->advmss) + printf(" advmss:%d", s->advmss); if (s->cwnd) - printf(" cwnd:%d", s->cwnd); + printf(" cwnd:%u", s->cwnd); if (s->ssthresh) printf(" ssthresh:%d", s->ssthresh); @@ -1741,6 +1899,10 @@ static void tcp_stats_print(struct tcpstat *s) printf(" segs_out:%u", s->segs_out); if (s->segs_in) printf(" segs_in:%u", s->segs_in); + if (s->data_segs_out) + printf(" data_segs_out:%u", s->data_segs_out); + if (s->data_segs_in) + printf(" data_segs_in:%u", s->data_segs_in); if (s->dctcp && s->dctcp->enabled) { struct dctcpstat *dctcp = s->dctcp; @@ -1752,6 +1914,25 @@ static void tcp_stats_print(struct tcpstat *s) printf(" dctcp:fallback_mode"); } + if (s->bbr_info) { + __u64 bw; + + bw = s->bbr_info->bbr_bw_hi; + bw <<= 32; + bw |= s->bbr_info->bbr_bw_lo; + + printf(" bbr:(bw:%sbps,mrtt:%g", + sprint_bw(b1, bw * 8.0), + (double)s->bbr_info->bbr_min_rtt / 1000.0); + if (s->bbr_info->bbr_pacing_gain) + printf(",pacing_gain:%g", + (double)s->bbr_info->bbr_pacing_gain / 256.0); + if (s->bbr_info->bbr_cwnd_gain) + printf(",cwnd_gain:%g", + (double)s->bbr_info->bbr_cwnd_gain / 256.0); + printf(")"); + } + if (s->send_bps) printf(" send %sbps", sprint_bw(b1, s->send_bps)); if (s->lastsnd) @@ -1768,6 +1949,23 @@ static void tcp_stats_print(struct tcpstat *s) s->pacing_rate_max)); } + if (s->delivery_rate) + printf(" delivery_rate %sbps", sprint_bw(b1, s->delivery_rate)); + if (s->app_limited) + printf(" app_limited"); + + if (s->busy_time) { + printf(" busy:%llums", s->busy_time / 1000); + if (s->rwnd_limited) + printf(" rwnd_limited:%llums(%.1f%%)", + s->rwnd_limited / 1000, + 100.0 * s->rwnd_limited / s->busy_time); + if (s->sndbuf_limited) + printf(" sndbuf_limited:%llums(%.1f%%)", + s->sndbuf_limited / 1000, + 100.0 * s->sndbuf_limited / s->busy_time); + } + if (s->unacked) printf(" unacked:%u", s->unacked); if (s->retrans || s->retrans_total) @@ -1784,10 +1982,23 @@ static void tcp_stats_print(struct tcpstat *s) printf(" rcv_rtt:%g", s->rcv_rtt); if (s->rcv_space) printf(" rcv_space:%d", s->rcv_space); + if (s->not_sent) + printf(" notsent:%u", s->not_sent); + if (s->min_rtt) + printf(" minrtt:%g", s->min_rtt); } static void tcp_timer_print(struct tcpstat *s) { + static const char * const tmr_name[] = { + "off", + "on", + "keepalive", + "timewait", + "persist", + "unknown" + }; + if (s->timer) { if (s->timer > 4) s->timer = 5; @@ -1798,6 +2009,13 @@ static void tcp_timer_print(struct tcpstat *s) } } +static void sctp_timer_print(struct tcpstat *s) +{ + if (s->timer) + printf(" timer:(T3_RTX,%s,%d)", + print_ms_timer(s->timeout), s->retrans); +} + static int tcp_show_line(char *line, const struct filter *f, int family) { int rto = 0, ato = 0; @@ -1811,6 +2029,7 @@ static int tcp_show_line(char *line, const struct filter *f, int family) return -1; int state = (data[1] >= 'A') ? (data[1] - 'A' + 10) : (data[1] - '0'); + if (!(f->states & (1 << state))) return 0; @@ -1820,7 +2039,7 @@ static int tcp_show_line(char *line, const struct filter *f, int family) return 0; opt[0] = 0; - n = sscanf(data, "%x %x:%x %x:%x %x %d %d %u %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 %u %d %[^\n]\n", &s.ss.state, &s.ss.wq, &s.ss.rq, &s.timer, &s.timeout, &s.retrans, &s.ss.uid, &s.probes, &s.ss.ino, &s.ss.refcnt, &s.ss.sk, &rto, &ato, &s.qack, &s.cwnd, @@ -1843,8 +2062,9 @@ static int tcp_show_line(char *line, const struct filter *f, int family) s.rto = (double)rto; s.ssthresh = s.ssthresh == -1 ? 0 : s.ssthresh; s.rto = s.rto != 3 * hz ? s.rto / hz : 0; + s.ss.type = IPPROTO_TCP; - inet_stats_print(&s.ss, IPPROTO_TCP); + inet_stats_print(&s.ss, false); if (show_options) tcp_timer_print(&s); @@ -1874,6 +2094,7 @@ static int generic_record_read(FILE *fp, while (fgets(line, sizeof(line), fp) != NULL) { int n = strlen(line); + if (n == 0 || line[n-1] != '\n') { errno = -EINVAL; return -1; @@ -1924,9 +2145,23 @@ static void print_skmeminfo(struct rtattr *tb[], int attrtype) (SK_MEMINFO_BACKLOG + 1) * sizeof(__u32)) printf(",bl%u", skmeminfo[SK_MEMINFO_BACKLOG]); + if (RTA_PAYLOAD(tb[attrtype]) >= + (SK_MEMINFO_DROPS + 1) * sizeof(__u32)) + printf(",d%u", skmeminfo[SK_MEMINFO_DROPS]); + printf(")"); } +static void print_md5sig(struct tcp_diag_md5sig *sig) +{ + printf("%s/%d=", + format_host(sig->tcpm_family, + sig->tcpm_family == AF_INET6 ? 16 : 4, + &sig->tcpm_addr), + sig->tcpm_prefixlen); + print_escape_buf(sig->tcpm_key, sig->tcpm_keylen, " ,"); +} + #define TCPI_HAS_OPT(info, opt) !!(info->tcpi_options & (opt)) static void tcp_show_info(const struct nlmsghdr *nlh, struct inet_diag_msg *r, @@ -1978,6 +2213,8 @@ static void tcp_show_info(const struct nlmsghdr *nlh, struct inet_diag_msg *r, s.rttvar = (double)info->tcpi_rttvar / 1000; s.ato = (double)info->tcpi_ato / 1000; s.mss = info->tcpi_snd_mss; + s.rcv_mss = info->tcpi_rcv_mss; + s.advmss = info->tcpi_advmss; s.rcv_space = info->tcpi_rcv_space; s.rcv_rtt = (double)info->tcpi_rcv_rtt / 1000; s.lastsnd = info->tcpi_last_data_sent; @@ -1988,6 +2225,7 @@ static void tcp_show_info(const struct nlmsghdr *nlh, struct inet_diag_msg *r, s.retrans_total = info->tcpi_total_retrans; s.lost = info->tcpi_lost; s.sacked = info->tcpi_sacked; + s.fackets = info->tcpi_fackets; s.reordering = info->tcpi_reordering; s.rcv_space = info->tcpi_rcv_space; s.cwnd = info->tcpi_snd_cwnd; @@ -2020,6 +2258,16 @@ static void tcp_show_info(const struct nlmsghdr *nlh, struct inet_diag_msg *r, s.dctcp = dctcp; } + if (tb[INET_DIAG_BBRINFO]) { + const void *bbr_info = RTA_DATA(tb[INET_DIAG_BBRINFO]); + int len = min(RTA_PAYLOAD(tb[INET_DIAG_BBRINFO]), + sizeof(*s.bbr_info)); + + s.bbr_info = calloc(1, sizeof(*s.bbr_info)); + if (s.bbr_info && bbr_info) + memcpy(s.bbr_info, bbr_info, len); + } + if (rtt > 0 && info->tcpi_snd_mss && info->tcpi_snd_cwnd) { s.send_bps = (double) info->tcpi_snd_cwnd * (double)info->tcpi_snd_mss * 8000000. / rtt; @@ -2037,51 +2285,144 @@ static void tcp_show_info(const struct nlmsghdr *nlh, struct inet_diag_msg *r, s.bytes_received = info->tcpi_bytes_received; s.segs_out = info->tcpi_segs_out; s.segs_in = info->tcpi_segs_in; + s.data_segs_out = info->tcpi_data_segs_out; + s.data_segs_in = info->tcpi_data_segs_in; + s.not_sent = info->tcpi_notsent_bytes; + if (info->tcpi_min_rtt && info->tcpi_min_rtt != ~0U) + s.min_rtt = (double) info->tcpi_min_rtt / 1000; + s.delivery_rate = info->tcpi_delivery_rate * 8.; + s.app_limited = info->tcpi_delivery_rate_app_limited; + s.busy_time = info->tcpi_busy_time; + s.rwnd_limited = info->tcpi_rwnd_limited; + s.sndbuf_limited = info->tcpi_sndbuf_limited; tcp_stats_print(&s); free(s.dctcp); + free(s.bbr_info); + } + if (tb[INET_DIAG_MD5SIG]) { + struct tcp_diag_md5sig *sig = RTA_DATA(tb[INET_DIAG_MD5SIG]); + int len = RTA_PAYLOAD(tb[INET_DIAG_MD5SIG]); + + printf(" md5keys:"); + print_md5sig(sig++); + for (len -= sizeof(*sig); len > 0; len -= sizeof(*sig)) { + printf(","); + print_md5sig(sig++); + } } } -static int inet_show_sock(struct nlmsghdr *nlh, struct filter *f, int protocol) +static const char *format_host_sa(struct sockaddr_storage *sa) { - struct rtattr * tb[INET_DIAG_MAX+1]; + union { + struct sockaddr_in sin; + struct sockaddr_in6 sin6; + } *saddr = (void *)sa; + + switch (sa->ss_family) { + case AF_INET: + return format_host(AF_INET, 4, &saddr->sin.sin_addr); + case AF_INET6: + return format_host(AF_INET6, 16, &saddr->sin6.sin6_addr); + default: + return ""; + } +} + +static void sctp_show_info(const struct nlmsghdr *nlh, struct inet_diag_msg *r, + struct rtattr *tb[]) +{ + struct sockaddr_storage *sa; + int len; + + print_skmeminfo(tb, INET_DIAG_SKMEMINFO); + + if (tb[INET_DIAG_LOCALS]) { + len = RTA_PAYLOAD(tb[INET_DIAG_LOCALS]); + sa = RTA_DATA(tb[INET_DIAG_LOCALS]); + + printf("locals:%s", format_host_sa(sa)); + for (sa++, len -= sizeof(*sa); len > 0; sa++, len -= sizeof(*sa)) + printf(",%s", format_host_sa(sa)); + + } + if (tb[INET_DIAG_PEERS]) { + len = RTA_PAYLOAD(tb[INET_DIAG_PEERS]); + sa = RTA_DATA(tb[INET_DIAG_PEERS]); + + printf(" peers:%s", format_host_sa(sa)); + for (sa++, len -= sizeof(*sa); len > 0; sa++, len -= sizeof(*sa)) + printf(",%s", format_host_sa(sa)); + } + if (tb[INET_DIAG_INFO]) { + struct sctp_info *info; + len = RTA_PAYLOAD(tb[INET_DIAG_INFO]); + + /* workaround for older kernels with less fields */ + if (len < sizeof(*info)) { + info = alloca(sizeof(*info)); + memcpy(info, RTA_DATA(tb[INET_DIAG_INFO]), len); + memset((char *)info + len, 0, sizeof(*info) - len); + } else + info = RTA_DATA(tb[INET_DIAG_INFO]); + + sctp_stats_print(info); + } +} + +static void parse_diag_msg(struct nlmsghdr *nlh, struct sockstat *s) +{ + struct rtattr *tb[INET_DIAG_MAX+1]; struct inet_diag_msg *r = NLMSG_DATA(nlh); - struct sockstat s = {}; - parse_rtattr(tb, INET_DIAG_MAX, (struct rtattr*)(r+1), + parse_rtattr(tb, INET_DIAG_MAX, (struct rtattr *)(r+1), nlh->nlmsg_len - NLMSG_LENGTH(sizeof(*r))); - 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); - s.wq = r->idiag_wqueue; - s.rq = r->idiag_rqueue; - s.ino = r->idiag_inode; - s.uid = r->idiag_uid; - s.iface = r->id.idiag_if; - s.sk = cookie_sk_get(&r->id.idiag_cookie[0]); - - s.mark = 0; + 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); + s->wq = r->idiag_wqueue; + s->rq = r->idiag_rqueue; + s->ino = r->idiag_inode; + s->uid = r->idiag_uid; + s->iface = r->id.idiag_if; + s->sk = cookie_sk_get(&r->id.idiag_cookie[0]); + + s->mark = 0; if (tb[INET_DIAG_MARK]) - s.mark = *(__u32 *) RTA_DATA(tb[INET_DIAG_MARK]); + s->mark = rta_getattr_u32(tb[INET_DIAG_MARK]); + if (tb[INET_DIAG_PROTOCOL]) + s->raw_prot = rta_getattr_u8(tb[INET_DIAG_PROTOCOL]); + else + s->raw_prot = 0; - if (s.local.family == AF_INET) { - s.local.bytelen = s.remote.bytelen = 4; - } else { - s.local.bytelen = s.remote.bytelen = 16; - } + 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.idiag_src, s.local.bytelen); - memcpy(s.remote.data, r->id.idiag_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; +static int inet_show_sock(struct nlmsghdr *nlh, + struct sockstat *s) +{ + struct rtattr *tb[INET_DIAG_MAX+1]; + struct inet_diag_msg *r = NLMSG_DATA(nlh); + unsigned char v6only = 0; + + parse_rtattr(tb, INET_DIAG_MAX, (struct rtattr *)(r+1), + nlh->nlmsg_len - NLMSG_LENGTH(sizeof(*r))); if (tb[INET_DIAG_PROTOCOL]) - protocol = *(__u8 *)RTA_DATA(tb[INET_DIAG_PROTOCOL]); + s->type = rta_getattr_u8(tb[INET_DIAG_PROTOCOL]); + + if (s->local.family == AF_INET6 && tb[INET_DIAG_SKV6ONLY]) + v6only = rta_getattr_u8(tb[INET_DIAG_SKV6ONLY]); - inet_stats_print(&s, protocol); + inet_stats_print(s, v6only); if (show_options) { struct tcpstat t = {}; @@ -2089,27 +2430,33 @@ static int inet_show_sock(struct nlmsghdr *nlh, struct filter *f, int protocol) t.timer = r->idiag_timer; t.timeout = r->idiag_expires; t.retrans = r->idiag_retrans; - tcp_timer_print(&t); + if (s->type == IPPROTO_SCTP) + sctp_timer_print(&t); + else + tcp_timer_print(&t); } if (show_details) { - sock_details_print(&s); - if (s.local.family == AF_INET6 && tb[INET_DIAG_SKV6ONLY]) { - unsigned char v6only; - v6only = *(__u8 *)RTA_DATA(tb[INET_DIAG_SKV6ONLY]); + sock_details_print(s); + if (s->local.family == AF_INET6 && tb[INET_DIAG_SKV6ONLY]) printf(" v6only:%u", v6only); - } + if (tb[INET_DIAG_SHUTDOWN]) { unsigned char mask; - mask = *(__u8 *)RTA_DATA(tb[INET_DIAG_SHUTDOWN]); + + mask = rta_getattr_u8(tb[INET_DIAG_SHUTDOWN]); printf(" %c-%c", mask & 1 ? '-' : '<', mask & 2 ? '-' : '>'); } } - if (show_mem || show_tcpinfo) { + if (show_mem || (show_tcpinfo && s->type != IPPROTO_UDP)) { printf("\n\t"); - tcp_show_info(nlh, r, tb); + if (s->type == IPPROTO_SCTP) + sctp_show_info(nlh, r, tb); + else + tcp_show_info(nlh, r, tb); } + sctp_ino = s->ino; printf("\n"); return 0; @@ -2117,34 +2464,31 @@ static int inet_show_sock(struct nlmsghdr *nlh, struct filter *f, int protocol) static int tcpdiag_send(int fd, int protocol, struct filter *f) { - struct sockaddr_nl nladdr; + struct sockaddr_nl nladdr = { .nl_family = AF_NETLINK }; struct { struct nlmsghdr nlh; struct inet_diag_req r; - } req; + } req = { + .nlh.nlmsg_len = sizeof(req), + .nlh.nlmsg_flags = NLM_F_ROOT | NLM_F_MATCH | NLM_F_REQUEST, + .nlh.nlmsg_seq = MAGIC_SEQ, + .r.idiag_family = AF_INET, + .r.idiag_states = f->states, + }; char *bc = NULL; int bclen; struct msghdr msg; struct rtattr rta; struct iovec iov[3]; + int iovlen = 1; if (protocol == IPPROTO_UDP) return -1; - memset(&nladdr, 0, sizeof(nladdr)); - nladdr.nl_family = AF_NETLINK; - - req.nlh.nlmsg_len = sizeof(req); 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 = MAGIC_SEQ; - memset(&req.r, 0, sizeof(req.r)); - 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)); @@ -2162,18 +2506,21 @@ static int tcpdiag_send(int fd, int protocol, struct filter *f) }; 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); + if (bclen) { + 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); + iovlen = 3; + } } msg = (struct msghdr) { - .msg_name = (void*)&nladdr, + .msg_name = (void *)&nladdr, .msg_namelen = sizeof(nladdr), .msg_iov = iov, - .msg_iovlen = f->f ? 3 : 1, + .msg_iovlen = iovlen, }; if (sendmsg(fd, &msg, 0) < 0) { @@ -2186,20 +2533,18 @@ static int tcpdiag_send(int fd, int protocol, struct filter *f) static int sockdiag_send(int family, int fd, int protocol, struct filter *f) { - struct sockaddr_nl nladdr; + struct sockaddr_nl nladdr = { .nl_family = AF_NETLINK }; DIAG_REQUEST(req, struct inet_diag_req_v2 r); char *bc = NULL; int bclen; struct msghdr msg; struct rtattr rta; struct iovec iov[3]; + int iovlen = 1; if (family == PF_UNSPEC) return tcpdiag_send(fd, protocol, f); - memset(&nladdr, 0, sizeof(nladdr)); - nladdr.nl_family = AF_NETLINK; - memset(&req.r, 0, sizeof(req.r)); req.r.sdiag_family = family; req.r.sdiag_protocol = protocol; @@ -2221,18 +2566,21 @@ static int sockdiag_send(int family, int fd, int protocol, struct filter *f) }; 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); + if (bclen) { + 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); + iovlen = 3; + } } msg = (struct msghdr) { - .msg_name = (void*)&nladdr, + .msg_name = (void *)&nladdr, .msg_namelen = sizeof(nladdr), .msg_iov = iov, - .msg_iovlen = f->f ? 3 : 1, + .msg_iovlen = iovlen, }; if (sendmsg(fd, &msg, 0) < 0) { @@ -2249,12 +2597,12 @@ struct inet_diag_arg { struct rtnl_handle *rth; }; -static int kill_inet_sock(const struct sockaddr_nl *addr, - struct nlmsghdr *h, void *arg) +static int kill_inet_sock(struct nlmsghdr *h, void *arg, struct sockstat *s) { struct inet_diag_msg *d = NLMSG_DATA(h); struct inet_diag_arg *diag_arg = arg; struct rtnl_handle *rth = diag_arg->rth; + DIAG_REQUEST(req, struct inet_diag_req_v2 r); req.nlh.nlmsg_type = SOCK_DESTROY; @@ -2264,6 +2612,13 @@ static int kill_inet_sock(const struct sockaddr_nl *addr, req.r.sdiag_protocol = diag_arg->protocol; req.r.id = d->id; + if (diag_arg->protocol == IPPROTO_RAW) { + struct inet_diag_req_raw *raw = (void *)&req.r; + + BUILD_BUG_ON(sizeof(req.r) != sizeof(*raw)); + raw->sdiag_raw_protocol = s->raw_prot; + } + return rtnl_talk(rth, &req.nlh, NULL, 0); } @@ -2273,10 +2628,18 @@ static int show_one_inet_sock(const struct sockaddr_nl *addr, int err; struct inet_diag_arg *diag_arg = arg; struct inet_diag_msg *r = NLMSG_DATA(h); + struct sockstat s = {}; if (!(diag_arg->f->families & (1 << r->idiag_family))) return 0; - if (diag_arg->f->kill && kill_inet_sock(addr, h, arg) != 0) { + + parse_diag_msg(h, &s); + s.type = diag_arg->protocol; + + if (diag_arg->f->f && run_ssfilter(diag_arg->f->f, &s) == 0) + return 0; + + if (diag_arg->f->kill && kill_inet_sock(h, arg, &s) != 0) { if (errno == EOPNOTSUPP || errno == ENOENT) { /* Socket can't be closed, or is already closed. */ return 0; @@ -2285,7 +2648,9 @@ static int show_one_inet_sock(const struct sockaddr_nl *addr, return -1; } } - if ((err = inet_show_sock(h, diag_arg->f, diag_arg->protocol)) < 0) + + err = inet_show_sock(h, &s); + if (err < 0) return err; return 0; @@ -2341,59 +2706,75 @@ static int tcp_show_netlink_file(struct filter *f) { FILE *fp; char buf[16384]; + int err = -1; if ((fp = fopen(getenv("TCPDIAG_FILE"), "r")) == NULL) { perror("fopen($TCPDIAG_FILE)"); - return -1; + return err; } while (1) { - int status, err; - struct nlmsghdr *h = (struct nlmsghdr*)buf; + int status, err2; + struct nlmsghdr *h = (struct nlmsghdr *)buf; + struct sockstat s = {}; status = fread(buf, 1, sizeof(*h), fp); if (status < 0) { perror("Reading header from $TCPDIAG_FILE"); - return -1; + break; } if (status != sizeof(*h)) { perror("Unexpected EOF reading $TCPDIAG_FILE"); - return -1; + break; } status = fread(h+1, 1, NLMSG_ALIGN(h->nlmsg_len-sizeof(*h)), fp); if (status < 0) { perror("Reading $TCPDIAG_FILE"); - return -1; + break; } if (status + sizeof(*h) < h->nlmsg_len) { perror("Unexpected EOF reading $TCPDIAG_FILE"); - return -1; + break; } /* The only legal exit point */ - if (h->nlmsg_type == NLMSG_DONE) - return 0; + if (h->nlmsg_type == NLMSG_DONE) { + err = 0; + break; + } if (h->nlmsg_type == NLMSG_ERROR) { - struct nlmsgerr *err = (struct nlmsgerr*)NLMSG_DATA(h); + 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; perror("TCPDIAG answered"); } - return -1; + break; } - err = inet_show_sock(h, f, IPPROTO_TCP); - if (err < 0) - return err; + parse_diag_msg(h, &s); + s.type = IPPROTO_TCP; + + if (f && f->f && run_ssfilter(f->f, &s) == 0) + continue; + + err2 = inet_show_sock(h, &s); + if (err2 < 0) { + err = err2; + break; + } } + + fclose(fp); + return err; } -static int tcp_show(struct filter *f, int socktype) +static int tcp_show(struct filter *f) { FILE *fp = NULL; char *buf = NULL; @@ -2408,7 +2789,7 @@ static int tcp_show(struct filter *f, int socktype) return tcp_show_netlink_file(f); if (!getenv("PROC_NET_TCP") && !getenv("PROC_ROOT") - && inet_show_netlink(f, NULL, socktype) == 0) + && inet_show_netlink(f, NULL, IPPROTO_TCP) == 0) return 0; /* Sigh... We have to parse /proc/net/tcp... */ @@ -2424,6 +2805,7 @@ static int tcp_show(struct filter *f, int socktype) get_slabstat(&slabstat); int guess = slabstat.socks+slabstat.tcp_syns; + if (f->states&(1<<SS_TIME_WAIT)) guess += slabstat.tcp_tws; if (guess > (16*1024*1024)/128) @@ -2466,6 +2848,7 @@ static int tcp_show(struct filter *f, int socktype) outerr: do { int saved_errno = errno; + free(buf); if (fp) fclose(fp); @@ -2474,6 +2857,29 @@ outerr: } while (0); } +static int dccp_show(struct filter *f) +{ + if (!filter_af_get(f, AF_INET) && !filter_af_get(f, AF_INET6)) + return 0; + + if (!getenv("PROC_NET_DCCP") && !getenv("PROC_ROOT") + && inet_show_netlink(f, NULL, IPPROTO_DCCP) == 0) + return 0; + + return 0; +} + +static int sctp_show(struct filter *f) +{ + if (!filter_af_get(f, AF_INET) && !filter_af_get(f, AF_INET6)) + return 0; + + if (!getenv("PROC_NET_SCTP") && !getenv("PROC_ROOT") + && inet_show_netlink(f, NULL, IPPROTO_SCTP) == 0) + return 0; + + return 0; +} static int dgram_show_line(char *line, const struct filter *f, int family) { @@ -2486,6 +2892,7 @@ static int dgram_show_line(char *line, const struct filter *f, int family) return -1; int state = (data[1] >= 'A') ? (data[1] - 'A' + 10) : (data[1] - '0'); + if (!(f->states & (1 << state))) return 0; @@ -2503,7 +2910,8 @@ static int dgram_show_line(char *line, const struct filter *f, int family) if (n < 9) opt[0] = 0; - inet_stats_print(&s, dg_proto == UDP_PROTO ? IPPROTO_UDP : 0); + s.type = dg_proto == UDP_PROTO ? IPPROTO_UDP : 0; + inet_stats_print(&s, false); if (show_details && opt[0]) printf(" opt:\"%s\"", opt); @@ -2544,6 +2952,7 @@ static int udp_show(struct filter *f) outerr: do { int saved_errno = errno; + if (fp) fclose(fp); errno = saved_errno; @@ -2560,6 +2969,10 @@ static int raw_show(struct filter *f) dg_proto = RAW_PROTO; + if (!getenv("PROC_NET_RAW") && !getenv("PROC_ROOT") && + inet_show_netlink(f, NULL, IPPROTO_RAW) == 0) + return 0; + if (f->families&(1<<AF_INET)) { if ((fp = net_raw_open()) == NULL) goto outerr; @@ -2579,6 +2992,7 @@ static int raw_show(struct filter *f) outerr: do { int saved_errno = errno; + if (fp) fclose(fp); errno = saved_errno; @@ -2586,39 +3000,15 @@ outerr: } while (0); } -int unix_state_map[] = { SS_CLOSE, SS_SYN_SENT, - SS_ESTABLISHED, SS_CLOSING }; - #define MAX_UNIX_REMEMBER (1024*1024/sizeof(struct sockstat)) -static void unix_list_free(struct sockstat *list) +static void unix_list_drop_first(struct sockstat **list) { - while (list) { - struct sockstat *s = list; + struct sockstat *s = *list; - list = list->next; - free(s->name); - free(s); - } -} - -static const char *unix_netid_name(int type) -{ - const char *netid; - - switch (type) { - case SOCK_STREAM: - netid = "u_str"; - break; - case SOCK_SEQPACKET: - netid = "u_seq"; - break; - case SOCK_DGRAM: - default: - netid = "u_dgr"; - break; - } - return netid; + (*list) = (*list)->next; + free(s->name); + free(s); } static bool unix_type_skip(struct sockstat *s, struct filter *f) @@ -2632,79 +3022,18 @@ static bool unix_type_skip(struct sockstat *s, struct filter *f) return false; } -static bool unix_use_proc(void) -{ - return getenv("PROC_NET_UNIX") || getenv("PROC_ROOT"); -} - -static void unix_stats_print(struct sockstat *list, struct filter *f) +static void unix_stats_print(struct sockstat *s, struct filter *f) { - struct sockstat *s; - char *peer; - char *ctx_buf = NULL; - bool use_proc = unix_use_proc(); char port_name[30] = {}; - for (s = list; s; s = s->next) { - if (!(f->states & (1 << s->state))) - continue; - if (unix_type_skip(s, f)) - continue; - - peer = "*"; - if (s->peer_name) - peer = s->peer_name; - - if (s->rport && use_proc) { - struct sockstat *p; - - for (p = list; p; p = p->next) { - if (s->rport == p->lport) - break; - } - - if (!p) { - peer = "?"; - } else { - peer = p->name ? : "*"; - } - } + sock_state_print(s); - if (use_proc && f->f) { - struct sockstat st; - st.local.family = AF_UNIX; - st.remote.family = AF_UNIX; - memcpy(st.local.data, &s->name, sizeof(s->name)); - if (strcmp(peer, "*") == 0) - memset(st.remote.data, 0, sizeof(peer)); - else - memcpy(st.remote.data, &peer, sizeof(peer)); - if (run_ssfilter(f->f, &st) == 0) - continue; - } - - sock_state_print(s, unix_netid_name(s->type)); + sock_addr_print(s->name ?: "*", " ", + int_to_str(s->lport, port_name), NULL); + sock_addr_print(s->peer_name ?: "*", " ", + int_to_str(s->rport, port_name), NULL); - sock_addr_print(s->name ?: "*", " ", - int_to_str(s->lport, port_name), NULL); - sock_addr_print(peer, " ", int_to_str(s->rport, port_name), - NULL); - - if (show_proc_ctx || show_sock_ctx) { - if (find_entry(s->ino, &ctx_buf, - (show_proc_ctx & show_sock_ctx) ? - PROC_SOCK_CTX : PROC_CTX) > 0) { - printf(" users:(%s)", ctx_buf); - free(ctx_buf); - } - } else if (show_users) { - if (find_entry(s->ino, &ctx_buf, USERS) > 0) { - printf(" users:(%s)", ctx_buf); - free(ctx_buf); - } - } - printf("\n"); - } + proc_ctx_print(s); } static int unix_show_sock(const struct sockaddr_nl *addr, struct nlmsghdr *nlh, @@ -2716,7 +3045,7 @@ static int unix_show_sock(const struct sockaddr_nl *addr, struct nlmsghdr *nlh, char name[128]; struct sockstat stat = { .name = "*", .peer_name = "*" }; - parse_rtattr(tb, UNIX_DIAG_MAX, (struct rtattr*)(r+1), + parse_rtattr(tb, UNIX_DIAG_MAX, (struct rtattr *)(r+1), nlh->nlmsg_len - NLMSG_LENGTH(sizeof(*r))); stat.type = r->udiag_type; @@ -2729,6 +3058,7 @@ static int unix_show_sock(const struct sockaddr_nl *addr, struct nlmsghdr *nlh, if (tb[UNIX_DIAG_RQLEN]) { struct unix_diag_rqlen *rql = RTA_DATA(tb[UNIX_DIAG_RQLEN]); + stat.rq = rql->udiag_rqueue; stat.wq = rql->udiag_wqueue; } @@ -2737,8 +3067,12 @@ static int unix_show_sock(const struct sockaddr_nl *addr, struct nlmsghdr *nlh, memcpy(name, RTA_DATA(tb[UNIX_DIAG_NAME]), len); name[len] = '\0'; - if (name[0] == '\0') - name[0] = '@'; + if (name[0] == '\0') { + int i; + for (i = 0; i < len; i++) + if (name[i] == '\0') + name[i] = '@'; + } stat.name = &name[0]; memcpy(stat.local.data, &stat.name, sizeof(stat.name)); } @@ -2750,19 +3084,17 @@ static int unix_show_sock(const struct sockaddr_nl *addr, struct nlmsghdr *nlh, unix_stats_print(&stat, f); - if (show_mem) { - printf("\t"); + if (show_mem) print_skmeminfo(tb, UNIX_DIAG_MEMINFO); - } if (show_details) { if (tb[UNIX_DIAG_SHUTDOWN]) { unsigned char mask; - mask = *(__u8 *)RTA_DATA(tb[UNIX_DIAG_SHUTDOWN]); + + mask = rta_getattr_u8(tb[UNIX_DIAG_SHUTDOWN]); printf(" %c-%c", mask & 1 ? '-' : '<', mask & 2 ? '-' : '>'); } } - if (show_mem || show_details) - printf("\n"); + printf("\n"); return 0; } @@ -2811,11 +3143,14 @@ static int unix_show(struct filter *f) int newformat = 0; int cnt; struct sockstat *list = NULL; + const int unix_state_map[] = { SS_CLOSE, SS_SYN_SENT, + SS_ESTABLISHED, SS_CLOSING }; if (!filter_af_get(f, AF_UNIX)) return 0; - if (!unix_use_proc() && unix_show_netlink(f) == 0) + if (!getenv("PROC_NET_UNIX") && !getenv("PROC_ROOT") + && unix_show_netlink(f) == 0) return 0; if ((fp = net_unix_open()) == NULL) @@ -2835,8 +3170,6 @@ static int unix_show(struct filter *f) if (!(u = calloc(1, sizeof(*u)))) break; - u->name = NULL; - u->peer_name = NULL; if (sscanf(buf, "%x: %x %x %x %x %x %d %s", &u->rport, &u->rq, &u->wq, &flags, &u->type, @@ -2848,11 +3181,17 @@ static int unix_show(struct filter *f) if (flags & (1 << 16)) { u->state = SS_LISTEN; - } else { + } else if (u->state > 0 && + u->state <= ARRAY_SIZE(unix_state_map)) { u->state = unix_state_map[u->state-1]; if (u->type == SOCK_DGRAM && u->state == SS_CLOSE && u->rport) u->state = SS_ESTABLISHED; } + if (unix_type_skip(u, f) || + !(f->states & (1 << u->state))) { + free(u); + continue; + } if (!newformat) { u->rport = 0; @@ -2860,6 +3199,44 @@ static int unix_show(struct filter *f) u->wq = 0; } + if (name[0]) { + u->name = strdup(name); + if (!u->name) { + free(u); + break; + } + } + + if (u->rport) { + struct sockstat *p; + + for (p = list; p; p = p->next) { + if (u->rport == p->lport) + break; + } + if (!p) + u->peer_name = "?"; + else + u->peer_name = p->name ? : "*"; + } + + if (f->f) { + struct sockstat st = { + .local.family = AF_UNIX, + .remote.family = AF_UNIX, + }; + + memcpy(st.local.data, &u->name, sizeof(u->name)); + if (strcmp(u->peer_name, "*")) + memcpy(st.remote.data, &u->peer_name, + sizeof(u->peer_name)); + if (run_ssfilter(f->f, &st) == 0) { + free(u->name); + free(u); + continue; + } + } + insp = &list; while (*insp) { if (u->type < (*insp)->type || @@ -2871,24 +3248,22 @@ static int unix_show(struct filter *f) u->next = *insp; *insp = u; - if (name[0]) { - if ((u->name = malloc(strlen(name)+1)) == NULL) - break; - strcpy(u->name, name); - } if (++cnt > MAX_UNIX_REMEMBER) { - unix_stats_print(list, f); - unix_list_free(list); - list = NULL; + while (list) { + unix_stats_print(list, f); + printf("\n"); + + unix_list_drop_first(&list); + } cnt = 0; } } fclose(fp); - if (list) { + while (list) { unix_stats_print(list, f); - unix_list_free(list); - list = NULL; - cnt = 0; + printf("\n"); + + unix_list_drop_first(&list); } return 0; @@ -2896,19 +3271,18 @@ static int unix_show(struct filter *f) static int packet_stats_print(struct sockstat *s, const struct filter *f) { - char *buf = NULL; const char *addr, *port; char ll_name[16]; + s->local.family = s->remote.family = AF_PACKET; + if (f->f) { - s->local.family = AF_PACKET; - s->remote.family = AF_PACKET; s->local.data[0] = s->prot; if (run_ssfilter(f->f, s) == 0) return 1; } - sock_state_print(s, s->type == SOCK_RAW ? "p_raw" : "p_dgr"); + sock_state_print(s); if (s->prot == 3) addr = "*"; @@ -2923,19 +3297,7 @@ static int packet_stats_print(struct sockstat *s, const struct filter *f) sock_addr_print(addr, ":", port, NULL); sock_addr_print("", "*", "", NULL); - if (show_proc_ctx || show_sock_ctx) { - if (find_entry(s->ino, &buf, - (show_proc_ctx & show_sock_ctx) ? - PROC_SOCK_CTX : PROC_CTX) > 0) { - printf(" users:(%s)", buf); - free(buf); - } - } else if (show_users) { - if (find_entry(s->ino, &buf, USERS) > 0) { - printf(" users:(%s)", buf); - free(buf); - } - } + proc_ctx_print(s); if (show_details) sock_details_print(s); @@ -2965,7 +3327,7 @@ static int packet_show_sock(const struct sockaddr_nl *addr, uint32_t fanout = 0; bool has_fanout = false; - parse_rtattr(tb, PACKET_DIAG_MAX, (struct rtattr*)(r+1), + parse_rtattr(tb, PACKET_DIAG_MAX, (struct rtattr *)(r+1), nlh->nlmsg_len - NLMSG_LENGTH(sizeof(*r))); /* use /proc/net/packet if all info are not available */ @@ -2980,6 +3342,7 @@ static int packet_show_sock(const struct sockaddr_nl *addr, if (tb[PACKET_DIAG_MEMINFO]) { __u32 *skmeminfo = RTA_DATA(tb[PACKET_DIAG_MEMINFO]); + stat.rq = skmeminfo[SK_MEMINFO_RMEM_ALLOC]; } @@ -2989,7 +3352,7 @@ static int packet_show_sock(const struct sockaddr_nl *addr, } if (tb[PACKET_DIAG_UID]) - stat.uid = *(__u32 *)RTA_DATA(tb[PACKET_DIAG_UID]); + stat.uid = rta_getattr_u32(tb[PACKET_DIAG_UID]); if (tb[PACKET_DIAG_RX_RING]) ring_rx = RTA_DATA(tb[PACKET_DIAG_RX_RING]); @@ -2999,7 +3362,7 @@ static int packet_show_sock(const struct sockaddr_nl *addr, if (tb[PACKET_DIAG_FANOUT]) { has_fanout = true; - fanout = *(uint32_t *)RTA_DATA(tb[PACKET_DIAG_FANOUT]); + fanout = rta_getattr_u32(tb[PACKET_DIAG_FANOUT]); } if (packet_stats_print(&stat, f)) @@ -3143,23 +3506,24 @@ static int packet_show(struct filter *f) } static int netlink_show_one(struct filter *f, - int prot, int pid, unsigned groups, - int state, int dst_pid, unsigned dst_group, + int prot, int pid, unsigned int groups, + int state, int dst_pid, unsigned int dst_group, int rq, int wq, unsigned long long sk, unsigned long long cb) { - struct sockstat st; + struct sockstat st = { + .state = SS_CLOSE, + .rq = rq, + .wq = wq, + .local.family = AF_NETLINK, + .remote.family = AF_NETLINK, + }; + SPRINT_BUF(prot_buf) = {}; const char *prot_name; char procname[64] = {}; - st.state = SS_CLOSE; - st.rq = rq; - st.wq = wq; - if (f->f) { - st.local.family = AF_NETLINK; - st.remote.family = AF_NETLINK; st.rport = -1; st.lport = pid; st.local.data[0] = prot; @@ -3167,7 +3531,7 @@ static int netlink_show_one(struct filter *f, return 1; } - sock_state_print(&st, "nl"); + sock_state_print(&st); if (resolve_services) prot_name = nl_proto_n2a(prot, prot_buf, sizeof(prot_buf)); @@ -3178,11 +3542,13 @@ static int netlink_show_one(struct filter *f, procname[0] = '*'; } else if (resolve_services) { int done = 0; + if (!pid) { done = 1; strncpy(procname, "kernel", 6); } else if (pid > 0) { FILE *fp; + snprintf(procname, sizeof(procname), "%s/%d/stat", getenv("PROC_ROOT") ? : "/proc", pid); if ((fp = fopen(procname, "r")) != NULL) { @@ -3206,6 +3572,7 @@ static int netlink_show_one(struct filter *f, if (state == NETLINK_CONNECTED) { char dst_group_buf[30]; char dst_pid_buf[30]; + sock_addr_print(int_to_str(dst_group, dst_group_buf), ":", int_to_str(dst_pid, dst_pid_buf), NULL); } else { @@ -3213,6 +3580,7 @@ static int netlink_show_one(struct filter *f, } char *pid_context = NULL; + if (show_proc_ctx) { /* The pid value will either be: * 0 if destination kernel - show kernel initial context. @@ -3225,12 +3593,8 @@ static int netlink_show_one(struct filter *f, else if (pid > 0) getpidcon(pid, &pid_context); - if (pid_context != NULL) { - printf("proc_ctx=%-*s ", serv_width, pid_context); - free(pid_context); - } else { - printf("proc_ctx=%-*s ", serv_width, "unavailable"); - } + printf(" proc_ctx=%s", pid_context ? : "unavailable"); + free(pid_context); } if (show_details) { @@ -3250,7 +3614,7 @@ static int netlink_show_sock(const struct sockaddr_nl *addr, int rq = 0, wq = 0; unsigned long groups = 0; - parse_rtattr(tb, NETLINK_DIAG_MAX, (struct rtattr*)(r+1), + parse_rtattr(tb, NETLINK_DIAG_MAX, (struct rtattr *)(r+1), nlh->nlmsg_len - NLMSG_LENGTH(sizeof(*r))); if (tb[NETLINK_DIAG_GROUPS] && RTA_PAYLOAD(tb[NETLINK_DIAG_GROUPS])) @@ -3258,6 +3622,7 @@ static int netlink_show_sock(const struct sockaddr_nl *addr, if (tb[NETLINK_DIAG_MEMINFO]) { const __u32 *skmeminfo; + skmeminfo = RTA_DATA(tb[NETLINK_DIAG_MEMINFO]); rq = skmeminfo[SK_MEMINFO_RMEM_ALLOC]; @@ -3295,7 +3660,7 @@ static int netlink_show(struct filter *f) FILE *fp; char buf[256]; int prot, pid; - unsigned groups; + unsigned int groups; int rq, wq, rc; unsigned long long sk, cb; @@ -3352,7 +3717,7 @@ static int generic_show_sock(const struct sockaddr_nl *addr, static int handle_follow_request(struct filter *f) { - int ret = -1; + int ret = 0; int groups = 0; struct rtnl_handle rth; @@ -3375,19 +3740,12 @@ static int handle_follow_request(struct filter *f) rth.local.nl_pid = 0; if (rtnl_dump_filter(&rth, generic_show_sock, f)) - goto Exit; + ret = -1; - ret = 0; -Exit: rtnl_close(&rth); return ret; } -struct snmpstat -{ - int tcp_estab; -}; - static int get_snmp_int(char *proto, char *key, int *result) { char buf[1024]; @@ -3403,6 +3761,7 @@ static int get_snmp_int(char *proto, char *key, int *result) while (fgets(buf, sizeof(buf), fp) != NULL) { char *p = buf; int pos = 0; + if (memcmp(buf, proto, protolen)) continue; while ((p = strchr(p, ' ')) != NULL) { @@ -3435,8 +3794,7 @@ static int get_snmp_int(char *proto, char *key, int *result) /* Get stats from sockstat */ -struct ssummary -{ +struct ssummary { int socks; int tcp_mem; int tcp_total; @@ -3492,13 +3850,13 @@ static int get_sockstat(struct ssummary *s) if ((fp = net_sockstat_open()) == NULL) return -1; - while(fgets(buf, sizeof(buf), fp) != NULL) + while (fgets(buf, sizeof(buf), fp) != NULL) get_sockstat_line(buf, s); fclose(fp); if ((fp = net_sockstat6_open()) == NULL) return 0; - while(fgets(buf, sizeof(buf), fp) != NULL) + while (fgets(buf, sizeof(buf), fp) != NULL) get_sockstat_line(buf, s); fclose(fp); @@ -3508,11 +3866,11 @@ static int get_sockstat(struct ssummary *s) static int print_summary(void) { struct ssummary s; - struct snmpstat sn; + int tcp_estab; if (get_sockstat(&s) < 0) perror("ss: get_sockstat"); - if (get_snmp_int("Tcp:", "CurrEstab", &sn.tcp_estab) < 0) + if (get_snmp_int("Tcp:", "CurrEstab", &tcp_estab) < 0) perror("ss: get_snmpstat"); get_slabstat(&slabstat); @@ -3521,7 +3879,7 @@ static int print_summary(void) printf("TCP: %d (estab %d, closed %d, orphaned %d, synrecv %d, timewait %d/%d), ports %d\n", s.tcp_total + slabstat.tcp_syns + s.tcp_tws, - sn.tcp_estab, + tcp_estab, s.tcp_total - (s.tcp4_hashed+s.tcp6_hashed-s.tcp_tws), s.tcp_orphans, slabstat.tcp_syns, @@ -3574,13 +3932,16 @@ static void _usage(FILE *dest) " -6, --ipv6 display only IP version 6 sockets\n" " -0, --packet display PACKET sockets\n" " -t, --tcp display only TCP sockets\n" +" -S, --sctp display only SCTP 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" +" FAMILY := {inet|inet6|link|unix|netlink|help}\n" "\n" " -K, --kill forcibly close sockets, display what was closed\n" +" -H, --no-header Suppress header line\n" "\n" " -A, --query=QUERY, --socket=QUERY\n" " QUERY := {all|inet|tcp|udp|raw|unix|unix_dgram|unix_stream|unix_seqpacket|packet|netlink}[,QUERY]\n" @@ -3589,11 +3950,11 @@ static void _usage(FILE *dest) " -F, --filter=FILE read filter information from FILE\n" " FILTER := [ state STATE-FILTER ] [ EXPRESSION ]\n" " STATE-FILTER := {all|connected|synchronized|bucket|big|TCP-STATES}\n" -" TCP-STATES := {established|syn-sent|syn-recv|fin-wait-{1,2}|time-wait|closed|close-wait|last-ack|listen|closing}\n" +" TCP-STATES := {established|syn-sent|syn-recv|fin-wait-{1,2}|time-wait|closed|close-wait|last-ack|listening|closing}\n" " connected := {established|syn-sent|syn-recv|fin-wait-{1,2}|time-wait|close-wait|last-ack|closing}\n" " synchronized := {established|syn-recv|fin-wait-{1,2}|time-wait|close-wait|last-ack|closing}\n" " bucket := {syn-recv|time-wait}\n" -" big := {established|syn-sent|fin-wait-{1,2}|closed|close-wait|last-ack|listen|closing}\n" +" big := {established|syn-sent|fin-wait-{1,2}|closed|close-wait|last-ack|listening|closing}\n" ); } @@ -3614,7 +3975,22 @@ static void usage(void) static int scan_state(const char *state) { + static const char * const sstate_namel[] = { + "UNKNOWN", + [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", + }; int i; + if (strcasecmp(state, "close") == 0 || strcasecmp(state, "closed") == 0) return (1<<SS_CLOSE); @@ -3632,7 +4008,7 @@ static int scan_state(const char *state) return (1<<SS_SYN_RECV)|(1<<SS_TIME_WAIT); if (strcasecmp(state, "big") == 0) return SS_ALL & ~((1<<SS_SYN_RECV)|(1<<SS_TIME_WAIT)); - for (i=0; i<SS_MAX; i++) { + for (i = 0; i < SS_MAX; i++) { if (strcasecmp(state, sstate_namel[i]) == 0) return (1<<i); } @@ -3653,6 +4029,7 @@ static const struct option long_opts[] = { { "events", 0, 0, 'E' }, { "dccp", 0, 0, 'd' }, { "tcp", 0, 0, 't' }, + { "sctp", 0, 0, 'S' }, { "udp", 0, 0, 'u' }, { "raw", 0, 0, 'w' }, { "unix", 0, 0, 'x' }, @@ -3673,6 +4050,7 @@ static const struct option long_opts[] = { { "contexts", 0, 0, 'z' }, { "net", 1, 0, 'N' }, { "kill", 0, 0, 'K' }, + { "no-header", 0, 0, 'H' }, { 0 } }; @@ -3686,10 +4064,12 @@ int main(int argc, char *argv[]) FILE *filter_fp = NULL; int ch; int state_filter = 0; + int addrp_width, screen_width = 80; - while ((ch = getopt_long(argc, argv, "dhaletuwxnro460spbEf:miA:D:F:vVzZN:K", + while ((ch = getopt_long(argc, argv, + "dhaletuwxnro460spbEf:miA:D:F:vVzZN:KHS", long_opts, NULL)) != EOF) { - switch(ch) { + switch (ch) { case 'n': resolve_services = 0; break; @@ -3726,6 +4106,9 @@ int main(int argc, char *argv[]) case 't': filter_db_set(¤t_filter, TCP_DB); break; + case 'S': + filter_db_set(¤t_filter, SCTP_DB); + break; case 'u': filter_db_set(¤t_filter, UDP_DB); break; @@ -3772,10 +4155,11 @@ int main(int argc, char *argv[]) case 'A': { char *p, *p1; + if (!saw_query) { current_filter.dbs = 0; state_filter = state_filter ? - state_filter : SS_CONN; + state_filter : SS_CONN; saw_query = 1; do_default = 0; } @@ -3789,6 +4173,7 @@ int main(int argc, char *argv[]) filter_db_set(¤t_filter, UDP_DB); filter_db_set(¤t_filter, DCCP_DB); filter_db_set(¤t_filter, TCP_DB); + filter_db_set(¤t_filter, SCTP_DB); filter_db_set(¤t_filter, RAW_DB); } else if (strcmp(p, "udp") == 0) { filter_db_set(¤t_filter, UDP_DB); @@ -3796,6 +4181,8 @@ int main(int argc, char *argv[]) filter_db_set(¤t_filter, DCCP_DB); } else if (strcmp(p, "tcp") == 0) { filter_db_set(¤t_filter, TCP_DB); + } else if (strcmp(p, "sctp") == 0) { + filter_db_set(¤t_filter, SCTP_DB); } else if (strcmp(p, "raw") == 0) { filter_db_set(¤t_filter, RAW_DB); } else if (strcmp(p, "unix") == 0) { @@ -3856,6 +4243,7 @@ int main(int argc, char *argv[]) exit(0); case 'z': show_sock_ctx++; + /* fall through */ case 'Z': if (is_selinux_enabled() <= 0) { fprintf(stderr, "ss: SELinux is not enabled.\n"); @@ -3871,6 +4259,9 @@ int main(int argc, char *argv[]) case 'K': current_filter.kill = 1; break; + case 'H': + show_header = 0; + break; case 'h': help(); case '?': @@ -3917,10 +4308,9 @@ int main(int argc, char *argv[]) filter_merge_defaults(¤t_filter); if (resolve_services && resolve_hosts && - (current_filter.dbs&(UNIX_DBM|(1<<TCP_DB)|(1<<UDP_DB)|(1<<DCCP_DB)))) + (current_filter.dbs & (UNIX_DBM|INET_L4_DBM))) init_service_resolver(); - if (current_filter.dbs == 0) { fprintf(stderr, "ss: no socket tables to show with such filter.\n"); exit(0); @@ -3936,6 +4326,7 @@ int main(int argc, char *argv[]) if (dump_tcpdiag) { FILE *dump_fp = stdout; + if (!(current_filter.dbs & (1<<TCP_DB))) { fprintf(stderr, "ss: tcpdiag dump requested and no tcp in filter.\n"); exit(0); @@ -3963,7 +4354,6 @@ int main(int argc, char *argv[]) if (current_filter.states&(current_filter.states-1)) state_width = 10; - screen_width = 80; if (isatty(STDOUT_FILENO)) { struct winsize w; @@ -3974,8 +4364,10 @@ int main(int argc, char *argv[]) } addrp_width = screen_width; - addrp_width -= netid_width+1; - addrp_width -= state_width+1; + if (netid_width) + addrp_width -= netid_width + 1; + if (state_width) + addrp_width -= state_width + 1; addrp_width -= 14; if (addrp_width&1) { @@ -3983,6 +4375,8 @@ int main(int argc, char *argv[]) netid_width++; else if (state_width) state_width++; + else + odd_width_pad = " "; } addrp_width /= 2; @@ -3995,19 +4389,23 @@ int main(int argc, char *argv[]) addr_width = addrp_width - serv_width - 1; - if (netid_width) - printf("%-*s ", netid_width, "Netid"); - if (state_width) - printf("%-*s ", state_width, "State"); - printf("%-6s %-6s ", "Recv-Q", "Send-Q"); + if (show_header) { + if (netid_width) + printf("%-*s ", netid_width, "Netid"); + if (state_width) + printf("%-*s ", state_width, "State"); + printf("%-6s %-6s %s", "Recv-Q", "Send-Q", odd_width_pad); + } /* Make enough space for the local/remote port field */ addr_width -= 13; serv_width += 13; - printf("%*s:%-*s %*s:%-*s\n", - addr_width, "Local Address", serv_width, "Port", - addr_width, "Peer Address", serv_width, "Port"); + if (show_header) { + printf("%*s:%-*s %*s:%-*s\n", + addr_width, "Local Address", serv_width, "Port", + addr_width, "Peer Address", serv_width, "Port"); + } fflush(stdout); @@ -4025,9 +4423,11 @@ int main(int argc, char *argv[]) if (current_filter.dbs & (1<<UDP_DB)) udp_show(¤t_filter); if (current_filter.dbs & (1<<TCP_DB)) - tcp_show(¤t_filter, IPPROTO_TCP); + tcp_show(¤t_filter); if (current_filter.dbs & (1<<DCCP_DB)) - tcp_show(¤t_filter, IPPROTO_DCCP); + dccp_show(¤t_filter); + if (current_filter.dbs & (1<<SCTP_DB)) + sctp_show(¤t_filter); if (show_users || show_proc_ctx || show_sock_ctx) user_ent_destroy(); |