Skip to content

Commit f92b40a

Browse files
Florian Westphalummakynes
authored andcommitted
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 <[email protected]> Signed-off-by: Pablo Neira Ayuso <[email protected]>
1 parent 03d13b6 commit f92b40a

File tree

5 files changed

+17
-0
lines changed

5 files changed

+17
-0
lines changed

include/linux/netfilter.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@ struct nf_hook_ops {
6767
struct net_device *dev;
6868
void *priv;
6969
u_int8_t pf;
70+
bool nat_hook;
7071
unsigned int hooknum;
7172
/* Hooks are ordered in ascending priority. */
7273
int priority;

net/ipv4/netfilter/iptable_nat.c

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,27 +72,31 @@ static const struct nf_hook_ops nf_nat_ipv4_ops[] = {
7272
{
7373
.hook = iptable_nat_ipv4_in,
7474
.pf = NFPROTO_IPV4,
75+
.nat_hook = true,
7576
.hooknum = NF_INET_PRE_ROUTING,
7677
.priority = NF_IP_PRI_NAT_DST,
7778
},
7879
/* After packet filtering, change source */
7980
{
8081
.hook = iptable_nat_ipv4_out,
8182
.pf = NFPROTO_IPV4,
83+
.nat_hook = true,
8284
.hooknum = NF_INET_POST_ROUTING,
8385
.priority = NF_IP_PRI_NAT_SRC,
8486
},
8587
/* Before packet filtering, change destination */
8688
{
8789
.hook = iptable_nat_ipv4_local_fn,
8890
.pf = NFPROTO_IPV4,
91+
.nat_hook = true,
8992
.hooknum = NF_INET_LOCAL_OUT,
9093
.priority = NF_IP_PRI_NAT_DST,
9194
},
9295
/* After packet filtering, change source */
9396
{
9497
.hook = iptable_nat_ipv4_fn,
9598
.pf = NFPROTO_IPV4,
99+
.nat_hook = true,
96100
.hooknum = NF_INET_LOCAL_IN,
97101
.priority = NF_IP_PRI_NAT_SRC,
98102
},

net/ipv6/netfilter/ip6table_nat.c

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,26 +74,30 @@ static const struct nf_hook_ops nf_nat_ipv6_ops[] = {
7474
{
7575
.hook = ip6table_nat_in,
7676
.pf = NFPROTO_IPV6,
77+
.nat_hook = true,
7778
.hooknum = NF_INET_PRE_ROUTING,
7879
.priority = NF_IP6_PRI_NAT_DST,
7980
},
8081
/* After packet filtering, change source */
8182
{
8283
.hook = ip6table_nat_out,
8384
.pf = NFPROTO_IPV6,
85+
.nat_hook = true,
8486
.hooknum = NF_INET_POST_ROUTING,
8587
.priority = NF_IP6_PRI_NAT_SRC,
8688
},
8789
/* Before packet filtering, change destination */
8890
{
8991
.hook = ip6table_nat_local_fn,
9092
.pf = NFPROTO_IPV6,
93+
.nat_hook = true,
9194
.hooknum = NF_INET_LOCAL_OUT,
9295
.priority = NF_IP6_PRI_NAT_DST,
9396
},
9497
/* After packet filtering, change source */
9598
{
9699
.hook = ip6table_nat_fn,
100+
.nat_hook = true,
97101
.pf = NFPROTO_IPV6,
98102
.hooknum = NF_INET_LOCAL_IN,
99103
.priority = NF_IP6_PRI_NAT_SRC,

net/netfilter/core.c

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -160,6 +160,12 @@ nf_hook_entries_grow(const struct nf_hook_entries *old,
160160
++i;
161161
continue;
162162
}
163+
164+
if (reg->nat_hook && orig_ops[i]->nat_hook) {
165+
kvfree(new);
166+
return ERR_PTR(-EEXIST);
167+
}
168+
163169
if (inserted || reg->priority > orig_ops[i]->priority) {
164170
new_ops[nhooks] = (void *)orig_ops[i];
165171
new->hooks[nhooks] = old->hooks[i];

net/netfilter/nf_tables_api.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1400,6 +1400,8 @@ static int nf_tables_addchain(struct nft_ctx *ctx, u8 family, u8 genmask,
14001400
ops->hook = hookfn;
14011401
if (afi->hook_ops_init)
14021402
afi->hook_ops_init(ops, i);
1403+
if (basechain->type->type == NFT_CHAIN_T_NAT)
1404+
ops->nat_hook = true;
14031405
}
14041406

14051407
chain->flags |= NFT_BASE_CHAIN;

0 commit comments

Comments
 (0)