diff --git a/misc/ss.c b/misc/ss.c index f3d01812..f0dd129e 100644 --- a/misc/ss.c +++ b/misc/ss.c @@ -63,6 +63,10 @@ #define AF_VSOCK PF_VSOCK #endif +#ifndef IPPROTO_MPTCP +#define IPPROTO_MPTCP 262 +#endif + #define BUF_CHUNK (1024 * 1024) /* Buffer chunk allocation size */ #define BUF_CHUNKS_MAX 5 /* Maximum number of allocated buffer chunks */ #define LEN_ALIGN(x) (((x) + 1) & ~1) @@ -189,6 +193,7 @@ static const char *dg_proto; enum { TCP_DB, + MPTCP_DB, DCCP_DB, UDP_DB, RAW_DB, @@ -209,7 +214,7 @@ enum { #define PACKET_DBM ((1<mptcpi_subflows) + out(" subflows:%d", s->mptcpi_subflows); + if (s->mptcpi_add_addr_signal) + out(" add_addr_signal:%d", s->mptcpi_add_addr_signal); + if (s->mptcpi_add_addr_signal) + out(" add_addr_accepted:%d", s->mptcpi_add_addr_accepted); + if (s->mptcpi_subflows_max) + out(" subflows_max:%d", s->mptcpi_subflows_max); + if (s->mptcpi_add_addr_signal_max) + out(" add_addr_signal_max:%d", s->mptcpi_add_addr_signal_max); + if (s->mptcpi_add_addr_accepted_max) + out(" add_addr_accepted_max:%d", s->mptcpi_add_addr_accepted_max); + if (s->mptcpi_flags & MPTCP_INFO_FLAG_FALLBACK) + out(" fallback"); + if (s->mptcpi_flags & MPTCP_INFO_FLAG_REMOTE_KEY_RECEIVED) + out(" remote_key"); + if (s->mptcpi_token) + out(" token:%x", s->mptcpi_token); + if (s->mptcpi_write_seq) + out(" write_seq:%llx", s->mptcpi_write_seq); + if (s->mptcpi_snd_una) + out(" snd_una:%llx", s->mptcpi_snd_una); + if (s->mptcpi_rcv_nxt) + out(" rcv_nxt:%llx", s->mptcpi_rcv_nxt); +} + +static void mptcp_show_info(const struct nlmsghdr *nlh, struct inet_diag_msg *r, + struct rtattr *tb[]) +{ + print_skmeminfo(tb, INET_DIAG_SKMEMINFO); + + if (tb[INET_DIAG_INFO]) { + struct mptcp_info *info; + int 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]); + + mptcp_stats_print(info); + } +} + static const char *format_host_sa(struct sockaddr_storage *sa) { union { @@ -3277,6 +3338,8 @@ static int inet_show_sock(struct nlmsghdr *nlh, out("\n\t"); if (s->type == IPPROTO_SCTP) sctp_show_info(nlh, r, tb); + else if (s->type == IPPROTO_MPTCP) + mptcp_show_info(nlh, r, tb); else tcp_show_info(nlh, r, tb); } @@ -3365,9 +3428,11 @@ static int sockdiag_send(int family, int fd, int protocol, struct filter *f) DIAG_REQUEST(req, struct inet_diag_req_v2 r); char *bc = NULL; int bclen; + __u32 proto; struct msghdr msg; - struct rtattr rta; - struct iovec iov[3]; + struct rtattr rta_bc; + struct rtattr rta_proto; + struct iovec iov[5]; int iovlen = 1; if (family == PF_UNSPEC) @@ -3400,15 +3465,26 @@ static int sockdiag_send(int family, int fd, int protocol, struct filter *f) if (f->f) { bclen = ssfilter_bytecompile(f->f, &bc); if (bclen) { - rta.rta_type = INET_DIAG_REQ_BYTECODE; - rta.rta_len = RTA_LENGTH(bclen); - iov[1] = (struct iovec){ &rta, sizeof(rta) }; + rta_bc.rta_type = INET_DIAG_REQ_BYTECODE; + rta_bc.rta_len = RTA_LENGTH(bclen); + iov[1] = (struct iovec){ &rta_bc, sizeof(rta_bc) }; iov[2] = (struct iovec){ bc, bclen }; req.nlh.nlmsg_len += RTA_LENGTH(bclen); iovlen = 3; } } + /* put extended protocol attribute, if required */ + if (protocol > 255) { + rta_proto.rta_type = INET_DIAG_REQ_PROTOCOL; + rta_proto.rta_len = RTA_LENGTH(sizeof(proto)); + proto = protocol; + iov[iovlen] = (struct iovec){ &rta_proto, sizeof(rta_proto) }; + iov[iovlen + 1] = (struct iovec){ &proto, sizeof(proto) }; + req.nlh.nlmsg_len += RTA_LENGTH(sizeof(proto)); + iovlen += 2; + } + msg = (struct msghdr) { .msg_name = (void *)&nladdr, .msg_namelen = sizeof(nladdr), @@ -3668,6 +3744,18 @@ outerr: } while (0); } +static int mptcp_show(struct filter *f) +{ + if (!filter_af_get(f, AF_INET) && !filter_af_get(f, AF_INET6)) + return 0; + + if (!getenv("PROC_NET_MPTCP") && !getenv("PROC_ROOT") + && inet_show_netlink(f, NULL, IPPROTO_MPTCP) == 0) + return 0; + + return 0; +} + static int dccp_show(struct filter *f) { if (!filter_af_get(f, AF_INET) && !filter_af_get(f, AF_INET6)) @@ -5108,6 +5196,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" +" -M, --mptcp display only MPTCP sockets\n" " -S, --sctp display only SCTP sockets\n" " -u, --udp display only UDP sockets\n" " -d, --dccp display only DCCP sockets\n" @@ -5123,7 +5212,7 @@ static void _usage(FILE *dest) " -O, --oneline socket's data printed on a single line\n" "\n" " -A, --query=QUERY, --socket=QUERY\n" -" QUERY := {all|inet|tcp|udp|raw|unix|unix_dgram|unix_stream|unix_seqpacket|packet|netlink|vsock_stream|vsock_dgram|tipc}[,QUERY]\n" +" QUERY := {all|inet|tcp|mptcp|udp|raw|unix|unix_dgram|unix_stream|unix_seqpacket|packet|netlink|vsock_stream|vsock_dgram|tipc}[,QUERY]\n" "\n" " -D, --diag=FILE Dump raw information about TCP sockets to FILE\n" " -F, --filter=FILE read filter information from FILE\n" @@ -5250,6 +5339,7 @@ static const struct option long_opts[] = { { "kill", 0, 0, 'K' }, { "no-header", 0, 0, 'H' }, { "xdp", 0, 0, OPT_XDPSOCK}, + { "mptcp", 0, 0, 'M' }, { "oneline", 0, 0, 'O' }, { 0 } @@ -5266,7 +5356,7 @@ int main(int argc, char *argv[]) int state_filter = 0; while ((ch = getopt_long(argc, argv, - "dhaletuwxnro460spbEf:miA:D:F:vVzZN:KHSO", + "dhaletuwxnro460spbEf:mMiA:D:F:vVzZN:KHSO", long_opts, NULL)) != EOF) { switch (ch) { case 'n': @@ -5341,6 +5431,9 @@ int main(int argc, char *argv[]) case OPT_XDPSOCK: filter_af_set(¤t_filter, AF_XDP); break; + case 'M': + filter_db_set(¤t_filter, MPTCP_DB, true); + break; case 'f': if (strcmp(optarg, "inet") == 0) filter_af_set(¤t_filter, AF_INET); @@ -5566,6 +5659,8 @@ int main(int argc, char *argv[]) tipc_show(¤t_filter); if (current_filter.dbs & (1<