Skip to contentSkip to conversation

Don't try too hard to NAT to unique tuple

PATCH In review Rusty Russell 1 participant 1 message Nov 18, 2004

Subsystems (2)
lore
Share & tools
lore
#0 lore raw

PATCH 3/5 Don't try too hard to NAT to unique tuple

Name: Don't try too hard to NAT to unique tuple
Status: Untested
Depends: Netfilter/remove-multiple-ranges.patch.gz

On NF_IP_LOCAL_OUT or NF_IP_PRE_ROUTING, if destination NAT is not sufficient to create a unique tuple, we try changing the source port as well. However, this is also not strictly necessary: if the tuple is not unique, we will also try to change the source on the NF_IP_POST_ROUTING hook.

When we finally confirm the connection, if the tuple is still not unique the packet will be dropped (this is required anyway as we could
race: the conntrack is not placed in the hash until the packet is about to leave the box anyway).

In fact, we only need best effort *everywhere*.

net/ipv4/netfilter/ip_nat_core.c +29 -61
Index: linux-2.6.10-rc2-bk1-Netfilter/net/ipv4/netfilter/ip_nat_core.c
===================================================================
@@ -182,7 +182,7 @@
 #ifdef CONFIG_IP_NF_NAT_LOCAL
 /* If it's really a local destination manip, it may need to do a
    source manip too. */
-static int
+static void
 do_extra_mangle(u_int32_t var_ip, u_int32_t *other_ipp)
 {
 	struct flowi fl = { .nl_u = { .ip4_u = { .daddr = var_ip } } };
@@ -192,12 +192,11 @@
 	if (ip_route_output_key(&rt, &fl) != 0) {
 		DEBUGP("do_extra_mangle: Can't get route to %u.%u.%u.%u\n",
 		       NIPQUAD(var_ip));
-		return 0;
+		return;
 	}
 
 	*other_ipp = rt->rt_src;
 	ip_rt_put(rt);
-	return 1;
 }
 #endif
 
@@ -239,14 +238,14 @@
    1-65535, we don't do pro-rata allocation based on ports; we choose
    the ip with the lowest src-ip/dst-ip/proto usage.
 */
-static int
+static void
 find_best_ips_proto(struct ip_conntrack_tuple *tuple,
 		    const struct ip_nat_range *range,
 		    const struct ip_conntrack *conntrack,
 		    unsigned int hooknum)
 {
 	unsigned int best_score = 0xFFFFFFFF;
-	struct ip_conntrack_tuple best_tuple;
+	struct ip_conntrack_tuple best_tuple = *tuple;
 	u_int32_t *var_ipp, *other_ipp, saved_ip, orig_dstip;
 	static unsigned int randomness;
 	/* Host order */
@@ -284,15 +283,8 @@
 
 #ifdef CONFIG_IP_NF_NAT_LOCAL
 		if (hooknum == NF_IP_LOCAL_OUT
-		    && *var_ipp != orig_dstip
-		    && !do_extra_mangle(*var_ipp, other_ipp)) {
-			DEBUGP("Range %u %u.%u.%u.%u rt failed!\n",
-			       i, NIPQUAD(*var_ipp));
-			/* Can't route?  This whole range part is
-			 * probably screwed, but keep trying
-			 * anyway. */
-			continue;
-		}
+		    && *var_ipp != orig_dstip)
+			do_extra_mangle(*var_ipp, other_ipp);
 #endif
 
 		/* Count how many others map onto this. */
@@ -302,23 +294,18 @@
 			/* Optimization: doesn't get any better than
 			   this. */
 			if (score == 0)
-				return 1;
+				return;
 
 			best_score = score;
 			best_tuple = *tuple;
 		}
 	}
-
-	if (best_score == 0xFFFFFFFF)
-		return 0;
-
 	*tuple = best_tuple;
-	return 1;
 }
 
 /* Fast version doesn't iterate through hash chains, but only handles
    common case of single IP address (null NAT, masquerade) */
-static int
+static void
 find_best_ips_proto_fast(struct ip_conntrack_tuple *tuple,
 			 const struct ip_nat_range *range,
 			 const struct ip_conntrack *conntrack,
@@ -326,10 +313,12 @@
 {
 	/* Leave IP address alone if we're not to map IP addresses. */
 	if (!(range->flags & IP_NAT_RANGE_MAP_IPS))
-		return 1;
+		return;
 
-	if (range->min_ip != range->max_ip)
-		return find_best_ips_proto(tuple, range, conntrack, hooknum);
+	if (range->min_ip != range->max_ip) {
+		find_best_ips_proto(tuple, range, conntrack, hooknum);
+		return;
+	}
 
 	if (HOOK2MANIP(hooknum) == IP_NAT_MANIP_SRC)
 		tuple->src.ip = range->min_ip;
@@ -338,17 +327,21 @@
 		   binding) */
 #ifdef CONFIG_IP_NF_NAT_LOCAL
 		if (tuple->dst.ip != range->min_ip
-		    && hooknum == NF_IP_LOCAL_OUT
-		    && !do_extra_mangle(range->min_ip, &tuple->src.ip))
-			return 0;
+		    && hooknum == NF_IP_LOCAL_OUT)
+			do_extra_mangle(range->min_ip, &tuple->src.ip);
+		else
 #endif
 		tuple->dst.ip = range->min_ip;
 	}
-
-	return 1;
 }
 
-static int
+/* Manipulate the tuple into the range given.  For NF_IP_POST_ROUTING,
+ * we change the source to map into the range.  For NF_IP_PRE_ROUTING
+ * and NF_IP_LOCAL_OUT, we change the destination to map into the
+ * range.  It might not be possible to get a unique tuple, but we try.
+ * At worst (or if we race), we will end up with a final duplicate in
+ * __ip_conntrack_confirm and drop the packet. */
+static void
 get_unique_tuple(struct ip_conntrack_tuple *tuple,
 		 const struct ip_conntrack_tuple *orig_tuple,
 		 const struct ip_nat_range *range,
@@ -369,15 +362,14 @@
 		if (find_appropriate_src(orig_tuple, tuple, range)) {
 			DEBUGP("get_unique_tuple: Found current src map\n");
 			if (!ip_nat_used_tuple(tuple, conntrack))
-				return 1;
+				return;
 		}
 	}
 
 	/* 2) Select the least-used IP/proto combination in the given
 	   range. */
 	*tuple = *orig_tuple;
-	if (!find_best_ips_proto_fast(tuple, range, conntrack, hooknum))
-		return 0;
+	find_best_ips_proto_fast(tuple, range, conntrack, hooknum);
 
 	/* 3) The per-protocol part of the manip is made to map into
 	   the range to make a unique tuple. */
@@ -387,30 +379,10 @@
 	     || proto->in_range(tuple, HOOK2MANIP(hooknum),
 				&range->min, &range->max))
 	    && !ip_nat_used_tuple(tuple, conntrack))
-		return 1;
+		return;
 
-	if (proto->unique_tuple(tuple, range, HOOK2MANIP(hooknum), conntrack)){
-		/* Must be unique. */
-		IP_NF_ASSERT(!ip_nat_used_tuple(tuple, conntrack));
-		return 1;
-	}
-
-	if (HOOK2MANIP(hooknum) == IP_NAT_MANIP_DST) {
-		/* Try implicit source NAT; protocol may be able to
-		   play with ports to make it unique. */
-		struct ip_nat_range r
-			= { IP_NAT_RANGE_MAP_IPS, 
-			    tuple->src.ip, tuple->src.ip, { 0 }, { 0 } };
-		DEBUGP("Trying implicit mapping\n");
-		if (proto->unique_tuple(tuple, &r, IP_NAT_MANIP_SRC,
-					conntrack)) {
-			/* Must be unique. */
-			IP_NF_ASSERT(!ip_nat_used_tuple(tuple, conntrack));
-			return 1;
-		}
-		DEBUGP("Protocol can't get unique tuple %u.\n", hooknum);
-	}
-	return 0;
+	/* Last change: get protocol to try to obtain unique tuple. */
+	proto->unique_tuple(tuple, range, HOOK2MANIP(hooknum), conntrack);
 }
 
 /* Where to manip the reply packets (will be reverse manip). */
@@ -475,11 +447,7 @@
 	}
 #endif
 
-	if (!get_unique_tuple(&new_tuple, &orig_tp, range,conntrack,hooknum)) {
-		DEBUGP("ip_nat_setup_info: Can't get unique for %p.\n",
-		       conntrack);
-		return NF_DROP;
-	}
+	get_unique_tuple(&new_tuple, &orig_tp, range, conntrack, hooknum);
 
 	/* We now have two tuples (SRCIP/SRCPT/DSTIP/DSTPT):
 	   the original (A/B/C/D') and the mangled one (E/F/G/H').
Signature · 1 line
A bad analogy is like a leaky screwdriver -- Richard Braakman