Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

netfilter: nf_nat: support user-specified SNAT rules in LOCAL_IN

2.6.34 introduced 'conntrack zones' to deal with cases where packets
from multiple identical networks are handled by conntrack/NAT. Packets
are looped through veth devices, during which they are NATed to private
addresses, after which they can continue normally through the stack
and possibly have NAT rules applied a second time.

This works well, but is needlessly complicated for cases where only
a single SNAT/DNAT mapping needs to be applied to these packets. In that
case, all that needs to be done is to assign each network to a seperate
zone and perform NAT as usual. However this doesn't work for packets
destined for the machine performing NAT itself since its corrently not
possible to configure SNAT mappings for the LOCAL_IN chain.

This patch adds a new INPUT chain to the NAT table and changes the
targets performing SNAT to be usable in that chain.

Example usage with two identical networks (192.168.0.0/24) on eth0/eth1:

iptables -t raw -A PREROUTING -i eth0 -j CT --zone 1
iptables -t raw -A PREROUTING -i eth0 -j MARK --set-mark 1
iptables -t raw -A PREROUTING -i eth1 -j CT --zone 2
iptabels -t raw -A PREROUTING -i eth1 -j MARK --set-mark 2

iptables -t nat -A INPUT -m mark --mark 1 -j NETMAP --to 10.0.0.0/24
iptables -t nat -A POSTROUTING -m mark --mark 1 -j NETMAP --to 10.0.0.0/24
iptables -t nat -A INPUT -m mark --mark 2 -j NETMAP --to 10.0.1.0/24
iptables -t nat -A POSTROUTING -m mark --mark 2 -j NETMAP --to 10.0.1.0/24

iptables -t raw -A PREROUTING -d 10.0.0.0/24 -j CT --zone 1
iptables -t raw -A OUTPUT -d 10.0.0.0/24 -j CT --zone 1
iptables -t raw -A PREROUTING -d 10.0.1.0/24 -j CT --zone 2
iptables -t raw -A OUTPUT -d 10.0.1.0/24 -j CT --zone 2

iptables -t nat -A PREROUTING -d 10.0.0.0/24 -j NETMAP --to 192.168.0.0/24
iptables -t nat -A OUTPUT -d 10.0.0.0/24 -j NETMAP --to 192.168.0.0/24
iptables -t nat -A PREROUTING -d 10.0.1.0/24 -j NETMAP --to 192.168.0.0/24
iptables -t nat -A OUTPUT -d 10.0.1.0/24 -j NETMAP --to 192.168.0.0/24

Signed-off-by: Patrick McHardy <kaber@trash.net>

+11 -15
-2
include/net/netfilter/nf_nat_rule.h
··· 12 12 const struct net_device *out, 13 13 struct nf_conn *ct); 14 14 15 - extern unsigned int 16 - alloc_null_binding(struct nf_conn *ct, unsigned int hooknum); 17 15 #endif /* _NF_NAT_RULE_H */
+4 -2
net/ipv4/netfilter/ipt_NETMAP.c
··· 48 48 49 49 NF_CT_ASSERT(par->hooknum == NF_INET_PRE_ROUTING || 50 50 par->hooknum == NF_INET_POST_ROUTING || 51 - par->hooknum == NF_INET_LOCAL_OUT); 51 + par->hooknum == NF_INET_LOCAL_OUT || 52 + par->hooknum == NF_INET_LOCAL_IN); 52 53 ct = nf_ct_get(skb, &ctinfo); 53 54 54 55 netmask = ~(mr->range[0].min_ip ^ mr->range[0].max_ip); ··· 78 77 .table = "nat", 79 78 .hooks = (1 << NF_INET_PRE_ROUTING) | 80 79 (1 << NF_INET_POST_ROUTING) | 81 - (1 << NF_INET_LOCAL_OUT), 80 + (1 << NF_INET_LOCAL_OUT) | 81 + (1 << NF_INET_LOCAL_IN), 82 82 .checkentry = netmap_tg_check, 83 83 .me = THIS_MODULE 84 84 };
+6 -4
net/ipv4/netfilter/nf_nat_rule.c
··· 28 28 29 29 #define NAT_VALID_HOOKS ((1 << NF_INET_PRE_ROUTING) | \ 30 30 (1 << NF_INET_POST_ROUTING) | \ 31 - (1 << NF_INET_LOCAL_OUT)) 31 + (1 << NF_INET_LOCAL_OUT) | \ 32 + (1 << NF_INET_LOCAL_IN)) 32 33 33 34 static const struct xt_table nat_table = { 34 35 .name = "nat", ··· 46 45 enum ip_conntrack_info ctinfo; 47 46 const struct nf_nat_multi_range_compat *mr = par->targinfo; 48 47 49 - NF_CT_ASSERT(par->hooknum == NF_INET_POST_ROUTING); 48 + NF_CT_ASSERT(par->hooknum == NF_INET_POST_ROUTING || 49 + par->hooknum == NF_INET_LOCAL_IN); 50 50 51 51 ct = nf_ct_get(skb, &ctinfo); 52 52 ··· 101 99 return 0; 102 100 } 103 101 104 - unsigned int 102 + static unsigned int 105 103 alloc_null_binding(struct nf_conn *ct, unsigned int hooknum) 106 104 { 107 105 /* Force range to this IP; let proto decide mapping for ··· 143 141 .target = ipt_snat_target, 144 142 .targetsize = sizeof(struct nf_nat_multi_range_compat), 145 143 .table = "nat", 146 - .hooks = 1 << NF_INET_POST_ROUTING, 144 + .hooks = (1 << NF_INET_POST_ROUTING) | (1 << NF_INET_LOCAL_IN), 147 145 .checkentry = ipt_snat_checkentry, 148 146 .family = AF_INET, 149 147 };
+1 -7
net/ipv4/netfilter/nf_nat_standalone.c
··· 131 131 if (!nf_nat_initialized(ct, maniptype)) { 132 132 unsigned int ret; 133 133 134 - if (hooknum == NF_INET_LOCAL_IN) 135 - /* LOCAL_IN hook doesn't have a chain! */ 136 - ret = alloc_null_binding(ct, hooknum); 137 - else 138 - ret = nf_nat_rule_find(skb, hooknum, in, out, 139 - ct); 140 - 134 + ret = nf_nat_rule_find(skb, hooknum, in, out, ct); 141 135 if (ret != NF_ACCEPT) 142 136 return ret; 143 137 } else