You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
147 lines
4.5 KiB
147 lines
4.5 KiB
7 years ago
|
From: Florian Westphal <fw@strlen.de>
|
||
|
Date: Fri, 8 Dec 2017 17:01:54 +0100
|
||
|
Subject: [PATCH] netfilter: core: only allow one nat hook per hook point
|
||
|
|
||
|
The netfilter NAT core cannot deal with more than one NAT hook per hook
|
||
|
location (prerouting, input ...), because the NAT hooks install a NAT null
|
||
|
binding in case the iptables nat table (iptable_nat hooks) or the
|
||
|
corresponding nftables chain (nft nat hooks) doesn't specify a nat
|
||
|
transformation.
|
||
|
|
||
|
Null bindings are needed to detect port collsisions between NAT-ed and
|
||
|
non-NAT-ed connections.
|
||
|
|
||
|
This causes nftables NAT rules to not work when iptable_nat module is
|
||
|
loaded, and vice versa because nat binding has already been attached
|
||
|
when the second nat hook is consulted.
|
||
|
|
||
|
The netfilter core is not really the correct location to handle this
|
||
|
(hooks are just hooks, the core has no notion of what kinds of side
|
||
|
effects a hook implements), but its the only place where we can check
|
||
|
for conflicts between both iptables hooks and nftables hooks without
|
||
|
adding dependencies.
|
||
|
|
||
|
So add nat annotation to hook_ops to describe those hooks that will
|
||
|
add NAT bindings and then make core reject if such a hook already exists.
|
||
|
The annotation fills a padding hole, in case further restrictions appar
|
||
|
we might change this to a 'u8 type' instead of bool.
|
||
|
|
||
|
iptables error if nft nat hook active:
|
||
|
iptables -t nat -A POSTROUTING -j MASQUERADE
|
||
|
iptables v1.4.21: can't initialize iptables table `nat': File exists
|
||
|
Perhaps iptables or your kernel needs to be upgraded.
|
||
|
|
||
|
nftables error if iptables nat table present:
|
||
|
nft -f /etc/nftables/ipv4-nat
|
||
|
/usr/etc/nftables/ipv4-nat:3:1-2: Error: Could not process rule: File exists
|
||
|
table nat {
|
||
|
^^
|
||
|
|
||
|
Signed-off-by: Florian Westphal <fw@strlen.de>
|
||
|
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
|
||
|
---
|
||
|
|
||
|
--- a/include/linux/netfilter.h
|
||
|
+++ b/include/linux/netfilter.h
|
||
|
@@ -67,6 +67,7 @@ struct nf_hook_ops {
|
||
|
struct net_device *dev;
|
||
|
void *priv;
|
||
|
u_int8_t pf;
|
||
|
+ bool nat_hook;
|
||
|
unsigned int hooknum;
|
||
|
/* Hooks are ordered in ascending priority. */
|
||
|
int priority;
|
||
|
--- a/net/ipv4/netfilter/iptable_nat.c
|
||
|
+++ b/net/ipv4/netfilter/iptable_nat.c
|
||
|
@@ -72,6 +72,7 @@ static const struct nf_hook_ops nf_nat_i
|
||
|
{
|
||
|
.hook = iptable_nat_ipv4_in,
|
||
|
.pf = NFPROTO_IPV4,
|
||
|
+ .nat_hook = true,
|
||
|
.hooknum = NF_INET_PRE_ROUTING,
|
||
|
.priority = NF_IP_PRI_NAT_DST,
|
||
|
},
|
||
|
@@ -79,6 +80,7 @@ static const struct nf_hook_ops nf_nat_i
|
||
|
{
|
||
|
.hook = iptable_nat_ipv4_out,
|
||
|
.pf = NFPROTO_IPV4,
|
||
|
+ .nat_hook = true,
|
||
|
.hooknum = NF_INET_POST_ROUTING,
|
||
|
.priority = NF_IP_PRI_NAT_SRC,
|
||
|
},
|
||
|
@@ -86,6 +88,7 @@ static const struct nf_hook_ops nf_nat_i
|
||
|
{
|
||
|
.hook = iptable_nat_ipv4_local_fn,
|
||
|
.pf = NFPROTO_IPV4,
|
||
|
+ .nat_hook = true,
|
||
|
.hooknum = NF_INET_LOCAL_OUT,
|
||
|
.priority = NF_IP_PRI_NAT_DST,
|
||
|
},
|
||
|
@@ -93,6 +96,7 @@ static const struct nf_hook_ops nf_nat_i
|
||
|
{
|
||
|
.hook = iptable_nat_ipv4_fn,
|
||
|
.pf = NFPROTO_IPV4,
|
||
|
+ .nat_hook = true,
|
||
|
.hooknum = NF_INET_LOCAL_IN,
|
||
|
.priority = NF_IP_PRI_NAT_SRC,
|
||
|
},
|
||
|
--- a/net/ipv6/netfilter/ip6table_nat.c
|
||
|
+++ b/net/ipv6/netfilter/ip6table_nat.c
|
||
|
@@ -74,6 +74,7 @@ static const struct nf_hook_ops nf_nat_i
|
||
|
{
|
||
|
.hook = ip6table_nat_in,
|
||
|
.pf = NFPROTO_IPV6,
|
||
|
+ .nat_hook = true,
|
||
|
.hooknum = NF_INET_PRE_ROUTING,
|
||
|
.priority = NF_IP6_PRI_NAT_DST,
|
||
|
},
|
||
|
@@ -81,6 +82,7 @@ static const struct nf_hook_ops nf_nat_i
|
||
|
{
|
||
|
.hook = ip6table_nat_out,
|
||
|
.pf = NFPROTO_IPV6,
|
||
|
+ .nat_hook = true,
|
||
|
.hooknum = NF_INET_POST_ROUTING,
|
||
|
.priority = NF_IP6_PRI_NAT_SRC,
|
||
|
},
|
||
|
@@ -88,12 +90,14 @@ static const struct nf_hook_ops nf_nat_i
|
||
|
{
|
||
|
.hook = ip6table_nat_local_fn,
|
||
|
.pf = NFPROTO_IPV6,
|
||
|
+ .nat_hook = true,
|
||
|
.hooknum = NF_INET_LOCAL_OUT,
|
||
|
.priority = NF_IP6_PRI_NAT_DST,
|
||
|
},
|
||
|
/* After packet filtering, change source */
|
||
|
{
|
||
|
.hook = ip6table_nat_fn,
|
||
|
+ .nat_hook = true,
|
||
|
.pf = NFPROTO_IPV6,
|
||
|
.hooknum = NF_INET_LOCAL_IN,
|
||
|
.priority = NF_IP6_PRI_NAT_SRC,
|
||
|
--- a/net/netfilter/core.c
|
||
|
+++ b/net/netfilter/core.c
|
||
|
@@ -135,6 +135,12 @@ nf_hook_entries_grow(const struct nf_hoo
|
||
|
++i;
|
||
|
continue;
|
||
|
}
|
||
|
+
|
||
|
+ if (reg->nat_hook && orig_ops[i]->nat_hook) {
|
||
|
+ kvfree(new);
|
||
|
+ return ERR_PTR(-EEXIST);
|
||
|
+ }
|
||
|
+
|
||
|
if (inserted || reg->priority > orig_ops[i]->priority) {
|
||
|
new_ops[nhooks] = (void *)orig_ops[i];
|
||
|
new->hooks[nhooks] = old->hooks[i];
|
||
|
--- a/net/netfilter/nf_tables_api.c
|
||
|
+++ b/net/netfilter/nf_tables_api.c
|
||
|
@@ -1400,6 +1400,8 @@ static int nf_tables_addchain(struct nft
|
||
|
ops->hook = hookfn;
|
||
|
if (afi->hook_ops_init)
|
||
|
afi->hook_ops_init(ops, i);
|
||
|
+ if (basechain->type->type == NFT_CHAIN_T_NAT)
|
||
|
+ ops->nat_hook = true;
|
||
|
}
|
||
|
|
||
|
chain->flags |= NFT_BASE_CHAIN;
|