Commit fb2594c1 authored by Lorenzo Colitti's avatar Lorenzo Colitti Committed by Stephen Hemminger

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: default avatarLorenzo Colitti <lorenzo@google.com>
parent 57fdf2d4
...@@ -48,6 +48,11 @@ Show process using socket. ...@@ -48,6 +48,11 @@ Show process using socket.
.B \-i, \-\-info .B \-i, \-\-info
Show internal TCP information. Show internal TCP information.
.TP .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 .B \-s, \-\-summary
Print summary statistics. This option does not parse socket lists obtaining 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 summary from various sources. It is useful when amount of sockets is so huge
......
...@@ -160,6 +160,7 @@ struct filter ...@@ -160,6 +160,7 @@ struct filter
int states; int states;
int families; int families;
struct ssfilter *f; struct ssfilter *f;
bool kill;
}; };
static const struct filter default_dbs[MAX_DB] = { 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) ...@@ -2194,8 +2195,27 @@ static int sockdiag_send(int family, int fd, int protocol, struct filter *f)
struct inet_diag_arg { struct inet_diag_arg {
struct filter *f; struct filter *f;
int protocol; 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, static int show_one_inet_sock(const struct sockaddr_nl *addr,
struct nlmsghdr *h, void *arg) struct nlmsghdr *h, void *arg)
{ {
...@@ -2205,6 +2225,15 @@ static int show_one_inet_sock(const struct sockaddr_nl *addr, ...@@ -2205,6 +2225,15 @@ static int show_one_inet_sock(const struct sockaddr_nl *addr,
if (!(diag_arg->f->families & (1 << r->idiag_family))) if (!(diag_arg->f->families & (1 << r->idiag_family)))
return 0; 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) if ((err = inet_show_sock(h, diag_arg->f, diag_arg->protocol)) < 0)
return err; return err;
...@@ -2214,12 +2243,21 @@ static int show_one_inet_sock(const struct sockaddr_nl *addr, ...@@ -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) static int inet_show_netlink(struct filter *f, FILE *dump_fp, int protocol)
{ {
int err = 0; int err = 0;
struct rtnl_handle rth; struct rtnl_handle rth, rth2;
int family = PF_INET; int family = PF_INET;
struct inet_diag_arg arg = { .f = f, .protocol = protocol }; struct inet_diag_arg arg = { .f = f, .protocol = protocol };
if (rtnl_open_byproto(&rth, 0, NETLINK_SOCK_DIAG)) if (rtnl_open_byproto(&rth, 0, NETLINK_SOCK_DIAG))
return -1; 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 = MAGIC_SEQ;
rth.dump_fp = dump_fp; rth.dump_fp = dump_fp;
if (preferred_family == PF_INET6) if (preferred_family == PF_INET6)
...@@ -2243,6 +2281,8 @@ again: ...@@ -2243,6 +2281,8 @@ again:
Exit: Exit:
rtnl_close(&rth); rtnl_close(&rth);
if (arg.rth)
rtnl_close(arg.rth);
return err; return err;
} }
...@@ -3489,6 +3529,8 @@ static void _usage(FILE *dest) ...@@ -3489,6 +3529,8 @@ static void _usage(FILE *dest)
" -x, --unix display only Unix domain sockets\n" " -x, --unix display only Unix domain sockets\n"
" -f, --family=FAMILY display sockets of type FAMILY\n" " -f, --family=FAMILY display sockets of type FAMILY\n"
"\n" "\n"
" -K, --kill forcibly close sockets, display what was closed\n"
"\n"
" -A, --query=QUERY, --socket=QUERY\n" " -A, --query=QUERY, --socket=QUERY\n"
" QUERY := {all|inet|tcp|udp|raw|unix|unix_dgram|unix_stream|unix_seqpacket|packet|netlink}[,QUERY]\n" " QUERY := {all|inet|tcp|udp|raw|unix|unix_dgram|unix_stream|unix_seqpacket|packet|netlink}[,QUERY]\n"
"\n" "\n"
...@@ -3579,6 +3621,7 @@ static const struct option long_opts[] = { ...@@ -3579,6 +3621,7 @@ static const struct option long_opts[] = {
{ "context", 0, 0, 'Z' }, { "context", 0, 0, 'Z' },
{ "contexts", 0, 0, 'z' }, { "contexts", 0, 0, 'z' },
{ "net", 1, 0, 'N' }, { "net", 1, 0, 'N' },
{ "kill", 0, 0, 'K' },
{ 0 } { 0 }
}; };
...@@ -3593,7 +3636,7 @@ int main(int argc, char *argv[]) ...@@ -3593,7 +3636,7 @@ int main(int argc, char *argv[])
int ch; int ch;
int state_filter = 0; 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) { long_opts, NULL)) != EOF) {
switch(ch) { switch(ch) {
case 'n': case 'n':
...@@ -3774,6 +3817,9 @@ int main(int argc, char *argv[]) ...@@ -3774,6 +3817,9 @@ int main(int argc, char *argv[])
if (netns_switch(optarg)) if (netns_switch(optarg))
exit(1); exit(1);
break; break;
case 'K':
current_filter.kill = 1;
break;
case 'h': case 'h':
help(); help();
case '?': case '?':
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment