All of lore.kernel.org
 help / color / mirror / Atom feed
* [RFC 0/2] nftables iptables nat co-existence patches
@ 2017-06-29 12:28 Florian Westphal
  2017-06-29 12:28 ` [RFC PATCH 1/2] netfilter: add nf_nat_netns_get infrasstructure Florian Westphal
  2017-06-29 12:28 ` [RFC PATCH 2/2] nefilter: nf_nat: split nat rewriting from do_chain logic Florian Westphal
  0 siblings, 2 replies; 3+ messages in thread
From: Florian Westphal @ 2017-06-29 12:28 UTC (permalink / raw)
  To: netfilter-devel

... another round of changes we can discuss at nfws.
This allows to use both iptables and nft nat at the same time,
first match wins.


^ permalink raw reply	[flat|nested] 3+ messages in thread

* [RFC PATCH 1/2] netfilter: add nf_nat_netns_get infrasstructure
  2017-06-29 12:28 [RFC 0/2] nftables iptables nat co-existence patches Florian Westphal
@ 2017-06-29 12:28 ` Florian Westphal
  2017-06-29 12:28 ` [RFC PATCH 2/2] nefilter: nf_nat: split nat rewriting from do_chain logic Florian Westphal
  1 sibling, 0 replies; 3+ messages in thread
From: Florian Westphal @ 2017-06-29 12:28 UTC (permalink / raw)
  To: netfilter-devel; +Cc: Florian Westphal

This adds nf_nat_netns_get and put helpers, and uses them in the nft
expressions and xtables targets that deal with nat setup.

The new helpers are just aliases for nf_ct_netns_get/put, this is done
to keep the actual change -- per netns register of nat rewrite hooks --
separate.

Signed-off-by: Florian Westphal <fw@strlen.de>
---
 include/net/netfilter/nf_nat.h      |  4 ++++
 net/ipv4/netfilter/ipt_MASQUERADE.c |  4 ++--
 net/ipv4/netfilter/nft_masq_ipv4.c  |  2 +-
 net/ipv4/netfilter/nft_redir_ipv4.c |  2 +-
 net/ipv6/netfilter/nft_masq_ipv6.c  |  2 +-
 net/ipv6/netfilter/nft_redir_ipv6.c |  2 +-
 net/netfilter/nf_nat_core.c         | 24 ++++++++++++++++++++++++
 net/netfilter/nft_masq.c            |  2 +-
 net/netfilter/nft_nat.c             |  4 ++--
 net/netfilter/nft_redir.c           |  2 +-
 net/netfilter/xt_NETMAP.c           |  6 +++---
 net/netfilter/xt_REDIRECT.c         |  6 +++---
 net/netfilter/xt_nat.c              |  6 +++---
 13 files changed, 47 insertions(+), 19 deletions(-)

diff --git a/include/net/netfilter/nf_nat.h b/include/net/netfilter/nf_nat.h
index 05c82a1a4267..5f67507dc381 100644
--- a/include/net/netfilter/nf_nat.h
+++ b/include/net/netfilter/nf_nat.h
@@ -47,6 +47,10 @@ extern unsigned int nf_nat_alloc_null_binding(struct nf_conn *ct,
 
 struct nf_conn_nat *nf_ct_nat_ext_add(struct nf_conn *ct);
 
+/* net namespace needs NAT hooks */
+int nf_nat_netns_get(struct net *net, u8 nfproto);
+void nf_nat_netns_put(struct net *net, u8 nfproto);
+
 /* Is this tuple already taken? (not by us)*/
 int nf_nat_used_tuple(const struct nf_conntrack_tuple *tuple,
 		      const struct nf_conn *ignored_conntrack);
diff --git a/net/ipv4/netfilter/ipt_MASQUERADE.c b/net/ipv4/netfilter/ipt_MASQUERADE.c
index a03e4e7ef5f9..a4d2515c835b 100644
--- a/net/ipv4/netfilter/ipt_MASQUERADE.c
+++ b/net/ipv4/netfilter/ipt_MASQUERADE.c
@@ -41,7 +41,7 @@ static int masquerade_tg_check(const struct xt_tgchk_param *par)
 		pr_debug("bad rangesize %u\n", mr->rangesize);
 		return -EINVAL;
 	}
-	return nf_ct_netns_get(par->net, par->family);
+	return nf_nat_netns_get(par->net, par->family);
 }
 
 static unsigned int
@@ -61,7 +61,7 @@ masquerade_tg(struct sk_buff *skb, const struct xt_action_param *par)
 
 static void masquerade_tg_destroy(const struct xt_tgdtor_param *par)
 {
-	nf_ct_netns_put(par->net, par->family);
+	nf_nat_netns_put(par->net, par->family);
 }
 
 static struct xt_target masquerade_tg_reg __read_mostly = {
diff --git a/net/ipv4/netfilter/nft_masq_ipv4.c b/net/ipv4/netfilter/nft_masq_ipv4.c
index f18677277119..165107887039 100644
--- a/net/ipv4/netfilter/nft_masq_ipv4.c
+++ b/net/ipv4/netfilter/nft_masq_ipv4.c
@@ -38,7 +38,7 @@ static void nft_masq_ipv4_eval(const struct nft_expr *expr,
 static void
 nft_masq_ipv4_destroy(const struct nft_ctx *ctx, const struct nft_expr *expr)
 {
-	nf_ct_netns_put(ctx->net, NFPROTO_IPV4);
+	nf_nat_netns_put(ctx->net, NFPROTO_IPV4);
 }
 
 static struct nft_expr_type nft_masq_ipv4_type;
diff --git a/net/ipv4/netfilter/nft_redir_ipv4.c b/net/ipv4/netfilter/nft_redir_ipv4.c
index 5120be1d3118..a2220ef2158e 100644
--- a/net/ipv4/netfilter/nft_redir_ipv4.c
+++ b/net/ipv4/netfilter/nft_redir_ipv4.c
@@ -41,7 +41,7 @@ static void nft_redir_ipv4_eval(const struct nft_expr *expr,
 static void
 nft_redir_ipv4_destroy(const struct nft_ctx *ctx, const struct nft_expr *expr)
 {
-	nf_ct_netns_put(ctx->net, NFPROTO_IPV4);
+	nf_nat_netns_put(ctx->net, NFPROTO_IPV4);
 }
 
 static struct nft_expr_type nft_redir_ipv4_type;
diff --git a/net/ipv6/netfilter/nft_masq_ipv6.c b/net/ipv6/netfilter/nft_masq_ipv6.c
index 4146536e9c15..d2cdbdcfd6d7 100644
--- a/net/ipv6/netfilter/nft_masq_ipv6.c
+++ b/net/ipv6/netfilter/nft_masq_ipv6.c
@@ -39,7 +39,7 @@ static void nft_masq_ipv6_eval(const struct nft_expr *expr,
 static void
 nft_masq_ipv6_destroy(const struct nft_ctx *ctx, const struct nft_expr *expr)
 {
-	nf_ct_netns_put(ctx->net, NFPROTO_IPV6);
+	nf_nat_netns_put(ctx->net, NFPROTO_IPV6);
 }
 
 static struct nft_expr_type nft_masq_ipv6_type;
diff --git a/net/ipv6/netfilter/nft_redir_ipv6.c b/net/ipv6/netfilter/nft_redir_ipv6.c
index a27e424f690d..56c2c5501623 100644
--- a/net/ipv6/netfilter/nft_redir_ipv6.c
+++ b/net/ipv6/netfilter/nft_redir_ipv6.c
@@ -42,7 +42,7 @@ static void nft_redir_ipv6_eval(const struct nft_expr *expr,
 static void
 nft_redir_ipv6_destroy(const struct nft_ctx *ctx, const struct nft_expr *expr)
 {
-	nf_ct_netns_put(ctx->net, NFPROTO_IPV6);
+	nf_nat_netns_put(ctx->net, NFPROTO_IPV6);
 }
 
 static struct nft_expr_type nft_redir_ipv6_type;
diff --git a/net/netfilter/nf_nat_core.c b/net/netfilter/nf_nat_core.c
index d26cc2f864e6..cb81a561e9d0 100644
--- a/net/netfilter/nf_nat_core.c
+++ b/net/netfilter/nf_nat_core.c
@@ -818,6 +818,30 @@ static struct nf_ct_helper_expectfn follow_master_nat = {
 	.expectfn	= nf_nat_follow_master,
 };
 
+int nf_nat_netns_get(struct net *net, u8 nfproto)
+{
+	int ret;
+
+	if (WARN_ON(nfproto >= ARRAY_SIZE(nat_net->users)))
+		return -EINVAL;
+
+	ret = nf_ct_netns_get(net, nfproto);
+	if (ret < 0)
+		return ret;
+
+	return ret;
+}
+EXPORT_SYMBOL_GPL(nf_nat_netns_get);
+
+void nf_nat_netns_put(struct net *net, u8 nfproto)
+{
+	if (WARN_ON(nfproto >= ARRAY_SIZE(nat_net->users)))
+		goto out;
+out:
+	nf_ct_netns_put(net, nfproto);
+}
+EXPORT_SYMBOL_GPL(nf_nat_netns_put);
+
 static int __init nf_nat_init(void)
 {
 	int ret;
diff --git a/net/netfilter/nft_masq.c b/net/netfilter/nft_masq.c
index 6ac03d4266c9..849103923ca2 100644
--- a/net/netfilter/nft_masq.c
+++ b/net/netfilter/nft_masq.c
@@ -73,7 +73,7 @@ int nft_masq_init(const struct nft_ctx *ctx,
 		}
 	}
 
-	return nf_ct_netns_get(ctx->net, ctx->afi->family);
+	return nf_nat_netns_get(ctx->net, ctx->afi->family);
 }
 EXPORT_SYMBOL_GPL(nft_masq_init);
 
diff --git a/net/netfilter/nft_nat.c b/net/netfilter/nft_nat.c
index ed548d06b6dd..19cc0a9cc624 100644
--- a/net/netfilter/nft_nat.c
+++ b/net/netfilter/nft_nat.c
@@ -205,7 +205,7 @@ static int nft_nat_init(const struct nft_ctx *ctx, const struct nft_expr *expr,
 			return -EINVAL;
 	}
 
-	return nf_ct_netns_get(ctx->net, family);
+	return nf_nat_netns_get(ctx->net, family);
 }
 
 static int nft_nat_dump(struct sk_buff *skb, const struct nft_expr *expr)
@@ -258,7 +258,7 @@ nft_nat_destroy(const struct nft_ctx *ctx, const struct nft_expr *expr)
 {
 	const struct nft_nat *priv = nft_expr_priv(expr);
 
-	nf_ct_netns_put(ctx->net, priv->family);
+	nf_nat_netns_put(ctx->net, priv->family);
 }
 
 static struct nft_expr_type nft_nat_type;
diff --git a/net/netfilter/nft_redir.c b/net/netfilter/nft_redir.c
index 1e66538bf0ff..06db5f027fc4 100644
--- a/net/netfilter/nft_redir.c
+++ b/net/netfilter/nft_redir.c
@@ -75,7 +75,7 @@ int nft_redir_init(const struct nft_ctx *ctx,
 			return -EINVAL;
 	}
 
-	return nf_ct_netns_get(ctx->net, ctx->afi->family);
+	return nf_nat_netns_get(ctx->net, ctx->afi->family);
 }
 EXPORT_SYMBOL_GPL(nft_redir_init);
 
diff --git a/net/netfilter/xt_NETMAP.c b/net/netfilter/xt_NETMAP.c
index e45a01255e70..b5dc9dcad34f 100644
--- a/net/netfilter/xt_NETMAP.c
+++ b/net/netfilter/xt_NETMAP.c
@@ -60,12 +60,12 @@ static int netmap_tg6_checkentry(const struct xt_tgchk_param *par)
 
 	if (!(range->flags & NF_NAT_RANGE_MAP_IPS))
 		return -EINVAL;
-	return nf_ct_netns_get(par->net, par->family);
+	return nf_nat_netns_get(par->net, par->family);
 }
 
 static void netmap_tg_destroy(const struct xt_tgdtor_param *par)
 {
-	nf_ct_netns_put(par->net, par->family);
+	nf_nat_netns_put(par->net, par->family);
 }
 
 static unsigned int
@@ -116,7 +116,7 @@ static int netmap_tg4_check(const struct xt_tgchk_param *par)
 		pr_debug("bad rangesize %u.\n", mr->rangesize);
 		return -EINVAL;
 	}
-	return nf_ct_netns_get(par->net, par->family);
+	return nf_nat_netns_get(par->net, par->family);
 }
 
 static struct xt_target netmap_tg_reg[] __read_mostly = {
diff --git a/net/netfilter/xt_REDIRECT.c b/net/netfilter/xt_REDIRECT.c
index 98a4c6d4f1cb..b5a7afd8d9fd 100644
--- a/net/netfilter/xt_REDIRECT.c
+++ b/net/netfilter/xt_REDIRECT.c
@@ -41,12 +41,12 @@ static int redirect_tg6_checkentry(const struct xt_tgchk_param *par)
 	if (range->flags & NF_NAT_RANGE_MAP_IPS)
 		return -EINVAL;
 
-	return nf_ct_netns_get(par->net, par->family);
+	return nf_nat_netns_get(par->net, par->family);
 }
 
 static void redirect_tg_destroy(const struct xt_tgdtor_param *par)
 {
-	nf_ct_netns_put(par->net, par->family);
+	nf_nat_netns_put(par->net, par->family);
 }
 
 /* FIXME: Take multiple ranges --RR */
@@ -62,7 +62,7 @@ static int redirect_tg4_check(const struct xt_tgchk_param *par)
 		pr_debug("bad rangesize %u.\n", mr->rangesize);
 		return -EINVAL;
 	}
-	return nf_ct_netns_get(par->net, par->family);
+	return nf_nat_netns_get(par->net, par->family);
 }
 
 static unsigned int
diff --git a/net/netfilter/xt_nat.c b/net/netfilter/xt_nat.c
index 8107b3eb865f..379a31aa1aae 100644
--- a/net/netfilter/xt_nat.c
+++ b/net/netfilter/xt_nat.c
@@ -23,17 +23,17 @@ static int xt_nat_checkentry_v0(const struct xt_tgchk_param *par)
 			par->target->name);
 		return -EINVAL;
 	}
-	return nf_ct_netns_get(par->net, par->family);
+	return nf_nat_netns_get(par->net, par->family);
 }
 
 static int xt_nat_checkentry(const struct xt_tgchk_param *par)
 {
-	return nf_ct_netns_get(par->net, par->family);
+	return nf_nat_netns_get(par->net, par->family);
 }
 
 static void xt_nat_destroy(const struct xt_tgdtor_param *par)
 {
-	nf_ct_netns_put(par->net, par->family);
+	nf_nat_netns_put(par->net, par->family);
 }
 
 static void xt_nat_convert_range(struct nf_nat_range *dst,
-- 
2.13.0


^ permalink raw reply related	[flat|nested] 3+ messages in thread

* [RFC PATCH 2/2] nefilter: nf_nat: split nat rewriting from do_chain logic
  2017-06-29 12:28 [RFC 0/2] nftables iptables nat co-existence patches Florian Westphal
  2017-06-29 12:28 ` [RFC PATCH 1/2] netfilter: add nf_nat_netns_get infrasstructure Florian Westphal
@ 2017-06-29 12:28 ` Florian Westphal
  1 sibling, 0 replies; 3+ messages in thread
From: Florian Westphal @ 2017-06-29 12:28 UTC (permalink / raw)
  To: netfilter-devel; +Cc: Florian Westphal

Currently the packet rewrite and instantiation of nat NULL bindings
happens from the protocol specific nat backend.

Problem is that this means invocation occurs either via ip(6)table_nat
or the nf_tables nat chain type.

This is a problem for two reasons:
1. Can't use iptables nat and nf_tables nat at the same time,
   as the first user adds a nat binding (we add a NULL binding
   if no nat rule matched so we can detect post-nat tuple collisions).
2. If you use e.g. nft_masq, snat, redir, etc. you need to register
an empty base chain so that the nat core does the reverse translation.

After this change, the nat core deals with null bindings and reverse
translation.  If both iptables and nftables nat exists, the first matching
one is used.

The rewrite/null addition hooks get added/removed once a net namespace
installs the first nat rule in either nftables or xtables nat tables.

The downside of this change is that we need one more hook function in all
the ip stack hook points (except forward),

Signed-off-by: Florian Westphal <fw@strlen.de>
---
 net/ipv4/netfilter/nf_nat_l3proto_ipv4.c |  32 +-----
 net/ipv6/netfilter/nf_nat_l3proto_ipv6.c |  29 +-----
 net/netfilter/nf_nat_core.c              | 174 +++++++++++++++++++++++++++++++
 3 files changed, 179 insertions(+), 56 deletions(-)

diff --git a/net/ipv4/netfilter/nf_nat_l3proto_ipv4.c b/net/ipv4/netfilter/nf_nat_l3proto_ipv4.c
index feedd759ca80..1e0a6f2ef74a 100644
--- a/net/ipv4/netfilter/nf_nat_l3proto_ipv4.c
+++ b/net/ipv4/netfilter/nf_nat_l3proto_ipv4.c
@@ -251,7 +251,6 @@ nf_nat_ipv4_fn(void *priv, struct sk_buff *skb,
 {
 	struct nf_conn *ct;
 	enum ip_conntrack_info ctinfo;
-	struct nf_conn_nat *nat;
 	/* maniptype == SRC for postrouting. */
 	enum nf_nat_manip_type maniptype = HOOK2MANIP(state->hook);
 
@@ -264,8 +263,6 @@ nf_nat_ipv4_fn(void *priv, struct sk_buff *skb,
 	if (!ct)
 		return NF_ACCEPT;
 
-	nat = nfct_nat(ct);
-
 	switch (ctinfo) {
 	case IP_CT_RELATED:
 	case IP_CT_RELATED_REPLY:
@@ -287,36 +284,13 @@ nf_nat_ipv4_fn(void *priv, struct sk_buff *skb,
 			ret = do_chain(priv, skb, state, ct);
 			if (ret != NF_ACCEPT)
 				return ret;
-
-			if (nf_nat_initialized(ct, HOOK2MANIP(state->hook)))
-				break;
-
-			ret = nf_nat_alloc_null_binding(ct, state->hook);
-			if (ret != NF_ACCEPT)
-				return ret;
-		} else {
-			pr_debug("Already setup manip %s for ct %p\n",
-				 maniptype == NF_NAT_MANIP_SRC ? "SRC" : "DST",
-				 ct);
-			if (nf_nat_oif_changed(state->hook, ctinfo, nat,
-					       state->out))
-				goto oif_changed;
 		}
 		break;
-
 	default:
-		/* ESTABLISHED */
-		NF_CT_ASSERT(ctinfo == IP_CT_ESTABLISHED ||
-			     ctinfo == IP_CT_ESTABLISHED_REPLY);
-		if (nf_nat_oif_changed(state->hook, ctinfo, nat, state->out))
-			goto oif_changed;
+		break;
 	}
 
-	return nf_nat_packet(ct, ctinfo, state->hook, skb);
-
-oif_changed:
-	nf_ct_kill_acct(ct, ctinfo, skb);
-	return NF_DROP;
+	return NF_ACCEPT;
 }
 EXPORT_SYMBOL_GPL(nf_nat_ipv4_fn);
 
@@ -436,8 +410,8 @@ static int __init nf_nat_l3proto_ipv4_init(void)
 	err = nf_nat_l3proto_register(&nf_nat_l3proto_ipv4);
 	if (err < 0)
 		goto err2;
-	return err;
 
+	return err;
 err2:
 	nf_nat_l4proto_unregister(NFPROTO_IPV4, &nf_nat_l4proto_icmp);
 err1:
diff --git a/net/ipv6/netfilter/nf_nat_l3proto_ipv6.c b/net/ipv6/netfilter/nf_nat_l3proto_ipv6.c
index b2b4f031b3a1..47ed866ec2e0 100644
--- a/net/ipv6/netfilter/nf_nat_l3proto_ipv6.c
+++ b/net/ipv6/netfilter/nf_nat_l3proto_ipv6.c
@@ -258,7 +258,6 @@ nf_nat_ipv6_fn(void *priv, struct sk_buff *skb,
 {
 	struct nf_conn *ct;
 	enum ip_conntrack_info ctinfo;
-	struct nf_conn_nat *nat;
 	enum nf_nat_manip_type maniptype = HOOK2MANIP(state->hook);
 	__be16 frag_off;
 	int hdrlen;
@@ -273,8 +272,6 @@ nf_nat_ipv6_fn(void *priv, struct sk_buff *skb,
 	if (!ct)
 		return NF_ACCEPT;
 
-	nat = nfct_nat(ct);
-
 	switch (ctinfo) {
 	case IP_CT_RELATED:
 	case IP_CT_RELATED_REPLY:
@@ -301,35 +298,13 @@ nf_nat_ipv6_fn(void *priv, struct sk_buff *skb,
 			ret = do_chain(priv, skb, state, ct);
 			if (ret != NF_ACCEPT)
 				return ret;
-
-			if (nf_nat_initialized(ct, HOOK2MANIP(state->hook)))
-				break;
-
-			ret = nf_nat_alloc_null_binding(ct, state->hook);
-			if (ret != NF_ACCEPT)
-				return ret;
-		} else {
-			pr_debug("Already setup manip %s for ct %p\n",
-				 maniptype == NF_NAT_MANIP_SRC ? "SRC" : "DST",
-				 ct);
-			if (nf_nat_oif_changed(state->hook, ctinfo, nat, state->out))
-				goto oif_changed;
 		}
 		break;
-
 	default:
-		/* ESTABLISHED */
-		NF_CT_ASSERT(ctinfo == IP_CT_ESTABLISHED ||
-			     ctinfo == IP_CT_ESTABLISHED_REPLY);
-		if (nf_nat_oif_changed(state->hook, ctinfo, nat, state->out))
-			goto oif_changed;
+		break;
 	}
 
-	return nf_nat_packet(ct, ctinfo, state->hook, skb);
-
-oif_changed:
-	nf_ct_kill_acct(ct, ctinfo, skb);
-	return NF_DROP;
+	return NF_ACCEPT;
 }
 EXPORT_SYMBOL_GPL(nf_nat_ipv6_fn);
 
diff --git a/net/netfilter/nf_nat_core.c b/net/netfilter/nf_nat_core.c
index cb81a561e9d0..533b30d8d76b 100644
--- a/net/netfilter/nf_nat_core.c
+++ b/net/netfilter/nf_nat_core.c
@@ -29,12 +29,14 @@
 #include <net/netfilter/nf_conntrack_l3proto.h>
 #include <net/netfilter/nf_conntrack_zones.h>
 #include <linux/netfilter/nf_nat.h>
+#include <uapi/linux/netfilter_ipv6.h>
 
 static DEFINE_MUTEX(nf_nat_proto_mutex);
 static const struct nf_nat_l3proto __rcu *nf_nat_l3protos[NFPROTO_NUMPROTO]
 						__read_mostly;
 static const struct nf_nat_l4proto __rcu **nf_nat_l4protos[NFPROTO_NUMPROTO]
 						__read_mostly;
+static int nat_net_id __read_mostly;
 
 struct nf_nat_conn_key {
 	const struct net *net;
@@ -42,6 +44,10 @@ struct nf_nat_conn_key {
 	const struct nf_conntrack_zone *zone;
 };
 
+struct nat_net {
+	unsigned int users[NFPROTO_NUMPROTO];
+};
+
 static struct rhltable nf_nat_bysource_table;
 
 inline const struct nf_nat_l3proto *
@@ -818,8 +824,97 @@ static struct nf_ct_helper_expectfn follow_master_nat = {
 	.expectfn	= nf_nat_follow_master,
 };
 
+static unsigned int nf_nat_do(void *priv, struct sk_buff *skb,
+			      const struct nf_hook_state *state)
+{
+	enum ip_conntrack_info ctinfo;
+	struct nf_conn *ct;
+	struct nf_conn_nat *nat;
+
+	ct = nf_ct_get(skb, &ctinfo);
+	if (!ct)
+		return NF_ACCEPT;
+
+	switch (ctinfo) {
+	case IP_CT_NEW:
+		if (!nf_nat_initialized(ct, HOOK2MANIP(state->hook))) {
+			int ret = nf_nat_alloc_null_binding(ct, state->hook);
+
+			if (ret != NF_ACCEPT)
+				return ret;
+			break;
+		}
+		/* fallthrough */
+	default:
+		nat = nfct_nat(ct);
+		if (nf_nat_oif_changed(state->hook, ctinfo, nat, state->out))
+			goto oif_changed;
+		break;
+	}
+
+	return nf_nat_packet(ct, ctinfo, state->hook, skb);
+oif_changed:
+	nf_ct_kill_acct(ct, ctinfo, skb);
+	return NF_DROP;
+}
+
+static struct nf_hook_ops nf_nat_ipv4_ops[] __read_mostly = {
+	{
+		.hook		= nf_nat_do,
+		.pf		= NFPROTO_IPV4,
+		.hooknum	= NF_INET_PRE_ROUTING,
+		.priority	= NF_IP_PRI_NAT_DST + 1,
+	},
+	{
+		.hook		= nf_nat_do,
+		.pf		= NFPROTO_IPV4,
+		.hooknum	= NF_INET_POST_ROUTING,
+		.priority	= NF_IP_PRI_NAT_SRC + 1,
+	},
+	{
+		.hook		= nf_nat_do,
+		.pf		= NFPROTO_IPV4,
+		.hooknum	= NF_INET_LOCAL_OUT,
+		.priority	= NF_IP_PRI_NAT_DST + 1,
+	},
+	{
+		.hook		= nf_nat_do,
+		.pf		= NFPROTO_IPV4,
+		.hooknum	= NF_INET_LOCAL_IN,
+		.priority	= NF_IP_PRI_NAT_SRC + 1,
+	},
+};
+
+static struct nf_hook_ops nf_nat_ipv6_ops[] __read_mostly = {
+	{
+		.hook		= nf_nat_do,
+		.pf		= NFPROTO_IPV6,
+		.hooknum	= NF_INET_PRE_ROUTING,
+		.priority	= NF_IP6_PRI_NAT_DST + 1,
+	},
+	{
+		.hook		= nf_nat_do,
+		.pf		= NFPROTO_IPV6,
+		.hooknum	= NF_INET_POST_ROUTING,
+		.priority	= NF_IP6_PRI_NAT_SRC + 1,
+	},
+	{
+		.hook		= nf_nat_do,
+		.pf		= NFPROTO_IPV6,
+		.hooknum	= NF_INET_LOCAL_OUT,
+		.priority	= NF_IP6_PRI_NAT_DST + 1,
+	},
+	{
+		.hook		= nf_nat_do,
+		.pf		= NFPROTO_IPV6,
+		.hooknum	= NF_INET_LOCAL_IN,
+		.priority	= NF_IP6_PRI_NAT_SRC + 1,
+	},
+};
+
 int nf_nat_netns_get(struct net *net, u8 nfproto)
 {
+	struct nat_net *nat_net = net_generic(net, nat_net_id);
 	int ret;
 
 	if (WARN_ON(nfproto >= ARRAY_SIZE(nat_net->users)))
@@ -829,19 +924,90 @@ int nf_nat_netns_get(struct net *net, u8 nfproto)
 	if (ret < 0)
 		return ret;
 
+	mutex_lock(&nf_nat_proto_mutex);
+	if (WARN_ON(nat_net->users[nfproto] == UINT_MAX)) {
+		ret = -EOVERFLOW;
+		goto err_unlock;
+	}
+
+	if (nat_net->users[nfproto] == 0) {
+		switch (nfproto) {
+		case NFPROTO_IPV4:
+			ret = nf_register_net_hooks(net, nf_nat_ipv4_ops,
+						    ARRAY_SIZE(nf_nat_ipv4_ops));
+			break;
+		case NFPROTO_IPV6:
+			ret = nf_register_net_hooks(net, nf_nat_ipv6_ops,
+						    ARRAY_SIZE(nf_nat_ipv6_ops));
+			break;
+		default:
+			ret = -EOPNOTSUPP;
+			break;
+		}
+
+		if (ret)
+			goto err_unlock;
+	}
+	nat_net->users[nfproto]++;
+	mutex_unlock(&nf_nat_proto_mutex);
+	return ret;
+
+err_unlock:
+	mutex_unlock(&nf_nat_proto_mutex);
+	nf_ct_netns_put(net, nfproto);
 	return ret;
 }
 EXPORT_SYMBOL_GPL(nf_nat_netns_get);
 
 void nf_nat_netns_put(struct net *net, u8 nfproto)
 {
+	struct nat_net *nat_net = net_generic(net, nat_net_id);
+
 	if (WARN_ON(nfproto >= ARRAY_SIZE(nat_net->users)))
 		goto out;
+
+	mutex_lock(&nf_nat_proto_mutex);
+	if (WARN_ON(nat_net->users[nfproto] == 0))
+		goto out_unlock;
+
+	nat_net->users[nfproto]--;
+	if (nat_net->users[nfproto])
+		goto out_unlock;
+
+	switch (nfproto) {
+	case NFPROTO_IPV4:
+		nf_unregister_net_hooks(net, nf_nat_ipv4_ops,
+					ARRAY_SIZE(nf_nat_ipv4_ops));
+		break;
+	case NFPROTO_IPV6:
+		nf_unregister_net_hooks(net, nf_nat_ipv6_ops,
+					ARRAY_SIZE(nf_nat_ipv6_ops));
+		break;
+	}
+out_unlock:
+	mutex_unlock(&nf_nat_proto_mutex);
 out:
 	nf_ct_netns_put(net, nfproto);
 }
 EXPORT_SYMBOL_GPL(nf_nat_netns_put);
 
+static void nat_net_exit(struct net *net)
+{
+	struct nat_net *nat_net = net_generic(net, nat_net_id);
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(nat_net->users); i++) {
+		if (nat_net->users[i])
+			nf_nat_netns_put(net, i);
+	}
+}
+
+static struct pernet_operations nat_net_ops = {
+	.exit = nat_net_exit,
+	.id = &nat_net_id,
+	.size = sizeof(struct nat_net),
+};
+
 static int __init nf_nat_init(void)
 {
 	int ret;
@@ -857,6 +1023,13 @@ static int __init nf_nat_init(void)
 		return ret;
 	}
 
+	ret = register_pernet_subsys(&nat_net_ops);
+	if (ret < 0) {
+		nf_ct_extend_unregister(&nat_extend);
+		rhltable_destroy(&nf_nat_bysource_table);
+		return ret;
+	}
+
 	nf_ct_helper_expectfn_register(&follow_master_nat);
 
 	BUG_ON(nfnetlink_parse_nat_setup_hook != NULL);
@@ -888,6 +1061,7 @@ static void __exit nf_nat_cleanup(void)
 		kfree(nf_nat_l4protos[i]);
 
 	rhltable_destroy(&nf_nat_bysource_table);
+	unregister_pernet_subsys(&nat_net_ops);
 }
 
 MODULE_LICENSE("GPL");
-- 
2.13.0


^ permalink raw reply related	[flat|nested] 3+ messages in thread

end of thread, other threads:[~2017-06-29 12:28 UTC | newest]

Thread overview: 3+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2017-06-29 12:28 [RFC 0/2] nftables iptables nat co-existence patches Florian Westphal
2017-06-29 12:28 ` [RFC PATCH 1/2] netfilter: add nf_nat_netns_get infrasstructure Florian Westphal
2017-06-29 12:28 ` [RFC PATCH 2/2] nefilter: nf_nat: split nat rewriting from do_chain logic Florian Westphal

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.