1 From: Florian Westphal <fw@strlen.de>
2 Date: Fri, 8 Dec 2017 17:01:54 +0100
3 Subject: [PATCH] netfilter: core: only allow one nat hook per hook point
5 The netfilter NAT core cannot deal with more than one NAT hook per hook
6 location (prerouting, input ...), because the NAT hooks install a NAT null
7 binding in case the iptables nat table (iptable_nat hooks) or the
8 corresponding nftables chain (nft nat hooks) doesn't specify a nat
11 Null bindings are needed to detect port collsisions between NAT-ed and
12 non-NAT-ed connections.
14 This causes nftables NAT rules to not work when iptable_nat module is
15 loaded, and vice versa because nat binding has already been attached
16 when the second nat hook is consulted.
18 The netfilter core is not really the correct location to handle this
19 (hooks are just hooks, the core has no notion of what kinds of side
20 effects a hook implements), but its the only place where we can check
21 for conflicts between both iptables hooks and nftables hooks without
24 So add nat annotation to hook_ops to describe those hooks that will
25 add NAT bindings and then make core reject if such a hook already exists.
26 The annotation fills a padding hole, in case further restrictions appar
27 we might change this to a 'u8 type' instead of bool.
29 iptables error if nft nat hook active:
30 iptables -t nat -A POSTROUTING -j MASQUERADE
31 iptables v1.4.21: can't initialize iptables table `nat': File exists
32 Perhaps iptables or your kernel needs to be upgraded.
34 nftables error if iptables nat table present:
35 nft -f /etc/nftables/ipv4-nat
36 /usr/etc/nftables/ipv4-nat:3:1-2: Error: Could not process rule: File exists
40 Signed-off-by: Florian Westphal <fw@strlen.de>
41 Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
44 --- a/include/linux/netfilter.h
45 +++ b/include/linux/netfilter.h
46 @@ -67,6 +67,7 @@ struct nf_hook_ops {
47 struct net_device *dev;
52 /* Hooks are ordered in ascending priority. */
54 --- a/net/ipv4/netfilter/iptable_nat.c
55 +++ b/net/ipv4/netfilter/iptable_nat.c
56 @@ -72,6 +72,7 @@ static const struct nf_hook_ops nf_nat_i
58 .hook = iptable_nat_ipv4_in,
61 .hooknum = NF_INET_PRE_ROUTING,
62 .priority = NF_IP_PRI_NAT_DST,
64 @@ -79,6 +80,7 @@ static const struct nf_hook_ops nf_nat_i
66 .hook = iptable_nat_ipv4_out,
69 .hooknum = NF_INET_POST_ROUTING,
70 .priority = NF_IP_PRI_NAT_SRC,
72 @@ -86,6 +88,7 @@ static const struct nf_hook_ops nf_nat_i
74 .hook = iptable_nat_ipv4_local_fn,
77 .hooknum = NF_INET_LOCAL_OUT,
78 .priority = NF_IP_PRI_NAT_DST,
80 @@ -93,6 +96,7 @@ static const struct nf_hook_ops nf_nat_i
82 .hook = iptable_nat_ipv4_fn,
85 .hooknum = NF_INET_LOCAL_IN,
86 .priority = NF_IP_PRI_NAT_SRC,
88 --- a/net/ipv6/netfilter/ip6table_nat.c
89 +++ b/net/ipv6/netfilter/ip6table_nat.c
90 @@ -74,6 +74,7 @@ static const struct nf_hook_ops nf_nat_i
92 .hook = ip6table_nat_in,
95 .hooknum = NF_INET_PRE_ROUTING,
96 .priority = NF_IP6_PRI_NAT_DST,
98 @@ -81,6 +82,7 @@ static const struct nf_hook_ops nf_nat_i
100 .hook = ip6table_nat_out,
103 .hooknum = NF_INET_POST_ROUTING,
104 .priority = NF_IP6_PRI_NAT_SRC,
106 @@ -88,12 +90,14 @@ static const struct nf_hook_ops nf_nat_i
108 .hook = ip6table_nat_local_fn,
111 .hooknum = NF_INET_LOCAL_OUT,
112 .priority = NF_IP6_PRI_NAT_DST,
114 /* After packet filtering, change source */
116 .hook = ip6table_nat_fn,
119 .hooknum = NF_INET_LOCAL_IN,
120 .priority = NF_IP6_PRI_NAT_SRC,
121 --- a/net/netfilter/core.c
122 +++ b/net/netfilter/core.c
123 @@ -160,6 +160,12 @@ nf_hook_entries_grow(const struct nf_hoo
128 + if (reg->nat_hook && orig_ops[i]->nat_hook) {
130 + return ERR_PTR(-EEXIST);
133 if (inserted || reg->priority > orig_ops[i]->priority) {
134 new_ops[nhooks] = (void *)orig_ops[i];
135 new->hooks[nhooks] = old->hooks[i];
136 --- a/net/netfilter/nf_tables_api.c
137 +++ b/net/netfilter/nf_tables_api.c
138 @@ -1434,6 +1434,8 @@ static int nf_tables_addchain(struct nft
140 if (afi->hook_ops_init)
141 afi->hook_ops_init(ops, i);
142 + if (basechain->type->type == NFT_CHAIN_T_NAT)
143 + ops->nat_hook = true;
146 chain->flags |= NFT_BASE_CHAIN;