From 48d997511c1537ec8e2d6227251f8be1ede064d4 Mon Sep 17 00:00:00 2001 From: Phil Sutter Date: Wed, 1 Feb 2017 12:10:00 +0100 Subject: [PATCH] ss: Add support for SCTP protocol Bugzilla: https://bugzilla.redhat.com/show_bug.cgi?id=1063934 Upstream Status: iproute2.git commit f89d46ad63f6f Conflicts: * missing commit 6885e3bf8efa3 ("ss: Include -E option for socket destroy events") * missing commit acd1e437befbb ("misc: fix style issues") * missing commit 82d73ea03a149 ("ss: Refactor inet_show_sock") * missing other commandline flags/features * fixed for missing commit a418e451643e7 ("make format_host non-reentrant by default") commit f89d46ad63f6f606f777da964205bc53b2197cfa Author: Phil Sutter Date: Wed Nov 9 12:12:24 2016 +0100 ss: Add support for SCTP protocol This makes use of the sctp_diag interface recently added to the kernel. Joint work with Xin Long who provided the PoC implementation which I merely polished up a bit. Signed-off-by: Phil Sutter --- man/man8/ss.8 | 3 + misc/ss.c | 215 +++++++++++++++++++++++++++++++++++++++++++++++++++++++--- 2 files changed, 210 insertions(+), 8 deletions(-) diff --git a/man/man8/ss.8 b/man/man8/ss.8 index f4d5264..b02a68c 100644 --- a/man/man8/ss.8 +++ b/man/man8/ss.8 @@ -114,6 +114,9 @@ Display RAW sockets. .B \-x, \-\-unix Display Unix domain sockets (alias for -f unix). .TP +.B \-S, \-\-sctp +Display SCTP sockets. +.TP .B \-f FAMILY, \-\-family=FAMILY Display sockets of type FAMILY. Currently the following families are supported: unix, inet, inet6, link, netlink. diff --git a/misc/ss.c b/misc/ss.c index 45fb4b0..d438428 100644 --- a/misc/ss.c +++ b/misc/ss.c @@ -42,6 +42,7 @@ #include #include #include +#include #define MAGIC_SEQ 123456 @@ -99,6 +100,7 @@ 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 sctp_ino; int netid_width; int state_width; @@ -108,6 +110,7 @@ int serv_width; int screen_width; 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; @@ -124,13 +127,14 @@ enum PACKET_DG_DB, PACKET_R_DB, NETLINK_DB, + SCTP_DB, MAX_DB }; #define PACKET_DBM ((1<ino) + return false; + return true; +} + static void sock_state_print(struct sockstat *s, const char *sock_name) { 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); } @@ -895,6 +944,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; @@ -1566,6 +1617,8 @@ static char *proto_name(int protocol) return "udp"; case IPPROTO_TCP: return "tcp"; + case IPPROTO_SCTP: + return "sctp"; case IPPROTO_DCCP: return "dccp"; } @@ -1658,6 +1711,56 @@ static char *sprint_bw(char *buf, double 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]; @@ -1762,6 +1865,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; @@ -2007,6 +2117,67 @@ static void tcp_show_info(const struct nlmsghdr *nlh, struct inet_diag_msg *r, } } +static const char *format_host_sa(struct sockaddr_storage *sa) +{ + char buf[1024]; + 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, + buf, sizeof(buf)); + case AF_INET6: + return format_host(AF_INET6, 16, &saddr->sin6.sin6_addr, + buf, sizeof(buf)); + 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 int inet_show_sock(struct nlmsghdr *nlh, struct filter *f, int protocol) { struct rtattr * tb[INET_DIAG_MAX+1]; @@ -2047,7 +2218,10 @@ 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 (protocol == IPPROTO_SCTP) + sctp_timer_print(&t); + else + tcp_timer_print(&t); } if (show_details) { @@ -2066,8 +2240,12 @@ static int inet_show_sock(struct nlmsghdr *nlh, struct filter *f, int protocol) if (show_mem || show_tcpinfo) { printf("\n\t"); - tcp_show_info(nlh, r, tb); + if (protocol == IPPROTO_SCTP) + sctp_show_info(nlh, r, tb); + else + tcp_show_info(nlh, r, tb); } + sctp_ino = s.ino; printf("\n"); return 0; @@ -2392,6 +2570,17 @@ outerr: } while (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) { @@ -3343,6 +3532,7 @@ 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" @@ -3419,6 +3609,7 @@ static const struct option long_opts[] = { { "bpf", 0, 0, 'b' }, { "dccp", 0, 0, 'd' }, { "tcp", 0, 0, 't' }, + { "sctp", 0, 0, 'S' }, { "udp", 0, 0, 'u' }, { "raw", 0, 0, 'w' }, { "unix", 0, 0, 'x' }, @@ -3452,7 +3643,7 @@ int main(int argc, char *argv[]) int ch; int state_filter = 0; - while ((ch = getopt_long(argc, argv, "dhaletuwxnro460spbf:miA:D:F:vVzZN:", + while ((ch = getopt_long(argc, argv, "dhaletuwxnro460spbf:miA:D:F:vVzZN:S", long_opts, NULL)) != EOF) { switch(ch) { case 'n': @@ -3488,6 +3679,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; @@ -3551,6 +3745,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); @@ -3558,6 +3753,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) { @@ -3682,7 +3879,7 @@ int main(int argc, char *argv[]) filter_merge_defaults(¤t_filter); if (resolve_services && resolve_hosts && - (current_filter.dbs&(UNIX_DBM|(1<