forked from mirrors/linux
		
	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>
			
			
This commit is contained in:
		
							parent
							
								
									03d13b6868
								
							
						
					
					
						commit
						f92b40a8b2
					
				
					 5 changed files with 17 additions and 0 deletions
				
			
		|  | @ -67,6 +67,7 @@ struct nf_hook_ops { | ||||||
| 	struct net_device	*dev; | 	struct net_device	*dev; | ||||||
| 	void			*priv; | 	void			*priv; | ||||||
| 	u_int8_t		pf; | 	u_int8_t		pf; | ||||||
|  | 	bool			nat_hook; | ||||||
| 	unsigned int		hooknum; | 	unsigned int		hooknum; | ||||||
| 	/* Hooks are ordered in ascending priority. */ | 	/* Hooks are ordered in ascending priority. */ | ||||||
| 	int			priority; | 	int			priority; | ||||||
|  |  | ||||||
|  | @ -72,6 +72,7 @@ static const struct nf_hook_ops nf_nat_ipv4_ops[] = { | ||||||
| 	{ | 	{ | ||||||
| 		.hook		= iptable_nat_ipv4_in, | 		.hook		= iptable_nat_ipv4_in, | ||||||
| 		.pf		= NFPROTO_IPV4, | 		.pf		= NFPROTO_IPV4, | ||||||
|  | 		.nat_hook	= true, | ||||||
| 		.hooknum	= NF_INET_PRE_ROUTING, | 		.hooknum	= NF_INET_PRE_ROUTING, | ||||||
| 		.priority	= NF_IP_PRI_NAT_DST, | 		.priority	= NF_IP_PRI_NAT_DST, | ||||||
| 	}, | 	}, | ||||||
|  | @ -79,6 +80,7 @@ static const struct nf_hook_ops nf_nat_ipv4_ops[] = { | ||||||
| 	{ | 	{ | ||||||
| 		.hook		= iptable_nat_ipv4_out, | 		.hook		= iptable_nat_ipv4_out, | ||||||
| 		.pf		= NFPROTO_IPV4, | 		.pf		= NFPROTO_IPV4, | ||||||
|  | 		.nat_hook	= true, | ||||||
| 		.hooknum	= NF_INET_POST_ROUTING, | 		.hooknum	= NF_INET_POST_ROUTING, | ||||||
| 		.priority	= NF_IP_PRI_NAT_SRC, | 		.priority	= NF_IP_PRI_NAT_SRC, | ||||||
| 	}, | 	}, | ||||||
|  | @ -86,6 +88,7 @@ static const struct nf_hook_ops nf_nat_ipv4_ops[] = { | ||||||
| 	{ | 	{ | ||||||
| 		.hook		= iptable_nat_ipv4_local_fn, | 		.hook		= iptable_nat_ipv4_local_fn, | ||||||
| 		.pf		= NFPROTO_IPV4, | 		.pf		= NFPROTO_IPV4, | ||||||
|  | 		.nat_hook	= true, | ||||||
| 		.hooknum	= NF_INET_LOCAL_OUT, | 		.hooknum	= NF_INET_LOCAL_OUT, | ||||||
| 		.priority	= NF_IP_PRI_NAT_DST, | 		.priority	= NF_IP_PRI_NAT_DST, | ||||||
| 	}, | 	}, | ||||||
|  | @ -93,6 +96,7 @@ static const struct nf_hook_ops nf_nat_ipv4_ops[] = { | ||||||
| 	{ | 	{ | ||||||
| 		.hook		= iptable_nat_ipv4_fn, | 		.hook		= iptable_nat_ipv4_fn, | ||||||
| 		.pf		= NFPROTO_IPV4, | 		.pf		= NFPROTO_IPV4, | ||||||
|  | 		.nat_hook	= true, | ||||||
| 		.hooknum	= NF_INET_LOCAL_IN, | 		.hooknum	= NF_INET_LOCAL_IN, | ||||||
| 		.priority	= NF_IP_PRI_NAT_SRC, | 		.priority	= NF_IP_PRI_NAT_SRC, | ||||||
| 	}, | 	}, | ||||||
|  |  | ||||||
|  | @ -74,6 +74,7 @@ static const struct nf_hook_ops nf_nat_ipv6_ops[] = { | ||||||
| 	{ | 	{ | ||||||
| 		.hook		= ip6table_nat_in, | 		.hook		= ip6table_nat_in, | ||||||
| 		.pf		= NFPROTO_IPV6, | 		.pf		= NFPROTO_IPV6, | ||||||
|  | 		.nat_hook	= true, | ||||||
| 		.hooknum	= NF_INET_PRE_ROUTING, | 		.hooknum	= NF_INET_PRE_ROUTING, | ||||||
| 		.priority	= NF_IP6_PRI_NAT_DST, | 		.priority	= NF_IP6_PRI_NAT_DST, | ||||||
| 	}, | 	}, | ||||||
|  | @ -81,6 +82,7 @@ static const struct nf_hook_ops nf_nat_ipv6_ops[] = { | ||||||
| 	{ | 	{ | ||||||
| 		.hook		= ip6table_nat_out, | 		.hook		= ip6table_nat_out, | ||||||
| 		.pf		= NFPROTO_IPV6, | 		.pf		= NFPROTO_IPV6, | ||||||
|  | 		.nat_hook	= true, | ||||||
| 		.hooknum	= NF_INET_POST_ROUTING, | 		.hooknum	= NF_INET_POST_ROUTING, | ||||||
| 		.priority	= NF_IP6_PRI_NAT_SRC, | 		.priority	= NF_IP6_PRI_NAT_SRC, | ||||||
| 	}, | 	}, | ||||||
|  | @ -88,12 +90,14 @@ static const struct nf_hook_ops nf_nat_ipv6_ops[] = { | ||||||
| 	{ | 	{ | ||||||
| 		.hook		= ip6table_nat_local_fn, | 		.hook		= ip6table_nat_local_fn, | ||||||
| 		.pf		= NFPROTO_IPV6, | 		.pf		= NFPROTO_IPV6, | ||||||
|  | 		.nat_hook	= true, | ||||||
| 		.hooknum	= NF_INET_LOCAL_OUT, | 		.hooknum	= NF_INET_LOCAL_OUT, | ||||||
| 		.priority	= NF_IP6_PRI_NAT_DST, | 		.priority	= NF_IP6_PRI_NAT_DST, | ||||||
| 	}, | 	}, | ||||||
| 	/* After packet filtering, change source */ | 	/* After packet filtering, change source */ | ||||||
| 	{ | 	{ | ||||||
| 		.hook		= ip6table_nat_fn, | 		.hook		= ip6table_nat_fn, | ||||||
|  | 		.nat_hook	= true, | ||||||
| 		.pf		= NFPROTO_IPV6, | 		.pf		= NFPROTO_IPV6, | ||||||
| 		.hooknum	= NF_INET_LOCAL_IN, | 		.hooknum	= NF_INET_LOCAL_IN, | ||||||
| 		.priority	= NF_IP6_PRI_NAT_SRC, | 		.priority	= NF_IP6_PRI_NAT_SRC, | ||||||
|  |  | ||||||
|  | @ -160,6 +160,12 @@ nf_hook_entries_grow(const struct nf_hook_entries *old, | ||||||
| 			++i; | 			++i; | ||||||
| 			continue; | 			continue; | ||||||
| 		} | 		} | ||||||
|  | 
 | ||||||
|  | 		if (reg->nat_hook && orig_ops[i]->nat_hook) { | ||||||
|  | 			kvfree(new); | ||||||
|  | 			return ERR_PTR(-EEXIST); | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
| 		if (inserted || reg->priority > orig_ops[i]->priority) { | 		if (inserted || reg->priority > orig_ops[i]->priority) { | ||||||
| 			new_ops[nhooks] = (void *)orig_ops[i]; | 			new_ops[nhooks] = (void *)orig_ops[i]; | ||||||
| 			new->hooks[nhooks] = old->hooks[i]; | 			new->hooks[nhooks] = old->hooks[i]; | ||||||
|  |  | ||||||
|  | @ -1400,6 +1400,8 @@ static int nf_tables_addchain(struct nft_ctx *ctx, u8 family, u8 genmask, | ||||||
| 				ops->hook = hookfn; | 				ops->hook = hookfn; | ||||||
| 			if (afi->hook_ops_init) | 			if (afi->hook_ops_init) | ||||||
| 				afi->hook_ops_init(ops, i); | 				afi->hook_ops_init(ops, i); | ||||||
|  | 			if (basechain->type->type == NFT_CHAIN_T_NAT) | ||||||
|  | 				ops->nat_hook = true; | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
| 		chain->flags |= NFT_BASE_CHAIN; | 		chain->flags |= NFT_BASE_CHAIN; | ||||||
|  |  | ||||||
		Loading…
	
		Reference in a new issue
	
	 Florian Westphal
						Florian Westphal