core-extra/kernel/core-kernel-2.6.38/patches/00-linux-2.6.38.nfnetlink.patch
2013-08-29 14:21:13 +00:00

1662 lines
47 KiB
Diff

netfilter: make nfnetlink network namespace aware
Move static variables to structures allocated per netns and accessed
with generic net pointers.
Make nfnetlink subsystem registration per netns.
Make proc filesystem entries per netns.
---
include/linux/netfilter.h | 2 +-
include/linux/netfilter/nfnetlink.h | 10 +-
include/net/netfilter/nf_queue.h | 3 +-
net/ipv4/netfilter/ip_queue.c | 6 +-
net/ipv6/netfilter/ip6_queue.c | 6 +-
net/netfilter/core.c | 55 +++++++--
net/netfilter/nf_conntrack_netlink.c | 55 ++++++---
net/netfilter/nf_log.c | 34 ++++--
net/netfilter/nf_queue.c | 47 ++++++-
net/netfilter/nfnetlink.c | 89 ++++++++++----
net/netfilter/nfnetlink_log.c | 218 ++++++++++++++++++++++------------
net/netfilter/nfnetlink_queue.c | 191 ++++++++++++++++++++----------
net/netfilter/xt_osf.c | 42 +++++--
13 files changed, 537 insertions(+), 221 deletions(-)
diff --git a/include/linux/netfilter.h b/include/linux/netfilter.h
index 1893837..f52e719 100644
--- a/include/linux/netfilter.h
+++ b/include/linux/netfilter.h
@@ -329,7 +329,7 @@ nf_nat_decode_session(struct sk_buff *skb, struct flowi *fl, u_int8_t family)
#ifdef CONFIG_PROC_FS
#include <linux/proc_fs.h>
-extern struct proc_dir_entry *proc_net_netfilter;
+extern struct proc_dir_entry *proc_net_netfilter(struct net *net);
#endif
#else /* !CONFIG_NETFILTER */
diff --git a/include/linux/netfilter/nfnetlink.h b/include/linux/netfilter/nfnetlink.h
index 361d6b5..8acaa17 100644
--- a/include/linux/netfilter/nfnetlink.h
+++ b/include/linux/netfilter/nfnetlink.h
@@ -70,8 +70,10 @@ struct nfnetlink_subsystem {
const struct nfnl_callback *cb; /* callback for individual types */
};
-extern int nfnetlink_subsys_register(const struct nfnetlink_subsystem *n);
-extern int nfnetlink_subsys_unregister(const struct nfnetlink_subsystem *n);
+extern int nfnetlink_subsys_register(struct net *net,
+ const struct nfnetlink_subsystem *n);
+extern int nfnetlink_subsys_unregister(struct net *net,
+ const struct nfnetlink_subsystem *n);
extern int nfnetlink_has_listeners(struct net *net, unsigned int group);
extern int nfnetlink_send(struct sk_buff *skb, struct net *net, u32 pid, unsigned group,
@@ -79,8 +81,8 @@ extern int nfnetlink_send(struct sk_buff *skb, struct net *net, u32 pid, unsigne
extern int nfnetlink_set_err(struct net *net, u32 pid, u32 group, int error);
extern int nfnetlink_unicast(struct sk_buff *skb, struct net *net, u_int32_t pid, int flags);
-extern void nfnl_lock(void);
-extern void nfnl_unlock(void);
+extern void nfnl_lock(struct net *net);
+extern void nfnl_unlock(struct net *net);
#define MODULE_ALIAS_NFNL_SUBSYS(subsys) \
MODULE_ALIAS("nfnetlink-subsys-" __stringify(subsys))
diff --git a/include/net/netfilter/nf_queue.h b/include/net/netfilter/nf_queue.h
index 252fd10..3e5bde8 100644
--- a/include/net/netfilter/nf_queue.h
+++ b/include/net/netfilter/nf_queue.h
@@ -19,7 +19,8 @@ struct nf_queue_entry {
/* Packet queuing */
struct nf_queue_handler {
- int (*outfn)(struct nf_queue_entry *entry,
+ int (*outfn)(struct net *net,
+ struct nf_queue_entry *entry,
unsigned int queuenum);
char *name;
};
diff --git a/net/ipv4/netfilter/ip_queue.c b/net/ipv4/netfilter/ip_queue.c
index d2c1311..bccef48 100644
--- a/net/ipv4/netfilter/ip_queue.c
+++ b/net/ipv4/netfilter/ip_queue.c
@@ -223,7 +223,8 @@ nlmsg_failure:
}
static int
-ipq_enqueue_packet(struct nf_queue_entry *entry, unsigned int queuenum)
+ipq_enqueue_packet(struct net *net, struct nf_queue_entry *entry,
+ unsigned int queuenum)
{
int status = -EINVAL;
struct sk_buff *nskb;
@@ -237,6 +238,9 @@ ipq_enqueue_packet(struct nf_queue_entry *entry, unsigned int queuenum)
spin_lock_bh(&queue_lock);
+ if (!net_eq(net, &init_net))
+ goto err_out_free_nskb;
+
if (!peer_pid)
goto err_out_free_nskb;
diff --git a/net/ipv6/netfilter/ip6_queue.c b/net/ipv6/netfilter/ip6_queue.c
index 413ab07..b227789 100644
--- a/net/ipv6/netfilter/ip6_queue.c
+++ b/net/ipv6/netfilter/ip6_queue.c
@@ -223,7 +223,8 @@ nlmsg_failure:
}
static int
-ipq_enqueue_packet(struct nf_queue_entry *entry, unsigned int queuenum)
+ipq_enqueue_packet(struct net *net, struct nf_queue_entry *entry,
+ unsigned int queuenum)
{
int status = -EINVAL;
struct sk_buff *nskb;
@@ -237,6 +238,9 @@ ipq_enqueue_packet(struct nf_queue_entry *entry, unsigned int queuenum)
spin_lock_bh(&queue_lock);
+ if (!net_eq(net, &init_net))
+ goto err_out_free_nskb;
+
if (!peer_pid)
goto err_out_free_nskb;
diff --git a/net/netfilter/core.c b/net/netfilter/core.c
index 4aa614b..157785f 100644
--- a/net/netfilter/core.c
+++ b/net/netfilter/core.c
@@ -21,6 +21,7 @@
#include <linux/mutex.h>
#include <linux/slab.h>
#include <net/net_namespace.h>
+#include <net/netns/generic.h>
#include <net/sock.h>
#include "nf_internals.h"
@@ -249,24 +250,62 @@ EXPORT_SYMBOL(nf_conntrack_destroy);
#endif /* CONFIG_NF_CONNTRACK */
#ifdef CONFIG_PROC_FS
-struct proc_dir_entry *proc_net_netfilter;
+static int netfilter_net_id __read_mostly;
+struct netns_netfilter {
+ struct proc_dir_entry *proc_net_netfilter;
+};
+
+struct proc_dir_entry *proc_net_netfilter(struct net *net)
+{
+ struct netns_netfilter *net_netfilter;
+
+ net_netfilter = net_generic(net, netfilter_net_id);
+ return net_netfilter->proc_net_netfilter;
+}
EXPORT_SYMBOL(proc_net_netfilter);
-#endif
+
+static int __net_init netfilter_net_init(struct net *net)
+{
+ struct netns_netfilter *net_netfilter =
+ net_generic(net, netfilter_net_id);
+
+ net_netfilter->proc_net_netfilter =
+ proc_net_mkdir(net, "netfilter", net->proc_net);
+ if (!net_netfilter->proc_net_netfilter) {
+ pr_err("cannot create netfilter proc entry\n");
+ return -ENOMEM;
+ }
+
+ return 0;
+}
+
+static void __net_exit netfilter_net_exit(struct net *net)
+{
+ proc_net_remove(net, "netfilter");
+}
+
+static struct pernet_operations netfilter_net_ops = {
+ .init = netfilter_net_init,
+ .exit = netfilter_net_exit,
+ .id = &netfilter_net_id,
+ .size = sizeof(struct netns_netfilter),
+};
+#endif /* CONFIG_PROC_FS */
void __init netfilter_init(void)
{
int i, h;
+
+#ifdef CONFIG_PROC_FS
+ if (register_pernet_subsys(&netfilter_net_ops))
+ panic("netfilter_init: cannot initialize per netns operations");
+#endif
+
for (i = 0; i < ARRAY_SIZE(nf_hooks); i++) {
for (h = 0; h < NF_MAX_HOOKS; h++)
INIT_LIST_HEAD(&nf_hooks[i][h]);
}
-#ifdef CONFIG_PROC_FS
- proc_net_netfilter = proc_mkdir("netfilter", init_net.proc_net);
- if (!proc_net_netfilter)
- panic("cannot create netfilter proc entry");
-#endif
-
if (netfilter_queue_init() < 0)
panic("cannot initialize nf_queue");
if (netfilter_log_init() < 0)
diff --git a/net/netfilter/nf_conntrack_netlink.c b/net/netfilter/nf_conntrack_netlink.c
index eead9db..6c22c74 100644
--- a/net/netfilter/nf_conntrack_netlink.c
+++ b/net/netfilter/nf_conntrack_netlink.c
@@ -984,20 +984,21 @@ ctnetlink_parse_nat_setup(struct nf_conn *ct,
const struct nlattr *attr)
{
typeof(nfnetlink_parse_nat_setup_hook) parse_nat_setup;
+ struct net *net = nf_ct_net(ct);
parse_nat_setup = rcu_dereference(nfnetlink_parse_nat_setup_hook);
if (!parse_nat_setup) {
#ifdef CONFIG_MODULES
rcu_read_unlock();
spin_unlock_bh(&nf_conntrack_lock);
- nfnl_unlock();
+ nfnl_unlock(net);
if (request_module("nf-nat-ipv4") < 0) {
- nfnl_lock();
+ nfnl_lock(net);
spin_lock_bh(&nf_conntrack_lock);
rcu_read_lock();
return -EOPNOTSUPP;
}
- nfnl_lock();
+ nfnl_lock(net);
spin_lock_bh(&nf_conntrack_lock);
rcu_read_lock();
if (nfnetlink_parse_nat_setup_hook)
@@ -2114,28 +2115,59 @@ MODULE_ALIAS("ip_conntrack_netlink");
MODULE_ALIAS_NFNL_SUBSYS(NFNL_SUBSYS_CTNETLINK);
MODULE_ALIAS_NFNL_SUBSYS(NFNL_SUBSYS_CTNETLINK_EXP);
-static int __init ctnetlink_init(void)
+static int __net_init ctnetlink_net_init(struct net *net)
{
int ret;
pr_info("ctnetlink v%s: registering with nfnetlink.\n", version);
- ret = nfnetlink_subsys_register(&ctnl_subsys);
+ ret = nfnetlink_subsys_register(net, &ctnl_subsys);
if (ret < 0) {
pr_err("ctnetlink_init: cannot register with nfnetlink.\n");
goto err_out;
}
- ret = nfnetlink_subsys_register(&ctnl_exp_subsys);
+ ret = nfnetlink_subsys_register(net, &ctnl_exp_subsys);
if (ret < 0) {
pr_err("ctnetlink_init: cannot register exp with nfnetlink.\n");
goto err_unreg_subsys;
}
+ return 0;
+
+err_unreg_subsys:
+ nfnetlink_subsys_unregister(net, &ctnl_subsys);
+err_out:
+ return ret;
+}
+
+static void __net_exit ctnetlink_net_exit(struct net *net)
+{
+ pr_info("ctnetlink: unregistering from nfnetlink.\n");
+
+ nfnetlink_subsys_unregister(net, &ctnl_exp_subsys);
+ nfnetlink_subsys_unregister(net, &ctnl_subsys);
+}
+
+static struct pernet_operations ctnetlink_net_ops = {
+ .init = ctnetlink_net_init,
+ .exit = ctnetlink_net_exit,
+};
+
+static int __init ctnetlink_init(void)
+{
+ int ret;
+
+ ret = register_pernet_subsys(&ctnetlink_net_ops);
+ if (ret) {
+ pr_err("ctnetlink_init: cannot initialize ctnetlink.\n");
+ goto err_out;
+ }
+
#ifdef CONFIG_NF_CONNTRACK_EVENTS
ret = nf_conntrack_register_notifier(&ctnl_notifier);
if (ret < 0) {
pr_err("ctnetlink_init: cannot register notifier.\n");
- goto err_unreg_exp_subsys;
+ goto err_out;
}
ret = nf_ct_expect_register_notifier(&ctnl_notifier_exp);
@@ -2150,27 +2182,20 @@ static int __init ctnetlink_init(void)
#ifdef CONFIG_NF_CONNTRACK_EVENTS
err_unreg_notifier:
nf_conntrack_unregister_notifier(&ctnl_notifier);
-err_unreg_exp_subsys:
- nfnetlink_subsys_unregister(&ctnl_exp_subsys);
#endif
-err_unreg_subsys:
- nfnetlink_subsys_unregister(&ctnl_subsys);
err_out:
return ret;
}
static void __exit ctnetlink_exit(void)
{
- pr_info("ctnetlink: unregistering from nfnetlink.\n");
-
nf_ct_remove_userspace_expectations();
#ifdef CONFIG_NF_CONNTRACK_EVENTS
nf_ct_expect_unregister_notifier(&ctnl_notifier_exp);
nf_conntrack_unregister_notifier(&ctnl_notifier);
#endif
- nfnetlink_subsys_unregister(&ctnl_exp_subsys);
- nfnetlink_subsys_unregister(&ctnl_subsys);
+ unregister_pernet_subsys(&ctnetlink_net_ops);
}
module_init(ctnetlink_init);
diff --git a/net/netfilter/nf_log.c b/net/netfilter/nf_log.c
index 9181699..6c193b1 100644
--- a/net/netfilter/nf_log.c
+++ b/net/netfilter/nf_log.c
@@ -209,7 +209,24 @@ static const struct file_operations nflog_file_ops = {
.release = seq_release,
};
+static int __net_init netfilter_log_net_init(struct net *net)
+{
+ if (!proc_create("nf_log", S_IRUGO,
+ proc_net_netfilter(net), &nflog_file_ops))
+ return -ENOMEM;
+
+ return 0;
+}
+static void __net_exit netfilter_log_net_exit(struct net *net)
+{
+ remove_proc_entry("nf_log", proc_net_netfilter(net));
+}
+
+static struct pernet_operations netfilter_log_net_ops = {
+ .init = netfilter_log_net_init,
+ .exit = netfilter_log_net_exit,
+};
#endif /* PROC_FS */
#ifdef CONFIG_SYSCTL
@@ -288,26 +305,27 @@ static __init int netfilter_log_sysctl_init(void)
return 0;
}
-#else
-static __init int netfilter_log_sysctl_init(void)
-{
- return 0;
-}
#endif /* CONFIG_SYSCTL */
int __init netfilter_log_init(void)
{
int i, r;
+
#ifdef CONFIG_PROC_FS
- if (!proc_create("nf_log", S_IRUGO,
- proc_net_netfilter, &nflog_file_ops))
- return -1;
+ r = register_pernet_subsys(&netfilter_log_net_ops);
+ if (r) {
+ pr_err("netfilter_log_init: "
+ "cannot initialize per netns operations\n");
+ return r;
+ }
#endif
+#ifdef CONFIG_SYSCTL
/* Errors will trigger panic, unroll on error is unnecessary. */
r = netfilter_log_sysctl_init();
if (r < 0)
return r;
+#endif
for (i = NFPROTO_UNSPEC; i < NFPROTO_NUMPROTO; i++)
INIT_LIST_HEAD(&(nf_loggers_l[i]));
diff --git a/net/netfilter/nf_queue.c b/net/netfilter/nf_queue.c
index 74aebed..c9fc05f 100644
--- a/net/netfilter/nf_queue.c
+++ b/net/netfilter/nf_queue.c
@@ -123,6 +123,7 @@ static int __nf_queue(struct sk_buff *skb,
#endif
const struct nf_afinfo *afinfo;
const struct nf_queue_handler *qh;
+ struct net *net = NULL;
/* QUEUE == DROP if noone is waiting, to be safe. */
rcu_read_lock();
@@ -156,6 +157,17 @@ static int __nf_queue(struct sk_buff *skb,
return 0;
}
+ if (indev)
+ net = dev_net(indev);
+ else if (outdev)
+ net = dev_net(outdev);
+ else if (skb->sk)
+ net = sock_net(skb->sk);
+ else {
+ pr_warn("%s: no net for skb: %p\n", __func__, skb);
+ goto err_unlock;
+ }
+
/* Bump dev refs so they don't vanish while packet is out */
if (indev)
dev_hold(indev);
@@ -173,7 +185,7 @@ static int __nf_queue(struct sk_buff *skb,
#endif
skb_dst_force(skb);
afinfo->saveroute(skb, entry);
- status = qh->outfn(entry, queuenum);
+ status = qh->outfn(net, entry, queuenum);
rcu_read_unlock();
@@ -344,16 +356,37 @@ static const struct file_operations nfqueue_file_ops = {
.llseek = seq_lseek,
.release = seq_release,
};
-#endif /* PROC_FS */
-
-int __init netfilter_queue_init(void)
+static int __net_init netfilter_queue_net_init(struct net *net)
{
-#ifdef CONFIG_PROC_FS
if (!proc_create("nf_queue", S_IRUGO,
- proc_net_netfilter, &nfqueue_file_ops))
+ proc_net_netfilter(net), &nfqueue_file_ops))
return -1;
-#endif
+
return 0;
}
+static void __net_exit netfilter_queue_net_exit(struct net *net)
+{
+ remove_proc_entry("nf_queue", proc_net_netfilter(net));
+}
+
+static struct pernet_operations netfilter_queue_net_ops = {
+ .init = netfilter_queue_net_init,
+ .exit = netfilter_queue_net_exit,
+};
+#endif /* PROC_FS */
+
+int __init netfilter_queue_init(void)
+{
+ int err = 0;
+
+#ifdef CONFIG_PROC_FS
+ err = register_pernet_subsys(&netfilter_queue_net_ops);
+ if (err)
+ pr_err("netfilter_queue_init: "
+ "cannot initialize per netns operations\n");
+#endif
+
+ return err;
+}
diff --git a/net/netfilter/nfnetlink.c b/net/netfilter/nfnetlink.c
index b4a4532..235da38 100644
--- a/net/netfilter/nfnetlink.c
+++ b/net/netfilter/nfnetlink.c
@@ -24,6 +24,8 @@
#include <linux/skbuff.h>
#include <asm/uaccess.h>
#include <asm/system.h>
+#include <net/net_namespace.h>
+#include <net/netns/generic.h>
#include <net/sock.h>
#include <net/netlink.h>
#include <linux/init.h>
@@ -35,55 +37,79 @@ MODULE_LICENSE("GPL");
MODULE_AUTHOR("Harald Welte <laforge@netfilter.org>");
MODULE_ALIAS_NET_PF_PROTO(PF_NETLINK, NETLINK_NETFILTER);
+static int nfnl_net_id __read_mostly;
+struct netns_nfnl {
+ const struct nfnetlink_subsystem *subsys_table[NFNL_SUBSYS_COUNT];
+ struct mutex nfnl_mutex;
+};
+
static char __initdata nfversion[] = "0.30";
-static const struct nfnetlink_subsystem *subsys_table[NFNL_SUBSYS_COUNT];
-static DEFINE_MUTEX(nfnl_mutex);
+static void __nfnl_lock(struct netns_nfnl *net_nfnl)
+{
+ mutex_lock(&net_nfnl->nfnl_mutex);
+}
-void nfnl_lock(void)
+void nfnl_lock(struct net *net)
{
- mutex_lock(&nfnl_mutex);
+ struct netns_nfnl *net_nfnl = net_generic(net, nfnl_net_id);
+
+ __nfnl_lock(net_nfnl);
}
EXPORT_SYMBOL_GPL(nfnl_lock);
-void nfnl_unlock(void)
+static void __nfnl_unlock(struct netns_nfnl *net_nfnl)
+{
+ mutex_unlock(&net_nfnl->nfnl_mutex);
+}
+
+void nfnl_unlock(struct net *net)
{
- mutex_unlock(&nfnl_mutex);
+ struct netns_nfnl *net_nfnl = net_generic(net, nfnl_net_id);
+
+ __nfnl_unlock(net_nfnl);
}
EXPORT_SYMBOL_GPL(nfnl_unlock);
-int nfnetlink_subsys_register(const struct nfnetlink_subsystem *n)
+int nfnetlink_subsys_register(struct net *net,
+ const struct nfnetlink_subsystem *n)
{
- nfnl_lock();
- if (subsys_table[n->subsys_id]) {
- nfnl_unlock();
+ struct netns_nfnl *net_nfnl = net_generic(net, nfnl_net_id);
+
+ __nfnl_lock(net_nfnl);
+ if (net_nfnl->subsys_table[n->subsys_id]) {
+ __nfnl_unlock(net_nfnl);
return -EBUSY;
}
- subsys_table[n->subsys_id] = n;
- nfnl_unlock();
+ net_nfnl->subsys_table[n->subsys_id] = n;
+ __nfnl_unlock(net_nfnl);
return 0;
}
EXPORT_SYMBOL_GPL(nfnetlink_subsys_register);
-int nfnetlink_subsys_unregister(const struct nfnetlink_subsystem *n)
+int nfnetlink_subsys_unregister(struct net *net,
+ const struct nfnetlink_subsystem *n)
{
- nfnl_lock();
- subsys_table[n->subsys_id] = NULL;
- nfnl_unlock();
+ struct netns_nfnl *net_nfnl = net_generic(net, nfnl_net_id);
+
+ __nfnl_lock(net_nfnl);
+ net_nfnl->subsys_table[n->subsys_id] = NULL;
+ __nfnl_unlock(net_nfnl);
return 0;
}
EXPORT_SYMBOL_GPL(nfnetlink_subsys_unregister);
-static inline const struct nfnetlink_subsystem *nfnetlink_get_subsys(u_int16_t type)
+static inline const struct nfnetlink_subsystem *nfnetlink_get_subsys(struct netns_nfnl *net_nfnl,
+ u_int16_t type)
{
u_int8_t subsys_id = NFNL_SUBSYS_ID(type);
if (subsys_id >= NFNL_SUBSYS_COUNT)
return NULL;
- return subsys_table[subsys_id];
+ return net_nfnl->subsys_table[subsys_id];
}
static inline const struct nfnl_callback *
@@ -129,6 +155,7 @@ static int nfnetlink_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh)
const struct nfnl_callback *nc;
const struct nfnetlink_subsystem *ss;
int type, err;
+ struct netns_nfnl *net_nfnl;
if (security_netlink_recv(skb, CAP_NET_ADMIN))
return -EPERM;
@@ -137,15 +164,17 @@ static int nfnetlink_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh)
if (nlh->nlmsg_len < NLMSG_LENGTH(sizeof(struct nfgenmsg)))
return 0;
+ net_nfnl = net_generic(net, nfnl_net_id);
+
type = nlh->nlmsg_type;
replay:
- ss = nfnetlink_get_subsys(type);
+ ss = nfnetlink_get_subsys(net_nfnl, type);
if (!ss) {
#ifdef CONFIG_MODULES
- nfnl_unlock();
+ nfnl_unlock(net);
request_module("nfnetlink-subsys-%d", NFNL_SUBSYS_ID(type));
- nfnl_lock();
- ss = nfnetlink_get_subsys(type);
+ nfnl_lock(net);
+ ss = nfnetlink_get_subsys(net_nfnl, type);
if (!ss)
#endif
return -EINVAL;
@@ -176,14 +205,18 @@ replay:
static void nfnetlink_rcv(struct sk_buff *skb)
{
- nfnl_lock();
+ struct net *net = sock_net(skb->sk);
+
+ nfnl_lock(net);
netlink_rcv_skb(skb, &nfnetlink_rcv_msg);
- nfnl_unlock();
+ nfnl_unlock(net);
}
static int __net_init nfnetlink_net_init(struct net *net)
{
struct sock *nfnl;
+ int i;
+ struct netns_nfnl *net_nfnl;
nfnl = netlink_kernel_create(net, NETLINK_NETFILTER, NFNLGRP_MAX,
nfnetlink_rcv, NULL, THIS_MODULE);
@@ -191,6 +224,12 @@ static int __net_init nfnetlink_net_init(struct net *net)
return -ENOMEM;
net->nfnl_stash = nfnl;
rcu_assign_pointer(net->nfnl, nfnl);
+
+ net_nfnl = net_generic(net, nfnl_net_id);
+ for (i = 0; i < NFNL_SUBSYS_COUNT; i++)
+ net_nfnl->subsys_table[i] = NULL;
+ mutex_init(&net_nfnl->nfnl_mutex);
+
return 0;
}
@@ -208,6 +247,8 @@ static void __net_exit nfnetlink_net_exit_batch(struct list_head *net_exit_list)
static struct pernet_operations nfnetlink_net_ops = {
.init = nfnetlink_net_init,
.exit_batch = nfnetlink_net_exit_batch,
+ .id = &nfnl_net_id,
+ .size = sizeof(struct netns_nfnl),
};
static int __init nfnetlink_init(void)
diff --git a/net/netfilter/nfnetlink_log.c b/net/netfilter/nfnetlink_log.c
index 6a1572b..44508bb 100644
--- a/net/netfilter/nfnetlink_log.c
+++ b/net/netfilter/nfnetlink_log.c
@@ -32,6 +32,8 @@
#include <net/sock.h>
#include <net/netfilter/nf_log.h>
#include <net/netfilter/nfnetlink_log.h>
+#include <net/net_namespace.h>
+#include <net/netns/generic.h>
#include <asm/atomic.h>
@@ -67,14 +69,17 @@ struct nfulnl_instance {
u_int16_t flags;
u_int8_t copy_mode;
struct rcu_head rcu;
-};
-static DEFINE_SPINLOCK(instances_lock);
-static atomic_t global_seq;
+ struct net *net;
+};
#define INSTANCE_BUCKETS 16
-static struct hlist_head instance_table[INSTANCE_BUCKETS];
-static unsigned int hash_init;
+static int nfulnl_net_id __read_mostly;
+struct netns_nfulnl {
+ spinlock_t instances_lock;
+ atomic_t global_seq;
+ struct hlist_head instance_table[INSTANCE_BUCKETS];
+};
static inline u_int8_t instance_hashfn(u_int16_t group_num)
{
@@ -82,13 +87,13 @@ static inline u_int8_t instance_hashfn(u_int16_t group_num)
}
static struct nfulnl_instance *
-__instance_lookup(u_int16_t group_num)
+__instance_lookup(struct netns_nfulnl *net_nfulnl, u_int16_t group_num)
{
struct hlist_head *head;
struct hlist_node *pos;
struct nfulnl_instance *inst;
- head = &instance_table[instance_hashfn(group_num)];
+ head = &net_nfulnl->instance_table[instance_hashfn(group_num)];
hlist_for_each_entry_rcu(inst, pos, head, hlist) {
if (inst->group_num == group_num)
return inst;
@@ -103,12 +108,12 @@ instance_get(struct nfulnl_instance *inst)
}
static struct nfulnl_instance *
-instance_lookup_get(u_int16_t group_num)
+instance_lookup_get(struct netns_nfulnl *net_nfulnl, u_int16_t group_num)
{
struct nfulnl_instance *inst;
rcu_read_lock_bh();
- inst = __instance_lookup(group_num);
+ inst = __instance_lookup(net_nfulnl, group_num);
if (inst && !atomic_inc_not_zero(&inst->use))
inst = NULL;
rcu_read_unlock_bh();
@@ -118,7 +123,10 @@ instance_lookup_get(u_int16_t group_num)
static void nfulnl_instance_free_rcu(struct rcu_head *head)
{
- kfree(container_of(head, struct nfulnl_instance, rcu));
+ struct nfulnl_instance *inst =
+ container_of(head, struct nfulnl_instance, rcu);
+ put_net(inst->net);
+ kfree(inst);
module_put(THIS_MODULE);
}
@@ -132,13 +140,14 @@ instance_put(struct nfulnl_instance *inst)
static void nfulnl_timer(unsigned long data);
static struct nfulnl_instance *
-instance_create(u_int16_t group_num, int pid)
+instance_create(struct net *net, struct netns_nfulnl *net_nfulnl,
+ u_int16_t group_num, int pid)
{
struct nfulnl_instance *inst;
int err;
- spin_lock_bh(&instances_lock);
- if (__instance_lookup(group_num)) {
+ spin_lock_bh(&net_nfulnl->instances_lock);
+ if (__instance_lookup(net_nfulnl, group_num)) {
err = -EEXIST;
goto out_unlock;
}
@@ -171,15 +180,17 @@ instance_create(u_int16_t group_num, int pid)
inst->copy_mode = NFULNL_COPY_PACKET;
inst->copy_range = NFULNL_COPY_RANGE_MAX;
+ inst->net = get_net(net);
+
hlist_add_head_rcu(&inst->hlist,
- &instance_table[instance_hashfn(group_num)]);
+ &net_nfulnl->instance_table[instance_hashfn(group_num)]);
- spin_unlock_bh(&instances_lock);
+ spin_unlock_bh(&net_nfulnl->instances_lock);
return inst;
out_unlock:
- spin_unlock_bh(&instances_lock);
+ spin_unlock_bh(&net_nfulnl->instances_lock);
return ERR_PTR(err);
}
@@ -208,11 +219,11 @@ __instance_destroy(struct nfulnl_instance *inst)
}
static inline void
-instance_destroy(struct nfulnl_instance *inst)
+instance_destroy(struct netns_nfulnl *net_nfulnl, struct nfulnl_instance *inst)
{
- spin_lock_bh(&instances_lock);
+ spin_lock_bh(&net_nfulnl->instances_lock);
__instance_destroy(inst);
- spin_unlock_bh(&instances_lock);
+ spin_unlock_bh(&net_nfulnl->instances_lock);
}
static int
@@ -334,7 +345,7 @@ __nfulnl_send(struct nfulnl_instance *inst)
NLMSG_DONE,
sizeof(struct nfgenmsg));
- status = nfnetlink_unicast(inst->skb, &init_net, inst->peer_pid,
+ status = nfnetlink_unicast(inst->skb, inst->net, inst->peer_pid,
MSG_DONTWAIT);
inst->qlen = 0;
@@ -369,7 +380,8 @@ nfulnl_timer(unsigned long data)
/* This is an inline function, we don't really care about a long
* list of arguments */
static inline int
-__build_packet_message(struct nfulnl_instance *inst,
+__build_packet_message(struct netns_nfulnl *net_nfulnl,
+ struct nfulnl_instance *inst,
const struct sk_buff *skb,
unsigned int data_len,
u_int8_t pf,
@@ -507,7 +519,7 @@ __build_packet_message(struct nfulnl_instance *inst,
/* global sequence number */
if (inst->flags & NFULNL_CFG_F_SEQ_GLOBAL)
NLA_PUT_BE32(inst->skb, NFULA_SEQ_GLOBAL,
- htonl(atomic_inc_return(&global_seq)));
+ htonl(atomic_inc_return(&net_nfulnl->global_seq)));
if (data_len) {
struct nlattr *nla;
@@ -563,13 +575,16 @@ nfulnl_log_packet(u_int8_t pf,
const struct nf_loginfo *li;
unsigned int qthreshold;
unsigned int plen;
+ struct netns_nfulnl *net_nfulnl;
+
+ net_nfulnl = net_generic(dev_net(in), nfulnl_net_id);
if (li_user && li_user->type == NF_LOG_TYPE_ULOG)
li = li_user;
else
li = &default_loginfo;
- inst = instance_lookup_get(li->u.ulog.group);
+ inst = instance_lookup_get(net_nfulnl, li->u.ulog.group);
if (!inst)
return;
@@ -651,7 +666,7 @@ nfulnl_log_packet(u_int8_t pf,
inst->qlen++;
- __build_packet_message(inst, skb, data_len, pf,
+ __build_packet_message(net_nfulnl, inst, skb, data_len, pf,
hooknum, in, out, li, prefix, plen);
if (inst->qlen >= qthreshold)
@@ -680,24 +695,26 @@ nfulnl_rcv_nl_event(struct notifier_block *this,
unsigned long event, void *ptr)
{
struct netlink_notify *n = ptr;
+ struct netns_nfulnl *net_nfulnl;
+ net_nfulnl = net_generic(n->net, nfulnl_net_id);
if (event == NETLINK_URELEASE && n->protocol == NETLINK_NETFILTER) {
int i;
/* destroy all instances for this pid */
- spin_lock_bh(&instances_lock);
+ spin_lock_bh(&net_nfulnl->instances_lock);
for (i = 0; i < INSTANCE_BUCKETS; i++) {
struct hlist_node *tmp, *t2;
struct nfulnl_instance *inst;
- struct hlist_head *head = &instance_table[i];
+ struct hlist_head *head =
+ &net_nfulnl->instance_table[i];
hlist_for_each_entry_safe(inst, tmp, t2, head, hlist) {
- if ((net_eq(n->net, &init_net)) &&
- (n->pid == inst->peer_pid))
+ if (n->pid == inst->peer_pid)
__instance_destroy(inst);
}
}
- spin_unlock_bh(&instances_lock);
+ spin_unlock_bh(&net_nfulnl->instances_lock);
}
return NOTIFY_DONE;
}
@@ -739,6 +756,10 @@ nfulnl_recv_config(struct sock *ctnl, struct sk_buff *skb,
struct nfulnl_instance *inst;
struct nfulnl_msg_config_cmd *cmd = NULL;
int ret = 0;
+ struct net *net = sock_net(ctnl);
+ struct netns_nfulnl *net_nfulnl;
+
+ net_nfulnl = net_generic(net, nfulnl_net_id);
if (nfula[NFULA_CFG_CMD]) {
u_int8_t pf = nfmsg->nfgen_family;
@@ -754,7 +775,7 @@ nfulnl_recv_config(struct sock *ctnl, struct sk_buff *skb,
}
}
- inst = instance_lookup_get(group_num);
+ inst = instance_lookup_get(net_nfulnl, group_num);
if (inst && inst->peer_pid != NETLINK_CB(skb).pid) {
ret = -EPERM;
goto out_put;
@@ -768,7 +789,7 @@ nfulnl_recv_config(struct sock *ctnl, struct sk_buff *skb,
goto out_put;
}
- inst = instance_create(group_num,
+ inst = instance_create(net, net_nfulnl, group_num,
NETLINK_CB(skb).pid);
if (IS_ERR(inst)) {
ret = PTR_ERR(inst);
@@ -781,7 +802,7 @@ nfulnl_recv_config(struct sock *ctnl, struct sk_buff *skb,
goto out;
}
- instance_destroy(inst);
+ instance_destroy(net_nfulnl, inst);
goto out_put;
default:
ret = -ENOTSUPP;
@@ -867,37 +888,45 @@ struct iter_state {
unsigned int bucket;
};
-static struct hlist_node *get_first(struct iter_state *st)
+static struct hlist_node *get_first(struct seq_file *seq)
{
+ struct iter_state *st = seq->private;
+ struct netns_nfulnl *net_nfulnl;
+
if (!st)
return NULL;
+ net_nfulnl = net_generic(seq_file_net(seq), nfulnl_net_id);
for (st->bucket = 0; st->bucket < INSTANCE_BUCKETS; st->bucket++) {
- if (!hlist_empty(&instance_table[st->bucket]))
- return rcu_dereference_bh(instance_table[st->bucket].first);
+ if (!hlist_empty(&net_nfulnl->instance_table[st->bucket]))
+ return rcu_dereference_bh(net_nfulnl->instance_table[st->bucket].first);
}
return NULL;
}
-static struct hlist_node *get_next(struct iter_state *st, struct hlist_node *h)
+static struct hlist_node *get_next(struct seq_file *seq, struct hlist_node *h)
{
+ struct iter_state *st = seq->private;
+ struct netns_nfulnl *net_nfulnl;
+
+ net_nfulnl = net_generic(seq_file_net(seq), nfulnl_net_id);
h = rcu_dereference_bh(h->next);
while (!h) {
if (++st->bucket >= INSTANCE_BUCKETS)
return NULL;
- h = rcu_dereference_bh(instance_table[st->bucket].first);
+ h = rcu_dereference_bh(net_nfulnl->instance_table[st->bucket].first);
}
return h;
}
-static struct hlist_node *get_idx(struct iter_state *st, loff_t pos)
+static struct hlist_node *get_idx(struct seq_file *seq, loff_t pos)
{
struct hlist_node *head;
- head = get_first(st);
+ head = get_first(seq);
if (head)
- while (pos && (head = get_next(st, head)))
+ while (pos && (head = get_next(seq, head)))
pos--;
return pos ? NULL : head;
}
@@ -906,13 +935,13 @@ static void *seq_start(struct seq_file *seq, loff_t *pos)
__acquires(rcu_bh)
{
rcu_read_lock_bh();
- return get_idx(seq->private, *pos);
+ return get_idx(seq, *pos);
}
static void *seq_next(struct seq_file *s, void *v, loff_t *pos)
{
(*pos)++;
- return get_next(s->private, v);
+ return get_next(s, v);
}
static void seq_stop(struct seq_file *s, void *v)
@@ -941,8 +970,8 @@ static const struct seq_operations nful_seq_ops = {
static int nful_open(struct inode *inode, struct file *file)
{
- return seq_open_private(file, &nful_seq_ops,
- sizeof(struct iter_state));
+ return seq_open_net(inode, file, &nful_seq_ops,
+ sizeof(struct iter_state));
}
static const struct file_operations nful_file_ops = {
@@ -950,62 +979,103 @@ static const struct file_operations nful_file_ops = {
.open = nful_open,
.read = seq_read,
.llseek = seq_lseek,
- .release = seq_release_private,
+ .release = seq_release_net,
};
#endif /* PROC_FS */
-static int __init nfnetlink_log_init(void)
+static int __net_init nfnetlink_log_net_init(struct net *net)
{
- int i, status = -ENOMEM;
+ int err, i;
+ struct netns_nfulnl *net_nfulnl = net_generic(net, nfulnl_net_id);
+ spin_lock_init(&net_nfulnl->instances_lock);
+ atomic_set(&net_nfulnl->global_seq, 0);
for (i = 0; i < INSTANCE_BUCKETS; i++)
- INIT_HLIST_HEAD(&instance_table[i]);
+ INIT_HLIST_HEAD(&net_nfulnl->instance_table[i]);
- /* it's not really all that important to have a random value, so
- * we can do this from the init function, even if there hasn't
- * been that much entropy yet */
- get_random_bytes(&hash_init, sizeof(hash_init));
+#ifdef CONFIG_PROC_FS
+ if (proc_net_fops_create(net, "nfnetlink_log", 0440,
+ &nful_file_ops))
+ return -ENOMEM;
+#endif
- netlink_register_notifier(&nfulnl_rtnl_notifier);
- status = nfnetlink_subsys_register(&nfulnl_subsys);
- if (status < 0) {
+ err = nfnetlink_subsys_register(net, &nfulnl_subsys);
+ if (err < 0) {
printk(KERN_ERR "log: failed to create netlink socket\n");
- goto cleanup_netlink_notifier;
+ goto cleanup_proc;
}
- status = nf_log_register(NFPROTO_UNSPEC, &nfulnl_logger);
- if (status < 0) {
- printk(KERN_ERR "log: failed to register logger\n");
- goto cleanup_subsys;
- }
+ return 0;
+cleanup_proc:
#ifdef CONFIG_PROC_FS
- if (!proc_create("nfnetlink_log", 0440,
- proc_net_netfilter, &nful_file_ops))
- goto cleanup_logger;
+ proc_net_remove(net, "nfnetlink_log");
#endif
- return status;
+ return err;
+}
+
+static void __net_exit nfnetlink_log_net_exit(struct net *net)
+{
+ int i;
+ struct netns_nfulnl *net_nfulnl;
+ nfnetlink_subsys_unregister(net, &nfulnl_subsys);
#ifdef CONFIG_PROC_FS
-cleanup_logger:
- nf_log_unregister(&nfulnl_logger);
+ proc_net_remove(net, "nfnetlink_log");
#endif
-cleanup_subsys:
- nfnetlink_subsys_unregister(&nfulnl_subsys);
+
+ net_nfulnl = net_generic(net, nfulnl_net_id);
+ spin_lock_bh(&net_nfulnl->instances_lock);
+ for (i = 0; i < INSTANCE_BUCKETS; i++) {
+ struct hlist_node *tmp, *t2;
+ struct nfulnl_instance *inst;
+ struct hlist_head *head = &net_nfulnl->instance_table[i];
+
+ hlist_for_each_entry_safe(inst, tmp, t2, head, hlist)
+ __instance_destroy(inst);
+ }
+ spin_unlock_bh(&net_nfulnl->instances_lock);
+}
+
+static struct pernet_operations nfnetlink_log_net_ops = {
+ .init = nfnetlink_log_net_init,
+ .exit = nfnetlink_log_net_exit,
+ .id = &nfulnl_net_id,
+ .size = sizeof(struct netns_nfulnl),
+};
+
+static int __init nfnetlink_log_init(void)
+{
+ int err;
+
+ err = register_pernet_subsys(&nfnetlink_log_net_ops);
+ if (err) {
+ pr_err("nfnetlink_log_init: "
+ "cannot initialize per netns operations\n");
+ return err;
+ }
+
+ netlink_register_notifier(&nfulnl_rtnl_notifier);
+
+ err = nf_log_register(NFPROTO_UNSPEC, &nfulnl_logger);
+ if (err < 0) {
+ pr_err("log: failed to register logger\n");
+ goto cleanup_netlink_notifier;
+ }
+
+ return err;
+
cleanup_netlink_notifier:
netlink_unregister_notifier(&nfulnl_rtnl_notifier);
- return status;
+ return err;
}
static void __exit nfnetlink_log_fini(void)
{
nf_log_unregister(&nfulnl_logger);
-#ifdef CONFIG_PROC_FS
- remove_proc_entry("nfnetlink_log", proc_net_netfilter);
-#endif
- nfnetlink_subsys_unregister(&nfulnl_subsys);
netlink_unregister_notifier(&nfulnl_rtnl_notifier);
+ unregister_pernet_subsys(&nfnetlink_log_net_ops);
}
MODULE_DESCRIPTION("netfilter userspace logging");
diff --git a/net/netfilter/nfnetlink_queue.c b/net/netfilter/nfnetlink_queue.c
index 68e67d1..3fa78a7 100644
--- a/net/netfilter/nfnetlink_queue.c
+++ b/net/netfilter/nfnetlink_queue.c
@@ -30,6 +30,8 @@
#include <linux/list.h>
#include <net/sock.h>
#include <net/netfilter/nf_queue.h>
+#include <net/net_namespace.h>
+#include <net/netns/generic.h>
#include <asm/atomic.h>
@@ -62,12 +64,14 @@ struct nfqnl_instance {
struct list_head queue_list; /* packets in queue */
};
-typedef int (*nfqnl_cmpfn)(struct nf_queue_entry *, unsigned long);
-
-static DEFINE_SPINLOCK(instances_lock);
-
#define INSTANCE_BUCKETS 16
-static struct hlist_head instance_table[INSTANCE_BUCKETS] __read_mostly;
+static int nfqnl_net_id __read_mostly;
+struct netns_nfqnl {
+ spinlock_t instances_lock;
+ struct hlist_head instance_table[INSTANCE_BUCKETS];
+};
+
+typedef int (*nfqnl_cmpfn)(struct nf_queue_entry *, unsigned long);
static inline u_int8_t instance_hashfn(u_int16_t queue_num)
{
@@ -75,13 +79,14 @@ static inline u_int8_t instance_hashfn(u_int16_t queue_num)
}
static struct nfqnl_instance *
-instance_lookup(u_int16_t queue_num)
+instance_lookup(struct net *net, u_int16_t queue_num)
{
struct hlist_head *head;
struct hlist_node *pos;
struct nfqnl_instance *inst;
+ struct netns_nfqnl *net_nfqnl = net_generic(net, nfqnl_net_id);
- head = &instance_table[instance_hashfn(queue_num)];
+ head = &net_nfqnl->instance_table[instance_hashfn(queue_num)];
hlist_for_each_entry_rcu(inst, pos, head, hlist) {
if (inst->queue_num == queue_num)
return inst;
@@ -90,14 +95,15 @@ instance_lookup(u_int16_t queue_num)
}
static struct nfqnl_instance *
-instance_create(u_int16_t queue_num, int pid)
+instance_create(struct net *net, u_int16_t queue_num, int pid)
{
+ struct netns_nfqnl *net_nfqnl = net_generic(net, nfqnl_net_id);
struct nfqnl_instance *inst;
unsigned int h;
int err;
- spin_lock(&instances_lock);
- if (instance_lookup(queue_num)) {
+ spin_lock(&net_nfqnl->instances_lock);
+ if (instance_lookup(net, queue_num)) {
err = -EEXIST;
goto out_unlock;
}
@@ -122,16 +128,16 @@ instance_create(u_int16_t queue_num, int pid)
}
h = instance_hashfn(queue_num);
- hlist_add_head_rcu(&inst->hlist, &instance_table[h]);
+ hlist_add_head_rcu(&inst->hlist, &net_nfqnl->instance_table[h]);
- spin_unlock(&instances_lock);
+ spin_unlock(&net_nfqnl->instances_lock);
return inst;
out_free:
kfree(inst);
out_unlock:
- spin_unlock(&instances_lock);
+ spin_unlock(&net_nfqnl->instances_lock);
return ERR_PTR(err);
}
@@ -157,11 +163,13 @@ __instance_destroy(struct nfqnl_instance *inst)
}
static void
-instance_destroy(struct nfqnl_instance *inst)
+instance_destroy(struct net *net, struct nfqnl_instance *inst)
{
- spin_lock(&instances_lock);
+ struct netns_nfqnl *net_nfqnl = net_generic(net, nfqnl_net_id);
+
+ spin_lock(&net_nfqnl->instances_lock);
__instance_destroy(inst);
- spin_unlock(&instances_lock);
+ spin_unlock(&net_nfqnl->instances_lock);
}
static inline void
@@ -383,14 +391,15 @@ nla_put_failure:
}
static int
-nfqnl_enqueue_packet(struct nf_queue_entry *entry, unsigned int queuenum)
+nfqnl_enqueue_packet(struct net *net, struct nf_queue_entry *entry,
+ unsigned int queuenum)
{
struct sk_buff *nskb;
struct nfqnl_instance *queue;
int err;
/* rcu_read_lock()ed by nf_hook_slow() */
- queue = instance_lookup(queuenum);
+ queue = instance_lookup(net, queuenum);
if (!queue)
goto err_out;
@@ -416,7 +425,7 @@ nfqnl_enqueue_packet(struct nf_queue_entry *entry, unsigned int queuenum)
}
/* nfnetlink_unicast will either free the nskb or add it to a socket */
- err = nfnetlink_unicast(nskb, &init_net, queue->peer_pid, MSG_DONTWAIT);
+ err = nfnetlink_unicast(nskb, net, queue->peer_pid, MSG_DONTWAIT);
if (err < 0) {
queue->queue_user_dropped++;
goto err_out_unlock;
@@ -525,16 +534,17 @@ dev_cmp(struct nf_queue_entry *entry, unsigned long ifindex)
/* drop all packets with either indev or outdev == ifindex from all queue
* instances */
static void
-nfqnl_dev_drop(int ifindex)
+nfqnl_dev_drop(struct net *net, int ifindex)
{
int i;
+ struct netns_nfqnl *net_nfqnl = net_generic(net, nfqnl_net_id);
rcu_read_lock();
for (i = 0; i < INSTANCE_BUCKETS; i++) {
struct hlist_node *tmp;
struct nfqnl_instance *inst;
- struct hlist_head *head = &instance_table[i];
+ struct hlist_head *head = &net_nfqnl->instance_table[i];
hlist_for_each_entry_rcu(inst, tmp, head, hlist)
nfqnl_flush(inst, dev_cmp, ifindex);
@@ -551,12 +561,9 @@ nfqnl_rcv_dev_event(struct notifier_block *this,
{
struct net_device *dev = ptr;
- if (!net_eq(dev_net(dev), &init_net))
- return NOTIFY_DONE;
-
/* Drop any packets associated with the downed device */
if (event == NETDEV_DOWN)
- nfqnl_dev_drop(dev->ifindex);
+ nfqnl_dev_drop(dev_net(dev), dev->ifindex);
return NOTIFY_DONE;
}
@@ -569,24 +576,25 @@ nfqnl_rcv_nl_event(struct notifier_block *this,
unsigned long event, void *ptr)
{
struct netlink_notify *n = ptr;
+ struct netns_nfqnl *net_nfqnl = net_generic(n->net, nfqnl_net_id);
if (event == NETLINK_URELEASE && n->protocol == NETLINK_NETFILTER) {
int i;
/* destroy all instances for this pid */
- spin_lock(&instances_lock);
+ spin_lock(&net_nfqnl->instances_lock);
for (i = 0; i < INSTANCE_BUCKETS; i++) {
struct hlist_node *tmp, *t2;
struct nfqnl_instance *inst;
- struct hlist_head *head = &instance_table[i];
+ struct hlist_head *head =
+ &net_nfqnl->instance_table[i];
hlist_for_each_entry_safe(inst, tmp, t2, head, hlist) {
- if ((n->net == &init_net) &&
- (n->pid == inst->peer_pid))
+ if (n->pid == inst->peer_pid)
__instance_destroy(inst);
}
}
- spin_unlock(&instances_lock);
+ spin_unlock(&net_nfqnl->instances_lock);
}
return NOTIFY_DONE;
}
@@ -614,9 +622,10 @@ nfqnl_recv_verdict(struct sock *ctnl, struct sk_buff *skb,
unsigned int verdict;
struct nf_queue_entry *entry;
int err;
+ struct net *net = sock_net(ctnl);
rcu_read_lock();
- queue = instance_lookup(queue_num);
+ queue = instance_lookup(net, queue_num);
if (!queue) {
err = -ENODEV;
goto err_out_unlock;
@@ -692,6 +701,7 @@ nfqnl_recv_config(struct sock *ctnl, struct sk_buff *skb,
struct nfqnl_instance *queue;
struct nfqnl_msg_config_cmd *cmd = NULL;
int ret = 0;
+ struct net *net = sock_net(ctnl);
if (nfqa[NFQA_CFG_CMD]) {
cmd = nla_data(nfqa[NFQA_CFG_CMD]);
@@ -708,7 +718,7 @@ nfqnl_recv_config(struct sock *ctnl, struct sk_buff *skb,
}
rcu_read_lock();
- queue = instance_lookup(queue_num);
+ queue = instance_lookup(net, queue_num);
if (queue && queue->peer_pid != NETLINK_CB(skb).pid) {
ret = -EPERM;
goto err_out_unlock;
@@ -721,7 +731,8 @@ nfqnl_recv_config(struct sock *ctnl, struct sk_buff *skb,
ret = -EBUSY;
goto err_out_unlock;
}
- queue = instance_create(queue_num, NETLINK_CB(skb).pid);
+ queue = instance_create(net, queue_num,
+ NETLINK_CB(skb).pid);
if (IS_ERR(queue)) {
ret = PTR_ERR(queue);
goto err_out_unlock;
@@ -732,7 +743,7 @@ nfqnl_recv_config(struct sock *ctnl, struct sk_buff *skb,
ret = -ENODEV;
goto err_out_unlock;
}
- instance_destroy(queue);
+ instance_destroy(net, queue);
break;
case NFQNL_CFG_CMD_PF_BIND:
case NFQNL_CFG_CMD_PF_UNBIND:
@@ -799,13 +810,15 @@ struct iter_state {
static struct hlist_node *get_first(struct seq_file *seq)
{
struct iter_state *st = seq->private;
+ struct net *net = seq_file_net(seq);
+ struct netns_nfqnl *net_nfqnl = net_generic(net, nfqnl_net_id);
if (!st)
return NULL;
for (st->bucket = 0; st->bucket < INSTANCE_BUCKETS; st->bucket++) {
- if (!hlist_empty(&instance_table[st->bucket]))
- return instance_table[st->bucket].first;
+ if (!hlist_empty(&net_nfqnl->instance_table[st->bucket]))
+ return net_nfqnl->instance_table[st->bucket].first;
}
return NULL;
}
@@ -813,13 +826,15 @@ static struct hlist_node *get_first(struct seq_file *seq)
static struct hlist_node *get_next(struct seq_file *seq, struct hlist_node *h)
{
struct iter_state *st = seq->private;
+ struct net *net = seq_file_net(seq);
+ struct netns_nfqnl *net_nfqnl = net_generic(net, nfqnl_net_id);
h = h->next;
while (!h) {
if (++st->bucket >= INSTANCE_BUCKETS)
return NULL;
- h = instance_table[st->bucket].first;
+ h = net_nfqnl->instance_table[st->bucket].first;
}
return h;
}
@@ -836,9 +851,11 @@ static struct hlist_node *get_idx(struct seq_file *seq, loff_t pos)
}
static void *seq_start(struct seq_file *seq, loff_t *pos)
- __acquires(instances_lock)
{
- spin_lock(&instances_lock);
+ struct netns_nfqnl *net_nfqnl =
+ net_generic(seq_file_net(seq), nfqnl_net_id);
+
+ spin_lock(&net_nfqnl->instances_lock);
return get_idx(seq, *pos);
}
@@ -849,9 +866,11 @@ static void *seq_next(struct seq_file *s, void *v, loff_t *pos)
}
static void seq_stop(struct seq_file *s, void *v)
- __releases(instances_lock)
{
- spin_unlock(&instances_lock);
+ struct netns_nfqnl *net_nfqnl =
+ net_generic(seq_file_net(s), nfqnl_net_id);
+
+ spin_unlock(&net_nfqnl->instances_lock);
}
static int seq_show(struct seq_file *s, void *v)
@@ -875,8 +894,8 @@ static const struct seq_operations nfqnl_seq_ops = {
static int nfqnl_open(struct inode *inode, struct file *file)
{
- return seq_open_private(file, &nfqnl_seq_ops,
- sizeof(struct iter_state));
+ return seq_open_net(inode, file, &nfqnl_seq_ops,
+ sizeof(struct iter_state));
}
static const struct file_operations nfqnl_file_ops = {
@@ -884,54 +903,96 @@ static const struct file_operations nfqnl_file_ops = {
.open = nfqnl_open,
.read = seq_read,
.llseek = seq_lseek,
- .release = seq_release_private,
+ .release = seq_release_net,
};
#endif /* PROC_FS */
-static int __init nfnetlink_queue_init(void)
+static int __net_init nfnetlink_queue_net_init(struct net *net)
{
- int i, status = -ENOMEM;
+ int err, i;
+ struct netns_nfqnl *net_nfqnl = net_generic(net, nfqnl_net_id);
+ spin_lock_init(&net_nfqnl->instances_lock);
for (i = 0; i < INSTANCE_BUCKETS; i++)
- INIT_HLIST_HEAD(&instance_table[i]);
+ INIT_HLIST_HEAD(&net_nfqnl->instance_table[i]);
- netlink_register_notifier(&nfqnl_rtnl_notifier);
- status = nfnetlink_subsys_register(&nfqnl_subsys);
- if (status < 0) {
+#ifdef CONFIG_PROC_FS
+ if (proc_net_fops_create(net, "nfnetlink_queue", 0440,
+ &nfqnl_file_ops) == NULL)
+ return -ENOMEM;
+#endif
+
+ err = nfnetlink_subsys_register(net, &nfqnl_subsys);
+ if (err < 0) {
printk(KERN_ERR "nf_queue: failed to create netlink socket\n");
- goto cleanup_netlink_notifier;
+ goto cleanup_proc;
}
+ return 0;
+
+cleanup_proc:
#ifdef CONFIG_PROC_FS
- if (!proc_create("nfnetlink_queue", 0440,
- proc_net_netfilter, &nfqnl_file_ops))
- goto cleanup_subsys;
+ proc_net_remove(net, "nfnetlink_queue");
#endif
+ return err;
+}
- register_netdevice_notifier(&nfqnl_dev_notifier);
- return status;
+static void __net_exit nfnetlink_queue_net_exit(struct net *net)
+{
+ int i;
+ struct netns_nfqnl *net_nfqnl = net_generic(net, nfqnl_net_id);
+ nfnetlink_subsys_unregister(net, &nfqnl_subsys);
#ifdef CONFIG_PROC_FS
-cleanup_subsys:
- nfnetlink_subsys_unregister(&nfqnl_subsys);
+ proc_net_remove(net, "nfnetlink_queue");
#endif
-cleanup_netlink_notifier:
- netlink_unregister_notifier(&nfqnl_rtnl_notifier);
- return status;
+
+ spin_lock(&net_nfqnl->instances_lock);
+ for (i = 0; i < INSTANCE_BUCKETS; i++) {
+ struct hlist_node *tmp, *t2;
+ struct nfqnl_instance *inst;
+ struct hlist_head *head = &net_nfqnl->instance_table[i];
+
+ hlist_for_each_entry_safe(inst, tmp, t2, head, hlist)
+ __instance_destroy(inst);
+ }
+ spin_unlock(&net_nfqnl->instances_lock);
+}
+
+static struct pernet_operations nfnetlink_queue_net_ops = {
+ .init = nfnetlink_queue_net_init,
+ .exit = nfnetlink_queue_net_exit,
+ .id = &nfqnl_net_id,
+ .size = sizeof(struct netns_nfqnl),
+};
+
+static int __init nfnetlink_queue_init(void)
+{
+ int err;
+
+ err = register_pernet_subsys(&nfnetlink_queue_net_ops);
+ if (err) {
+ pr_err("nfnetlink_queue_init: "
+ "cannot initialize per netns operations\n");
+ return err;
+ }
+
+ netlink_register_notifier(&nfqnl_rtnl_notifier);
+ register_netdevice_notifier(&nfqnl_dev_notifier);
+
+ return err;
}
static void __exit nfnetlink_queue_fini(void)
{
nf_unregister_queue_handlers(&nfqh);
unregister_netdevice_notifier(&nfqnl_dev_notifier);
-#ifdef CONFIG_PROC_FS
- remove_proc_entry("nfnetlink_queue", proc_net_netfilter);
-#endif
- nfnetlink_subsys_unregister(&nfqnl_subsys);
netlink_unregister_notifier(&nfqnl_rtnl_notifier);
rcu_barrier(); /* Wait for completion of call_rcu()'s */
+
+ unregister_pernet_subsys(&nfnetlink_queue_net_ops);
}
MODULE_DESCRIPTION("netfilter packet queue handler");
diff --git a/net/netfilter/xt_osf.c b/net/netfilter/xt_osf.c
index 4327e10..62fdeb1 100644
--- a/net/netfilter/xt_osf.c
+++ b/net/netfilter/xt_osf.c
@@ -31,6 +31,7 @@
#include <net/ip.h>
#include <net/tcp.h>
+#include <net/net_namespace.h>
#include <linux/netfilter/nfnetlink.h>
#include <linux/netfilter/x_tables.h>
@@ -359,6 +360,27 @@ xt_osf_match_packet(const struct sk_buff *skb, struct xt_action_param *p)
return fmatch == FMATCH_OK;
}
+static int __net_init xt_osf_net_init(struct net *net)
+{
+ int err;
+
+ err = nfnetlink_subsys_register(net, &xt_osf_nfnetlink);
+ if (err < 0)
+ pr_err("Failed to register OSF nsfnetlink helper (%d)\n", err);
+
+ return err;
+}
+
+static void __net_exit xt_osf_net_exit(struct net *net)
+{
+ nfnetlink_subsys_unregister(net, &xt_osf_nfnetlink);
+}
+
+static struct pernet_operations xt_osf_net_ops = {
+ .init = xt_osf_net_init,
+ .exit = xt_osf_net_exit,
+};
+
static struct xt_match xt_osf_match = {
.name = "osf",
.revision = 0,
@@ -374,29 +396,26 @@ static struct xt_match xt_osf_match = {
static int __init xt_osf_init(void)
{
- int err = -EINVAL;
+ int err;
int i;
for (i=0; i<ARRAY_SIZE(xt_osf_fingers); ++i)
INIT_LIST_HEAD(&xt_osf_fingers[i]);
- err = nfnetlink_subsys_register(&xt_osf_nfnetlink);
- if (err < 0) {
- pr_err("Failed to register OSF nsfnetlink helper (%d)\n", err);
- goto err_out_exit;
- }
-
err = xt_register_match(&xt_osf_match);
if (err) {
pr_err("Failed to register OS fingerprint "
"matching module (%d)\n", err);
- goto err_out_remove;
+ goto err_out_exit;
}
- return 0;
+ err = register_pernet_subsys(&xt_osf_net_ops);
+ if (err) {
+ pr_err("xt_osf_init: "
+ "cannot initialize per netns operations\n");
+ xt_unregister_match(&xt_osf_match);
+ }
-err_out_remove:
- nfnetlink_subsys_unregister(&xt_osf_nfnetlink);
err_out_exit:
return err;
}
@@ -406,7 +425,6 @@ static void __exit xt_osf_fini(void)
struct xt_osf_finger *f;
int i;
- nfnetlink_subsys_unregister(&xt_osf_nfnetlink);
xt_unregister_match(&xt_osf_match);
rcu_read_lock();