1662 lines
47 KiB
Diff
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();
|