Skip to content

Commit

Permalink
net: diag: support SOCK_DESTROY for UDP sockets
Browse files Browse the repository at this point in the history
This implements SOCK_DESTROY for UDP sockets similar to what was done
for TCP with commit c1e64e298b8ca ("net: diag: Support destroying TCP
sockets.") A process with a UDP socket targeted for destroy is awakened
and recvmsg fails with ECONNABORTED.

[backport of net-next 5d77dca82839ef016a93ad7acd7058b14d967752]

Change-Id: I84e71e774c859002f98dcdb5e0ca01f35227a44c
Signed-off-by: David Ahern <[email protected]>
Signed-off-by: David S. Miller <[email protected]>
Signed-off-by: Ad!thya R <[email protected]>
  • Loading branch information
David Ahern authored and adithya2306 committed Jul 14, 2018
1 parent 0970c7d commit ec75d85
Show file tree
Hide file tree
Showing 4 changed files with 97 additions and 0 deletions.
1 change: 1 addition & 0 deletions include/net/udp.h
Original file line number Diff line number Diff line change
Expand Up @@ -179,6 +179,7 @@ extern int udp_get_port(struct sock *sk, unsigned short snum,
int (*saddr_cmp)(const struct sock *,
const struct sock *));
extern void udp_err(struct sk_buff *, u32);
int udp_abort(struct sock *sk, int err);
extern int udp_sendmsg(struct kiocb *iocb, struct sock *sk,
struct msghdr *msg, size_t len);
extern int udp_push_pending_frames(struct sock *sk);
Expand Down
15 changes: 15 additions & 0 deletions net/ipv4/udp.c
Original file line number Diff line number Diff line change
Expand Up @@ -1977,6 +1977,20 @@ unsigned int udp_poll(struct file *file, struct socket *sock, poll_table *wait)
}
EXPORT_SYMBOL(udp_poll);

int udp_abort(struct sock *sk, int err)
{
lock_sock(sk);

sk->sk_err = err;
sk->sk_error_report(sk);
udp_disconnect(sk, 0);

release_sock(sk);

return 0;
}
EXPORT_SYMBOL_GPL(udp_abort);

struct proto udp_prot = {
.name = "UDP",
.owner = THIS_MODULE,
Expand Down Expand Up @@ -2008,6 +2022,7 @@ struct proto udp_prot = {
.compat_getsockopt = compat_udp_getsockopt,
#endif
.clear_sk = sk_prot_clear_portaddr_nulls,
.diag_destroy = udp_abort,
};
EXPORT_SYMBOL(udp_prot);

Expand Down
80 changes: 80 additions & 0 deletions net/ipv4/udp_diag.c
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
#include <net/udp.h>
#include <net/udplite.h>
#include <linux/sock_diag.h>
#include <net/ipv6.h>

static int sk_diag_dump(struct sock *sk, struct sk_buff *skb,
struct netlink_callback *cb, struct inet_diag_req_v2 *req,
Expand Down Expand Up @@ -162,11 +163,87 @@ static void udp_diag_get_info(struct sock *sk, struct inet_diag_msg *r,
r->idiag_wqueue = sk_wmem_alloc_get(sk);
}

#ifdef CONFIG_INET_DIAG_DESTROY
static int __udp_diag_destroy(struct sk_buff *in_skb,
const struct inet_diag_req_v2 *req,
struct udp_table *tbl)
{
struct net *net = sock_net(in_skb->sk);
struct sock *sk;
int err;

rcu_read_lock();

if (req->sdiag_family == AF_INET)
sk = __udp4_lib_lookup(net,
req->id.idiag_dst[0], req->id.idiag_dport,
req->id.idiag_src[0], req->id.idiag_sport,
req->id.idiag_if, tbl);
#if IS_ENABLED(CONFIG_IPV6)
else if (req->sdiag_family == AF_INET6) {
if (ipv6_addr_v4mapped((struct in6_addr *)req->id.idiag_dst) &&
ipv6_addr_v4mapped((struct in6_addr *)req->id.idiag_src))
sk = __udp4_lib_lookup(net,
req->id.idiag_dst[0], req->id.idiag_dport,
req->id.idiag_src[0], req->id.idiag_sport,
req->id.idiag_if, tbl);

else
sk = __udp6_lib_lookup(net,
(struct in6_addr *)req->id.idiag_dst,
req->id.idiag_dport,
(struct in6_addr *)req->id.idiag_src,
req->id.idiag_sport,
req->id.idiag_if, tbl);
}
#endif
else {
rcu_read_unlock();
return -EINVAL;
}

if (sk && !atomic_inc_not_zero(&sk->sk_refcnt))
sk = NULL;

rcu_read_unlock();

if (!sk)
return -ENOENT;

if (sock_diag_check_cookie(sk, (__u32 *) req->id.idiag_cookie)) {
sock_put(sk);
return -ENOENT;
}

err = sock_diag_destroy(sk, ECONNABORTED);

sock_put(sk);

return err;
}

static int udp_diag_destroy(struct sk_buff *in_skb,
struct inet_diag_req_v2 *req)
{
return __udp_diag_destroy(in_skb, req, &udp_table);
}

static int udplite_diag_destroy(struct sk_buff *in_skb,
struct inet_diag_req_v2 *req)
{
return __udp_diag_destroy(in_skb, req, &udplite_table);
}

#endif

static const struct inet_diag_handler udp_diag_handler = {
.dump = udp_diag_dump,
.dump_one = udp_diag_dump_one,
.idiag_get_info = udp_diag_get_info,
.idiag_type = IPPROTO_UDP,
#ifdef CONFIG_INET_DIAG_DESTROY
.destroy = udp_diag_destroy,
#endif
};

static void udplite_diag_dump(struct sk_buff *skb, struct netlink_callback *cb,
Expand All @@ -186,6 +263,9 @@ static const struct inet_diag_handler udplite_diag_handler = {
.dump_one = udplite_diag_dump_one,
.idiag_get_info = udp_diag_get_info,
.idiag_type = IPPROTO_UDPLITE,
#ifdef CONFIG_INET_DIAG_DESTROY
.destroy = udplite_diag_destroy,
#endif
};

static int __init udp_diag_init(void)
Expand Down
1 change: 1 addition & 0 deletions net/ipv6/udp.c
Original file line number Diff line number Diff line change
Expand Up @@ -1485,6 +1485,7 @@ struct proto udpv6_prot = {
.compat_getsockopt = compat_udpv6_getsockopt,
#endif
.clear_sk = udp_v6_clear_sk,
.diag_destroy = udp_abort,
};

static struct inet_protosw udpv6_protosw = {
Expand Down

0 comments on commit ec75d85

Please sign in to comment.