From: Lorenzo Colitti Date: Fri, 8 Jan 2016 08:32:37 +0000 (+0900) Subject: ss: support closing inet sockets via SOCK_DESTROY. X-Git-Tag: v4.5.0~93^2 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=fb2594c183fbedbe8f91fe7b1f7fed1331bb3194;p=thirdparty%2Fiproute2.git ss: support closing inet sockets via SOCK_DESTROY. This patch adds a -K / --kill option to ss that attempts to forcibly close matching sockets using SOCK_DESTROY. Because ss typically prints sockets instead of acting on them, and because the kernel only supports forcibly closing some types of sockets, the output of -K is as follows: - If closing the socket succeeds, the socket is printed. - If the kernel does not support forcibly closing this type of socket (e.g., if it's a UDP socket, or a TIME_WAIT socket), the socket is silently skipped. - If an error occurs (e.g., permission denied), the error is reported and ss exits. Signed-off-by: Lorenzo Colitti --- diff --git a/man/man8/ss.8 b/man/man8/ss.8 index f4d5264f3..758460c27 100644 --- a/man/man8/ss.8 +++ b/man/man8/ss.8 @@ -48,6 +48,11 @@ Show process using socket. .B \-i, \-\-info Show internal TCP information. .TP +.B \-K, \-\-kill +Attempts to forcibly close sockets. This option displays sockets that are +successfully closed and silently skips sockets that the kernel does not support +closing. It supports IPv4 and IPv6 sockets only. +.TP .B \-s, \-\-summary Print summary statistics. This option does not parse socket lists obtaining summary from various sources. It is useful when amount of sockets is so huge diff --git a/misc/ss.c b/misc/ss.c index 0dab32cef..13fcc8f66 100644 --- a/misc/ss.c +++ b/misc/ss.c @@ -160,6 +160,7 @@ struct filter int states; int families; struct ssfilter *f; + bool kill; }; static const struct filter default_dbs[MAX_DB] = { @@ -2194,8 +2195,27 @@ static int sockdiag_send(int family, int fd, int protocol, struct filter *f) struct inet_diag_arg { struct filter *f; int protocol; + struct rtnl_handle *rth; }; +static int kill_inet_sock(const struct sockaddr_nl *addr, + struct nlmsghdr *h, void *arg) +{ + 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; + req.nlh.nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK; + req.nlh.nlmsg_seq = ++rth->seq; + req.r.sdiag_family = d->idiag_family; + req.r.sdiag_protocol = diag_arg->protocol; + req.r.id = d->id; + + return rtnl_talk(rth, &req.nlh, NULL, 0); +} + static int show_one_inet_sock(const struct sockaddr_nl *addr, struct nlmsghdr *h, void *arg) { @@ -2205,6 +2225,15 @@ static int show_one_inet_sock(const struct sockaddr_nl *addr, if (!(diag_arg->f->families & (1 << r->idiag_family))) return 0; + if (diag_arg->f->kill && kill_inet_sock(addr, h, arg) != 0) { + if (errno == EOPNOTSUPP || errno == ENOENT) { + /* Socket can't be closed, or is already closed. */ + return 0; + } else { + perror("SOCK_DESTROY answers"); + return -1; + } + } if ((err = inet_show_sock(h, diag_arg->f, diag_arg->protocol)) < 0) return err; @@ -2214,12 +2243,21 @@ static int show_one_inet_sock(const struct sockaddr_nl *addr, static int inet_show_netlink(struct filter *f, FILE *dump_fp, int protocol) { int err = 0; - struct rtnl_handle rth; + struct rtnl_handle rth, rth2; int family = PF_INET; struct inet_diag_arg arg = { .f = f, .protocol = protocol }; if (rtnl_open_byproto(&rth, 0, NETLINK_SOCK_DIAG)) return -1; + + if (f->kill) { + if (rtnl_open_byproto(&rth2, 0, NETLINK_SOCK_DIAG)) { + rtnl_close(&rth); + return -1; + } + arg.rth = &rth2; + } + rth.dump = MAGIC_SEQ; rth.dump_fp = dump_fp; if (preferred_family == PF_INET6) @@ -2243,6 +2281,8 @@ again: Exit: rtnl_close(&rth); + if (arg.rth) + rtnl_close(arg.rth); return err; } @@ -3489,6 +3529,8 @@ static void _usage(FILE *dest) " -x, --unix display only Unix domain sockets\n" " -f, --family=FAMILY display sockets of type FAMILY\n" "\n" +" -K, --kill forcibly close sockets, display what was closed\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" "\n" @@ -3579,6 +3621,7 @@ static const struct option long_opts[] = { { "context", 0, 0, 'Z' }, { "contexts", 0, 0, 'z' }, { "net", 1, 0, 'N' }, + { "kill", 0, 0, 'K' }, { 0 } }; @@ -3593,7 +3636,7 @@ int main(int argc, char *argv[]) int ch; int state_filter = 0; - while ((ch = getopt_long(argc, argv, "dhaletuwxnro460spbEf:miA:D:F:vVzZN:", + while ((ch = getopt_long(argc, argv, "dhaletuwxnro460spbEf:miA:D:F:vVzZN:K", long_opts, NULL)) != EOF) { switch(ch) { case 'n': @@ -3774,6 +3817,9 @@ int main(int argc, char *argv[]) if (netns_switch(optarg)) exit(1); break; + case 'K': + current_filter.kill = 1; + break; case 'h': help(); case '?':