forked from mirrors/linux
		
	net: diag: support SOCK_DESTROY for UDP sockets
This implements SOCK_DESTROY for UDP sockets similar to what was done
for TCP with commit c1e64e298b ("net: diag: Support destroying TCP
sockets.") A process with a UDP socket targeted for destroy is awakened
and recvmsg fails with ECONNABORTED.
Signed-off-by: David Ahern <dsa@cumulusnetworks.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
			
			
This commit is contained in:
		
							parent
							
								
									5128b18522
								
							
						
					
					
						commit
						5d77dca828
					
				
					 4 changed files with 96 additions and 0 deletions
				
			
		|  | @ -251,6 +251,7 @@ int udp_get_port(struct sock *sk, unsigned short snum, | ||||||
| 		 int (*saddr_cmp)(const struct sock *, | 		 int (*saddr_cmp)(const struct sock *, | ||||||
| 				  const struct sock *)); | 				  const struct sock *)); | ||||||
| void udp_err(struct sk_buff *, u32); | void udp_err(struct sk_buff *, u32); | ||||||
|  | int udp_abort(struct sock *sk, int err); | ||||||
| int udp_sendmsg(struct sock *sk, struct msghdr *msg, size_t len); | int udp_sendmsg(struct sock *sk, struct msghdr *msg, size_t len); | ||||||
| int udp_push_pending_frames(struct sock *sk); | int udp_push_pending_frames(struct sock *sk); | ||||||
| void udp_flush_pending_frames(struct sock *sk); | void udp_flush_pending_frames(struct sock *sk); | ||||||
|  |  | ||||||
|  | @ -2193,6 +2193,20 @@ unsigned int udp_poll(struct file *file, struct socket *sock, poll_table *wait) | ||||||
| } | } | ||||||
| EXPORT_SYMBOL(udp_poll); | 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 = { | struct proto udp_prot = { | ||||||
| 	.name		   = "UDP", | 	.name		   = "UDP", | ||||||
| 	.owner		   = THIS_MODULE, | 	.owner		   = THIS_MODULE, | ||||||
|  | @ -2224,6 +2238,7 @@ struct proto udp_prot = { | ||||||
| 	.compat_getsockopt = compat_udp_getsockopt, | 	.compat_getsockopt = compat_udp_getsockopt, | ||||||
| #endif | #endif | ||||||
| 	.clear_sk	   = sk_prot_clear_portaddr_nulls, | 	.clear_sk	   = sk_prot_clear_portaddr_nulls, | ||||||
|  | 	.diag_destroy	   = udp_abort, | ||||||
| }; | }; | ||||||
| EXPORT_SYMBOL(udp_prot); | EXPORT_SYMBOL(udp_prot); | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -165,12 +165,88 @@ static void udp_diag_get_info(struct sock *sk, struct inet_diag_msg *r, | ||||||
| 	r->idiag_wqueue = sk_wmem_alloc_get(sk); | 	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, NULL); | ||||||
|  | #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, NULL); | ||||||
|  | 
 | ||||||
|  | 		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, NULL); | ||||||
|  | 	} | ||||||
|  | #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, 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, | ||||||
|  | 			    const 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, | ||||||
|  | 				const 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 = { | static const struct inet_diag_handler udp_diag_handler = { | ||||||
| 	.dump		 = udp_diag_dump, | 	.dump		 = udp_diag_dump, | ||||||
| 	.dump_one	 = udp_diag_dump_one, | 	.dump_one	 = udp_diag_dump_one, | ||||||
| 	.idiag_get_info  = udp_diag_get_info, | 	.idiag_get_info  = udp_diag_get_info, | ||||||
| 	.idiag_type	 = IPPROTO_UDP, | 	.idiag_type	 = IPPROTO_UDP, | ||||||
| 	.idiag_info_size = 0, | 	.idiag_info_size = 0, | ||||||
|  | #ifdef CONFIG_INET_DIAG_DESTROY | ||||||
|  | 	.destroy	 = udp_diag_destroy, | ||||||
|  | #endif | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| static void udplite_diag_dump(struct sk_buff *skb, struct netlink_callback *cb, | static void udplite_diag_dump(struct sk_buff *skb, struct netlink_callback *cb, | ||||||
|  | @ -192,6 +268,9 @@ static const struct inet_diag_handler udplite_diag_handler = { | ||||||
| 	.idiag_get_info  = udp_diag_get_info, | 	.idiag_get_info  = udp_diag_get_info, | ||||||
| 	.idiag_type	 = IPPROTO_UDPLITE, | 	.idiag_type	 = IPPROTO_UDPLITE, | ||||||
| 	.idiag_info_size = 0, | 	.idiag_info_size = 0, | ||||||
|  | #ifdef CONFIG_INET_DIAG_DESTROY | ||||||
|  | 	.destroy	 = udplite_diag_destroy, | ||||||
|  | #endif | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| static int __init udp_diag_init(void) | static int __init udp_diag_init(void) | ||||||
|  |  | ||||||
|  | @ -1467,6 +1467,7 @@ struct proto udpv6_prot = { | ||||||
| 	.compat_getsockopt = compat_udpv6_getsockopt, | 	.compat_getsockopt = compat_udpv6_getsockopt, | ||||||
| #endif | #endif | ||||||
| 	.clear_sk	   = udp_v6_clear_sk, | 	.clear_sk	   = udp_v6_clear_sk, | ||||||
|  | 	.diag_destroy      = udp_abort, | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| static struct inet_protosw udpv6_protosw = { | static struct inet_protosw udpv6_protosw = { | ||||||
|  |  | ||||||
		Loading…
	
		Reference in a new issue
	
	 David Ahern
						David Ahern