newnat patch

From kadlec@blackhole.kfki.hu Mon Sep 17 11:09:30 2001
Date: Fri, 22 Jun 2001 23:28:09 +0200 (CEST)
From: Jozsef Kadlecsik 
To: Harald Welte 
Cc: Netfilter Development Mailinglist 
Subject: Re: Status of the newnat code

On Fri, 22 Jun 2001, Jozsef Kadlecsik wrote:

> So I wrote a version, in which the (naive) checking for resent packets is
> done in the core. It required to integrate ip_conntrack_alloc_expect into
> ip_conntrack_expect_related. Also, because all checks are done at holding
> the ip_conntrack_lock, I converted the expectations counter into a simple
> integer.
>
> It passed all (ftp) tests from the testsuite, so at least backward
> compatible :-). The incremental patch against the original newnat code is
> below.

Arrrgh, very sorry! Stupid, stupid me: incomplete patch, two lines were
missing.

However, there is a bigger problem, and I don't see a solution without
adding more data to the ip_conntrack_expect structure: iff we limit
the maximal number of simultaneous expectations and there is NAT and we
want to support resent packets, then as far as I see, it cannot be done
properly:

- first packet carrying the data of the expectation arrives
	- conntrack detects it, registers the expectation
	- NAT deregister the expectation and registers the NATed one
- resent packet arrives with the data of the same expectation
	- conntrack can't check that it's resent data, because
	  the registered expectation it could compare to is NATed

It seems to me, something like

#ifdef CONFIG_IP_NF_NAT_NEEDED
	/* Saved tuple, mask for conntrack */
	struct ip_conntrack_tuple ct_tuple, ct_mask;
#endif

should be added to the ip_conntrack_expect structure.

Wish I were wrong...

Regards,
Jozsef
-
E-mail  : kadlec@blackhole.kfki.hu, kadlec@sunserv.kfki.hu
WWW-Home: http://www.kfki.hu/~kadlec
Address : KFKI Research Institute for Particle and Nuclear Physics
          H-1525 Budapest 114, POB. 49, Hungary

diff -urN --exclude-from=diff.exclude linux-2.4.5/include/linux/netfilter_ipv4.newnat1/ip_conntrack.h linux-2.4.5/include/linux/netfilter_ipv4.newnat2/ip_conntrack.h
--- linux-2.4.5/include/linux/netfilter_ipv4.newnat1/ip_conntrack.h	Tue Jun 12 09:10:50 2001
+++ linux-2.4.5/include/linux/netfilter_ipv4.newnat2/ip_conntrack.h	Wed Jun 20 17:15:12 2001
@@ -126,6 +126,9 @@
 	/* If we're expecting another related connection, this will be
            in expected linked list */
 	struct list_head sibling_list;
+
+	/* Current number of expected connections */
+	unsigned int expecting;

 	/* If we were expected by an expectation, this will be it */
 	struct ip_conntrack_expect *master;
diff -urN --exclude-from=diff.exclude linux-2.4.5/include/linux/netfilter_ipv4.newnat1/ip_conntrack_helper.h linux-2.4.5/include/linux/netfilter_ipv4.newnat2/ip_conntrack_helper.h
--- linux-2.4.5/include/linux/netfilter_ipv4.newnat1/ip_conntrack_helper.h	Tue Jun 12 09:10:50 2001
+++ linux-2.4.5/include/linux/netfilter_ipv4.newnat2/ip_conntrack_helper.h	Wed Jun 20 17:31:22 2001
@@ -14,6 +14,9 @@
 	struct ip_conntrack_tuple tuple;
 	struct ip_conntrack_tuple mask;

+	/* Maximum number of concurrent expected connections */
+	unsigned int max_expected;
+
 	/* Function to call when data passes; return verdict, or -1 to
            invalidate. */
 	int (*help)(const struct iphdr *, size_t len,
@@ -24,10 +27,7 @@
 extern int ip_conntrack_helper_register(struct ip_conntrack_helper *);
 extern void ip_conntrack_helper_unregister(struct ip_conntrack_helper *);

-struct ip_conntrack_expect *
-ip_conntrack_alloc_expect(struct ip_conntrack *related_to);
-
-/* Add an expected connection: can only have one per connection */
+/* Add an expected connection: can have more than one per connection */
 extern int ip_conntrack_expect_related(struct ip_conntrack *related_to,
 				       struct ip_conntrack_expect *exp);
 extern void ip_conntrack_unexpect_related(struct ip_conntrack_expect *exp);
diff -urN --exclude-from=diff.exclude linux-2.4.5/net/ipv4/netfilter.newnat1/ip_conntrack_core.c linux-2.4.5/net/ipv4/netfilter.newnat2/ip_conntrack_core.c
--- linux-2.4.5/net/ipv4/netfilter.newnat1/ip_conntrack_core.c	Tue Jun 12 09:10:50 2001
+++ linux-2.4.5/net/ipv4/netfilter.newnat2/ip_conntrack_core.c	Fri Jun 22 19:10:00 2001
@@ -163,6 +163,8 @@
 	/* delete from global and local lists */
 	list_del(&expect->list);
 	list_del(&expect->expected_list);
+	if (!expect->sibling)
+		expect->expectant->expecting--;
 	kfree(expect);
 }

@@ -182,8 +184,10 @@

 		/* we skip established expectations, as we want to delete
 		 * the un-established ones only */
-		if (exp->sibling)
+		if (exp->sibling) {
+			DEBUGP("destroy_expectations: skipping established %p of %p\n", exp->sibling, ct);
 			continue;
+		}

 		IP_NF_ASSERT(list_inlist(&expect_list, exp));
 		IP_NF_ASSERT(exp->expectant == ct);
@@ -616,6 +620,7 @@
 		expected->sibling = conntrack;
 		IP_NF_ASSERT(master_ct(conntrack));
 		LIST_DELETE(&expect_list, expected);
+		expected->expectant->expecting--;
 		nf_conntrack_get(&master_ct(conntrack)->infos[0]);
 	}
 	atomic_inc(&ip_conntrack_count);
@@ -786,53 +791,60 @@
 	return ip_ct_tuple_mask_cmp(&i->tuple, &new->tuple, &intersect_mask);
 }

-/* Allocate a new expectation */
-struct ip_conntrack_expect *
-ip_conntrack_alloc_expect(struct ip_conntrack *related_to)
-{
-	struct ip_conntrack_expect *expect;
-
-	/* FIXME: Ideally we want to have a helper-defined limit
-	 * for the maximum number of expectations for one master conn -HW */
-
-	expect = (struct ip_conntrack_expect *)
-		 kmalloc(sizeof(*expect), GFP_ATOMIC);
-
-	if (!expect) {
-		if (net_ratelimit())
-			printk("ip_conntrack: OOM allocating expect\n");
-		return NULL;
-	}
-
-	/* initialize */
-	memset(expect, 0, sizeof(*expect));
-	INIT_LIST_HEAD(&expect->list);
-	INIT_LIST_HEAD(&expect->expected_list);
-
-	/* FIXME: bump the above mentioned expectation count -HW */
-
-	return expect;
-}
-
 /* Add a related connection. */
 int ip_conntrack_expect_related(struct ip_conntrack *related_to,
-				struct ip_conntrack_expect *exp)
+				struct ip_conntrack_expect *expect)
 {
+	struct ip_conntrack_expect *new;
+
 	WRITE_LOCK(&ip_conntrack_lock);
-	DEBUGP("ip_conntrack_expect_related(%p, %p)\n", related_to, exp);
+	DEBUGP("ip_conntrack_expect_related %p\n", related_to);
+	DUMP_TUPLE(&expect->tuple);
+	DUMP_TUPLE(&expect->mask);
+
+	if (LIST_FIND(&related_to->sibling_list, expect_clash,
+		      struct ip_conntrack_expect *, expect)) {
+		WRITE_UNLOCK(&ip_conntrack_lock);
+		DEBUGP("expect_related: resent packet\n");
+		return -EEXIST;
+	}

+	if (related_to->helper->max_expected
+	    && related_to->expecting >= related_to->helper->max_expected) {
+	    	DEBUGP("expect_related: max number of expected connections (%i) reached\n",
+	    	       related_to->helper->max_expected);
+		WRITE_UNLOCK(&ip_conntrack_lock);
+		return -EPERM;
+	}
+
 	if (LIST_FIND(&expect_list, expect_clash,
-		      struct ip_conntrack_expect *, exp)) {
+		      struct ip_conntrack_expect *, expect)) {
 		WRITE_UNLOCK(&ip_conntrack_lock);
 		DEBUGP("expect_related: busy!\n");
 		return -EBUSY;
 	}

-	exp->expectant = related_to;
+	new = (struct ip_conntrack_expect *)
+	      kmalloc(sizeof(*expect), GFP_ATOMIC);
+
+	if (!new) {
+		WRITE_UNLOCK(&ip_conntrack_lock);
+		DEBUGP("expect_relaed: OOM allocating expect\n");
+		return -ENOMEM;
+	}
+
+	/* Fill the structure with the data */
+	memcpy(new, expect, sizeof(*expect));
+	INIT_LIST_HEAD(&new->list);
+	INIT_LIST_HEAD(&new->expected_list);
+	new->expectant = related_to;
+	new->sibling = NULL;
+
 	/* add to expected list for this connection */
-	list_add(&exp->expected_list, &related_to->sibling_list);
+	list_prepend(&related_to->sibling_list, &new->expected_list);
 	/* add to global list of expectations */
-	list_prepend(&expect_list, &exp->list);
+	list_prepend(&expect_list, &new->list);
+	related_to->expecting++;

 	WRITE_UNLOCK(&ip_conntrack_lock);

diff -urN --exclude-from=diff.exclude linux-2.4.5/net/ipv4/netfilter.newnat1/ip_conntrack_ftp.c linux-2.4.5/net/ipv4/netfilter.newnat2/ip_conntrack_ftp.c
--- linux-2.4.5/net/ipv4/netfilter.newnat1/ip_conntrack_ftp.c	Tue Jun 12 09:10:50 2001
+++ linux-2.4.5/net/ipv4/netfilter.newnat2/ip_conntrack_ftp.c	Fri Jun 22 22:59:25 2001
@@ -243,8 +243,8 @@
 	int dir = CTINFO2DIR(ctinfo);
 	unsigned int matchlen, matchoff;
 	struct ip_ct_ftp_master *ct_ftp_info = &ct->help.ct_ftp_info;
-	struct ip_ct_ftp_expect *exp_ftp_info;
-	struct ip_conntrack_expect *exp;
+	struct ip_conntrack_expect expect, *exp = &expect;
+	struct ip_ct_ftp_expect *exp_ftp_info = &exp->help.exp_ftp_info;

 	unsigned int i;
 	int found = 0;
@@ -334,17 +334,6 @@
 	       (int)matchlen, data + matchoff,
 	       matchlen, ntohl(tcph->seq) + matchoff);

-	/* allocate a new expectation */
-	exp = ip_conntrack_alloc_expect(ct);
-	if (!exp) {
-		if (net_ratelimit())
-			printk("conntrack_ftp: unable to allocate new "
-			       "expectation\n");
-		return NF_ACCEPT;
-	}
-
-	exp_ftp_info = &exp->help.exp_ftp_info;
-
 	/* Update the ftp info */
 	LOCK_BH(&ip_ftp_lock);
 	if (htonl((array[0] << 24) | (array[1] << 16) | (array[2] << 8) | array[3])
@@ -416,6 +405,7 @@
 		ftp[i].tuple.dst.protonum = IPPROTO_TCP;
 		ftp[i].mask.src.u.tcp.port = 0xFFFF;
 		ftp[i].mask.dst.protonum = 0xFFFF;
+		ftp[i].max_expected = 1;
 		ftp[i].help = help;
 		DEBUGP("ip_ct_ftp: registering helper for port %d\n",
 				ports[i]);
diff -urN --exclude-from=diff.exclude linux-2.4.5/net/ipv4/netfilter.newnat1/ip_conntrack_standalone.c linux-2.4.5/net/ipv4/netfilter.newnat2/ip_conntrack_standalone.c
--- linux-2.4.5/net/ipv4/netfilter.newnat1/ip_conntrack_standalone.c	Tue Jun 12 09:10:50 2001
+++ linux-2.4.5/net/ipv4/netfilter.newnat2/ip_conntrack_standalone.c	Wed Jun 20 17:32:12 2001
@@ -327,7 +327,6 @@
 EXPORT_SYMBOL(ip_conntrack_helper_unregister);
 EXPORT_SYMBOL(ip_ct_selective_cleanup);
 EXPORT_SYMBOL(ip_ct_refresh);
-EXPORT_SYMBOL(ip_conntrack_alloc_expect);
 EXPORT_SYMBOL(ip_conntrack_expect_related);
 EXPORT_SYMBOL(ip_conntrack_unexpect_related);
 EXPORT_SYMBOL(ip_conntrack_tuple_taken);
diff -urN --exclude-from=diff.exclude linux-2.4.5/net/ipv4/netfilter.newnat1/ip_nat_core.c linux-2.4.5/net/ipv4/netfilter.newnat2/ip_nat_core.c
--- linux-2.4.5/net/ipv4/netfilter.newnat1/ip_nat_core.c	Tue Jun 12 09:10:50 2001
+++ linux-2.4.5/net/ipv4/netfilter.newnat2/ip_nat_core.c	Thu Jun 14 11:54:03 2001
@@ -21,6 +21,7 @@
 #define ASSERT_READ_LOCK(x) MUST_BE_READ_LOCKED(&ip_nat_lock)
 #define ASSERT_WRITE_LOCK(x) MUST_BE_WRITE_LOCKED(&ip_nat_lock)

+#include 
 #include 
 #include 
 #include 
diff -urN --exclude-from=diff.exclude linux-2.4.5/net/ipv4/netfilter.newnat1/ip_nat_ftp.c linux-2.4.5/net/ipv4/netfilter.newnat2/ip_nat_ftp.c
--- linux-2.4.5/net/ipv4/netfilter.newnat1/ip_nat_ftp.c	Tue Jun 12 09:10:50 2001
+++ linux-2.4.5/net/ipv4/netfilter.newnat2/ip_nat_ftp.c	Fri Jun 22 22:59:11 2001
@@ -177,11 +177,7 @@
 	struct iphdr *iph = (*pskb)->nh.iph;
 	struct tcphdr *tcph = (void *)iph + iph->ihl*4;
 	u_int16_t port;
-	struct ip_conntrack_expect *exp;
-
-	exp = ip_conntrack_alloc_expect(ct);
-	if (!exp)
-		return 0;
+	struct ip_conntrack_expect expect, *exp = &expect;

 	/* copy whole old expectation */
 	memcpy(exp, oldexp, sizeof(*oldexp));
@@ -298,7 +294,7 @@

 static int __init init(void)
 {
-	int i, ret;
+	int i, ret = 0;
 	char *tmpname;

 	if (ports[0] == 0)
diff -urN --exclude-from=diff.exclude linux-2.4.5/net/ipv4/netfilter.newnat1/ip_nat_helper.c linux-2.4.5/net/ipv4/netfilter.newnat2/ip_nat_helper.c
--- linux-2.4.5/net/ipv4/netfilter.newnat1/ip_nat_helper.c	Fri Apr 27 23:15:01 2001
+++ linux-2.4.5/net/ipv4/netfilter.newnat2/ip_nat_helper.c	Thu Jun 14 11:54:40 2001
@@ -19,6 +19,7 @@
 #define ASSERT_READ_LOCK(x) MUST_BE_READ_LOCKED(&ip_nat_lock)
 #define ASSERT_WRITE_LOCK(x) MUST_BE_WRITE_LOCKED(&ip_nat_lock)

+#include 
 #include 
 #include 
 #include