[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-Id: <20161109111224.3946-3-phil@nwl.cc>
Date: Wed, 9 Nov 2016 12:12:24 +0100
From: Phil Sutter <phil@....cc>
To: Stephen Hemminger <stephen@...workplumber.org>
Cc: netdev@...r.kernel.org
Subject: [iproute PATCH v2 2/2] 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 <phil@....cc>
---
Changes since v1:
- Added missing bits to ss man page.
---
man/man8/ss.8 | 3 +
misc/ss.c | 212 +++++++++++++++++++++++++++++++++++++++++++++++++++++++---
2 files changed, 207 insertions(+), 8 deletions(-)
diff --git a/man/man8/ss.8 b/man/man8/ss.8
index 8911976faa35b..4ef11523b4268 100644
--- a/man/man8/ss.8
+++ b/man/man8/ss.8
@@ -122,6 +122,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 dd77b8153b6da..bb72fafe6f3cd 100644
--- a/misc/ss.c
+++ b/misc/ss.c
@@ -43,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
@@ -102,6 +103,7 @@ int show_header = 1;
/* If show_users & show_proc_ctx only do user_ent_hash_build() once */
int user_ent_hash_build_init;
int follow_events;
+int sctp_ino;
int netid_width;
int state_width;
@@ -111,6 +113,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;
@@ -126,13 +129,14 @@ 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_DBM ((1<<TCP_DB)|(1<<UDP_DB)|(1<<DCCP_DB)|(1<<RAW_DB)|(1<<SCTP_DB))
enum {
SS_UNKNOWN,
@@ -150,6 +154,17 @@ 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)))
@@ -204,6 +219,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,6 +283,7 @@ 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)
@@ -705,6 +725,17 @@ static const char *sstate_name[] = {
[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",
@@ -793,12 +824,30 @@ struct tcpstat {
struct tcp_bbr_info *bbr_info;
};
+/* 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 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);
}
@@ -908,6 +957,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;
@@ -1679,6 +1730,8 @@ static char *proto_name(int protocol)
return "udp";
case IPPROTO_TCP:
return "tcp";
+ case IPPROTO_SCTP:
+ return "sctp";
case IPPROTO_DCCP:
return "dccp";
}
@@ -1771,6 +1824,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];
@@ -1902,6 +2005,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;
@@ -2168,6 +2278,64 @@ static void tcp_show_info(const struct nlmsghdr *nlh, struct inet_diag_msg *r,
}
}
+static const char *format_host_sa(struct sockaddr_storage *sa)
+{
+ 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];
@@ -2221,7 +2389,10 @@ static int inet_show_sock(struct nlmsghdr *nlh,
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) {
@@ -2242,8 +2413,12 @@ static int inet_show_sock(struct nlmsghdr *nlh,
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;
@@ -2627,6 +2802,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)
{
@@ -3738,6 +3924,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"
@@ -3820,6 +4007,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' },
@@ -3855,7 +4043,7 @@ int main(int argc, char *argv[])
int ch;
int state_filter = 0;
- while ((ch = getopt_long(argc, argv, "dhaletuwxnro460spbEf:miA:D:F:vVzZN:KH",
+ while ((ch = getopt_long(argc, argv, "dhaletuwxnro460spbEf:miA:D:F:vVzZN:KHS",
long_opts, NULL)) != EOF) {
switch (ch) {
case 'n':
@@ -3894,6 +4082,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;
@@ -3958,6 +4149,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);
@@ -3965,6 +4157,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) {
@@ -4089,7 +4283,7 @@ 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|(1<<TCP_DB)|(1<<UDP_DB)|(1<<DCCP_DB)|(1<<SCTP_DB))))
init_service_resolver();
@@ -4205,6 +4399,8 @@ int main(int argc, char *argv[])
tcp_show(¤t_filter, IPPROTO_TCP);
if (current_filter.dbs & (1<<DCCP_DB))
tcp_show(¤t_filter, IPPROTO_DCCP);
+ if (current_filter.dbs & (1<<SCTP_DB))
+ sctp_show(¤t_filter);
if (show_users || show_proc_ctx || show_sock_ctx)
user_ent_destroy();
--
2.10.0
Powered by blists - more mailing lists