Commit 51d7cccf authored by Andrey Vagin's avatar Andrey Vagin Committed by David S. Miller

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: default avatarAndrew Vagin <avagin@openvz.org>
Acked-by: default avatarPavel Emelyanov <xemul@parallels.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent cbc89c8c
...@@ -44,6 +44,5 @@ void sock_diag_save_cookie(void *sk, __u32 *cookie); ...@@ -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); int sock_diag_put_meminfo(struct sock *sk, struct sk_buff *skb, int attr);
extern struct sock *sock_diag_nlsk;
#endif /* KERNEL */ #endif /* KERNEL */
#endif #endif
...@@ -101,6 +101,7 @@ struct net { ...@@ -101,6 +101,7 @@ struct net {
struct netns_xfrm xfrm; struct netns_xfrm xfrm;
#endif #endif
struct netns_ipvs *ipvs; struct netns_ipvs *ipvs;
struct sock *diag_nlsk;
}; };
......
...@@ -166,23 +166,36 @@ static void sock_diag_rcv(struct sk_buff *skb) ...@@ -166,23 +166,36 @@ static void sock_diag_rcv(struct sk_buff *skb)
mutex_unlock(&sock_diag_mutex); mutex_unlock(&sock_diag_mutex);
} }
struct sock *sock_diag_nlsk; static int __net_init diag_net_init(struct net *net)
EXPORT_SYMBOL_GPL(sock_diag_nlsk);
static int __init sock_diag_init(void)
{ {
struct netlink_kernel_cfg cfg = { struct netlink_kernel_cfg cfg = {
.input = sock_diag_rcv, .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); 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) static void __exit sock_diag_exit(void)
{ {
netlink_kernel_release(sock_diag_nlsk); unregister_pernet_subsys(&diag_net_ops);
} }
module_init(sock_diag_init); module_init(sock_diag_init);
......
...@@ -272,16 +272,17 @@ int inet_diag_dump_one_icsk(struct inet_hashinfo *hashinfo, struct sk_buff *in_s ...@@ -272,16 +272,17 @@ int inet_diag_dump_one_icsk(struct inet_hashinfo *hashinfo, struct sk_buff *in_s
int err; int err;
struct sock *sk; struct sock *sk;
struct sk_buff *rep; struct sk_buff *rep;
struct net *net = sock_net(in_skb->sk);
err = -EINVAL; err = -EINVAL;
if (req->sdiag_family == AF_INET) { 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_dport, req->id.idiag_src[0],
req->id.idiag_sport, req->id.idiag_if); req->id.idiag_sport, req->id.idiag_if);
} }
#if IS_ENABLED(CONFIG_IPV6) #if IS_ENABLED(CONFIG_IPV6)
else if (req->sdiag_family == AF_INET6) { 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, (struct in6_addr *)req->id.idiag_dst,
req->id.idiag_dport, req->id.idiag_dport,
(struct in6_addr *)req->id.idiag_src, (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 ...@@ -317,7 +318,7 @@ int inet_diag_dump_one_icsk(struct inet_hashinfo *hashinfo, struct sk_buff *in_s
nlmsg_free(rep); nlmsg_free(rep);
goto out; 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); MSG_DONTWAIT);
if (err > 0) if (err > 0)
err = 0; err = 0;
...@@ -724,6 +725,7 @@ void inet_diag_dump_icsk(struct inet_hashinfo *hashinfo, struct sk_buff *skb, ...@@ -724,6 +725,7 @@ void inet_diag_dump_icsk(struct inet_hashinfo *hashinfo, struct sk_buff *skb,
{ {
int i, num; int i, num;
int s_i, s_num; int s_i, s_num;
struct net *net = sock_net(skb->sk);
s_i = cb->args[1]; s_i = cb->args[1];
s_num = num = cb->args[2]; s_num = num = cb->args[2];
...@@ -743,6 +745,9 @@ void inet_diag_dump_icsk(struct inet_hashinfo *hashinfo, struct sk_buff *skb, ...@@ -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) { sk_nulls_for_each(sk, node, &ilb->head) {
struct inet_sock *inet = inet_sk(sk); struct inet_sock *inet = inet_sk(sk);
if (!net_eq(sock_net(sk), net))
continue;
if (num < s_num) { if (num < s_num) {
num++; num++;
continue; continue;
...@@ -813,6 +818,8 @@ void inet_diag_dump_icsk(struct inet_hashinfo *hashinfo, struct sk_buff *skb, ...@@ -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) { sk_nulls_for_each(sk, node, &head->chain) {
struct inet_sock *inet = inet_sk(sk); struct inet_sock *inet = inet_sk(sk);
if (!net_eq(sock_net(sk), net))
continue;
if (num < s_num) if (num < s_num)
goto next_normal; goto next_normal;
if (!(r->idiag_states & (1 << sk->sk_state))) 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, ...@@ -839,6 +846,8 @@ void inet_diag_dump_icsk(struct inet_hashinfo *hashinfo, struct sk_buff *skb,
inet_twsk_for_each(tw, node, inet_twsk_for_each(tw, node,
&head->twchain) { &head->twchain) {
if (!net_eq(twsk_net(tw), net))
continue;
if (num < s_num) if (num < s_num)
goto next_dying; goto next_dying;
...@@ -943,6 +952,7 @@ static int inet_diag_get_exact_compat(struct sk_buff *in_skb, ...@@ -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) static int inet_diag_rcv_msg_compat(struct sk_buff *skb, struct nlmsghdr *nlh)
{ {
int hdrlen = sizeof(struct inet_diag_req); int hdrlen = sizeof(struct inet_diag_req);
struct net *net = sock_net(skb->sk);
if (nlh->nlmsg_type >= INET_DIAG_GETSOCK_MAX || if (nlh->nlmsg_type >= INET_DIAG_GETSOCK_MAX ||
nlmsg_len(nlh) < hdrlen) nlmsg_len(nlh) < hdrlen)
...@@ -963,7 +973,7 @@ static int inet_diag_rcv_msg_compat(struct sk_buff *skb, struct nlmsghdr *nlh) ...@@ -963,7 +973,7 @@ static int inet_diag_rcv_msg_compat(struct sk_buff *skb, struct nlmsghdr *nlh)
struct netlink_dump_control c = { struct netlink_dump_control c = {
.dump = inet_diag_dump_compat, .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) ...@@ -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) static int inet_diag_handler_dump(struct sk_buff *skb, struct nlmsghdr *h)
{ {
int hdrlen = sizeof(struct inet_diag_req_v2); int hdrlen = sizeof(struct inet_diag_req_v2);
struct net *net = sock_net(skb->sk);
if (nlmsg_len(h) < hdrlen) if (nlmsg_len(h) < hdrlen)
return -EINVAL; return -EINVAL;
...@@ -991,7 +1002,7 @@ static int inet_diag_handler_dump(struct sk_buff *skb, struct nlmsghdr *h) ...@@ -991,7 +1002,7 @@ static int inet_diag_handler_dump(struct sk_buff *skb, struct nlmsghdr *h)
struct netlink_dump_control c = { struct netlink_dump_control c = {
.dump = inet_diag_dump, .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, ...@@ -34,15 +34,16 @@ static int udp_dump_one(struct udp_table *tbl, struct sk_buff *in_skb,
int err = -EINVAL; int err = -EINVAL;
struct sock *sk; struct sock *sk;
struct sk_buff *rep; struct sk_buff *rep;
struct net *net = sock_net(in_skb->sk);
if (req->sdiag_family == AF_INET) 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_src[0], req->id.idiag_sport,
req->id.idiag_dst[0], req->id.idiag_dport, req->id.idiag_dst[0], req->id.idiag_dport,
req->id.idiag_if, tbl); req->id.idiag_if, tbl);
#if IS_ENABLED(CONFIG_IPV6) #if IS_ENABLED(CONFIG_IPV6)
else if (req->sdiag_family == AF_INET6) 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, (struct in6_addr *)req->id.idiag_src,
req->id.idiag_sport, req->id.idiag_sport,
(struct in6_addr *)req->id.idiag_dst, (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, ...@@ -75,7 +76,7 @@ static int udp_dump_one(struct udp_table *tbl, struct sk_buff *in_skb,
kfree_skb(rep); kfree_skb(rep);
goto out; 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); MSG_DONTWAIT);
if (err > 0) if (err > 0)
err = 0; err = 0;
...@@ -90,6 +91,7 @@ static void udp_dump(struct udp_table *table, struct sk_buff *skb, struct netlin ...@@ -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) struct inet_diag_req_v2 *r, struct nlattr *bc)
{ {
int num, s_num, slot, s_slot; int num, s_num, slot, s_slot;
struct net *net = sock_net(skb->sk);
s_slot = cb->args[0]; s_slot = cb->args[0];
num = s_num = cb->args[1]; num = s_num = cb->args[1];
...@@ -106,6 +108,8 @@ static void udp_dump(struct udp_table *table, struct sk_buff *skb, struct netlin ...@@ -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) { sk_nulls_for_each(sk, node, &hslot->head) {
struct inet_sock *inet = inet_sk(sk); struct inet_sock *inet = inet_sk(sk);
if (!net_eq(sock_net(sk), net))
continue;
if (num < s_num) if (num < s_num)
goto next; goto next;
if (!(r->idiag_states & (1 << sk->sk_state))) 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) ...@@ -177,6 +177,7 @@ static int unix_diag_dump(struct sk_buff *skb, struct netlink_callback *cb)
{ {
struct unix_diag_req *req; struct unix_diag_req *req;
int num, s_num, slot, s_slot; int num, s_num, slot, s_slot;
struct net *net = sock_net(skb->sk);
req = nlmsg_data(cb->nlh); req = nlmsg_data(cb->nlh);
...@@ -192,6 +193,8 @@ static int unix_diag_dump(struct sk_buff *skb, struct netlink_callback *cb) ...@@ -192,6 +193,8 @@ static int unix_diag_dump(struct sk_buff *skb, struct netlink_callback *cb)
num = 0; num = 0;
sk_for_each(sk, node, &unix_socket_table[slot]) { sk_for_each(sk, node, &unix_socket_table[slot]) {
if (!net_eq(sock_net(sk), net))
continue;
if (num < s_num) if (num < s_num)
goto next; goto next;
if (!(req->udiag_states & (1 << sk->sk_state))) if (!(req->udiag_states & (1 << sk->sk_state)))
...@@ -243,6 +246,7 @@ static int unix_diag_get_exact(struct sk_buff *in_skb, ...@@ -243,6 +246,7 @@ static int unix_diag_get_exact(struct sk_buff *in_skb,
struct sock *sk; struct sock *sk;
struct sk_buff *rep; struct sk_buff *rep;
unsigned int extra_len; unsigned int extra_len;
struct net *net = sock_net(in_skb->sk);
if (req->udiag_ino == 0) if (req->udiag_ino == 0)
goto out_nosk; goto out_nosk;
...@@ -273,7 +277,7 @@ static int unix_diag_get_exact(struct sk_buff *in_skb, ...@@ -273,7 +277,7 @@ static int unix_diag_get_exact(struct sk_buff *in_skb,
goto again; 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); MSG_DONTWAIT);
if (err > 0) if (err > 0)
err = 0; err = 0;
...@@ -287,6 +291,7 @@ static int unix_diag_get_exact(struct sk_buff *in_skb, ...@@ -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) static int unix_diag_handler_dump(struct sk_buff *skb, struct nlmsghdr *h)
{ {
int hdrlen = sizeof(struct unix_diag_req); int hdrlen = sizeof(struct unix_diag_req);
struct net *net = sock_net(skb->sk);
if (nlmsg_len(h) < hdrlen) if (nlmsg_len(h) < hdrlen)
return -EINVAL; return -EINVAL;
...@@ -295,7 +300,7 @@ static int unix_diag_handler_dump(struct sk_buff *skb, struct nlmsghdr *h) ...@@ -295,7 +300,7 @@ static int unix_diag_handler_dump(struct sk_buff *skb, struct nlmsghdr *h)
struct netlink_dump_control c = { struct netlink_dump_control c = {
.dump = unix_diag_dump, .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 } else
return unix_diag_get_exact(skb, h, nlmsg_data(h)); return unix_diag_get_exact(skb, h, nlmsg_data(h));
} }
......
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