forked from mirrors/linux
		
	net: make sock diag per-namespace
Before this patch sock_diag works for init_net only and dumps
information about sockets from all namespaces.
This patch expands sock_diag for all name-spaces.
It creates a netlink kernel socket for each netns and filters
data during dumping.
v2: filter accoding with netns in all places
    remove an unused variable.
Cc: "David S. Miller" <davem@davemloft.net>
Cc: Alexey Kuznetsov <kuznet@ms2.inr.ac.ru>
Cc: James Morris <jmorris@namei.org>
Cc: Hideaki YOSHIFUJI <yoshfuji@linux-ipv6.org>
Cc: Patrick McHardy <kaber@trash.net>
Cc: Pavel Emelyanov <xemul@parallels.com>
CC: Eric Dumazet <eric.dumazet@gmail.com>
Cc: linux-kernel@vger.kernel.org
Cc: netdev@vger.kernel.org
Signed-off-by: Andrew Vagin <avagin@openvz.org>
Acked-by: Pavel Emelyanov <xemul@parallels.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
			
			
This commit is contained in:
		
							parent
							
								
									cbc89c8cf2
								
							
						
					
					
						commit
						51d7cccf07
					
				
					 6 changed files with 51 additions and 18 deletions
				
			
		|  | @ -44,6 +44,5 @@ void sock_diag_save_cookie(void *sk, __u32 *cookie); | |||
| 
 | ||||
| int sock_diag_put_meminfo(struct sock *sk, struct sk_buff *skb, int attr); | ||||
| 
 | ||||
| extern struct sock *sock_diag_nlsk; | ||||
| #endif /* KERNEL */ | ||||
| #endif | ||||
|  |  | |||
|  | @ -101,6 +101,7 @@ struct net { | |||
| 	struct netns_xfrm	xfrm; | ||||
| #endif | ||||
| 	struct netns_ipvs	*ipvs; | ||||
| 	struct sock		*diag_nlsk; | ||||
| }; | ||||
| 
 | ||||
| 
 | ||||
|  |  | |||
|  | @ -166,23 +166,36 @@ static void sock_diag_rcv(struct sk_buff *skb) | |||
| 	mutex_unlock(&sock_diag_mutex); | ||||
| } | ||||
| 
 | ||||
| struct sock *sock_diag_nlsk; | ||||
| EXPORT_SYMBOL_GPL(sock_diag_nlsk); | ||||
| 
 | ||||
| static int __init sock_diag_init(void) | ||||
| static int __net_init diag_net_init(struct net *net) | ||||
| { | ||||
| 	struct netlink_kernel_cfg cfg = { | ||||
| 		.input	= sock_diag_rcv, | ||||
| 	}; | ||||
| 
 | ||||
| 	sock_diag_nlsk = netlink_kernel_create(&init_net, NETLINK_SOCK_DIAG, | ||||
| 	net->diag_nlsk = netlink_kernel_create(net, NETLINK_SOCK_DIAG, | ||||
| 					       THIS_MODULE, &cfg); | ||||
| 	return sock_diag_nlsk == NULL ? -ENOMEM : 0; | ||||
| 	return net->diag_nlsk == NULL ? -ENOMEM : 0; | ||||
| } | ||||
| 
 | ||||
| static void __net_exit diag_net_exit(struct net *net) | ||||
| { | ||||
| 	netlink_kernel_release(net->diag_nlsk); | ||||
| 	net->diag_nlsk = NULL; | ||||
| } | ||||
| 
 | ||||
| static struct pernet_operations diag_net_ops = { | ||||
| 	.init = diag_net_init, | ||||
| 	.exit = diag_net_exit, | ||||
| }; | ||||
| 
 | ||||
| static int __init sock_diag_init(void) | ||||
| { | ||||
| 	return register_pernet_subsys(&diag_net_ops); | ||||
| } | ||||
| 
 | ||||
| static void __exit sock_diag_exit(void) | ||||
| { | ||||
| 	netlink_kernel_release(sock_diag_nlsk); | ||||
| 	unregister_pernet_subsys(&diag_net_ops); | ||||
| } | ||||
| 
 | ||||
| module_init(sock_diag_init); | ||||
|  |  | |||
|  | @ -272,16 +272,17 @@ int inet_diag_dump_one_icsk(struct inet_hashinfo *hashinfo, struct sk_buff *in_s | |||
| 	int err; | ||||
| 	struct sock *sk; | ||||
| 	struct sk_buff *rep; | ||||
| 	struct net *net = sock_net(in_skb->sk); | ||||
| 
 | ||||
| 	err = -EINVAL; | ||||
| 	if (req->sdiag_family == AF_INET) { | ||||
| 		sk = inet_lookup(&init_net, hashinfo, req->id.idiag_dst[0], | ||||
| 		sk = inet_lookup(net, hashinfo, req->id.idiag_dst[0], | ||||
| 				 req->id.idiag_dport, req->id.idiag_src[0], | ||||
| 				 req->id.idiag_sport, req->id.idiag_if); | ||||
| 	} | ||||
| #if IS_ENABLED(CONFIG_IPV6) | ||||
| 	else if (req->sdiag_family == AF_INET6) { | ||||
| 		sk = inet6_lookup(&init_net, hashinfo, | ||||
| 		sk = inet6_lookup(net, hashinfo, | ||||
| 				  (struct in6_addr *)req->id.idiag_dst, | ||||
| 				  req->id.idiag_dport, | ||||
| 				  (struct in6_addr *)req->id.idiag_src, | ||||
|  | @ -317,7 +318,7 @@ int inet_diag_dump_one_icsk(struct inet_hashinfo *hashinfo, struct sk_buff *in_s | |||
| 		nlmsg_free(rep); | ||||
| 		goto out; | ||||
| 	} | ||||
| 	err = netlink_unicast(sock_diag_nlsk, rep, NETLINK_CB(in_skb).pid, | ||||
| 	err = netlink_unicast(net->diag_nlsk, rep, NETLINK_CB(in_skb).pid, | ||||
| 			      MSG_DONTWAIT); | ||||
| 	if (err > 0) | ||||
| 		err = 0; | ||||
|  | @ -724,6 +725,7 @@ void inet_diag_dump_icsk(struct inet_hashinfo *hashinfo, struct sk_buff *skb, | |||
| { | ||||
| 	int i, num; | ||||
| 	int s_i, s_num; | ||||
| 	struct net *net = sock_net(skb->sk); | ||||
| 
 | ||||
| 	s_i = cb->args[1]; | ||||
| 	s_num = num = cb->args[2]; | ||||
|  | @ -743,6 +745,9 @@ void inet_diag_dump_icsk(struct inet_hashinfo *hashinfo, struct sk_buff *skb, | |||
| 			sk_nulls_for_each(sk, node, &ilb->head) { | ||||
| 				struct inet_sock *inet = inet_sk(sk); | ||||
| 
 | ||||
| 				if (!net_eq(sock_net(sk), net)) | ||||
| 					continue; | ||||
| 
 | ||||
| 				if (num < s_num) { | ||||
| 					num++; | ||||
| 					continue; | ||||
|  | @ -813,6 +818,8 @@ void inet_diag_dump_icsk(struct inet_hashinfo *hashinfo, struct sk_buff *skb, | |||
| 		sk_nulls_for_each(sk, node, &head->chain) { | ||||
| 			struct inet_sock *inet = inet_sk(sk); | ||||
| 
 | ||||
| 			if (!net_eq(sock_net(sk), net)) | ||||
| 				continue; | ||||
| 			if (num < s_num) | ||||
| 				goto next_normal; | ||||
| 			if (!(r->idiag_states & (1 << sk->sk_state))) | ||||
|  | @ -839,6 +846,8 @@ void inet_diag_dump_icsk(struct inet_hashinfo *hashinfo, struct sk_buff *skb, | |||
| 
 | ||||
| 			inet_twsk_for_each(tw, node, | ||||
| 				    &head->twchain) { | ||||
| 				if (!net_eq(twsk_net(tw), net)) | ||||
| 					continue; | ||||
| 
 | ||||
| 				if (num < s_num) | ||||
| 					goto next_dying; | ||||
|  | @ -943,6 +952,7 @@ static int inet_diag_get_exact_compat(struct sk_buff *in_skb, | |||
| static int inet_diag_rcv_msg_compat(struct sk_buff *skb, struct nlmsghdr *nlh) | ||||
| { | ||||
| 	int hdrlen = sizeof(struct inet_diag_req); | ||||
| 	struct net *net = sock_net(skb->sk); | ||||
| 
 | ||||
| 	if (nlh->nlmsg_type >= INET_DIAG_GETSOCK_MAX || | ||||
| 	    nlmsg_len(nlh) < hdrlen) | ||||
|  | @ -963,7 +973,7 @@ static int inet_diag_rcv_msg_compat(struct sk_buff *skb, struct nlmsghdr *nlh) | |||
| 			struct netlink_dump_control c = { | ||||
| 				.dump = inet_diag_dump_compat, | ||||
| 			}; | ||||
| 			return netlink_dump_start(sock_diag_nlsk, skb, nlh, &c); | ||||
| 			return netlink_dump_start(net->diag_nlsk, skb, nlh, &c); | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
|  | @ -973,6 +983,7 @@ static int inet_diag_rcv_msg_compat(struct sk_buff *skb, struct nlmsghdr *nlh) | |||
| static int inet_diag_handler_dump(struct sk_buff *skb, struct nlmsghdr *h) | ||||
| { | ||||
| 	int hdrlen = sizeof(struct inet_diag_req_v2); | ||||
| 	struct net *net = sock_net(skb->sk); | ||||
| 
 | ||||
| 	if (nlmsg_len(h) < hdrlen) | ||||
| 		return -EINVAL; | ||||
|  | @ -991,7 +1002,7 @@ static int inet_diag_handler_dump(struct sk_buff *skb, struct nlmsghdr *h) | |||
| 			struct netlink_dump_control c = { | ||||
| 				.dump = inet_diag_dump, | ||||
| 			}; | ||||
| 			return netlink_dump_start(sock_diag_nlsk, skb, h, &c); | ||||
| 			return netlink_dump_start(net->diag_nlsk, skb, h, &c); | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
|  |  | |||
|  | @ -34,15 +34,16 @@ static int udp_dump_one(struct udp_table *tbl, struct sk_buff *in_skb, | |||
| 	int err = -EINVAL; | ||||
| 	struct sock *sk; | ||||
| 	struct sk_buff *rep; | ||||
| 	struct net *net = sock_net(in_skb->sk); | ||||
| 
 | ||||
| 	if (req->sdiag_family == AF_INET) | ||||
| 		sk = __udp4_lib_lookup(&init_net, | ||||
| 		sk = __udp4_lib_lookup(net, | ||||
| 				req->id.idiag_src[0], req->id.idiag_sport, | ||||
| 				req->id.idiag_dst[0], req->id.idiag_dport, | ||||
| 				req->id.idiag_if, tbl); | ||||
| #if IS_ENABLED(CONFIG_IPV6) | ||||
| 	else if (req->sdiag_family == AF_INET6) | ||||
| 		sk = __udp6_lib_lookup(&init_net, | ||||
| 		sk = __udp6_lib_lookup(net, | ||||
| 				(struct in6_addr *)req->id.idiag_src, | ||||
| 				req->id.idiag_sport, | ||||
| 				(struct in6_addr *)req->id.idiag_dst, | ||||
|  | @ -75,7 +76,7 @@ static int udp_dump_one(struct udp_table *tbl, struct sk_buff *in_skb, | |||
| 		kfree_skb(rep); | ||||
| 		goto out; | ||||
| 	} | ||||
| 	err = netlink_unicast(sock_diag_nlsk, rep, NETLINK_CB(in_skb).pid, | ||||
| 	err = netlink_unicast(net->diag_nlsk, rep, NETLINK_CB(in_skb).pid, | ||||
| 			      MSG_DONTWAIT); | ||||
| 	if (err > 0) | ||||
| 		err = 0; | ||||
|  | @ -90,6 +91,7 @@ static void udp_dump(struct udp_table *table, struct sk_buff *skb, struct netlin | |||
| 		struct inet_diag_req_v2 *r, struct nlattr *bc) | ||||
| { | ||||
| 	int num, s_num, slot, s_slot; | ||||
| 	struct net *net = sock_net(skb->sk); | ||||
| 
 | ||||
| 	s_slot = cb->args[0]; | ||||
| 	num = s_num = cb->args[1]; | ||||
|  | @ -106,6 +108,8 @@ static void udp_dump(struct udp_table *table, struct sk_buff *skb, struct netlin | |||
| 		sk_nulls_for_each(sk, node, &hslot->head) { | ||||
| 			struct inet_sock *inet = inet_sk(sk); | ||||
| 
 | ||||
| 			if (!net_eq(sock_net(sk), net)) | ||||
| 				continue; | ||||
| 			if (num < s_num) | ||||
| 				goto next; | ||||
| 			if (!(r->idiag_states & (1 << sk->sk_state))) | ||||
|  |  | |||
|  | @ -177,6 +177,7 @@ static int unix_diag_dump(struct sk_buff *skb, struct netlink_callback *cb) | |||
| { | ||||
| 	struct unix_diag_req *req; | ||||
| 	int num, s_num, slot, s_slot; | ||||
| 	struct net *net = sock_net(skb->sk); | ||||
| 
 | ||||
| 	req = nlmsg_data(cb->nlh); | ||||
| 
 | ||||
|  | @ -192,6 +193,8 @@ static int unix_diag_dump(struct sk_buff *skb, struct netlink_callback *cb) | |||
| 
 | ||||
| 		num = 0; | ||||
| 		sk_for_each(sk, node, &unix_socket_table[slot]) { | ||||
| 			if (!net_eq(sock_net(sk), net)) | ||||
| 				continue; | ||||
| 			if (num < s_num) | ||||
| 				goto next; | ||||
| 			if (!(req->udiag_states & (1 << sk->sk_state))) | ||||
|  | @ -243,6 +246,7 @@ static int unix_diag_get_exact(struct sk_buff *in_skb, | |||
| 	struct sock *sk; | ||||
| 	struct sk_buff *rep; | ||||
| 	unsigned int extra_len; | ||||
| 	struct net *net = sock_net(in_skb->sk); | ||||
| 
 | ||||
| 	if (req->udiag_ino == 0) | ||||
| 		goto out_nosk; | ||||
|  | @ -273,7 +277,7 @@ static int unix_diag_get_exact(struct sk_buff *in_skb, | |||
| 
 | ||||
| 		goto again; | ||||
| 	} | ||||
| 	err = netlink_unicast(sock_diag_nlsk, rep, NETLINK_CB(in_skb).pid, | ||||
| 	err = netlink_unicast(net->diag_nlsk, rep, NETLINK_CB(in_skb).pid, | ||||
| 			      MSG_DONTWAIT); | ||||
| 	if (err > 0) | ||||
| 		err = 0; | ||||
|  | @ -287,6 +291,7 @@ static int unix_diag_get_exact(struct sk_buff *in_skb, | |||
| static int unix_diag_handler_dump(struct sk_buff *skb, struct nlmsghdr *h) | ||||
| { | ||||
| 	int hdrlen = sizeof(struct unix_diag_req); | ||||
| 	struct net *net = sock_net(skb->sk); | ||||
| 
 | ||||
| 	if (nlmsg_len(h) < hdrlen) | ||||
| 		return -EINVAL; | ||||
|  | @ -295,7 +300,7 @@ static int unix_diag_handler_dump(struct sk_buff *skb, struct nlmsghdr *h) | |||
| 		struct netlink_dump_control c = { | ||||
| 			.dump = unix_diag_dump, | ||||
| 		}; | ||||
| 		return netlink_dump_start(sock_diag_nlsk, skb, h, &c); | ||||
| 		return netlink_dump_start(net->diag_nlsk, skb, h, &c); | ||||
| 	} else | ||||
| 		return unix_diag_get_exact(skb, h, nlmsg_data(h)); | ||||
| } | ||||
|  |  | |||
		Loading…
	
		Reference in a new issue
	
	 Andrey Vagin
						Andrey Vagin