Commit 25e3f70f authored by Cong Wang's avatar Cong Wang Committed by David S. Miller

netlink: make netlink tap per netns

nlmon device is not supposed to capture netlink events from
other netns, so instead of filtering events, we can simply
make netlink tap itself per netns.

Cc: Daniel Borkmann <daniel@iogearbox.net>
Cc: Kevin Cernekee <cernekee@chromium.org>
Signed-off-by: default avatarCong Wang <xiyou.wangcong@gmail.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 9944a0f2
......@@ -65,6 +65,7 @@
#include <linux/net_namespace.h>
#include <net/net_namespace.h>
#include <net/netns/generic.h>
#include <net/sock.h>
#include <net/scm.h>
#include <net/netlink.h>
......@@ -145,8 +146,6 @@ static atomic_t nl_table_users = ATOMIC_INIT(0);
static BLOCKING_NOTIFIER_HEAD(netlink_chain);
static DEFINE_SPINLOCK(netlink_tap_lock);
static struct list_head netlink_tap_all __read_mostly;
static const struct rhashtable_params netlink_rhashtable_params;
......@@ -173,14 +172,24 @@ static struct sk_buff *netlink_to_full_skb(const struct sk_buff *skb,
return new;
}
static unsigned int netlink_tap_net_id;
struct netlink_tap_net {
struct list_head netlink_tap_all;
spinlock_t netlink_tap_lock;
};
int netlink_add_tap(struct netlink_tap *nt)
{
struct net *net = dev_net(nt->dev);
struct netlink_tap_net *nn = net_generic(net, netlink_tap_net_id);
if (unlikely(nt->dev->type != ARPHRD_NETLINK))
return -EINVAL;
spin_lock(&netlink_tap_lock);
list_add_rcu(&nt->list, &netlink_tap_all);
spin_unlock(&netlink_tap_lock);
spin_lock(&nn->netlink_tap_lock);
list_add_rcu(&nt->list, &nn->netlink_tap_all);
spin_unlock(&nn->netlink_tap_lock);
__module_get(nt->module);
......@@ -190,12 +199,14 @@ EXPORT_SYMBOL_GPL(netlink_add_tap);
static int __netlink_remove_tap(struct netlink_tap *nt)
{
struct net *net = dev_net(nt->dev);
struct netlink_tap_net *nn = net_generic(net, netlink_tap_net_id);
bool found = false;
struct netlink_tap *tmp;
spin_lock(&netlink_tap_lock);
spin_lock(&nn->netlink_tap_lock);
list_for_each_entry(tmp, &netlink_tap_all, list) {
list_for_each_entry(tmp, &nn->netlink_tap_all, list) {
if (nt == tmp) {
list_del_rcu(&nt->list);
found = true;
......@@ -205,7 +216,7 @@ static int __netlink_remove_tap(struct netlink_tap *nt)
pr_warn("__netlink_remove_tap: %p not found\n", nt);
out:
spin_unlock(&netlink_tap_lock);
spin_unlock(&nn->netlink_tap_lock);
if (found)
module_put(nt->module);
......@@ -224,6 +235,26 @@ int netlink_remove_tap(struct netlink_tap *nt)
}
EXPORT_SYMBOL_GPL(netlink_remove_tap);
static __net_init int netlink_tap_init_net(struct net *net)
{
struct netlink_tap_net *nn = net_generic(net, netlink_tap_net_id);
INIT_LIST_HEAD(&nn->netlink_tap_all);
spin_lock_init(&nn->netlink_tap_lock);
return 0;
}
static void __net_exit netlink_tap_exit_net(struct net *net)
{
}
static struct pernet_operations netlink_tap_net_ops = {
.init = netlink_tap_init_net,
.exit = netlink_tap_exit_net,
.id = &netlink_tap_net_id,
.size = sizeof(struct netlink_tap_net),
};
static bool netlink_filter_tap(const struct sk_buff *skb)
{
struct sock *sk = skb->sk;
......@@ -274,7 +305,7 @@ static int __netlink_deliver_tap_skb(struct sk_buff *skb,
return ret;
}
static void __netlink_deliver_tap(struct sk_buff *skb)
static void __netlink_deliver_tap(struct sk_buff *skb, struct netlink_tap_net *nn)
{
int ret;
struct netlink_tap *tmp;
......@@ -282,19 +313,21 @@ static void __netlink_deliver_tap(struct sk_buff *skb)
if (!netlink_filter_tap(skb))
return;
list_for_each_entry_rcu(tmp, &netlink_tap_all, list) {
list_for_each_entry_rcu(tmp, &nn->netlink_tap_all, list) {
ret = __netlink_deliver_tap_skb(skb, tmp->dev);
if (unlikely(ret))
break;
}
}
static void netlink_deliver_tap(struct sk_buff *skb)
static void netlink_deliver_tap(struct net *net, struct sk_buff *skb)
{
struct netlink_tap_net *nn = net_generic(net, netlink_tap_net_id);
rcu_read_lock();
if (unlikely(!list_empty(&netlink_tap_all)))
__netlink_deliver_tap(skb);
if (unlikely(!list_empty(&nn->netlink_tap_all)))
__netlink_deliver_tap(skb, nn);
rcu_read_unlock();
}
......@@ -303,7 +336,7 @@ static void netlink_deliver_tap_kernel(struct sock *dst, struct sock *src,
struct sk_buff *skb)
{
if (!(netlink_is_kernel(dst) && netlink_is_kernel(src)))
netlink_deliver_tap(skb);
netlink_deliver_tap(sock_net(dst), skb);
}
static void netlink_overrun(struct sock *sk)
......@@ -1213,7 +1246,7 @@ static int __netlink_sendskb(struct sock *sk, struct sk_buff *skb)
{
int len = skb->len;
netlink_deliver_tap(skb);
netlink_deliver_tap(sock_net(sk), skb);
skb_queue_tail(&sk->sk_receive_queue, skb);
sk->sk_data_ready(sk);
......@@ -2731,12 +2764,11 @@ static int __init netlink_proto_init(void)
}
}
INIT_LIST_HEAD(&netlink_tap_all);
netlink_add_usersock_entry();
sock_register(&netlink_family_ops);
register_pernet_subsys(&netlink_net_ops);
register_pernet_subsys(&netlink_tap_net_ops);
/* The netlink device handler may be needed early. */
rtnetlink_init();
out:
......
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