* [PATCH -next 0/8] netfilter: don't copy init ns hooks to new namespaces
@ 2015-10-02 11:49 Florian Westphal
2015-10-02 11:49 ` [PATCH -next 1/8] netfilter: ingress: don't use nf_hook_list_active Florian Westphal
` (7 more replies)
0 siblings, 8 replies; 11+ messages in thread
From: Florian Westphal @ 2015-10-02 11:49 UTC (permalink / raw)
To: netfilter-devel; +Cc: ebiederm
Historically, a particular table or netfilter feature (defrag, iptables
filter table ...) was registered with the netfilter core hook mechanism
on module load.
When netns support was added to iptables only the ip/ip6tables ruleset
was made namespace aware, not the actual hook points.
This has changed -- after Eric Biedermans recent work we now have
per net namespace hooks.
When a new namespace is created, all the hooks registered 'globally'
(i.e. via nf_register_hook() instead of a particular namespace via
nf_register_net_hook api) get copied to the new netns.
This means f.e. that when ipt_filter table/module is loaded on a system,
then each namespace on that system has an (empty) iptables filter ruleset.
This work aims to change all major hook users to nf_register_net_hook
so that when a new netns is created it has no hooks at all, even when the
initial namespace uses conntrack, iptables and bridge netfilter.
To keep bahaviour somewhat compatible, hooks are registered once a
iptables set/getsockopt call is made within a net namespace.
This also means that e.g. conntrack behaviour is not yet optimal, we
still create all the data structures and only skip hook registration
at this time.
Note that I expect that I will need several iterations of this patch set,
I am sending this now so that reviewing can commence and to benefit from
recent addition of kbuild robot magic to patchwork (this is awesome,
thanks a lot!).
The patch set survives allmodconfig build with NAMESPACES=y and =N.
If anyone has further ideas on how to improve this, please let me know.
include/linux/netfilter.h | 29 +++------
include/linux/netfilter/x_tables.h | 10 ++-
include/linux/netfilter_ingress.h | 9 ++-
include/net/netfilter/ipv4/nf_defrag_ipv4.h | 3 -
include/net/netfilter/ipv6/nf_defrag_ipv6.h | 3 -
include/net/netfilter/nf_conntrack.h | 4 +
include/net/netfilter/nf_conntrack_l3proto.h | 4 +
net/bridge/br_netfilter_hooks.c | 68 ++++++++++++++++++++++-
net/ipv4/netfilter/arptable_filter.c | 39 ++++++++-----
net/ipv4/netfilter/ipt_CLUSTERIP.c | 4 -
net/ipv4/netfilter/ipt_SYNPROXY.c | 4 -
net/ipv4/netfilter/iptable_filter.c | 65 ++++++++++++++++------
net/ipv4/netfilter/iptable_mangle.c | 50 +++++++++++++----
net/ipv4/netfilter/iptable_nat.c | 55 +++++++++++++-----
net/ipv4/netfilter/iptable_raw.c | 50 +++++++++++++----
net/ipv4/netfilter/iptable_security.c | 52 +++++++++++++----
net/ipv4/netfilter/nf_conntrack_l3proto_ipv4.c | 62 +++++++++++++++++----
net/ipv4/netfilter/nf_defrag_ipv4.c | 49 +++++++++++++++-
net/ipv6/netfilter/ip6t_SYNPROXY.c | 4 -
net/ipv6/netfilter/ip6table_filter.c | 54 +++++++++++++-----
net/ipv6/netfilter/ip6table_mangle.c | 53 +++++++++++++-----
net/ipv6/netfilter/ip6table_nat.c | 55 +++++++++++++-----
net/ipv6/netfilter/ip6table_raw.c | 54 +++++++++++++-----
net/ipv6/netfilter/ip6table_security.c | 53 +++++++++++++-----
net/ipv6/netfilter/nf_conntrack_l3proto_ipv6.c | 60 ++++++++++++++++----
net/ipv6/netfilter/nf_defrag_ipv6_hooks.c | 50 +++++++++++++++--
net/netfilter/nf_conntrack_proto.c | 48 ++++++++++++++++
net/netfilter/nft_ct.c | 24 ++++----
net/netfilter/x_tables.c | 73 ++++++++++++++++++-------
net/netfilter/xt_CONNSECMARK.c | 4 -
net/netfilter/xt_CT.c | 6 +-
net/netfilter/xt_TPROXY.c | 15 +++--
net/netfilter/xt_connbytes.c | 4 -
net/netfilter/xt_connlabel.c | 6 +-
net/netfilter/xt_connlimit.c | 6 +-
net/netfilter/xt_connmark.c | 8 +-
net/netfilter/xt_conntrack.c | 2
net/netfilter/xt_helper.c | 4 -
net/netfilter/xt_socket.c | 33 +++++++++--
net/netfilter/xt_state.c | 4 -
40 files changed, 893 insertions(+), 287 deletions(-)
^ permalink raw reply [flat|nested] 11+ messages in thread
* [PATCH -next 1/8] netfilter: ingress: don't use nf_hook_list_active
2015-10-02 11:49 [PATCH -next 0/8] netfilter: don't copy init ns hooks to new namespaces Florian Westphal
@ 2015-10-02 11:49 ` Florian Westphal
2015-10-02 11:49 ` [PATCH -next 2/8] netfilter: add and use nf_ct_netns_get/put Florian Westphal
` (6 subsequent siblings)
7 siblings, 0 replies; 11+ messages in thread
From: Florian Westphal @ 2015-10-02 11:49 UTC (permalink / raw)
To: netfilter-devel; +Cc: ebiederm, Florian Westphal
nf_hook_list_active() always returns true once at least one device has
NF_INGRESS hook enabled.
Thus, don't use this function. Instead, inverse the test and use the static
key to elide list_empty test if no NF_INGRESS hooks are active.
Signed-off-by: Florian Westphal <fw@strlen.de>
---
include/linux/netfilter_ingress.h | 9 ++++++---
1 file changed, 6 insertions(+), 3 deletions(-)
diff --git a/include/linux/netfilter_ingress.h b/include/linux/netfilter_ingress.h
index 187feab..ba7ce88 100644
--- a/include/linux/netfilter_ingress.h
+++ b/include/linux/netfilter_ingress.h
@@ -5,10 +5,13 @@
#include <linux/netdevice.h>
#ifdef CONFIG_NETFILTER_INGRESS
-static inline int nf_hook_ingress_active(struct sk_buff *skb)
+static inline bool nf_hook_ingress_active(const struct sk_buff *skb)
{
- return nf_hook_list_active(&skb->dev->nf_hooks_ingress,
- NFPROTO_NETDEV, NF_NETDEV_INGRESS);
+#ifdef HAVE_JUMP_LABEL
+ if (!static_key_false(&nf_hooks_needed[NFPROTO_NETDEV][NF_NETDEV_INGRESS]))
+ return false;
+#endif
+ return !list_empty(&skb->dev->nf_hooks_ingress);
}
static inline int nf_hook_ingress(struct sk_buff *skb)
--
2.0.5
^ permalink raw reply related [flat|nested] 11+ messages in thread
* [PATCH -next 2/8] netfilter: add and use nf_ct_netns_get/put
2015-10-02 11:49 [PATCH -next 0/8] netfilter: don't copy init ns hooks to new namespaces Florian Westphal
2015-10-02 11:49 ` [PATCH -next 1/8] netfilter: ingress: don't use nf_hook_list_active Florian Westphal
@ 2015-10-02 11:49 ` Florian Westphal
2015-10-03 10:46 ` Jan Engelhardt
2015-10-02 11:49 ` [PATCH -next 3/8] netfilter: conntrack: register hooks in netns when needed by ruleset Florian Westphal
` (5 subsequent siblings)
7 siblings, 1 reply; 11+ messages in thread
From: Florian Westphal @ 2015-10-02 11:49 UTC (permalink / raw)
To: netfilter-devel; +Cc: ebiederm, Florian Westphal
currently aliased to try_module_get/_put.
Will be changed in next patch when we add functions to make use of ->net
argument to store usercount per l3proto tracker.
This is needed to avoid registering the conntrack hooks in all netns and
later only enable connection tracking in those that need conntrack.
Signed-off-by: Florian Westphal <fw@strlen.de>
---
include/net/netfilter/nf_conntrack.h | 4 ++++
net/ipv4/netfilter/ipt_CLUSTERIP.c | 4 ++--
net/ipv4/netfilter/ipt_SYNPROXY.c | 4 ++--
net/ipv6/netfilter/ip6t_SYNPROXY.c | 4 ++--
net/netfilter/nf_conntrack_proto.c | 12 ++++++++++++
net/netfilter/nft_ct.c | 24 ++++++++++++------------
net/netfilter/xt_CONNSECMARK.c | 4 ++--
net/netfilter/xt_CT.c | 6 +++---
net/netfilter/xt_connbytes.c | 4 ++--
net/netfilter/xt_connlabel.c | 6 +++---
net/netfilter/xt_connlimit.c | 6 +++---
net/netfilter/xt_connmark.c | 8 ++++----
net/netfilter/xt_conntrack.c | 2 +-
net/netfilter/xt_helper.c | 4 ++--
net/netfilter/xt_state.c | 4 ++--
15 files changed, 56 insertions(+), 40 deletions(-)
diff --git a/include/net/netfilter/nf_conntrack.h b/include/net/netfilter/nf_conntrack.h
index d642f68..0d16730 100644
--- a/include/net/netfilter/nf_conntrack.h
+++ b/include/net/netfilter/nf_conntrack.h
@@ -175,6 +175,10 @@ static inline void nf_ct_put(struct nf_conn *ct)
int nf_ct_l3proto_try_module_get(unsigned short l3proto);
void nf_ct_l3proto_module_put(unsigned short l3proto);
+/* load module; enable/disable conntrack in this namespace */
+int nf_ct_netns_get(struct net *net, u8 nfproto);
+void nf_ct_netns_put(struct net *net, u8 nfproto);
+
/*
* Allocate a hashtable of hlist_head (if nulls == 0),
* or hlist_nulls_head (if nulls == 1)
diff --git a/net/ipv4/netfilter/ipt_CLUSTERIP.c b/net/ipv4/netfilter/ipt_CLUSTERIP.c
index 3f32c03..1b4501c 100644
--- a/net/ipv4/netfilter/ipt_CLUSTERIP.c
+++ b/net/ipv4/netfilter/ipt_CLUSTERIP.c
@@ -419,7 +419,7 @@ static int clusterip_tg_check(const struct xt_tgchk_param *par)
}
cipinfo->config = config;
- ret = nf_ct_l3proto_try_module_get(par->family);
+ ret = nf_ct_netns_get(par->net, par->family);
if (ret < 0)
pr_info("cannot load conntrack support for proto=%u\n",
par->family);
@@ -444,7 +444,7 @@ static void clusterip_tg_destroy(const struct xt_tgdtor_param *par)
clusterip_config_put(cipinfo->config);
- nf_ct_l3proto_module_put(par->family);
+ nf_ct_netns_get(par->net, par->family);
}
#ifdef CONFIG_COMPAT
diff --git a/net/ipv4/netfilter/ipt_SYNPROXY.c b/net/ipv4/netfilter/ipt_SYNPROXY.c
index 6a6e762..01a2322 100644
--- a/net/ipv4/netfilter/ipt_SYNPROXY.c
+++ b/net/ipv4/netfilter/ipt_SYNPROXY.c
@@ -415,12 +415,12 @@ static int synproxy_tg4_check(const struct xt_tgchk_param *par)
e->ip.invflags & XT_INV_PROTO)
return -EINVAL;
- return nf_ct_l3proto_try_module_get(par->family);
+ return nf_ct_netns_get(par->net, NFPROTO_IPV4);
}
static void synproxy_tg4_destroy(const struct xt_tgdtor_param *par)
{
- nf_ct_l3proto_module_put(par->family);
+ nf_ct_netns_put(par->net, NFPROTO_IPV4);
}
static struct xt_target synproxy_tg4_reg __read_mostly = {
diff --git a/net/ipv6/netfilter/ip6t_SYNPROXY.c b/net/ipv6/netfilter/ip6t_SYNPROXY.c
index c235660..213f429 100644
--- a/net/ipv6/netfilter/ip6t_SYNPROXY.c
+++ b/net/ipv6/netfilter/ip6t_SYNPROXY.c
@@ -436,12 +436,12 @@ static int synproxy_tg6_check(const struct xt_tgchk_param *par)
e->ipv6.invflags & XT_INV_PROTO)
return -EINVAL;
- return nf_ct_l3proto_try_module_get(par->family);
+ return nf_ct_netns_get(par->net, par->family);
}
static void synproxy_tg6_destroy(const struct xt_tgdtor_param *par)
{
- nf_ct_l3proto_module_put(par->family);
+ nf_ct_netns_put(par->net, par->family);
}
static struct xt_target synproxy_tg6_reg __read_mostly = {
diff --git a/net/netfilter/nf_conntrack_proto.c b/net/netfilter/nf_conntrack_proto.c
index b65d586..609c789 100644
--- a/net/netfilter/nf_conntrack_proto.c
+++ b/net/netfilter/nf_conntrack_proto.c
@@ -125,6 +125,18 @@ void nf_ct_l3proto_module_put(unsigned short l3proto)
}
EXPORT_SYMBOL_GPL(nf_ct_l3proto_module_put);
+int nf_ct_netns_get(struct net *net, u8 nfproto)
+{
+ return nf_ct_l3proto_try_module_get(nfproto);
+}
+EXPORT_SYMBOL_GPL(nf_ct_netns_get);
+
+void nf_ct_netns_put(struct net *net, u8 nfproto)
+{
+ nf_ct_l3proto_module_put(nfproto);
+}
+EXPORT_SYMBOL_GPL(nf_ct_netns_put);
+
struct nf_conntrack_l4proto *
nf_ct_l4proto_find_get(u_int16_t l3num, u_int8_t l4num)
{
diff --git a/net/netfilter/nft_ct.c b/net/netfilter/nft_ct.c
index 8cbca34..8c775b1 100644
--- a/net/netfilter/nft_ct.c
+++ b/net/netfilter/nft_ct.c
@@ -186,37 +186,37 @@ static const struct nla_policy nft_ct_policy[NFTA_CT_MAX + 1] = {
[NFTA_CT_SREG] = { .type = NLA_U32 },
};
-static int nft_ct_l3proto_try_module_get(uint8_t family)
+static int nft_ct_netns_get(struct net *net, uint8_t family)
{
int err;
if (family == NFPROTO_INET) {
- err = nf_ct_l3proto_try_module_get(NFPROTO_IPV4);
+ err = nf_ct_netns_get(net, NFPROTO_IPV4);
if (err < 0)
goto err1;
- err = nf_ct_l3proto_try_module_get(NFPROTO_IPV6);
+ err = nf_ct_netns_get(net, NFPROTO_IPV6);
if (err < 0)
goto err2;
} else {
- err = nf_ct_l3proto_try_module_get(family);
+ err = nf_ct_netns_get(net, family);
if (err < 0)
goto err1;
}
return 0;
err2:
- nf_ct_l3proto_module_put(NFPROTO_IPV4);
+ nf_ct_netns_put(net, NFPROTO_IPV4);
err1:
return err;
}
-static void nft_ct_l3proto_module_put(uint8_t family)
+static void nft_ct_netns_put(struct net *net, uint8_t family)
{
if (family == NFPROTO_INET) {
- nf_ct_l3proto_module_put(NFPROTO_IPV4);
- nf_ct_l3proto_module_put(NFPROTO_IPV6);
+ nf_ct_netns_put(net, NFPROTO_IPV4);
+ nf_ct_netns_put(net, NFPROTO_IPV6);
} else
- nf_ct_l3proto_module_put(family);
+ nf_ct_netns_put(net, family);
}
static int nft_ct_get_init(const struct nft_ctx *ctx,
@@ -312,7 +312,7 @@ static int nft_ct_get_init(const struct nft_ctx *ctx,
if (err < 0)
return err;
- err = nft_ct_l3proto_try_module_get(ctx->afi->family);
+ err = nft_ct_netns_get(ctx->net, ctx->afi->family);
if (err < 0)
return err;
@@ -343,7 +343,7 @@ static int nft_ct_set_init(const struct nft_ctx *ctx,
if (err < 0)
return err;
- err = nft_ct_l3proto_try_module_get(ctx->afi->family);
+ err = nft_ct_netns_get(ctx->net, ctx->afi->family);
if (err < 0)
return err;
@@ -353,7 +353,7 @@ static int nft_ct_set_init(const struct nft_ctx *ctx,
static void nft_ct_destroy(const struct nft_ctx *ctx,
const struct nft_expr *expr)
{
- nft_ct_l3proto_module_put(ctx->afi->family);
+ nft_ct_netns_put(ctx->net, ctx->afi->family);
}
static int nft_ct_get_dump(struct sk_buff *skb, const struct nft_expr *expr)
diff --git a/net/netfilter/xt_CONNSECMARK.c b/net/netfilter/xt_CONNSECMARK.c
index e04dc28..da56c06 100644
--- a/net/netfilter/xt_CONNSECMARK.c
+++ b/net/netfilter/xt_CONNSECMARK.c
@@ -106,7 +106,7 @@ static int connsecmark_tg_check(const struct xt_tgchk_param *par)
return -EINVAL;
}
- ret = nf_ct_l3proto_try_module_get(par->family);
+ ret = nf_ct_netns_get(par->net, par->family);
if (ret < 0)
pr_info("cannot load conntrack support for proto=%u\n",
par->family);
@@ -115,7 +115,7 @@ static int connsecmark_tg_check(const struct xt_tgchk_param *par)
static void connsecmark_tg_destroy(const struct xt_tgdtor_param *par)
{
- nf_ct_l3proto_module_put(par->family);
+ nf_ct_netns_put(par->net, par->family);
}
static struct xt_target connsecmark_tg_reg __read_mostly = {
diff --git a/net/netfilter/xt_CT.c b/net/netfilter/xt_CT.c
index faf32d8..a262568 100644
--- a/net/netfilter/xt_CT.c
+++ b/net/netfilter/xt_CT.c
@@ -213,7 +213,7 @@ static int xt_ct_tg_check(const struct xt_tgchk_param *par,
goto err1;
#endif
- ret = nf_ct_l3proto_try_module_get(par->family);
+ ret = nf_ct_netns_get(par->net, par->family);
if (ret < 0)
goto err1;
@@ -257,7 +257,7 @@ out:
err3:
nf_ct_tmpl_free(ct);
err2:
- nf_ct_l3proto_module_put(par->family);
+ nf_ct_netns_put(par->net, par->family);
err1:
return ret;
}
@@ -336,7 +336,7 @@ static void xt_ct_tg_destroy(const struct xt_tgdtor_param *par,
if (help)
module_put(help->helper->me);
- nf_ct_l3proto_module_put(par->family);
+ nf_ct_netns_put(par->net, par->family);
xt_ct_destroy_timeout(ct);
nf_ct_put(info->ct);
diff --git a/net/netfilter/xt_connbytes.c b/net/netfilter/xt_connbytes.c
index d4bec26..cad0b7b 100644
--- a/net/netfilter/xt_connbytes.c
+++ b/net/netfilter/xt_connbytes.c
@@ -110,7 +110,7 @@ static int connbytes_mt_check(const struct xt_mtchk_param *par)
sinfo->direction != XT_CONNBYTES_DIR_BOTH)
return -EINVAL;
- ret = nf_ct_l3proto_try_module_get(par->family);
+ ret = nf_ct_netns_get(par->net, par->family);
if (ret < 0)
pr_info("cannot load conntrack support for proto=%u\n",
par->family);
@@ -129,7 +129,7 @@ static int connbytes_mt_check(const struct xt_mtchk_param *par)
static void connbytes_mt_destroy(const struct xt_mtdtor_param *par)
{
- nf_ct_l3proto_module_put(par->family);
+ nf_ct_netns_put(par->net, par->family);
}
static struct xt_match connbytes_mt_reg __read_mostly = {
diff --git a/net/netfilter/xt_connlabel.c b/net/netfilter/xt_connlabel.c
index bb9cbeb..b7e57f2 100644
--- a/net/netfilter/xt_connlabel.c
+++ b/net/netfilter/xt_connlabel.c
@@ -48,7 +48,7 @@ static int connlabel_mt_check(const struct xt_mtchk_param *par)
return -EINVAL;
}
- ret = nf_ct_l3proto_try_module_get(par->family);
+ ret = nf_ct_netns_get(par->net, par->family);
if (ret < 0) {
pr_info("cannot load conntrack support for proto=%u\n",
par->family);
@@ -57,14 +57,14 @@ static int connlabel_mt_check(const struct xt_mtchk_param *par)
ret = nf_connlabels_get(par->net, info->bit + 1);
if (ret < 0)
- nf_ct_l3proto_module_put(par->family);
+ nf_ct_netns_put(par->net, par->family);
return ret;
}
static void connlabel_mt_destroy(const struct xt_mtdtor_param *par)
{
nf_connlabels_put(par->net);
- nf_ct_l3proto_module_put(par->family);
+ nf_ct_netns_put(par->net, par->family);
}
static struct xt_match connlabels_mt_reg __read_mostly = {
diff --git a/net/netfilter/xt_connlimit.c b/net/netfilter/xt_connlimit.c
index 99bbc82..66f5480 100644
--- a/net/netfilter/xt_connlimit.c
+++ b/net/netfilter/xt_connlimit.c
@@ -374,7 +374,7 @@ static int connlimit_mt_check(const struct xt_mtchk_param *par)
} while (!rand);
cmpxchg(&connlimit_rnd, 0, rand);
}
- ret = nf_ct_l3proto_try_module_get(par->family);
+ ret = nf_ct_netns_get(par->net, par->family);
if (ret < 0) {
pr_info("cannot load conntrack support for "
"address family %u\n", par->family);
@@ -384,7 +384,7 @@ static int connlimit_mt_check(const struct xt_mtchk_param *par)
/* init private data */
info->data = kmalloc(sizeof(struct xt_connlimit_data), GFP_KERNEL);
if (info->data == NULL) {
- nf_ct_l3proto_module_put(par->family);
+ nf_ct_netns_put(par->net, par->family);
return -ENOMEM;
}
@@ -420,7 +420,7 @@ static void connlimit_mt_destroy(const struct xt_mtdtor_param *par)
const struct xt_connlimit_info *info = par->matchinfo;
unsigned int i;
- nf_ct_l3proto_module_put(par->family);
+ nf_ct_netns_put(par->net, par->family);
for (i = 0; i < ARRAY_SIZE(info->data->climit_root4); ++i)
destroy_tree(&info->data->climit_root4[i]);
diff --git a/net/netfilter/xt_connmark.c b/net/netfilter/xt_connmark.c
index 69f78e9..ec377cc 100644
--- a/net/netfilter/xt_connmark.c
+++ b/net/netfilter/xt_connmark.c
@@ -77,7 +77,7 @@ static int connmark_tg_check(const struct xt_tgchk_param *par)
{
int ret;
- ret = nf_ct_l3proto_try_module_get(par->family);
+ ret = nf_ct_netns_get(par->net, par->family);
if (ret < 0)
pr_info("cannot load conntrack support for proto=%u\n",
par->family);
@@ -86,7 +86,7 @@ static int connmark_tg_check(const struct xt_tgchk_param *par)
static void connmark_tg_destroy(const struct xt_tgdtor_param *par)
{
- nf_ct_l3proto_module_put(par->family);
+ nf_ct_netns_put(par->net, par->family);
}
static bool
@@ -107,7 +107,7 @@ static int connmark_mt_check(const struct xt_mtchk_param *par)
{
int ret;
- ret = nf_ct_l3proto_try_module_get(par->family);
+ ret = nf_ct_netns_get(par->net, par->family);
if (ret < 0)
pr_info("cannot load conntrack support for proto=%u\n",
par->family);
@@ -116,7 +116,7 @@ static int connmark_mt_check(const struct xt_mtchk_param *par)
static void connmark_mt_destroy(const struct xt_mtdtor_param *par)
{
- nf_ct_l3proto_module_put(par->family);
+ nf_ct_netns_put(par->net, par->family);
}
static struct xt_target connmark_tg_reg __read_mostly = {
diff --git a/net/netfilter/xt_conntrack.c b/net/netfilter/xt_conntrack.c
index 188404b9..bc2c9cd 100644
--- a/net/netfilter/xt_conntrack.c
+++ b/net/netfilter/xt_conntrack.c
@@ -273,7 +273,7 @@ static int conntrack_mt_check(const struct xt_mtchk_param *par)
{
int ret;
- ret = nf_ct_l3proto_try_module_get(par->family);
+ ret = nf_ct_netns_get(par->net, par->family);
if (ret < 0)
pr_info("cannot load conntrack support for proto=%u\n",
par->family);
diff --git a/net/netfilter/xt_helper.c b/net/netfilter/xt_helper.c
index 9f4ab00..8e451ec 100644
--- a/net/netfilter/xt_helper.c
+++ b/net/netfilter/xt_helper.c
@@ -59,7 +59,7 @@ static int helper_mt_check(const struct xt_mtchk_param *par)
struct xt_helper_info *info = par->matchinfo;
int ret;
- ret = nf_ct_l3proto_try_module_get(par->family);
+ ret = nf_ct_netns_get(par->net, par->family);
if (ret < 0) {
pr_info("cannot load conntrack support for proto=%u\n",
par->family);
@@ -71,7 +71,7 @@ static int helper_mt_check(const struct xt_mtchk_param *par)
static void helper_mt_destroy(const struct xt_mtdtor_param *par)
{
- nf_ct_l3proto_module_put(par->family);
+ nf_ct_netns_put(par->net, par->family);
}
static struct xt_match helper_mt_reg __read_mostly = {
diff --git a/net/netfilter/xt_state.c b/net/netfilter/xt_state.c
index a507922..5746a33 100644
--- a/net/netfilter/xt_state.c
+++ b/net/netfilter/xt_state.c
@@ -43,7 +43,7 @@ static int state_mt_check(const struct xt_mtchk_param *par)
{
int ret;
- ret = nf_ct_l3proto_try_module_get(par->family);
+ ret = nf_ct_netns_get(par->net, par->family);
if (ret < 0)
pr_info("cannot load conntrack support for proto=%u\n",
par->family);
@@ -52,7 +52,7 @@ static int state_mt_check(const struct xt_mtchk_param *par)
static void state_mt_destroy(const struct xt_mtdtor_param *par)
{
- nf_ct_l3proto_module_put(par->family);
+ nf_ct_netns_put(par->net, par->family);
}
static struct xt_match state_mt_reg __read_mostly = {
--
2.0.5
^ permalink raw reply related [flat|nested] 11+ messages in thread
* [PATCH -next 3/8] netfilter: conntrack: register hooks in netns when needed by ruleset
2015-10-02 11:49 [PATCH -next 0/8] netfilter: don't copy init ns hooks to new namespaces Florian Westphal
2015-10-02 11:49 ` [PATCH -next 1/8] netfilter: ingress: don't use nf_hook_list_active Florian Westphal
2015-10-02 11:49 ` [PATCH -next 2/8] netfilter: add and use nf_ct_netns_get/put Florian Westphal
@ 2015-10-02 11:49 ` Florian Westphal
2015-10-02 11:49 ` [PATCH -next 4/8] netfilter: xtables: don't register table hooks in namespace at init time Florian Westphal
` (4 subsequent siblings)
7 siblings, 0 replies; 11+ messages in thread
From: Florian Westphal @ 2015-10-02 11:49 UTC (permalink / raw)
To: netfilter-devel; +Cc: ebiederm, Florian Westphal
This makes use of nf_ct_netns_get/put added in previous patch.
We add get/put functions to nf_conntrack_l3proto structure, ipv4 and
ipv6 then implement use-count to track how many users (nftables or
xtables modules) have a dependency on ipv4 and/or ipv6 connection
tracking functionality.
When count reaches zero, the hooks are unregistered.
The main goal of this patch is to delay activation of connection
tracking inside a namespace until a point in time where such
functionality is needed (which might be "never").
Signed-off-by: Florian Westphal <fw@strlen.de>
---
include/net/netfilter/nf_conntrack_l3proto.h | 4 ++
net/ipv4/netfilter/nf_conntrack_l3proto_ipv4.c | 55 ++++++++++++++++++++------
net/ipv6/netfilter/nf_conntrack_l3proto_ipv6.c | 53 +++++++++++++++++++------
net/netfilter/nf_conntrack_proto.c | 38 +++++++++++++++++-
4 files changed, 127 insertions(+), 23 deletions(-)
diff --git a/include/net/netfilter/nf_conntrack_l3proto.h b/include/net/netfilter/nf_conntrack_l3proto.h
index cdc920b..e0238db 100644
--- a/include/net/netfilter/nf_conntrack_l3proto.h
+++ b/include/net/netfilter/nf_conntrack_l3proto.h
@@ -52,6 +52,10 @@ struct nf_conntrack_l3proto {
int (*tuple_to_nlattr)(struct sk_buff *skb,
const struct nf_conntrack_tuple *t);
+ /* Called when netns wants to use connection tracking */
+ int (*net_ns_get)(struct net *);
+ void (*net_ns_put)(struct net *);
+
/*
* Calculate size of tuple nlattr
*/
diff --git a/net/ipv4/netfilter/nf_conntrack_l3proto_ipv4.c b/net/ipv4/netfilter/nf_conntrack_l3proto_ipv4.c
index 752fb40..470fd78 100644
--- a/net/ipv4/netfilter/nf_conntrack_l3proto_ipv4.c
+++ b/net/ipv4/netfilter/nf_conntrack_l3proto_ipv4.c
@@ -31,6 +31,13 @@
#include <net/netfilter/ipv4/nf_defrag_ipv4.h>
#include <net/netfilter/nf_log.h>
+static int conntrack4_net_id __read_mostly;
+static DEFINE_MUTEX(register_ipv4_hooks);
+
+struct conntrack4_net {
+ unsigned int users;
+};
+
static bool ipv4_pkt_to_tuple(const struct sk_buff *skb, unsigned int nhoff,
struct nf_conntrack_tuple *tuple)
{
@@ -373,6 +380,38 @@ static int ipv4_init_net(struct net *net)
return 0;
}
+static int nf_conntrack_l3proto_ipv4_hooks_register(struct net *net)
+{
+ struct conntrack4_net *cnet = net_generic(net, conntrack4_net_id);
+ int err = 0;
+
+ mutex_lock(®ister_ipv4_hooks);
+
+ cnet->users++;
+ if (cnet->users > 1)
+ goto out_unlock;
+
+ err = nf_register_net_hooks(net, ipv4_conntrack_ops,
+ ARRAY_SIZE(ipv4_conntrack_ops));
+
+ if (err)
+ cnet->users = 0;
+ out_unlock:
+ mutex_unlock(®ister_ipv4_hooks);
+ return err;
+}
+
+static void nf_conntrack_l3proto_ipv4_hooks_unregister(struct net *net)
+{
+ struct conntrack4_net *cnet = net_generic(net, conntrack4_net_id);
+
+ mutex_lock(®ister_ipv4_hooks);
+ if (--cnet->users == 0)
+ nf_unregister_net_hooks(net, ipv4_conntrack_ops,
+ ARRAY_SIZE(ipv4_conntrack_ops));
+ mutex_unlock(®ister_ipv4_hooks);
+}
+
struct nf_conntrack_l3proto nf_conntrack_l3proto_ipv4 __read_mostly = {
.l3proto = PF_INET,
.name = "ipv4",
@@ -389,6 +428,8 @@ struct nf_conntrack_l3proto nf_conntrack_l3proto_ipv4 __read_mostly = {
#if defined(CONFIG_SYSCTL) && defined(CONFIG_NF_CONNTRACK_PROC_COMPAT)
.ctl_table_path = "net/ipv4/netfilter",
#endif
+ .net_ns_get = nf_conntrack_l3proto_ipv4_hooks_register,
+ .net_ns_put = nf_conntrack_l3proto_ipv4_hooks_unregister,
.init_net = ipv4_init_net,
.me = THIS_MODULE,
};
@@ -446,6 +487,8 @@ static void ipv4_net_exit(struct net *net)
static struct pernet_operations ipv4_net_ops = {
.init = ipv4_net_init,
.exit = ipv4_net_exit,
+ .id = &conntrack4_net_id,
+ .size = sizeof(struct conntrack4_net),
};
static int __init nf_conntrack_l3proto_ipv4_init(void)
@@ -467,17 +510,10 @@ static int __init nf_conntrack_l3proto_ipv4_init(void)
goto cleanup_sockopt;
}
- ret = nf_register_hooks(ipv4_conntrack_ops,
- ARRAY_SIZE(ipv4_conntrack_ops));
- if (ret < 0) {
- pr_err("nf_conntrack_ipv4: can't register hooks.\n");
- goto cleanup_pernet;
- }
-
ret = nf_ct_l4proto_register(&nf_conntrack_l4proto_tcp4);
if (ret < 0) {
pr_err("nf_conntrack_ipv4: can't register tcp4 proto.\n");
- goto cleanup_hooks;
+ goto cleanup_pernet;
}
ret = nf_ct_l4proto_register(&nf_conntrack_l4proto_udp4);
@@ -514,8 +550,6 @@ static int __init nf_conntrack_l3proto_ipv4_init(void)
nf_ct_l4proto_unregister(&nf_conntrack_l4proto_udp4);
cleanup_tcp4:
nf_ct_l4proto_unregister(&nf_conntrack_l4proto_tcp4);
- cleanup_hooks:
- nf_unregister_hooks(ipv4_conntrack_ops, ARRAY_SIZE(ipv4_conntrack_ops));
cleanup_pernet:
unregister_pernet_subsys(&ipv4_net_ops);
cleanup_sockopt:
@@ -533,7 +567,6 @@ static void __exit nf_conntrack_l3proto_ipv4_fini(void)
nf_ct_l4proto_unregister(&nf_conntrack_l4proto_icmp);
nf_ct_l4proto_unregister(&nf_conntrack_l4proto_udp4);
nf_ct_l4proto_unregister(&nf_conntrack_l4proto_tcp4);
- nf_unregister_hooks(ipv4_conntrack_ops, ARRAY_SIZE(ipv4_conntrack_ops));
unregister_pernet_subsys(&ipv4_net_ops);
nf_unregister_sockopt(&so_getorigdst);
}
diff --git a/net/ipv6/netfilter/nf_conntrack_l3proto_ipv6.c b/net/ipv6/netfilter/nf_conntrack_l3proto_ipv6.c
index dd83ad4..67b2be8 100644
--- a/net/ipv6/netfilter/nf_conntrack_l3proto_ipv6.c
+++ b/net/ipv6/netfilter/nf_conntrack_l3proto_ipv6.c
@@ -34,6 +34,13 @@
#include <net/netfilter/ipv6/nf_defrag_ipv6.h>
#include <net/netfilter/nf_log.h>
+static int conntrack6_net_id;
+static DEFINE_MUTEX(register_ipv6_hooks);
+
+struct conntrack6_net {
+ unsigned int users;
+};
+
static bool ipv6_pkt_to_tuple(const struct sk_buff *skb, unsigned int nhoff,
struct nf_conntrack_tuple *tuple)
{
@@ -314,6 +321,36 @@ static int ipv6_nlattr_tuple_size(void)
}
#endif
+static int nf_conntrack_l3proto_ipv6_hooks_register(struct net *net)
+{
+ struct conntrack6_net *cnet = net_generic(net, conntrack6_net_id);
+ int err = 0;
+
+ mutex_lock(®ister_ipv6_hooks);
+ cnet->users++;
+ if (cnet->users > 1)
+ goto out_unlock;
+
+ err = nf_register_net_hooks(net, ipv6_conntrack_ops,
+ ARRAY_SIZE(ipv6_conntrack_ops));
+ if (err)
+ cnet->users = 0;
+ out_unlock:
+ mutex_unlock(®ister_ipv6_hooks);
+ return err;
+}
+
+static void nf_conntrack_l3proto_ipv6_hooks_unregister(struct net *net)
+{
+ struct conntrack6_net *cnet = net_generic(net, conntrack6_net_id);
+
+ mutex_lock(®ister_ipv6_hooks);
+ if (--cnet->users == 0)
+ nf_unregister_net_hooks(net, ipv6_conntrack_ops,
+ ARRAY_SIZE(ipv6_conntrack_ops));
+ mutex_unlock(®ister_ipv6_hooks);
+}
+
struct nf_conntrack_l3proto nf_conntrack_l3proto_ipv6 __read_mostly = {
.l3proto = PF_INET6,
.name = "ipv6",
@@ -327,6 +364,8 @@ struct nf_conntrack_l3proto nf_conntrack_l3proto_ipv6 __read_mostly = {
.nlattr_to_tuple = ipv6_nlattr_to_tuple,
.nla_policy = ipv6_nla_policy,
#endif
+ .net_ns_get = nf_conntrack_l3proto_ipv6_hooks_register,
+ .net_ns_put = nf_conntrack_l3proto_ipv6_hooks_unregister,
.me = THIS_MODULE,
};
@@ -388,6 +427,8 @@ static void ipv6_net_exit(struct net *net)
static struct pernet_operations ipv6_net_ops = {
.init = ipv6_net_init,
.exit = ipv6_net_exit,
+ .id = &conntrack6_net_id,
+ .size = sizeof(struct conntrack6_net),
};
static int __init nf_conntrack_l3proto_ipv6_init(void)
@@ -407,18 +448,10 @@ static int __init nf_conntrack_l3proto_ipv6_init(void)
if (ret < 0)
goto cleanup_sockopt;
- ret = nf_register_hooks(ipv6_conntrack_ops,
- ARRAY_SIZE(ipv6_conntrack_ops));
- if (ret < 0) {
- pr_err("nf_conntrack_ipv6: can't register pre-routing defrag "
- "hook.\n");
- goto cleanup_pernet;
- }
-
ret = nf_ct_l4proto_register(&nf_conntrack_l4proto_tcp6);
if (ret < 0) {
pr_err("nf_conntrack_ipv6: can't register tcp6 proto.\n");
- goto cleanup_hooks;
+ goto cleanup_pernet;
}
ret = nf_ct_l4proto_register(&nf_conntrack_l4proto_udp6);
@@ -446,8 +479,6 @@ static int __init nf_conntrack_l3proto_ipv6_init(void)
nf_ct_l4proto_unregister(&nf_conntrack_l4proto_udp6);
cleanup_tcp6:
nf_ct_l4proto_unregister(&nf_conntrack_l4proto_tcp6);
- cleanup_hooks:
- nf_unregister_hooks(ipv6_conntrack_ops, ARRAY_SIZE(ipv6_conntrack_ops));
cleanup_pernet:
unregister_pernet_subsys(&ipv6_net_ops);
cleanup_sockopt:
diff --git a/net/netfilter/nf_conntrack_proto.c b/net/netfilter/nf_conntrack_proto.c
index 609c789..1fb11b6 100644
--- a/net/netfilter/nf_conntrack_proto.c
+++ b/net/netfilter/nf_conntrack_proto.c
@@ -127,12 +127,48 @@ EXPORT_SYMBOL_GPL(nf_ct_l3proto_module_put);
int nf_ct_netns_get(struct net *net, u8 nfproto)
{
- return nf_ct_l3proto_try_module_get(nfproto);
+ const struct nf_conntrack_l3proto *l3proto;
+ int ret;
+
+ might_sleep();
+
+ ret = nf_ct_l3proto_try_module_get(nfproto);
+ if (ret < 0)
+ return ret;
+
+ /* we already have a reference, can't fail */
+ rcu_read_lock();
+ l3proto = __nf_ct_l3proto_find(nfproto);
+ rcu_read_unlock();
+
+ if (!l3proto->net_ns_get)
+ return 0;
+
+ ret = l3proto->net_ns_get(net);
+ if (ret < 0)
+ nf_ct_l3proto_module_put(nfproto);
+
+ return ret;
}
EXPORT_SYMBOL_GPL(nf_ct_netns_get);
void nf_ct_netns_put(struct net *net, u8 nfproto)
{
+ const struct nf_conntrack_l3proto *l3proto;
+
+ might_sleep();
+
+ /* same as nf_conntrack_netns_get(), reference assumed */
+ rcu_read_lock();
+ l3proto = __nf_ct_l3proto_find(nfproto);
+ rcu_read_unlock();
+
+ if (WARN_ON(!l3proto))
+ return;
+
+ if (l3proto->net_ns_put)
+ l3proto->net_ns_put(net);
+
nf_ct_l3proto_module_put(nfproto);
}
EXPORT_SYMBOL_GPL(nf_ct_netns_put);
--
2.0.5
^ permalink raw reply related [flat|nested] 11+ messages in thread
* [PATCH -next 4/8] netfilter: xtables: don't register table hooks in namespace at init time
2015-10-02 11:49 [PATCH -next 0/8] netfilter: don't copy init ns hooks to new namespaces Florian Westphal
` (2 preceding siblings ...)
2015-10-02 11:49 ` [PATCH -next 3/8] netfilter: conntrack: register hooks in netns when needed by ruleset Florian Westphal
@ 2015-10-02 11:49 ` Florian Westphal
2015-10-04 19:58 ` Pablo Neira Ayuso
2015-10-02 11:49 ` [PATCH -next 5/8] netfilter: defrag: only register defrag functionality if needed Florian Westphal
` (3 subsequent siblings)
7 siblings, 1 reply; 11+ messages in thread
From: Florian Westphal @ 2015-10-02 11:49 UTC (permalink / raw)
To: netfilter-devel; +Cc: ebiederm, Florian Westphal
delay hook registration until the table is being requested inside a
namespace.
Historically, a particular table (iptables mangle, ip6tables filter,
etc) was registered on module load.
When netns support was added to iptables only the ip/ip6tables ruleset
was made namespace aware, not the actual hook points.
This means f.e. that when ipt_filter table/module is loaded on a system,
then each namespace on that system has an (empty) iptables filter ruleset.
In other words, if a namespace sends a packet, such skb is 'caught'
by netfilter machinery and fed to hooking points for that table
(i.e. INPUT, FORWARD, etc).
Thanks to Eric Biederman, hooks are no longer global, but per namespace.
This means that we can avoid allocation of empty ruleset in a namespace
and defer hook registration until we need the functionality.
We register a tables hook entry points ONLY in the initial namespace.
When an iptables get/setockopt is issued inside a given namespace,
we check if the table is found in the per-namespace list.
If not, we attempt to find it in the initial namespace, and,
if found, create an empty default table in the requesting namespace
and register the needed hooks.
Hook points are destroyed only once namespace is deleted, there is no
'usage count' (it makes no sense since there is no 'remove table'
operation in xtables api).
Signed-off-by: Florian Westphal <fw@strlen.de>
---
include/linux/netfilter/x_tables.h | 10 ++++-
net/ipv4/netfilter/arptable_filter.c | 39 +++++++++++-------
net/ipv4/netfilter/iptable_filter.c | 65 ++++++++++++++++++++++--------
net/ipv4/netfilter/iptable_mangle.c | 50 ++++++++++++++++++-----
net/ipv4/netfilter/iptable_nat.c | 51 ++++++++++++++++--------
net/ipv4/netfilter/iptable_raw.c | 50 ++++++++++++++++++-----
net/ipv4/netfilter/iptable_security.c | 52 +++++++++++++++++-------
net/ipv6/netfilter/ip6table_filter.c | 54 ++++++++++++++++++-------
net/ipv6/netfilter/ip6table_mangle.c | 53 +++++++++++++++++-------
net/ipv6/netfilter/ip6table_nat.c | 51 ++++++++++++++++--------
net/ipv6/netfilter/ip6table_raw.c | 54 ++++++++++++++++++-------
net/ipv6/netfilter/ip6table_security.c | 53 +++++++++++++++++-------
net/netfilter/x_tables.c | 73 +++++++++++++++++++++++++---------
13 files changed, 475 insertions(+), 180 deletions(-)
diff --git a/include/linux/netfilter/x_tables.h b/include/linux/netfilter/x_tables.h
index c557741..7e0920e 100644
--- a/include/linux/netfilter/x_tables.h
+++ b/include/linux/netfilter/x_tables.h
@@ -200,6 +200,9 @@ struct xt_table {
u_int8_t af; /* address/protocol family */
int priority; /* hook order */
+ /* called when table is needed in the given netns */
+ int (*table_init)(struct net *net);
+
/* A unique name... */
const char name[XT_TABLE_MAXNAMELEN];
};
@@ -408,8 +411,11 @@ xt_get_per_cpu_counter(struct xt_counters *cnt, unsigned int cpu)
return cnt;
}
-struct nf_hook_ops *xt_hook_link(const struct xt_table *, nf_hookfn *);
-void xt_hook_unlink(const struct xt_table *, struct nf_hook_ops *);
+struct nf_hook_ops *xt_hook_ops_alloc(const struct xt_table *, nf_hookfn *);
+int xt_hook_link_net(struct net *, const struct xt_table *,
+ const struct nf_hook_ops *);
+void xt_hook_unlink_net(struct net *net, const struct xt_table *,
+ const struct nf_hook_ops *);
#ifdef CONFIG_COMPAT
#include <net/compat.h>
diff --git a/net/ipv4/netfilter/arptable_filter.c b/net/ipv4/netfilter/arptable_filter.c
index 1897ee1..c77832e 100644
--- a/net/ipv4/netfilter/arptable_filter.c
+++ b/net/ipv4/netfilter/arptable_filter.c
@@ -17,12 +17,15 @@ MODULE_DESCRIPTION("arptables filter table");
#define FILTER_VALID_HOOKS ((1 << NF_ARP_IN) | (1 << NF_ARP_OUT) | \
(1 << NF_ARP_FORWARD))
+static int __net_init arptable_filter_table_init(struct net *net);
+
static const struct xt_table packet_filter = {
.name = "filter",
.valid_hooks = FILTER_VALID_HOOKS,
.me = THIS_MODULE,
.af = NFPROTO_ARP,
.priority = NF_IP_PRI_FILTER,
+ .table_init = arptable_filter_table_init,
};
/* The work comes in here from netfilter.c */
@@ -35,26 +38,35 @@ arptable_filter_hook(void *priv, struct sk_buff *skb,
static struct nf_hook_ops *arpfilter_ops __read_mostly;
-static int __net_init arptable_filter_net_init(struct net *net)
+static int __net_init arptable_filter_table_init(struct net *net)
{
struct arpt_replace *repl;
-
+ int err = 0;
+
+ if (net->ipv4.arptable_filter)
+ return 0;
+
repl = arpt_alloc_initial_table(&packet_filter);
if (repl == NULL)
return -ENOMEM;
net->ipv4.arptable_filter =
arpt_register_table(net, &packet_filter, repl);
kfree(repl);
- return PTR_ERR_OR_ZERO(net->ipv4.arptable_filter);
+
+ if (PTR_ERR_OR_ZERO(net->ipv4.arptable_filter)) {
+ err = PTR_ERR(net->ipv4.arptable_filter);
+ net->ipv4.arptable_filter = NULL;
+ }
+ return err;
}
static void __net_exit arptable_filter_net_exit(struct net *net)
{
- arpt_unregister_table(net->ipv4.arptable_filter);
+ if (net->ipv4.arptable_filter)
+ arpt_unregister_table(net->ipv4.arptable_filter);
}
static struct pernet_operations arptable_filter_net_ops = {
- .init = arptable_filter_net_init,
.exit = arptable_filter_net_exit,
};
@@ -62,26 +74,23 @@ static int __init arptable_filter_init(void)
{
int ret;
+ arpfilter_ops = xt_hook_ops_alloc(&packet_filter, arptable_filter_hook);
+ if (IS_ERR(arpfilter_ops))
+ return PTR_ERR(arpfilter_ops);
+
ret = register_pernet_subsys(&arptable_filter_net_ops);
- if (ret < 0)
+ if (ret < 0) {
+ kfree(arpfilter_ops);
return ret;
-
- arpfilter_ops = xt_hook_link(&packet_filter, arptable_filter_hook);
- if (IS_ERR(arpfilter_ops)) {
- ret = PTR_ERR(arpfilter_ops);
- goto cleanup_table;
}
- return ret;
-cleanup_table:
- unregister_pernet_subsys(&arptable_filter_net_ops);
return ret;
}
static void __exit arptable_filter_fini(void)
{
- xt_hook_unlink(&packet_filter, arpfilter_ops);
unregister_pernet_subsys(&arptable_filter_net_ops);
+ kfree(arpfilter_ops);
}
module_init(arptable_filter_init);
diff --git a/net/ipv4/netfilter/iptable_filter.c b/net/ipv4/netfilter/iptable_filter.c
index 397ef2d..18d559f 100644
--- a/net/ipv4/netfilter/iptable_filter.c
+++ b/net/ipv4/netfilter/iptable_filter.c
@@ -24,12 +24,21 @@ MODULE_DESCRIPTION("iptables filter table");
(1 << NF_INET_FORWARD) | \
(1 << NF_INET_LOCAL_OUT))
+static struct nf_hook_ops *filter_ops __read_mostly;
+
+/* Default to forward because I got too much mail already. */
+static bool forward __read_mostly = true;
+module_param(forward, bool, 0000);
+
+static int __net_init iptable_filter_table_init(struct net *net);
+
static const struct xt_table packet_filter = {
.name = "filter",
.valid_hooks = FILTER_VALID_HOOKS,
.me = THIS_MODULE,
.af = NFPROTO_IPV4,
.priority = NF_IP_PRI_FILTER,
+ .table_init = iptable_filter_table_init,
};
static unsigned int
@@ -45,15 +54,13 @@ iptable_filter_hook(void *priv, struct sk_buff *skb,
return ipt_do_table(skb, state, state->net->ipv4.iptable_filter);
}
-static struct nf_hook_ops *filter_ops __read_mostly;
-
-/* Default to forward because I got too much mail already. */
-static bool forward = true;
-module_param(forward, bool, 0000);
-
-static int __net_init iptable_filter_net_init(struct net *net)
+static int __net_init iptable_filter_table_init(struct net *net)
{
struct ipt_replace *repl;
+ int err;
+
+ if (net->ipv4.iptable_filter)
+ return 0;
repl = ipt_alloc_initial_table(&packet_filter);
if (repl == NULL)
@@ -65,12 +72,37 @@ static int __net_init iptable_filter_net_init(struct net *net)
net->ipv4.iptable_filter =
ipt_register_table(net, &packet_filter, repl);
kfree(repl);
- return PTR_ERR_OR_ZERO(net->ipv4.iptable_filter);
+ err = PTR_ERR_OR_ZERO(net->ipv4.iptable_filter);
+ if (err)
+ goto err;
+
+ err = xt_hook_link_net(net, net->ipv4.iptable_filter, filter_ops);
+ if (err) {
+ ipt_unregister_table(net, net->ipv4.iptable_filter);
+ goto err;
+ }
+ return err;
+ err:
+ net->ipv4.iptable_filter = NULL;
+ return err;
+}
+
+static int __net_init iptable_filter_net_init(struct net *net)
+{
+ if (net == &init_net || !forward)
+ return iptable_filter_table_init(net);
+
+ return 0;
}
static void __net_exit iptable_filter_net_exit(struct net *net)
{
+ if (!net->ipv4.iptable_filter)
+ return;
+
+ xt_hook_unlink_net(net, net->ipv4.iptable_filter, filter_ops);
ipt_unregister_table(net, net->ipv4.iptable_filter);
+ net->ipv4.iptable_filter = NULL;
}
static struct pernet_operations iptable_filter_net_ops = {
@@ -82,15 +114,16 @@ static int __init iptable_filter_init(void)
{
int ret;
- ret = register_pernet_subsys(&iptable_filter_net_ops);
- if (ret < 0)
- return ret;
-
- /* Register hooks */
- filter_ops = xt_hook_link(&packet_filter, iptable_filter_hook);
+ filter_ops = xt_hook_ops_alloc(&packet_filter, iptable_filter_hook);
if (IS_ERR(filter_ops)) {
ret = PTR_ERR(filter_ops);
- unregister_pernet_subsys(&iptable_filter_net_ops);
+ return ret;
+ }
+
+ ret = register_pernet_subsys(&iptable_filter_net_ops);
+ if (ret < 0) {
+ kfree(filter_ops);
+ return ret;
}
return ret;
@@ -98,8 +131,8 @@ static int __init iptable_filter_init(void)
static void __exit iptable_filter_fini(void)
{
- xt_hook_unlink(&packet_filter, filter_ops);
unregister_pernet_subsys(&iptable_filter_net_ops);
+ kfree(filter_ops);
}
module_init(iptable_filter_init);
diff --git a/net/ipv4/netfilter/iptable_mangle.c b/net/ipv4/netfilter/iptable_mangle.c
index ba5d392..0824dce 100644
--- a/net/ipv4/netfilter/iptable_mangle.c
+++ b/net/ipv4/netfilter/iptable_mangle.c
@@ -28,12 +28,15 @@ MODULE_DESCRIPTION("iptables mangle table");
(1 << NF_INET_LOCAL_OUT) | \
(1 << NF_INET_POST_ROUTING))
+static int __net_init iptable_mangle_table_init(struct net *net);
+
static const struct xt_table packet_mangler = {
.name = "mangle",
.valid_hooks = MANGLE_VALID_HOOKS,
.me = THIS_MODULE,
.af = NFPROTO_IPV4,
.priority = NF_IP_PRI_MANGLE,
+ .table_init = iptable_mangle_table_init,
};
static unsigned int
@@ -92,10 +95,13 @@ iptable_mangle_hook(void *priv,
}
static struct nf_hook_ops *mangle_ops __read_mostly;
-
-static int __net_init iptable_mangle_net_init(struct net *net)
+static int __net_init iptable_mangle_table_init(struct net *net)
{
struct ipt_replace *repl;
+ int ret;
+
+ if (net->ipv4.iptable_mangle)
+ return 0;
repl = ipt_alloc_initial_table(&packet_mangler);
if (repl == NULL)
@@ -103,16 +109,33 @@ static int __net_init iptable_mangle_net_init(struct net *net)
net->ipv4.iptable_mangle =
ipt_register_table(net, &packet_mangler, repl);
kfree(repl);
- return PTR_ERR_OR_ZERO(net->ipv4.iptable_mangle);
+ ret = PTR_ERR_OR_ZERO(net->ipv4.iptable_mangle);
+ if (ret < 0)
+ goto err;
+ /* Register hooks */
+ ret = xt_hook_link_net(net, net->ipv4.iptable_mangle, mangle_ops);
+ if (ret) {
+ ipt_unregister_table(net, net->ipv4.iptable_mangle);
+ goto err;
+ }
+
+ return ret;
+ err:
+ net->ipv4.iptable_mangle = NULL;
+ return ret;
}
static void __net_exit iptable_mangle_net_exit(struct net *net)
{
+ if (!net->ipv4.iptable_mangle)
+ return;
+
+ xt_hook_unlink_net(net, net->ipv4.iptable_mangle, mangle_ops);
ipt_unregister_table(net, net->ipv4.iptable_mangle);
+ net->ipv4.iptable_mangle = NULL;
}
static struct pernet_operations iptable_mangle_net_ops = {
- .init = iptable_mangle_net_init,
.exit = iptable_mangle_net_exit,
};
@@ -120,15 +143,22 @@ static int __init iptable_mangle_init(void)
{
int ret;
+ mangle_ops = xt_hook_ops_alloc(&packet_mangler, iptable_mangle_hook);
+ if (IS_ERR(mangle_ops)) {
+ ret = PTR_ERR(mangle_ops);
+ return ret;
+ }
+
ret = register_pernet_subsys(&iptable_mangle_net_ops);
- if (ret < 0)
+ if (ret < 0) {
+ kfree(mangle_ops);
return ret;
+ }
- /* Register hooks */
- mangle_ops = xt_hook_link(&packet_mangler, iptable_mangle_hook);
- if (IS_ERR(mangle_ops)) {
- ret = PTR_ERR(mangle_ops);
+ ret = iptable_mangle_table_init(&init_net);
+ if (ret) {
unregister_pernet_subsys(&iptable_mangle_net_ops);
+ kfree(mangle_ops);
}
return ret;
@@ -136,8 +166,8 @@ static int __init iptable_mangle_init(void)
static void __exit iptable_mangle_fini(void)
{
- xt_hook_unlink(&packet_mangler, mangle_ops);
unregister_pernet_subsys(&iptable_mangle_net_ops);
+ kfree(mangle_ops);
}
module_init(iptable_mangle_init);
diff --git a/net/ipv4/netfilter/iptable_nat.c b/net/ipv4/netfilter/iptable_nat.c
index 3a2e4d8..4e0cc89 100644
--- a/net/ipv4/netfilter/iptable_nat.c
+++ b/net/ipv4/netfilter/iptable_nat.c
@@ -18,6 +18,8 @@
#include <net/netfilter/nf_nat_core.h>
#include <net/netfilter/nf_nat_l3proto.h>
+static int __net_init iptable_nat_table_init(struct net *net);
+
static const struct xt_table nf_nat_ipv4_table = {
.name = "nat",
.valid_hooks = (1 << NF_INET_PRE_ROUTING) |
@@ -26,6 +28,7 @@ static const struct xt_table nf_nat_ipv4_table = {
(1 << NF_INET_LOCAL_IN),
.me = THIS_MODULE,
.af = NFPROTO_IPV4,
+ .table_init = iptable_nat_table_init,
};
static unsigned int iptable_nat_do_chain(void *priv,
@@ -99,50 +102,64 @@ static struct nf_hook_ops nf_nat_ipv4_ops[] __read_mostly = {
},
};
-static int __net_init iptable_nat_net_init(struct net *net)
+static int __net_init iptable_nat_table_init(struct net *net)
{
struct ipt_replace *repl;
+ int ret;
+
+ if (net->ipv4.nat_table)
+ return 0;
repl = ipt_alloc_initial_table(&nf_nat_ipv4_table);
if (repl == NULL)
return -ENOMEM;
net->ipv4.nat_table = ipt_register_table(net, &nf_nat_ipv4_table, repl);
kfree(repl);
- return PTR_ERR_OR_ZERO(net->ipv4.nat_table);
+ ret = PTR_ERR_OR_ZERO(net->ipv4.nat_table);
+ if (ret < 0)
+ goto err;
+
+ ret = nf_register_net_hooks(net, nf_nat_ipv4_ops,
+ ARRAY_SIZE(nf_nat_ipv4_ops));
+ if (ret < 0) {
+ ipt_unregister_table(net, net->ipv4.nat_table);
+ goto err;
+ }
+ return ret;
+ err:
+ net->ipv4.nat_table = NULL;
+ return ret;
}
static void __net_exit iptable_nat_net_exit(struct net *net)
{
+ if (!net->ipv4.nat_table)
+ return;
+ nf_unregister_net_hooks(net, nf_nat_ipv4_ops,
+ ARRAY_SIZE(nf_nat_ipv4_ops));
ipt_unregister_table(net, net->ipv4.nat_table);
+ net->ipv4.nat_table = NULL;
}
static struct pernet_operations iptable_nat_net_ops = {
- .init = iptable_nat_net_init,
.exit = iptable_nat_net_exit,
};
static int __init iptable_nat_init(void)
{
- int err;
+ int ret = register_pernet_subsys(&iptable_nat_net_ops);
- err = register_pernet_subsys(&iptable_nat_net_ops);
- if (err < 0)
- goto err1;
+ if (ret)
+ return ret;
- err = nf_register_hooks(nf_nat_ipv4_ops, ARRAY_SIZE(nf_nat_ipv4_ops));
- if (err < 0)
- goto err2;
- return 0;
-
-err2:
- unregister_pernet_subsys(&iptable_nat_net_ops);
-err1:
- return err;
+ ret = iptable_nat_table_init(&init_net);
+ if (ret)
+ unregister_pernet_subsys(&iptable_nat_net_ops);
+ return ret;
}
static void __exit iptable_nat_exit(void)
{
- nf_unregister_hooks(nf_nat_ipv4_ops, ARRAY_SIZE(nf_nat_ipv4_ops));
unregister_pernet_subsys(&iptable_nat_net_ops);
}
diff --git a/net/ipv4/netfilter/iptable_raw.c b/net/ipv4/netfilter/iptable_raw.c
index 1ba0281..a72e925 100644
--- a/net/ipv4/netfilter/iptable_raw.c
+++ b/net/ipv4/netfilter/iptable_raw.c
@@ -10,12 +10,17 @@
#define RAW_VALID_HOOKS ((1 << NF_INET_PRE_ROUTING) | (1 << NF_INET_LOCAL_OUT))
+static struct nf_hook_ops *rawtable_ops __read_mostly;
+
+static int __net_init iptable_raw_table_init(struct net *net);
+
static const struct xt_table packet_raw = {
.name = "raw",
.valid_hooks = RAW_VALID_HOOKS,
.me = THIS_MODULE,
.af = NFPROTO_IPV4,
.priority = NF_IP_PRI_RAW,
+ .table_init = iptable_raw_table_init,
};
/* The work comes in here from netfilter.c. */
@@ -32,11 +37,13 @@ iptable_raw_hook(void *priv, struct sk_buff *skb,
return ipt_do_table(skb, state, state->net->ipv4.iptable_raw);
}
-static struct nf_hook_ops *rawtable_ops __read_mostly;
-
-static int __net_init iptable_raw_net_init(struct net *net)
+static int __net_init iptable_raw_table_init(struct net *net)
{
struct ipt_replace *repl;
+ int ret;
+
+ if (net->ipv4.iptable_raw)
+ return 0;
repl = ipt_alloc_initial_table(&packet_raw);
if (repl == NULL)
@@ -44,16 +51,32 @@ static int __net_init iptable_raw_net_init(struct net *net)
net->ipv4.iptable_raw =
ipt_register_table(net, &packet_raw, repl);
kfree(repl);
- return PTR_ERR_OR_ZERO(net->ipv4.iptable_raw);
+ ret = PTR_ERR_OR_ZERO(net->ipv4.iptable_raw);
+ if (ret < 0)
+ goto err;
+ /* Register hooks */
+ ret = xt_hook_link_net(net, net->ipv4.iptable_raw, rawtable_ops);
+ if (ret) {
+ ipt_unregister_table(net, net->ipv4.iptable_raw);
+ goto err;
+ }
+ return ret;
+ err:
+ net->ipv4.iptable_raw = NULL;
+ return ret;
}
static void __net_exit iptable_raw_net_exit(struct net *net)
{
+ if (!net->ipv4.iptable_raw)
+ return;
+
+ xt_hook_unlink_net(net, net->ipv4.iptable_raw, rawtable_ops);
ipt_unregister_table(net, net->ipv4.iptable_raw);
+ net->ipv4.iptable_raw = NULL;
}
static struct pernet_operations iptable_raw_net_ops = {
- .init = iptable_raw_net_init,
.exit = iptable_raw_net_exit,
};
@@ -61,15 +84,20 @@ static int __init iptable_raw_init(void)
{
int ret;
+ rawtable_ops = xt_hook_ops_alloc(&packet_raw, iptable_raw_hook);
+ if (IS_ERR(rawtable_ops))
+ return PTR_ERR(rawtable_ops);
+
ret = register_pernet_subsys(&iptable_raw_net_ops);
- if (ret < 0)
+ if (ret < 0) {
+ kfree(rawtable_ops);
return ret;
+ }
- /* Register hooks */
- rawtable_ops = xt_hook_link(&packet_raw, iptable_raw_hook);
- if (IS_ERR(rawtable_ops)) {
- ret = PTR_ERR(rawtable_ops);
+ ret = iptable_raw_table_init(&init_net);
+ if (ret) {
unregister_pernet_subsys(&iptable_raw_net_ops);
+ kfree(rawtable_ops);
}
return ret;
@@ -77,8 +105,8 @@ static int __init iptable_raw_init(void)
static void __exit iptable_raw_fini(void)
{
- xt_hook_unlink(&packet_raw, rawtable_ops);
unregister_pernet_subsys(&iptable_raw_net_ops);
+ kfree(rawtable_ops);
}
module_init(iptable_raw_init);
diff --git a/net/ipv4/netfilter/iptable_security.c b/net/ipv4/netfilter/iptable_security.c
index f534e2f..03b3d0f 100644
--- a/net/ipv4/netfilter/iptable_security.c
+++ b/net/ipv4/netfilter/iptable_security.c
@@ -28,12 +28,15 @@ MODULE_DESCRIPTION("iptables security table, for MAC rules");
(1 << NF_INET_FORWARD) | \
(1 << NF_INET_LOCAL_OUT)
+static int __net_init iptable_security_table_init(struct net *net);
+
static const struct xt_table security_table = {
.name = "security",
.valid_hooks = SECURITY_VALID_HOOKS,
.me = THIS_MODULE,
.af = NFPROTO_IPV4,
.priority = NF_IP_PRI_SECURITY,
+ .table_init = iptable_security_table_init,
};
static unsigned int
@@ -51,9 +54,13 @@ iptable_security_hook(void *priv, struct sk_buff *skb,
static struct nf_hook_ops *sectbl_ops __read_mostly;
-static int __net_init iptable_security_net_init(struct net *net)
+static int __net_init iptable_security_table_init(struct net *net)
{
struct ipt_replace *repl;
+ int ret;
+
+ if (net->ipv4.iptable_security)
+ return 0;
repl = ipt_alloc_initial_table(&security_table);
if (repl == NULL)
@@ -61,16 +68,32 @@ static int __net_init iptable_security_net_init(struct net *net)
net->ipv4.iptable_security =
ipt_register_table(net, &security_table, repl);
kfree(repl);
- return PTR_ERR_OR_ZERO(net->ipv4.iptable_security);
+ ret = PTR_ERR_OR_ZERO(net->ipv4.iptable_security);
+ if (ret < 0)
+ goto err;
+ /* Register hooks */
+ ret = xt_hook_link_net(net, net->ipv4.iptable_security, sectbl_ops);
+ if (ret) {
+ ipt_unregister_table(net, net->ipv4.iptable_security);
+ goto err;
+ }
+ return ret;
+ err:
+ net->ipv4.iptable_security = NULL;
+ return ret;
}
static void __net_exit iptable_security_net_exit(struct net *net)
{
+ if (!net->ipv4.iptable_security)
+ return;
+
+ xt_hook_unlink_net(net, net->ipv4.iptable_security, sectbl_ops);
ipt_unregister_table(net, net->ipv4.iptable_security);
+ net->ipv4.iptable_security = NULL;
}
static struct pernet_operations iptable_security_net_ops = {
- .init = iptable_security_net_init,
.exit = iptable_security_net_exit,
};
@@ -78,28 +101,29 @@ static int __init iptable_security_init(void)
{
int ret;
+ sectbl_ops = xt_hook_ops_alloc(&security_table, iptable_security_hook);
+ if (IS_ERR(sectbl_ops))
+ return PTR_ERR(sectbl_ops);
+
ret = register_pernet_subsys(&iptable_security_net_ops);
- if (ret < 0)
+ if (ret < 0) {
+ kfree(sectbl_ops);
return ret;
-
- sectbl_ops = xt_hook_link(&security_table, iptable_security_hook);
- if (IS_ERR(sectbl_ops)) {
- ret = PTR_ERR(sectbl_ops);
- goto cleanup_table;
}
- return ret;
+ ret = iptable_security_table_init(&init_net);
+ if (ret) {
+ unregister_pernet_subsys(&iptable_security_net_ops);
+ kfree(sectbl_ops);
+ }
-cleanup_table:
- unregister_pernet_subsys(&iptable_security_net_ops);
return ret;
}
static void __exit iptable_security_fini(void)
{
- xt_hook_unlink(&security_table, sectbl_ops);
unregister_pernet_subsys(&iptable_security_net_ops);
+ kfree(sectbl_ops);
}
-
module_init(iptable_security_init);
module_exit(iptable_security_fini);
diff --git a/net/ipv6/netfilter/ip6table_filter.c b/net/ipv6/netfilter/ip6table_filter.c
index 8b277b9..dd77384 100644
--- a/net/ipv6/netfilter/ip6table_filter.c
+++ b/net/ipv6/netfilter/ip6table_filter.c
@@ -22,12 +22,15 @@ MODULE_DESCRIPTION("ip6tables filter table");
(1 << NF_INET_FORWARD) | \
(1 << NF_INET_LOCAL_OUT))
+static int __net_init ip6table_filter_table_init(struct net *net);
+
static const struct xt_table packet_filter = {
.name = "filter",
.valid_hooks = FILTER_VALID_HOOKS,
.me = THIS_MODULE,
.af = NFPROTO_IPV6,
.priority = NF_IP6_PRI_FILTER,
+ .table_init = ip6table_filter_table_init,
};
/* The work comes in here from netfilter.c. */
@@ -44,9 +47,13 @@ static struct nf_hook_ops *filter_ops __read_mostly;
static bool forward = true;
module_param(forward, bool, 0000);
-static int __net_init ip6table_filter_net_init(struct net *net)
+static int __net_init ip6table_filter_table_init(struct net *net)
{
struct ip6t_replace *repl;
+ int err;
+
+ if (net->ipv6.ip6table_filter)
+ return 0;
repl = ip6t_alloc_initial_table(&packet_filter);
if (repl == NULL)
@@ -58,12 +65,36 @@ static int __net_init ip6table_filter_net_init(struct net *net)
net->ipv6.ip6table_filter =
ip6t_register_table(net, &packet_filter, repl);
kfree(repl);
- return PTR_ERR_OR_ZERO(net->ipv6.ip6table_filter);
+ err = PTR_ERR_OR_ZERO(net->ipv6.ip6table_filter);
+ if (err)
+ goto err;
+
+ err = xt_hook_link_net(net, net->ipv6.ip6table_filter, filter_ops);
+ if (err) {
+ ip6t_unregister_table(net, net->ipv6.ip6table_filter);
+ goto err;
+ }
+ return err;
+ err:
+ net->ipv6.ip6table_filter = NULL;
+ return err;
+}
+
+static int __net_init ip6table_filter_net_init(struct net *net)
+{
+ if (net == &init_net || !forward)
+ return ip6table_filter_table_init(net);
+
+ return 0;
}
static void __net_exit ip6table_filter_net_exit(struct net *net)
{
+ if (!net->ipv6.ip6table_filter)
+ return;
+
ip6t_unregister_table(net, net->ipv6.ip6table_filter);
+ net->ipv6.ip6table_filter = NULL;
}
static struct pernet_operations ip6table_filter_net_ops = {
@@ -75,28 +106,21 @@ static int __init ip6table_filter_init(void)
{
int ret;
+ filter_ops = xt_hook_ops_alloc(&packet_filter, ip6table_filter_hook);
+ if (IS_ERR(filter_ops))
+ return PTR_ERR(filter_ops);
+
ret = register_pernet_subsys(&ip6table_filter_net_ops);
if (ret < 0)
- return ret;
-
- /* Register hooks */
- filter_ops = xt_hook_link(&packet_filter, ip6table_filter_hook);
- if (IS_ERR(filter_ops)) {
- ret = PTR_ERR(filter_ops);
- goto cleanup_table;
- }
-
- return ret;
+ kfree(filter_ops);
- cleanup_table:
- unregister_pernet_subsys(&ip6table_filter_net_ops);
return ret;
}
static void __exit ip6table_filter_fini(void)
{
- xt_hook_unlink(&packet_filter, filter_ops);
unregister_pernet_subsys(&ip6table_filter_net_ops);
+ kfree(filter_ops);
}
module_init(ip6table_filter_init);
diff --git a/net/ipv6/netfilter/ip6table_mangle.c b/net/ipv6/netfilter/ip6table_mangle.c
index abe278b..22cc8db 100644
--- a/net/ipv6/netfilter/ip6table_mangle.c
+++ b/net/ipv6/netfilter/ip6table_mangle.c
@@ -23,12 +23,14 @@ MODULE_DESCRIPTION("ip6tables mangle table");
(1 << NF_INET_LOCAL_OUT) | \
(1 << NF_INET_POST_ROUTING))
+static int __net_init ip6table_mangle_table_init(struct net *net);
static const struct xt_table packet_mangler = {
.name = "mangle",
.valid_hooks = MANGLE_VALID_HOOKS,
.me = THIS_MODULE,
.af = NFPROTO_IPV6,
.priority = NF_IP6_PRI_MANGLE,
+ .table_init = ip6table_mangle_table_init,
};
static unsigned int
@@ -88,9 +90,13 @@ ip6table_mangle_hook(void *priv, struct sk_buff *skb,
}
static struct nf_hook_ops *mangle_ops __read_mostly;
-static int __net_init ip6table_mangle_net_init(struct net *net)
+static int __net_init ip6table_mangle_table_init(struct net *net)
{
struct ip6t_replace *repl;
+ int ret;
+
+ if (net->ipv6.ip6table_mangle)
+ return 0;
repl = ip6t_alloc_initial_table(&packet_mangler);
if (repl == NULL)
@@ -98,16 +104,33 @@ static int __net_init ip6table_mangle_net_init(struct net *net)
net->ipv6.ip6table_mangle =
ip6t_register_table(net, &packet_mangler, repl);
kfree(repl);
- return PTR_ERR_OR_ZERO(net->ipv6.ip6table_mangle);
+ ret = PTR_ERR_OR_ZERO(net->ipv6.ip6table_mangle);
+ if (ret < 0)
+ goto err;
+
+ ret = xt_hook_link_net(net, net->ipv6.ip6table_mangle, mangle_ops);
+ if (ret) {
+ ip6t_unregister_table(net, net->ipv6.ip6table_mangle);
+ goto err;
+ }
+
+ return ret;
+ err:
+ net->ipv6.ip6table_mangle = NULL;
+ return ret;
}
static void __net_exit ip6table_mangle_net_exit(struct net *net)
{
+ if (!net->ipv6.ip6table_mangle)
+ return;
+
+ xt_hook_unlink_net(net, net->ipv6.ip6table_mangle, mangle_ops);
ip6t_unregister_table(net, net->ipv6.ip6table_mangle);
+ net->ipv6.ip6table_mangle = NULL;
}
static struct pernet_operations ip6table_mangle_net_ops = {
- .init = ip6table_mangle_net_init,
.exit = ip6table_mangle_net_exit,
};
@@ -115,28 +138,28 @@ static int __init ip6table_mangle_init(void)
{
int ret;
+ mangle_ops = xt_hook_ops_alloc(&packet_mangler, ip6table_mangle_hook);
+ if (IS_ERR(mangle_ops))
+ return PTR_ERR(mangle_ops);
+
ret = register_pernet_subsys(&ip6table_mangle_net_ops);
- if (ret < 0)
+ if (ret < 0) {
+ kfree(mangle_ops);
return ret;
-
- /* Register hooks */
- mangle_ops = xt_hook_link(&packet_mangler, ip6table_mangle_hook);
- if (IS_ERR(mangle_ops)) {
- ret = PTR_ERR(mangle_ops);
- goto cleanup_table;
}
- return ret;
-
- cleanup_table:
- unregister_pernet_subsys(&ip6table_mangle_net_ops);
+ ret = ip6table_mangle_table_init(&init_net);
+ if (ret) {
+ unregister_pernet_subsys(&ip6table_mangle_net_ops);
+ kfree(mangle_ops);
+ }
return ret;
}
static void __exit ip6table_mangle_fini(void)
{
- xt_hook_unlink(&packet_mangler, mangle_ops);
unregister_pernet_subsys(&ip6table_mangle_net_ops);
+ kfree(mangle_ops);
}
module_init(ip6table_mangle_init);
diff --git a/net/ipv6/netfilter/ip6table_nat.c b/net/ipv6/netfilter/ip6table_nat.c
index abea175..ee6a3fe 100644
--- a/net/ipv6/netfilter/ip6table_nat.c
+++ b/net/ipv6/netfilter/ip6table_nat.c
@@ -20,6 +20,8 @@
#include <net/netfilter/nf_nat_core.h>
#include <net/netfilter/nf_nat_l3proto.h>
+static int __net_init ip6table_nat_table_init(struct net *net);
+
static const struct xt_table nf_nat_ipv6_table = {
.name = "nat",
.valid_hooks = (1 << NF_INET_PRE_ROUTING) |
@@ -28,6 +30,7 @@ static const struct xt_table nf_nat_ipv6_table = {
(1 << NF_INET_LOCAL_IN),
.me = THIS_MODULE,
.af = NFPROTO_IPV6,
+ .table_init = ip6table_nat_table_init,
};
static unsigned int ip6table_nat_do_chain(void *priv,
@@ -101,50 +104,64 @@ static struct nf_hook_ops nf_nat_ipv6_ops[] __read_mostly = {
},
};
-static int __net_init ip6table_nat_net_init(struct net *net)
+static int __net_init ip6table_nat_table_init(struct net *net)
{
struct ip6t_replace *repl;
+ int ret;
+
+ if (net->ipv6.ip6table_nat)
+ return 0;
repl = ip6t_alloc_initial_table(&nf_nat_ipv6_table);
if (repl == NULL)
return -ENOMEM;
net->ipv6.ip6table_nat = ip6t_register_table(net, &nf_nat_ipv6_table, repl);
kfree(repl);
- return PTR_ERR_OR_ZERO(net->ipv6.ip6table_nat);
+ ret = PTR_ERR_OR_ZERO(net->ipv6.ip6table_nat);
+ if (ret < 0)
+ goto err;
+
+ ret = nf_register_net_hooks(net, nf_nat_ipv6_ops,
+ ARRAY_SIZE(nf_nat_ipv6_ops));
+ if (ret < 0) {
+ ip6t_unregister_table(net, net->ipv6.ip6table_nat);
+ goto err;
+ }
+ return ret;
+ err:
+ net->ipv6.ip6table_nat = NULL;
+ return ret;
}
static void __net_exit ip6table_nat_net_exit(struct net *net)
{
+ if (!net->ipv6.ip6table_nat)
+ return;
+ nf_unregister_net_hooks(net, nf_nat_ipv6_ops,
+ ARRAY_SIZE(nf_nat_ipv6_ops));
ip6t_unregister_table(net, net->ipv6.ip6table_nat);
+ net->ipv6.ip6table_nat = NULL;
}
static struct pernet_operations ip6table_nat_net_ops = {
- .init = ip6table_nat_net_init,
.exit = ip6table_nat_net_exit,
};
static int __init ip6table_nat_init(void)
{
- int err;
+ int ret = register_pernet_subsys(&ip6table_nat_net_ops);
- err = register_pernet_subsys(&ip6table_nat_net_ops);
- if (err < 0)
- goto err1;
+ if (ret)
+ return ret;
- err = nf_register_hooks(nf_nat_ipv6_ops, ARRAY_SIZE(nf_nat_ipv6_ops));
- if (err < 0)
- goto err2;
- return 0;
-
-err2:
- unregister_pernet_subsys(&ip6table_nat_net_ops);
-err1:
- return err;
+ ret = ip6table_nat_table_init(&init_net);
+ if (ret)
+ unregister_pernet_subsys(&ip6table_nat_net_ops);
+ return ret;
}
static void __exit ip6table_nat_exit(void)
{
- nf_unregister_hooks(nf_nat_ipv6_ops, ARRAY_SIZE(nf_nat_ipv6_ops));
unregister_pernet_subsys(&ip6table_nat_net_ops);
}
diff --git a/net/ipv6/netfilter/ip6table_raw.c b/net/ipv6/netfilter/ip6table_raw.c
index 9021963..dc6255f 100644
--- a/net/ipv6/netfilter/ip6table_raw.c
+++ b/net/ipv6/netfilter/ip6table_raw.c
@@ -9,12 +9,15 @@
#define RAW_VALID_HOOKS ((1 << NF_INET_PRE_ROUTING) | (1 << NF_INET_LOCAL_OUT))
+static int __net_init ip6table_raw_table_init(struct net *net);
+
static const struct xt_table packet_raw = {
.name = "raw",
.valid_hooks = RAW_VALID_HOOKS,
.me = THIS_MODULE,
.af = NFPROTO_IPV6,
.priority = NF_IP6_PRI_RAW,
+ .table_init = ip6table_raw_table_init,
};
/* The work comes in here from netfilter.c. */
@@ -27,9 +30,13 @@ ip6table_raw_hook(void *priv, struct sk_buff *skb,
static struct nf_hook_ops *rawtable_ops __read_mostly;
-static int __net_init ip6table_raw_net_init(struct net *net)
+static int __net_init ip6table_raw_table_init(struct net *net)
{
struct ip6t_replace *repl;
+ int ret;
+
+ if (net->ipv6.ip6table_raw)
+ return 0;
repl = ip6t_alloc_initial_table(&packet_raw);
if (repl == NULL)
@@ -37,16 +44,32 @@ static int __net_init ip6table_raw_net_init(struct net *net)
net->ipv6.ip6table_raw =
ip6t_register_table(net, &packet_raw, repl);
kfree(repl);
- return PTR_ERR_OR_ZERO(net->ipv6.ip6table_raw);
+ ret = PTR_ERR_OR_ZERO(net->ipv6.ip6table_raw);
+ if (ret < 0)
+ goto err;
+ ret = xt_hook_link_net(net, net->ipv6.ip6table_raw, rawtable_ops);
+ if (ret) {
+ ip6t_unregister_table(net, net->ipv6.ip6table_raw);
+ goto err;
+ }
+
+ return ret;
+ err:
+ net->ipv6.ip6table_raw = NULL;
+ return ret;
}
static void __net_exit ip6table_raw_net_exit(struct net *net)
{
+ if (!net->ipv6.ip6table_raw)
+ return;
+
+ xt_hook_unlink_net(net, net->ipv6.ip6table_raw, rawtable_ops);
ip6t_unregister_table(net, net->ipv6.ip6table_raw);
+ net->ipv6.ip6table_raw = NULL;
}
static struct pernet_operations ip6table_raw_net_ops = {
- .init = ip6table_raw_net_init,
.exit = ip6table_raw_net_exit,
};
@@ -54,28 +77,29 @@ static int __init ip6table_raw_init(void)
{
int ret;
+ /* Register hooks */
+ rawtable_ops = xt_hook_ops_alloc(&packet_raw, ip6table_raw_hook);
+ if (IS_ERR(rawtable_ops))
+ return PTR_ERR(rawtable_ops);
+
ret = register_pernet_subsys(&ip6table_raw_net_ops);
- if (ret < 0)
+ if (ret < 0) {
+ kfree(rawtable_ops);
return ret;
-
- /* Register hooks */
- rawtable_ops = xt_hook_link(&packet_raw, ip6table_raw_hook);
- if (IS_ERR(rawtable_ops)) {
- ret = PTR_ERR(rawtable_ops);
- goto cleanup_table;
}
- return ret;
-
- cleanup_table:
- unregister_pernet_subsys(&ip6table_raw_net_ops);
+ ret = ip6table_raw_table_init(&init_net);
+ if (ret) {
+ unregister_pernet_subsys(&ip6table_raw_net_ops);
+ kfree(rawtable_ops);
+ }
return ret;
}
static void __exit ip6table_raw_fini(void)
{
- xt_hook_unlink(&packet_raw, rawtable_ops);
unregister_pernet_subsys(&ip6table_raw_net_ops);
+ kfree(rawtable_ops);
}
module_init(ip6table_raw_init);
diff --git a/net/ipv6/netfilter/ip6table_security.c b/net/ipv6/netfilter/ip6table_security.c
index 0d856fe..b4ea40d 100644
--- a/net/ipv6/netfilter/ip6table_security.c
+++ b/net/ipv6/netfilter/ip6table_security.c
@@ -27,12 +27,15 @@ MODULE_DESCRIPTION("ip6tables security table, for MAC rules");
(1 << NF_INET_FORWARD) | \
(1 << NF_INET_LOCAL_OUT)
+static int __net_init ip6table_security_table_init(struct net *net);
+
static const struct xt_table security_table = {
.name = "security",
.valid_hooks = SECURITY_VALID_HOOKS,
.me = THIS_MODULE,
.af = NFPROTO_IPV6,
.priority = NF_IP6_PRI_SECURITY,
+ .table_init = ip6table_security_table_init,
};
static unsigned int
@@ -44,9 +47,13 @@ ip6table_security_hook(void *priv, struct sk_buff *skb,
static struct nf_hook_ops *sectbl_ops __read_mostly;
-static int __net_init ip6table_security_net_init(struct net *net)
+static int __net_init ip6table_security_table_init(struct net *net)
{
struct ip6t_replace *repl;
+ int ret;
+
+ if (net->ipv6.ip6table_security)
+ return 0;
repl = ip6t_alloc_initial_table(&security_table);
if (repl == NULL)
@@ -54,16 +61,33 @@ static int __net_init ip6table_security_net_init(struct net *net)
net->ipv6.ip6table_security =
ip6t_register_table(net, &security_table, repl);
kfree(repl);
- return PTR_ERR_OR_ZERO(net->ipv6.ip6table_security);
+ ret = PTR_ERR_OR_ZERO(net->ipv6.ip6table_security);
+ if (ret < 0)
+ goto err;
+
+ ret = xt_hook_link_net(net, net->ipv6.ip6table_security, sectbl_ops);
+ if (ret) {
+ ip6t_unregister_table(net, net->ipv6.ip6table_security);
+ goto err;
+ }
+
+ return ret;
+ err:
+ net->ipv6.ip6table_security = NULL;
+ return ret;
}
static void __net_exit ip6table_security_net_exit(struct net *net)
{
+ if (!net->ipv6.ip6table_security)
+ return;
+
+ xt_hook_unlink_net(net, net->ipv6.ip6table_security, sectbl_ops);
ip6t_unregister_table(net, net->ipv6.ip6table_security);
+ net->ipv6.ip6table_security = NULL;
}
static struct pernet_operations ip6table_security_net_ops = {
- .init = ip6table_security_net_init,
.exit = ip6table_security_net_exit,
};
@@ -71,27 +95,28 @@ static int __init ip6table_security_init(void)
{
int ret;
+ sectbl_ops = xt_hook_ops_alloc(&security_table, ip6table_security_hook);
+ if (IS_ERR(sectbl_ops))
+ return PTR_ERR(sectbl_ops);
+
ret = register_pernet_subsys(&ip6table_security_net_ops);
- if (ret < 0)
+ if (ret < 0) {
+ kfree(sectbl_ops);
return ret;
-
- sectbl_ops = xt_hook_link(&security_table, ip6table_security_hook);
- if (IS_ERR(sectbl_ops)) {
- ret = PTR_ERR(sectbl_ops);
- goto cleanup_table;
}
- return ret;
-
-cleanup_table:
- unregister_pernet_subsys(&ip6table_security_net_ops);
+ ret = ip6table_security_table_init(&init_net);
+ if (ret) {
+ unregister_pernet_subsys(&ip6table_security_net_ops);
+ kfree(sectbl_ops);
+ }
return ret;
}
static void __exit ip6table_security_fini(void)
{
- xt_hook_unlink(&security_table, sectbl_ops);
unregister_pernet_subsys(&ip6table_security_net_ops);
+ kfree(sectbl_ops);
}
module_init(ip6table_security_init);
diff --git a/net/netfilter/x_tables.c b/net/netfilter/x_tables.c
index 9b42b5e..d768ea5 100644
--- a/net/netfilter/x_tables.c
+++ b/net/netfilter/x_tables.c
@@ -693,12 +693,45 @@ EXPORT_SYMBOL(xt_free_table_info);
struct xt_table *xt_find_table_lock(struct net *net, u_int8_t af,
const char *name)
{
- struct xt_table *t;
+ struct xt_table *t, *found = NULL;
mutex_lock(&xt[af].mutex);
list_for_each_entry(t, &net->xt.tables[af], list)
if (strcmp(t->name, name) == 0 && try_module_get(t->me))
return t;
+
+ if (net == &init_net)
+ goto out;
+
+ /* Table doesn't exist in this netns, re-try init */
+ list_for_each_entry(t, &init_net.xt.tables[af], list) {
+ if (strcmp(t->name, name))
+ continue;
+ if (!try_module_get(t->me))
+ return NULL;
+
+ mutex_unlock(&xt[af].mutex);
+ if (t->table_init(net) != 0) {
+ module_put(t->me);
+ return NULL;
+ }
+
+ found = t;
+
+ mutex_lock(&xt[af].mutex);
+ break;
+ }
+
+ if (!found)
+ goto out;
+
+ /* and once again: */
+ list_for_each_entry(t, &net->xt.tables[af], list)
+ if (strcmp(t->name, name) == 0)
+ return t;
+
+ module_put(found->me);
+ out:
mutex_unlock(&xt[af].mutex);
return NULL;
}
@@ -1169,20 +1202,20 @@ static const struct file_operations xt_target_ops = {
#endif /* CONFIG_PROC_FS */
/**
- * xt_hook_link - set up hooks for a new table
+ * xt_hook_ops_alloc - set up hooks for a new table
* @table: table with metadata needed to set up hooks
* @fn: Hook function
*
- * This function will take care of creating and registering the necessary
- * Netfilter hooks for XT tables.
+ * This function will create the nf_hook_ops that the x_table needs
+ * to hand to xt_hook_link_net().
*/
-struct nf_hook_ops *xt_hook_link(const struct xt_table *table, nf_hookfn *fn)
+struct nf_hook_ops *
+xt_hook_ops_alloc(const struct xt_table *table, nf_hookfn *fn)
{
unsigned int hook_mask = table->valid_hooks;
uint8_t i, num_hooks = hweight32(hook_mask);
uint8_t hooknum;
struct nf_hook_ops *ops;
- int ret;
ops = kmalloc(sizeof(*ops) * num_hooks, GFP_KERNEL);
if (ops == NULL)
@@ -1200,27 +1233,29 @@ struct nf_hook_ops *xt_hook_link(const struct xt_table *table, nf_hookfn *fn)
++i;
}
- ret = nf_register_hooks(ops, num_hooks);
- if (ret < 0) {
- kfree(ops);
- return ERR_PTR(ret);
- }
-
return ops;
}
-EXPORT_SYMBOL_GPL(xt_hook_link);
+EXPORT_SYMBOL_GPL(xt_hook_ops_alloc);
+
+int xt_hook_link_net(struct net *net, const struct xt_table *table,
+ const struct nf_hook_ops *ops)
+{
+ return nf_register_net_hooks(net, ops, hweight32(table->valid_hooks));
+}
+EXPORT_SYMBOL_GPL(xt_hook_link_net);
/**
- * xt_hook_unlink - remove hooks for a table
+ * xt_hook_unlink_net - remove hooks for a table in this netns
+ * @net: netns that should get the table hook removed
+ * @table: the table hook to remove in this netns
* @ops: nf_hook_ops array as returned by nf_hook_link
- * @hook_mask: the very same mask that was passed to nf_hook_link
*/
-void xt_hook_unlink(const struct xt_table *table, struct nf_hook_ops *ops)
+void xt_hook_unlink_net(struct net *net, const struct xt_table *table,
+ const struct nf_hook_ops *ops)
{
- nf_unregister_hooks(ops, hweight32(table->valid_hooks));
- kfree(ops);
+ nf_unregister_net_hooks(net, ops, hweight32(table->valid_hooks));
}
-EXPORT_SYMBOL_GPL(xt_hook_unlink);
+EXPORT_SYMBOL_GPL(xt_hook_unlink_net);
int xt_proto_init(struct net *net, u_int8_t af)
{
--
2.0.5
^ permalink raw reply related [flat|nested] 11+ messages in thread
* [PATCH -next 5/8] netfilter: defrag: only register defrag functionality if needed
2015-10-02 11:49 [PATCH -next 0/8] netfilter: don't copy init ns hooks to new namespaces Florian Westphal
` (3 preceding siblings ...)
2015-10-02 11:49 ` [PATCH -next 4/8] netfilter: xtables: don't register table hooks in namespace at init time Florian Westphal
@ 2015-10-02 11:49 ` Florian Westphal
2015-10-02 11:49 ` [PATCH -next 6/8] netfilter: nat: depend on conntrack module Florian Westphal
` (2 subsequent siblings)
7 siblings, 0 replies; 11+ messages in thread
From: Florian Westphal @ 2015-10-02 11:49 UTC (permalink / raw)
To: netfilter-devel; +Cc: ebiederm, Florian Westphal
nf_defrag modules for ipv4 and ipv6 export an empty stub function.
Any module that needs the defragmentation hooks registered simply
'calls' this empty function to create a 'phony' module dependency --
modprobe magic will make sure the appropriate defrag module is loaded.
This extends defragmentation to delay the defragmentation hook
registration until the functionality is requested within a network
namespace instead of module load time for all namespaces.
Hooks are only un-registered on module unload or when a namespace
that used such defrag functionality exits.
Signed-off-by: Florian Westphal <fw@strlen.de>
---
include/net/netfilter/ipv4/nf_defrag_ipv4.h | 3 +-
include/net/netfilter/ipv6/nf_defrag_ipv6.h | 3 +-
net/ipv4/netfilter/nf_conntrack_l3proto_ipv4.c | 7 +++-
net/ipv4/netfilter/nf_defrag_ipv4.c | 49 +++++++++++++++++++++++--
net/ipv6/netfilter/nf_conntrack_l3proto_ipv6.c | 7 +++-
net/ipv6/netfilter/nf_defrag_ipv6_hooks.c | 50 +++++++++++++++++++++++---
net/netfilter/xt_TPROXY.c | 15 +++++---
net/netfilter/xt_socket.c | 33 ++++++++++++++---
8 files changed, 146 insertions(+), 21 deletions(-)
diff --git a/include/net/netfilter/ipv4/nf_defrag_ipv4.h b/include/net/netfilter/ipv4/nf_defrag_ipv4.h
index f01ef20..db405f7 100644
--- a/include/net/netfilter/ipv4/nf_defrag_ipv4.h
+++ b/include/net/netfilter/ipv4/nf_defrag_ipv4.h
@@ -1,6 +1,7 @@
#ifndef _NF_DEFRAG_IPV4_H
#define _NF_DEFRAG_IPV4_H
-void nf_defrag_ipv4_enable(void);
+struct net;
+int nf_defrag_ipv4_enable(struct net *);
#endif /* _NF_DEFRAG_IPV4_H */
diff --git a/include/net/netfilter/ipv6/nf_defrag_ipv6.h b/include/net/netfilter/ipv6/nf_defrag_ipv6.h
index 27666d8..9b86e81 100644
--- a/include/net/netfilter/ipv6/nf_defrag_ipv6.h
+++ b/include/net/netfilter/ipv6/nf_defrag_ipv6.h
@@ -1,7 +1,8 @@
#ifndef _NF_DEFRAG_IPV6_H
#define _NF_DEFRAG_IPV6_H
-void nf_defrag_ipv6_enable(void);
+struct net;
+int nf_defrag_ipv6_enable(struct net *);
int nf_ct_frag6_init(void);
void nf_ct_frag6_cleanup(void);
diff --git a/net/ipv4/netfilter/nf_conntrack_l3proto_ipv4.c b/net/ipv4/netfilter/nf_conntrack_l3proto_ipv4.c
index 470fd78..d2c053d 100644
--- a/net/ipv4/netfilter/nf_conntrack_l3proto_ipv4.c
+++ b/net/ipv4/netfilter/nf_conntrack_l3proto_ipv4.c
@@ -391,6 +391,12 @@ static int nf_conntrack_l3proto_ipv4_hooks_register(struct net *net)
if (cnet->users > 1)
goto out_unlock;
+ err = nf_defrag_ipv4_enable(net);
+ if (err) {
+ cnet->users = 0;
+ goto out_unlock;
+ }
+
err = nf_register_net_hooks(net, ipv4_conntrack_ops,
ARRAY_SIZE(ipv4_conntrack_ops));
@@ -496,7 +502,6 @@ static int __init nf_conntrack_l3proto_ipv4_init(void)
int ret = 0;
need_conntrack();
- nf_defrag_ipv4_enable();
ret = nf_register_sockopt(&so_getorigdst);
if (ret < 0) {
diff --git a/net/ipv4/netfilter/nf_defrag_ipv4.c b/net/ipv4/netfilter/nf_defrag_ipv4.c
index b246346..38b9734 100644
--- a/net/ipv4/netfilter/nf_defrag_ipv4.c
+++ b/net/ipv4/netfilter/nf_defrag_ipv4.c
@@ -11,6 +11,7 @@
#include <linux/netfilter.h>
#include <linux/module.h>
#include <linux/skbuff.h>
+#include <net/netns/generic.h>
#include <net/route.h>
#include <net/ip.h>
@@ -22,6 +23,13 @@
#endif
#include <net/netfilter/nf_conntrack_zones.h>
+static int defrag4_net_id __read_mostly;
+static DEFINE_MUTEX(defrag4_mutex);
+
+struct defrag4_net {
+ bool enabled;
+};
+
static int nf_ct_ipv4_gather_frags(struct sk_buff *skb, u_int32_t user)
{
int err;
@@ -108,18 +116,53 @@ static struct nf_hook_ops ipv4_defrag_ops[] = {
},
};
+static void __net_exit defrag4_net_exit(struct net *net)
+{
+ struct defrag4_net *n = net_generic(net, defrag4_net_id);
+
+ if (n->enabled)
+ nf_unregister_net_hooks(net, ipv4_defrag_ops,
+ ARRAY_SIZE(ipv4_defrag_ops));
+}
+
+static struct pernet_operations defrag4_net_ops = {
+ .exit = defrag4_net_exit,
+ .id = &defrag4_net_id,
+ .size = sizeof(struct defrag4_net),
+};
+
static int __init nf_defrag_init(void)
{
- return nf_register_hooks(ipv4_defrag_ops, ARRAY_SIZE(ipv4_defrag_ops));
+ return register_pernet_subsys(&defrag4_net_ops);
}
static void __exit nf_defrag_fini(void)
{
- nf_unregister_hooks(ipv4_defrag_ops, ARRAY_SIZE(ipv4_defrag_ops));
+ unregister_pernet_subsys(&defrag4_net_ops);
}
-void nf_defrag_ipv4_enable(void)
+int nf_defrag_ipv4_enable(struct net *net)
{
+ struct defrag4_net *n = net_generic(net, defrag4_net_id);
+ int err = 0;
+
+ might_sleep();
+
+ if (n->enabled)
+ return 0;
+
+ mutex_lock(&defrag4_mutex);
+ if (n->enabled)
+ goto out_unlock;
+
+ err = nf_register_net_hooks(net, ipv4_defrag_ops,
+ ARRAY_SIZE(ipv4_defrag_ops));
+ if (err == 0)
+ n->enabled = true;
+
+ out_unlock:
+ mutex_unlock(&defrag4_mutex);
+ return err;
}
EXPORT_SYMBOL_GPL(nf_defrag_ipv4_enable);
diff --git a/net/ipv6/netfilter/nf_conntrack_l3proto_ipv6.c b/net/ipv6/netfilter/nf_conntrack_l3proto_ipv6.c
index 67b2be8..a0b547b 100644
--- a/net/ipv6/netfilter/nf_conntrack_l3proto_ipv6.c
+++ b/net/ipv6/netfilter/nf_conntrack_l3proto_ipv6.c
@@ -331,6 +331,12 @@ static int nf_conntrack_l3proto_ipv6_hooks_register(struct net *net)
if (cnet->users > 1)
goto out_unlock;
+ err = nf_defrag_ipv6_enable(net);
+ if (err < 0) {
+ cnet->users = 0;
+ goto out_unlock;
+ }
+
err = nf_register_net_hooks(net, ipv6_conntrack_ops,
ARRAY_SIZE(ipv6_conntrack_ops));
if (err)
@@ -436,7 +442,6 @@ static int __init nf_conntrack_l3proto_ipv6_init(void)
int ret = 0;
need_conntrack();
- nf_defrag_ipv6_enable();
ret = nf_register_sockopt(&so_getorigdst6);
if (ret < 0) {
diff --git a/net/ipv6/netfilter/nf_defrag_ipv6_hooks.c b/net/ipv6/netfilter/nf_defrag_ipv6_hooks.c
index a99baf6..c2112cf 100644
--- a/net/ipv6/netfilter/nf_defrag_ipv6_hooks.c
+++ b/net/ipv6/netfilter/nf_defrag_ipv6_hooks.c
@@ -30,6 +30,13 @@
#include <net/netfilter/nf_conntrack_zones.h>
#include <net/netfilter/ipv6/nf_defrag_ipv6.h>
+static int defrag6_net_id __read_mostly;
+static DEFINE_MUTEX(defrag6_mutex);
+
+struct defrag6_net {
+ bool enabled;
+};
+
static enum ip6_defrag_users nf_ct6_defrag_user(unsigned int hooknum,
struct sk_buff *skb)
{
@@ -98,6 +105,21 @@ static struct nf_hook_ops ipv6_defrag_ops[] = {
},
};
+static void __net_exit defrag6_net_exit(struct net *net)
+{
+ struct defrag6_net *n = net_generic(net, defrag6_net_id);
+
+ if (n->enabled)
+ nf_unregister_net_hooks(net, ipv6_defrag_ops,
+ ARRAY_SIZE(ipv6_defrag_ops));
+}
+
+static struct pernet_operations defrag6_net_ops = {
+ .exit = defrag6_net_exit,
+ .id = &defrag6_net_id,
+ .size = sizeof(struct defrag6_net),
+};
+
static int __init nf_defrag_init(void)
{
int ret = 0;
@@ -107,9 +129,9 @@ static int __init nf_defrag_init(void)
pr_err("nf_defrag_ipv6: can't initialize frag6.\n");
return ret;
}
- ret = nf_register_hooks(ipv6_defrag_ops, ARRAY_SIZE(ipv6_defrag_ops));
+ ret = register_pernet_subsys(&defrag6_net_ops);
if (ret < 0) {
- pr_err("nf_defrag_ipv6: can't register hooks\n");
+ pr_err("nf_defrag_ipv6: can't register pernet ops\n");
goto cleanup_frag6;
}
return ret;
@@ -122,12 +144,32 @@ cleanup_frag6:
static void __exit nf_defrag_fini(void)
{
- nf_unregister_hooks(ipv6_defrag_ops, ARRAY_SIZE(ipv6_defrag_ops));
+ unregister_pernet_subsys(&defrag6_net_ops);
nf_ct_frag6_cleanup();
}
-void nf_defrag_ipv6_enable(void)
+int nf_defrag_ipv6_enable(struct net *net)
{
+ struct defrag6_net *n = net_generic(net, defrag6_net_id);
+ int err = 0;
+
+ might_sleep();
+
+ if (n->enabled)
+ return 0;
+
+ mutex_lock(&defrag6_mutex);
+ if (n->enabled)
+ goto out_unlock;
+
+ err = nf_register_net_hooks(net, ipv6_defrag_ops,
+ ARRAY_SIZE(ipv6_defrag_ops));
+ if (err == 0)
+ n->enabled = true;
+
+ out_unlock:
+ mutex_unlock(&defrag6_mutex);
+ return err;
}
EXPORT_SYMBOL_GPL(nf_defrag_ipv6_enable);
diff --git a/net/netfilter/xt_TPROXY.c b/net/netfilter/xt_TPROXY.c
index 3ab591e..f091244 100644
--- a/net/netfilter/xt_TPROXY.c
+++ b/net/netfilter/xt_TPROXY.c
@@ -516,6 +516,11 @@ tproxy_tg6_v1(struct sk_buff *skb, const struct xt_action_param *par)
static int tproxy_tg6_check(const struct xt_tgchk_param *par)
{
const struct ip6t_ip6 *i = par->entryinfo;
+ int err;
+
+ err = nf_defrag_ipv6_enable(par->net);
+ if (err)
+ return err;
if ((i->proto == IPPROTO_TCP || i->proto == IPPROTO_UDP) &&
!(i->invflags & IP6T_INV_PROTO))
@@ -530,6 +535,11 @@ static int tproxy_tg6_check(const struct xt_tgchk_param *par)
static int tproxy_tg4_check(const struct xt_tgchk_param *par)
{
const struct ipt_ip *i = par->entryinfo;
+ int err;
+
+ err = nf_defrag_ipv4_enable(par->net);
+ if (err)
+ return err;
if ((i->proto == IPPROTO_TCP || i->proto == IPPROTO_UDP)
&& !(i->invflags & IPT_INV_PROTO))
@@ -581,11 +591,6 @@ static struct xt_target tproxy_tg_reg[] __read_mostly = {
static int __init tproxy_tg_init(void)
{
- nf_defrag_ipv4_enable();
-#ifdef XT_TPROXY_HAVE_IPV6
- nf_defrag_ipv6_enable();
-#endif
-
return xt_register_targets(tproxy_tg_reg, ARRAY_SIZE(tproxy_tg_reg));
}
diff --git a/net/netfilter/xt_socket.c b/net/netfilter/xt_socket.c
index 2ec08f0..d0f0064 100644
--- a/net/netfilter/xt_socket.c
+++ b/net/netfilter/xt_socket.c
@@ -418,9 +418,28 @@ socket_mt6_v1_v2_v3(const struct sk_buff *skb, struct xt_action_param *par)
}
#endif
+static int socket_mt_enable_defrag(struct net *net, int family)
+{
+ switch (family) {
+ case NFPROTO_IPV4:
+ return nf_defrag_ipv4_enable(net);
+#ifdef XT_SOCKET_HAVE_IPV6
+ case NFPROTO_IPV6:
+ return nf_defrag_ipv6_enable(net);
+#endif
+ }
+ WARN_ONCE(1, "Unknown family %d\n", family);
+ return 0;
+}
+
static int socket_mt_v1_check(const struct xt_mtchk_param *par)
{
const struct xt_socket_mtinfo1 *info = (struct xt_socket_mtinfo1 *) par->matchinfo;
+ int err;
+
+ err = socket_mt_enable_defrag(par->net, par->family);
+ if (err)
+ return err;
if (info->flags & ~XT_SOCKET_FLAGS_V1) {
pr_info("unknown flags 0x%x\n", info->flags & ~XT_SOCKET_FLAGS_V1);
@@ -432,6 +451,11 @@ static int socket_mt_v1_check(const struct xt_mtchk_param *par)
static int socket_mt_v2_check(const struct xt_mtchk_param *par)
{
const struct xt_socket_mtinfo2 *info = (struct xt_socket_mtinfo2 *) par->matchinfo;
+ int err;
+
+ err = socket_mt_enable_defrag(par->net, par->family);
+ if (err)
+ return err;
if (info->flags & ~XT_SOCKET_FLAGS_V2) {
pr_info("unknown flags 0x%x\n", info->flags & ~XT_SOCKET_FLAGS_V2);
@@ -444,7 +468,11 @@ static int socket_mt_v3_check(const struct xt_mtchk_param *par)
{
const struct xt_socket_mtinfo3 *info =
(struct xt_socket_mtinfo3 *)par->matchinfo;
+ int err;
+ err = socket_mt_enable_defrag(par->net, par->family);
+ if (err)
+ return err;
if (info->flags & ~XT_SOCKET_FLAGS_V3) {
pr_info("unknown flags 0x%x\n",
info->flags & ~XT_SOCKET_FLAGS_V3);
@@ -539,11 +567,6 @@ static struct xt_match socket_mt_reg[] __read_mostly = {
static int __init socket_mt_init(void)
{
- nf_defrag_ipv4_enable();
-#ifdef XT_SOCKET_HAVE_IPV6
- nf_defrag_ipv6_enable();
-#endif
-
return xt_register_matches(socket_mt_reg, ARRAY_SIZE(socket_mt_reg));
}
--
2.0.5
^ permalink raw reply related [flat|nested] 11+ messages in thread
* [PATCH -next 6/8] netfilter: nat: depend on conntrack module
2015-10-02 11:49 [PATCH -next 0/8] netfilter: don't copy init ns hooks to new namespaces Florian Westphal
` (4 preceding siblings ...)
2015-10-02 11:49 ` [PATCH -next 5/8] netfilter: defrag: only register defrag functionality if needed Florian Westphal
@ 2015-10-02 11:49 ` Florian Westphal
2015-10-02 11:49 ` [PATCH -next 7/8] netfilter: bridge: register hooks only when bridge interface is added Florian Westphal
2015-10-02 11:49 ` [PATCH -next 8/8] netfilter: don't call nf_hook_state_init/_hook_slow unless needed Florian Westphal
7 siblings, 0 replies; 11+ messages in thread
From: Florian Westphal @ 2015-10-02 11:49 UTC (permalink / raw)
To: netfilter-devel; +Cc: ebiederm, Florian Westphal
Technically this isn't needed, since MASQUERADE, S/DNAT, etc. targets
call functions that in one way or another depend on the conntrack module.
However, since the conntrack hooks are now registered in a lazy fashion
(i.e., only when needed) a symbol reference is not enough anymore.
Thus, when something is added to a nat table, make sure that it will see
packets by calling nf_ct_netns_get() which will register the conntrack
hooks in the current netns.
Another -- more elaborate solution -- is to move this refcounting to all
nat targets instead, i.e. S/DNAT, MASQUERADE and NETMAP.
However, the nat table is more of a 'configuration database' -- its a
sane assumption that if a rule is added to it it does involve one of the
targets listed above.
Signed-off-by: Florian Westphal <fw@strlen.de>
---
net/ipv4/netfilter/iptable_nat.c | 4 ++++
net/ipv6/netfilter/ip6table_nat.c | 4 ++++
2 files changed, 8 insertions(+)
diff --git a/net/ipv4/netfilter/iptable_nat.c b/net/ipv4/netfilter/iptable_nat.c
index 4e0cc89..f82db1c 100644
--- a/net/ipv4/netfilter/iptable_nat.c
+++ b/net/ipv4/netfilter/iptable_nat.c
@@ -110,6 +110,9 @@ static int __net_init iptable_nat_table_init(struct net *net)
if (net->ipv4.nat_table)
return 0;
+ ret = nf_ct_netns_get(net, NFPROTO_IPV4);
+ if (ret)
+ return ret;
repl = ipt_alloc_initial_table(&nf_nat_ipv4_table);
if (repl == NULL)
return -ENOMEM;
@@ -138,6 +141,7 @@ static void __net_exit iptable_nat_net_exit(struct net *net)
nf_unregister_net_hooks(net, nf_nat_ipv4_ops,
ARRAY_SIZE(nf_nat_ipv4_ops));
ipt_unregister_table(net, net->ipv4.nat_table);
+ nf_ct_netns_put(net, NFPROTO_IPV6);
net->ipv4.nat_table = NULL;
}
diff --git a/net/ipv6/netfilter/ip6table_nat.c b/net/ipv6/netfilter/ip6table_nat.c
index ee6a3fe..f40302a 100644
--- a/net/ipv6/netfilter/ip6table_nat.c
+++ b/net/ipv6/netfilter/ip6table_nat.c
@@ -112,6 +112,9 @@ static int __net_init ip6table_nat_table_init(struct net *net)
if (net->ipv6.ip6table_nat)
return 0;
+ ret = nf_ct_netns_get(net, NFPROTO_IPV6);
+ if (ret)
+ return ret;
repl = ip6t_alloc_initial_table(&nf_nat_ipv6_table);
if (repl == NULL)
return -ENOMEM;
@@ -141,6 +144,7 @@ static void __net_exit ip6table_nat_net_exit(struct net *net)
ARRAY_SIZE(nf_nat_ipv6_ops));
ip6t_unregister_table(net, net->ipv6.ip6table_nat);
net->ipv6.ip6table_nat = NULL;
+ nf_ct_netns_put(net, NFPROTO_IPV6);
}
static struct pernet_operations ip6table_nat_net_ops = {
--
2.0.5
^ permalink raw reply related [flat|nested] 11+ messages in thread
* [PATCH -next 7/8] netfilter: bridge: register hooks only when bridge interface is added
2015-10-02 11:49 [PATCH -next 0/8] netfilter: don't copy init ns hooks to new namespaces Florian Westphal
` (5 preceding siblings ...)
2015-10-02 11:49 ` [PATCH -next 6/8] netfilter: nat: depend on conntrack module Florian Westphal
@ 2015-10-02 11:49 ` Florian Westphal
2015-10-02 11:49 ` [PATCH -next 8/8] netfilter: don't call nf_hook_state_init/_hook_slow unless needed Florian Westphal
7 siblings, 0 replies; 11+ messages in thread
From: Florian Westphal @ 2015-10-02 11:49 UTC (permalink / raw)
To: netfilter-devel; +Cc: ebiederm, Florian Westphal
This moves the last 'common' hooks to a 'register only when needed'
scheme.
We use a device notifier to register all the 'call-iptables'
netfilter hooks only when a bridge gets added.
This means that if the initial namespace uses a bridge, newly created
network namespaces no longer 'inherit' the PRE_ROUTING ipt_sabotage hook,
instead it will only be registered in that network namespace if a bridge
is added within that namespace.
After this patch, only a handful of netfilter modules still use
global hooks:
- PF_BRIDGE hooks
- CLUSTER match (deprecated)
- ipvs hooks
- SYNPROXY
As long as these modules are not loaded/used, a new network namespace
has empty hook list and NF_HOOK() will boil down to single list_empty
test even if initial namespace does packet filtering, conntrack, etc.
Signed-off-by: Florian Westphal <fw@strlen.de>
---
net/bridge/br_netfilter_hooks.c | 68 +++++++++++++++++++++++++++++++++++++++--
1 file changed, 65 insertions(+), 3 deletions(-)
diff --git a/net/bridge/br_netfilter_hooks.c b/net/bridge/br_netfilter_hooks.c
index 13f0367..d299014 100644
--- a/net/bridge/br_netfilter_hooks.c
+++ b/net/bridge/br_netfilter_hooks.c
@@ -37,6 +37,7 @@
#include <net/addrconf.h>
#include <net/route.h>
#include <net/netfilter/br_netfilter.h>
+#include <net/netns/generic.h>
#include <asm/uaccess.h>
#include "br_private.h"
@@ -44,6 +45,12 @@
#include <linux/sysctl.h>
#endif
+static int brnf_net_id __read_mostly;
+
+struct brnf_net {
+ bool enabled;
+};
+
#ifdef CONFIG_SYSCTL
static struct ctl_table_header *brnf_sysctl_header;
static int brnf_call_iptables __read_mostly = 1;
@@ -958,6 +965,53 @@ static struct nf_hook_ops br_nf_ops[] __read_mostly = {
},
};
+static int brnf_device_event(struct notifier_block *unused, unsigned long event,
+ void *ptr)
+{
+ struct net_device *dev = netdev_notifier_info_to_dev(ptr);
+ struct brnf_net *brnet;
+ struct net *net;
+ int ret;
+
+ if (event != NETDEV_REGISTER || !(dev->priv_flags & IFF_EBRIDGE))
+ return NOTIFY_DONE;
+
+ ASSERT_RTNL();
+
+ net = dev_net(dev);
+ brnet = net_generic(net, brnf_net_id);
+ if (brnet->enabled)
+ return NOTIFY_OK;
+
+ ret = nf_register_net_hooks(net, br_nf_ops, ARRAY_SIZE(br_nf_ops));
+ if (ret)
+ return NOTIFY_BAD;
+
+ brnet->enabled = true;
+ return NOTIFY_OK;
+}
+
+static void __net_exit brnf_exit_net(struct net *net)
+{
+ struct brnf_net *brnet = net_generic(net, brnf_net_id);
+
+ if (!brnet->enabled)
+ return;
+
+ nf_unregister_net_hooks(net, br_nf_ops, ARRAY_SIZE(br_nf_ops));
+ brnet->enabled = false;
+}
+
+static struct pernet_operations brnf_net_ops __read_mostly = {
+ .exit = brnf_exit_net,
+ .id = &brnf_net_id,
+ .size = sizeof(struct brnf_net),
+};
+
+static struct notifier_block brnf_notifier __read_mostly = {
+ .notifier_call = brnf_device_event,
+};
+
#ifdef CONFIG_SYSCTL
static
int brnf_sysctl_call_tables(struct ctl_table *ctl, int write,
@@ -1023,16 +1077,23 @@ static int __init br_netfilter_init(void)
{
int ret;
- ret = nf_register_hooks(br_nf_ops, ARRAY_SIZE(br_nf_ops));
+ ret = register_pernet_subsys(&brnf_net_ops);
if (ret < 0)
return ret;
+ ret = register_netdevice_notifier(&brnf_notifier);
+ if (ret < 0) {
+ unregister_pernet_subsys(&brnf_net_ops);
+ return ret;
+ }
+
#ifdef CONFIG_SYSCTL
brnf_sysctl_header = register_net_sysctl(&init_net, "net/bridge", brnf_table);
if (brnf_sysctl_header == NULL) {
printk(KERN_WARNING
"br_netfilter: can't register to sysctl.\n");
- nf_unregister_hooks(br_nf_ops, ARRAY_SIZE(br_nf_ops));
+ unregister_netdevice_notifier(&brnf_notifier);
+ unregister_pernet_subsys(&brnf_net_ops);
return -ENOMEM;
}
#endif
@@ -1044,7 +1105,8 @@ static int __init br_netfilter_init(void)
static void __exit br_netfilter_fini(void)
{
RCU_INIT_POINTER(nf_br_ops, NULL);
- nf_unregister_hooks(br_nf_ops, ARRAY_SIZE(br_nf_ops));
+ unregister_netdevice_notifier(&brnf_notifier);
+ unregister_pernet_subsys(&brnf_net_ops);
#ifdef CONFIG_SYSCTL
unregister_net_sysctl_table(brnf_sysctl_header);
#endif
--
2.0.5
^ permalink raw reply related [flat|nested] 11+ messages in thread
* [PATCH -next 8/8] netfilter: don't call nf_hook_state_init/_hook_slow unless needed
2015-10-02 11:49 [PATCH -next 0/8] netfilter: don't copy init ns hooks to new namespaces Florian Westphal
` (6 preceding siblings ...)
2015-10-02 11:49 ` [PATCH -next 7/8] netfilter: bridge: register hooks only when bridge interface is added Florian Westphal
@ 2015-10-02 11:49 ` Florian Westphal
7 siblings, 0 replies; 11+ messages in thread
From: Florian Westphal @ 2015-10-02 11:49 UTC (permalink / raw)
To: netfilter-devel; +Cc: ebiederm, Florian Westphal
With the previous patches in place, a netns nf_hook_list might be empty,
even if e.g. init_net performs filtering/conntrack.
Thus, change nf_hook_thresh to check the hook_list as well before
initializing hook_state and calling nf_hook_slow().
We still make use of static keys, if no netfilter hooks are loaded
we can elide further testing since list is guaranteed to be empty.
Signed-off-by: Florian Westphal <fw@strlen.de>
---
include/linux/netfilter.h | 29 +++++++++++------------------
1 file changed, 11 insertions(+), 18 deletions(-)
diff --git a/include/linux/netfilter.h b/include/linux/netfilter.h
index 165ab2d..3f2d9b8 100644
--- a/include/linux/netfilter.h
+++ b/include/linux/netfilter.h
@@ -142,22 +142,6 @@ void nf_unregister_sockopt(struct nf_sockopt_ops *reg);
#ifdef HAVE_JUMP_LABEL
extern struct static_key nf_hooks_needed[NFPROTO_NUMPROTO][NF_MAX_HOOKS];
-
-static inline bool nf_hook_list_active(struct list_head *hook_list,
- u_int8_t pf, unsigned int hook)
-{
- if (__builtin_constant_p(pf) &&
- __builtin_constant_p(hook))
- return static_key_false(&nf_hooks_needed[pf][hook]);
-
- return !list_empty(hook_list);
-}
-#else
-static inline bool nf_hook_list_active(struct list_head *hook_list,
- u_int8_t pf, unsigned int hook)
-{
- return !list_empty(hook_list);
-}
#endif
int nf_hook_slow(struct sk_buff *skb, struct nf_hook_state *state);
@@ -178,9 +162,18 @@ static inline int nf_hook_thresh(u_int8_t pf, unsigned int hook,
int (*okfn)(struct net *, struct sock *, struct sk_buff *),
int thresh)
{
- struct list_head *hook_list = &net->nf.hooks[pf][hook];
+ struct list_head *hook_list;
+
+#ifdef HAVE_JUMP_LABEL
+ if (__builtin_constant_p(pf) &&
+ __builtin_constant_p(hook) &&
+ !static_key_false(&nf_hooks_needed[pf][hook]))
+ return 1;
+#endif
+
+ hook_list = &net->nf.hooks[pf][hook];
- if (nf_hook_list_active(hook_list, pf, hook)) {
+ if (!list_empty(hook_list)) {
struct nf_hook_state state;
nf_hook_state_init(&state, hook_list, hook, thresh,
--
2.0.5
^ permalink raw reply related [flat|nested] 11+ messages in thread
* Re: [PATCH -next 2/8] netfilter: add and use nf_ct_netns_get/put
2015-10-02 11:49 ` [PATCH -next 2/8] netfilter: add and use nf_ct_netns_get/put Florian Westphal
@ 2015-10-03 10:46 ` Jan Engelhardt
0 siblings, 0 replies; 11+ messages in thread
From: Jan Engelhardt @ 2015-10-03 10:46 UTC (permalink / raw)
To: Florian Westphal; +Cc: netfilter-devel, ebiederm
On Friday 2015-10-02 13:49, Florian Westphal wrote:
>diff --git a/net/ipv4/netfilter/ipt_SYNPROXY.c b/net/ipv4/netfilter/ipt_SYNPROXY.c
>index 6a6e762..01a2322 100644
>--- a/net/ipv4/netfilter/ipt_SYNPROXY.c
>+++ b/net/ipv4/netfilter/ipt_SYNPROXY.c
>@@ -415,12 +415,12 @@ static int synproxy_tg4_check(const struct xt_tgchk_param *par)
> e->ip.invflags & XT_INV_PROTO)
> return -EINVAL;
>
>- return nf_ct_l3proto_try_module_get(par->family);
>+ return nf_ct_netns_get(par->net, NFPROTO_IPV4);
> }
>
> static void synproxy_tg4_destroy(const struct xt_tgdtor_param *par)
> {
>- nf_ct_l3proto_module_put(par->family);
>+ nf_ct_netns_put(par->net, NFPROTO_IPV4);
> }
>
> static struct xt_target synproxy_tg4_reg __read_mostly = {
In the other places (like ip6t_SYNPROXY, xt_CONNSECMARK, ..), you chose
par->family, why hardcode NFPROTO_IPV4 in just this place?
^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [PATCH -next 4/8] netfilter: xtables: don't register table hooks in namespace at init time
2015-10-02 11:49 ` [PATCH -next 4/8] netfilter: xtables: don't register table hooks in namespace at init time Florian Westphal
@ 2015-10-04 19:58 ` Pablo Neira Ayuso
0 siblings, 0 replies; 11+ messages in thread
From: Pablo Neira Ayuso @ 2015-10-04 19:58 UTC (permalink / raw)
To: Florian Westphal; +Cc: netfilter-devel, ebiederm
On Fri, Oct 02, 2015 at 01:49:13PM +0200, Florian Westphal wrote:
> delay hook registration until the table is being requested inside a
> namespace.
>
> Historically, a particular table (iptables mangle, ip6tables filter,
> etc) was registered on module load.
>
> When netns support was added to iptables only the ip/ip6tables ruleset
> was made namespace aware, not the actual hook points.
>
> This means f.e. that when ipt_filter table/module is loaded on a system,
> then each namespace on that system has an (empty) iptables filter ruleset.
>
> In other words, if a namespace sends a packet, such skb is 'caught'
> by netfilter machinery and fed to hooking points for that table
> (i.e. INPUT, FORWARD, etc).
>
> Thanks to Eric Biederman, hooks are no longer global, but per namespace.
>
> This means that we can avoid allocation of empty ruleset in a namespace
> and defer hook registration until we need the functionality.
>
> We register a tables hook entry points ONLY in the initial namespace.
> When an iptables get/setockopt is issued inside a given namespace,
> we check if the table is found in the per-namespace list.
>
> If not, we attempt to find it in the initial namespace, and,
> if found, create an empty default table in the requesting namespace
> and register the needed hooks.
>
> Hook points are destroyed only once namespace is deleted, there is no
> 'usage count' (it makes no sense since there is no 'remove table'
> operation in xtables api).
>
> Signed-off-by: Florian Westphal <fw@strlen.de>
> ---
> include/linux/netfilter/x_tables.h | 10 ++++-
> net/ipv4/netfilter/arptable_filter.c | 39 +++++++++++-------
> net/ipv4/netfilter/iptable_filter.c | 65 ++++++++++++++++++++++--------
> net/ipv4/netfilter/iptable_mangle.c | 50 ++++++++++++++++++-----
> net/ipv4/netfilter/iptable_nat.c | 51 ++++++++++++++++--------
> net/ipv4/netfilter/iptable_raw.c | 50 ++++++++++++++++++-----
> net/ipv4/netfilter/iptable_security.c | 52 +++++++++++++++++-------
> net/ipv6/netfilter/ip6table_filter.c | 54 ++++++++++++++++++-------
> net/ipv6/netfilter/ip6table_mangle.c | 53 +++++++++++++++++-------
> net/ipv6/netfilter/ip6table_nat.c | 51 ++++++++++++++++--------
> net/ipv6/netfilter/ip6table_raw.c | 54 ++++++++++++++++++-------
> net/ipv6/netfilter/ip6table_security.c | 53 +++++++++++++++++-------
> net/netfilter/x_tables.c | 73 +++++++++++++++++++++++++---------
> 13 files changed, 475 insertions(+), 180 deletions(-)
Can we get this smaller by performing the same netns hook registration
from xx_register_table()?
I remember the NAT table was specifically problematic when I sent my
RFC patchset to add per-netns hook, but it just required some previous
refactoring to handle that particular thing.
> @@ -103,16 +109,33 @@ static int __net_init iptable_mangle_net_init(struct net *net)
> net->ipv4.iptable_mangle =
> ipt_register_table(net, &packet_mangler, repl);
> kfree(repl);
> - return PTR_ERR_OR_ZERO(net->ipv4.iptable_mangle);
> + ret = PTR_ERR_OR_ZERO(net->ipv4.iptable_mangle);
> + if (ret < 0)
> + goto err;
> + /* Register hooks */
> + ret = xt_hook_link_net(net, net->ipv4.iptable_mangle, mangle_ops);
> + if (ret) {
> + ipt_unregister_table(net, net->ipv4.iptable_mangle);
> + goto err;
> + }
> +
> + return ret;
> + err:
> + net->ipv4.iptable_mangle = NULL;
> + return ret;
> }
I'm refering to the code pattern above, it looks like it's repeated
several times.
Thanks.
^ permalink raw reply [flat|nested] 11+ messages in thread
end of thread, other threads:[~2015-10-04 19:51 UTC | newest]
Thread overview: 11+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2015-10-02 11:49 [PATCH -next 0/8] netfilter: don't copy init ns hooks to new namespaces Florian Westphal
2015-10-02 11:49 ` [PATCH -next 1/8] netfilter: ingress: don't use nf_hook_list_active Florian Westphal
2015-10-02 11:49 ` [PATCH -next 2/8] netfilter: add and use nf_ct_netns_get/put Florian Westphal
2015-10-03 10:46 ` Jan Engelhardt
2015-10-02 11:49 ` [PATCH -next 3/8] netfilter: conntrack: register hooks in netns when needed by ruleset Florian Westphal
2015-10-02 11:49 ` [PATCH -next 4/8] netfilter: xtables: don't register table hooks in namespace at init time Florian Westphal
2015-10-04 19:58 ` Pablo Neira Ayuso
2015-10-02 11:49 ` [PATCH -next 5/8] netfilter: defrag: only register defrag functionality if needed Florian Westphal
2015-10-02 11:49 ` [PATCH -next 6/8] netfilter: nat: depend on conntrack module Florian Westphal
2015-10-02 11:49 ` [PATCH -next 7/8] netfilter: bridge: register hooks only when bridge interface is added Florian Westphal
2015-10-02 11:49 ` [PATCH -next 8/8] netfilter: don't call nf_hook_state_init/_hook_slow unless needed Florian Westphal
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).