Commit c1e64e29 authored by Lorenzo Colitti's avatar Lorenzo Colitti Committed by David S. Miller

net: diag: Support destroying TCP sockets.

This implements SOCK_DESTROY for TCP sockets. It causes all
blocking calls on the socket to fail fast with ECONNABORTED and
causes a protocol close of the socket. It informs the other end
of the connection by sending a RST, i.e., initiating a TCP ABORT
as per RFC 793. ECONNABORTED was chosen for consistency with
FreeBSD.
Signed-off-by: default avatarLorenzo Colitti <lorenzo@google.com>
Acked-by: default avatarEric Dumazet <edumazet@google.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 6eb5d2e0
...@@ -1170,6 +1170,8 @@ void tcp_set_state(struct sock *sk, int state); ...@@ -1170,6 +1170,8 @@ void tcp_set_state(struct sock *sk, int state);
void tcp_done(struct sock *sk); void tcp_done(struct sock *sk);
int tcp_abort(struct sock *sk, int err);
static inline void tcp_sack_reset(struct tcp_options_received *rx_opt) static inline void tcp_sack_reset(struct tcp_options_received *rx_opt)
{ {
rx_opt->dsack = 0; rx_opt->dsack = 0;
......
...@@ -436,6 +436,19 @@ config INET_UDP_DIAG ...@@ -436,6 +436,19 @@ config INET_UDP_DIAG
Support for UDP socket monitoring interface used by the ss tool. Support for UDP socket monitoring interface used by the ss tool.
If unsure, say Y. If unsure, say Y.
config INET_DIAG_DESTROY
bool "INET: allow privileged process to administratively close sockets"
depends on INET_DIAG
default n
---help---
Provides a SOCK_DESTROY operation that allows privileged processes
(e.g., a connection manager or a network administration tool such as
ss) to close sockets opened by other processes. Closing a socket in
this way interrupts any blocking read/write/connect operations on
the socket and causes future socket calls to behave as if the socket
had been disconnected.
If unsure, say N.
menuconfig TCP_CONG_ADVANCED menuconfig TCP_CONG_ADVANCED
bool "TCP: advanced congestion control" bool "TCP: advanced congestion control"
---help--- ---help---
......
...@@ -3080,6 +3080,38 @@ void tcp_done(struct sock *sk) ...@@ -3080,6 +3080,38 @@ void tcp_done(struct sock *sk)
} }
EXPORT_SYMBOL_GPL(tcp_done); EXPORT_SYMBOL_GPL(tcp_done);
int tcp_abort(struct sock *sk, int err)
{
if (!sk_fullsock(sk)) {
sock_gen_put(sk);
return -EOPNOTSUPP;
}
/* Don't race with userspace socket closes such as tcp_close. */
lock_sock(sk);
/* Don't race with BH socket closes such as inet_csk_listen_stop. */
local_bh_disable();
bh_lock_sock(sk);
if (!sock_flag(sk, SOCK_DEAD)) {
sk->sk_err = err;
/* This barrier is coupled with smp_rmb() in tcp_poll() */
smp_wmb();
sk->sk_error_report(sk);
if (tcp_need_reset(sk->sk_state))
tcp_send_active_reset(sk, GFP_ATOMIC);
tcp_done(sk);
}
bh_unlock_sock(sk);
local_bh_enable();
release_sock(sk);
sock_put(sk);
return 0;
}
EXPORT_SYMBOL_GPL(tcp_abort);
extern struct tcp_congestion_ops tcp_reno; extern struct tcp_congestion_ops tcp_reno;
static __initdata unsigned long thash_entries; static __initdata unsigned long thash_entries;
......
...@@ -10,6 +10,8 @@ ...@@ -10,6 +10,8 @@
*/ */
#include <linux/module.h> #include <linux/module.h>
#include <linux/net.h>
#include <linux/sock_diag.h>
#include <linux/inet_diag.h> #include <linux/inet_diag.h>
#include <linux/tcp.h> #include <linux/tcp.h>
...@@ -46,12 +48,29 @@ static int tcp_diag_dump_one(struct sk_buff *in_skb, const struct nlmsghdr *nlh, ...@@ -46,12 +48,29 @@ static int tcp_diag_dump_one(struct sk_buff *in_skb, const struct nlmsghdr *nlh,
return inet_diag_dump_one_icsk(&tcp_hashinfo, in_skb, nlh, req); return inet_diag_dump_one_icsk(&tcp_hashinfo, in_skb, nlh, req);
} }
#ifdef CONFIG_INET_DIAG_DESTROY
static int tcp_diag_destroy(struct sk_buff *in_skb,
const struct inet_diag_req_v2 *req)
{
struct net *net = sock_net(in_skb->sk);
struct sock *sk = inet_diag_find_one_icsk(net, &tcp_hashinfo, req);
if (IS_ERR(sk))
return PTR_ERR(sk);
return sock_diag_destroy(sk, ECONNABORTED);
}
#endif
static const struct inet_diag_handler tcp_diag_handler = { static const struct inet_diag_handler tcp_diag_handler = {
.dump = tcp_diag_dump, .dump = tcp_diag_dump,
.dump_one = tcp_diag_dump_one, .dump_one = tcp_diag_dump_one,
.idiag_get_info = tcp_diag_get_info, .idiag_get_info = tcp_diag_get_info,
.idiag_type = IPPROTO_TCP, .idiag_type = IPPROTO_TCP,
.idiag_info_size = sizeof(struct tcp_info), .idiag_info_size = sizeof(struct tcp_info),
#ifdef CONFIG_INET_DIAG_DESTROY
.destroy = tcp_diag_destroy,
#endif
}; };
static int __init tcp_diag_init(void) static int __init tcp_diag_init(void)
......
...@@ -2342,6 +2342,7 @@ struct proto tcp_prot = { ...@@ -2342,6 +2342,7 @@ struct proto tcp_prot = {
.destroy_cgroup = tcp_destroy_cgroup, .destroy_cgroup = tcp_destroy_cgroup,
.proto_cgroup = tcp_proto_cgroup, .proto_cgroup = tcp_proto_cgroup,
#endif #endif
.diag_destroy = tcp_abort,
}; };
EXPORT_SYMBOL(tcp_prot); EXPORT_SYMBOL(tcp_prot);
......
...@@ -1890,6 +1890,7 @@ struct proto tcpv6_prot = { ...@@ -1890,6 +1890,7 @@ struct proto tcpv6_prot = {
.proto_cgroup = tcp_proto_cgroup, .proto_cgroup = tcp_proto_cgroup,
#endif #endif
.clear_sk = tcp_v6_clear_sk, .clear_sk = tcp_v6_clear_sk,
.diag_destroy = tcp_abort,
}; };
static const struct inet6_protocol tcpv6_protocol = { static const struct inet6_protocol tcpv6_protocol = {
......
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