aboutsummaryrefslogtreecommitdiffstats
path: root/misc/ss.c
diff options
context:
space:
mode:
Diffstat (limited to 'misc/ss.c')
-rw-r--r--misc/ss.c1474
1 files changed, 937 insertions, 537 deletions
diff --git a/misc/ss.c b/misc/ss.c
index 12a343c0..45a0c330 100644
--- a/misc/ss.c
+++ b/misc/ss.c
@@ -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(&current_filter, TCP_DB);
break;
+ case 'S':
+ filter_db_set(&current_filter, SCTP_DB);
+ break;
case 'u':
filter_db_set(&current_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(&current_filter, UDP_DB);
filter_db_set(&current_filter, DCCP_DB);
filter_db_set(&current_filter, TCP_DB);
+ filter_db_set(&current_filter, SCTP_DB);
filter_db_set(&current_filter, RAW_DB);
} else if (strcmp(p, "udp") == 0) {
filter_db_set(&current_filter, UDP_DB);
@@ -3796,6 +4181,8 @@ int main(int argc, char *argv[])
filter_db_set(&current_filter, DCCP_DB);
} else if (strcmp(p, "tcp") == 0) {
filter_db_set(&current_filter, TCP_DB);
+ } else if (strcmp(p, "sctp") == 0) {
+ filter_db_set(&current_filter, SCTP_DB);
} else if (strcmp(p, "raw") == 0) {
filter_db_set(&current_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(&current_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(&current_filter);
if (current_filter.dbs & (1<<TCP_DB))
- tcp_show(&current_filter, IPPROTO_TCP);
+ tcp_show(&current_filter);
if (current_filter.dbs & (1<<DCCP_DB))
- tcp_show(&current_filter, IPPROTO_DCCP);
+ dccp_show(&current_filter);
+ if (current_filter.dbs & (1<<SCTP_DB))
+ sctp_show(&current_filter);
if (show_users || show_proc_ctx || show_sock_ctx)
user_ent_destroy();