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

[NETFILTER]: Handle NAT module load race

When the NAT module is loaded when connections are already confirmed
it must not change their tuples anymore. This is especially important
with CONFIG_NETFILTER_DEBUG, the netfilter listhelp functions will
refuse to remove an entry from a list when it can not be found on
the list, so when a changed tuple hashes to a new bucket the entry
is kept in the list until and after the conntrack is freed.

Allocate the exact conntrack tuple for NAT for already confirmed
connections or drop them if that fails.

Signed-off-by: Patrick McHardy <kaber@trash.net>
Signed-off-by: David S. Miller <davem@davemloft.net>

authored by

Patrick McHardy and committed by
David S. Miller
03486a4f 31c913e7

+32 -2
+5
include/linux/netfilter_ipv4/ip_nat_rule.h
··· 19 19 alloc_null_binding(struct ip_conntrack *conntrack, 20 20 struct ip_nat_info *info, 21 21 unsigned int hooknum); 22 + 23 + extern unsigned int 24 + alloc_null_binding_confirmed(struct ip_conntrack *conntrack, 25 + struct ip_nat_info *info, 26 + unsigned int hooknum); 22 27 #endif 23 28 #endif /* _IP_NAT_RULE_H */
+21
net/ipv4/netfilter/ip_nat_rule.c
··· 255 255 return ip_nat_setup_info(conntrack, &range, hooknum); 256 256 } 257 257 258 + unsigned int 259 + alloc_null_binding_confirmed(struct ip_conntrack *conntrack, 260 + struct ip_nat_info *info, 261 + unsigned int hooknum) 262 + { 263 + u_int32_t ip 264 + = (HOOK2MANIP(hooknum) == IP_NAT_MANIP_SRC 265 + ? conntrack->tuplehash[IP_CT_DIR_REPLY].tuple.dst.ip 266 + : conntrack->tuplehash[IP_CT_DIR_REPLY].tuple.src.ip); 267 + u_int16_t all 268 + = (HOOK2MANIP(hooknum) == IP_NAT_MANIP_SRC 269 + ? conntrack->tuplehash[IP_CT_DIR_REPLY].tuple.dst.u.all 270 + : conntrack->tuplehash[IP_CT_DIR_REPLY].tuple.src.u.all); 271 + struct ip_nat_range range 272 + = { IP_NAT_RANGE_MAP_IPS, ip, ip, { all }, { all } }; 273 + 274 + DEBUGP("Allocating NULL binding for confirmed %p (%u.%u.%u.%u)\n", 275 + conntrack, NIPQUAD(ip)); 276 + return ip_nat_setup_info(conntrack, &range, hooknum); 277 + } 278 + 258 279 int ip_nat_rule_find(struct sk_buff **pskb, 259 280 unsigned int hooknum, 260 281 const struct net_device *in,
+6 -2
net/ipv4/netfilter/ip_nat_standalone.c
··· 123 123 if (!ip_nat_initialized(ct, maniptype)) { 124 124 unsigned int ret; 125 125 126 - /* LOCAL_IN hook doesn't have a chain! */ 127 - if (hooknum == NF_IP_LOCAL_IN) 126 + if (unlikely(is_confirmed(ct))) 127 + /* NAT module was loaded late */ 128 + ret = alloc_null_binding_confirmed(ct, info, 129 + hooknum); 130 + else if (hooknum == NF_IP_LOCAL_IN) 131 + /* LOCAL_IN hook doesn't have a chain! */ 128 132 ret = alloc_null_binding(ct, info, hooknum); 129 133 else 130 134 ret = ip_nat_rule_find(pskb, hooknum,