diff -urN --exclude-from=diff.exclude linux-2.4.9/include/linux/netfilter_ipv4.orig/ip_conntrack_tcp.h linux-2.4.9/include/linux/netfilter_ipv4/ip_conntrack_tcp.h
--- linux-2.4.9/include/linux/netfilter_ipv4.orig/ip_conntrack_tcp.h	Fri Aug  4 22:07:24 2000
+++ linux-2.4.9/include/linux/netfilter_ipv4/ip_conntrack_tcp.h	Wed Sep 19 17:30:03 2001
@@ -8,25 +8,35 @@
 
 enum tcp_conntrack {
 	TCP_CONNTRACK_NONE,
-	TCP_CONNTRACK_ESTABLISHED,
 	TCP_CONNTRACK_SYN_SENT,
 	TCP_CONNTRACK_SYN_RECV,
+	TCP_CONNTRACK_ESTABLISHED,
 	TCP_CONNTRACK_FIN_WAIT,
-	TCP_CONNTRACK_TIME_WAIT,
-	TCP_CONNTRACK_CLOSE,
 	TCP_CONNTRACK_CLOSE_WAIT,
 	TCP_CONNTRACK_LAST_ACK,
+	TCP_CONNTRACK_TIME_WAIT,
+	TCP_CONNTRACK_CLOSE,
 	TCP_CONNTRACK_LISTEN,
 	TCP_CONNTRACK_MAX
 };
 
+struct ip_ct_tcp_state {
+	u_int32_t	td_end;			/* max of seq + len */
+	u_int32_t	td_maxend;		/* max of ack + max(win, 1) */
+	u_int32_t	td_maxwin;		/* max(win) */
+	u_int8_t	td_scale;		/* window scale factor */
+};
+
 struct ip_ct_tcp
 {
-	enum tcp_conntrack state;
-
-	/* Poor man's window tracking: sequence number of valid ACK
-           handshake completion packet */
-	u_int32_t handshake_ack;
+	enum tcp_conntrack state;		/* state of the connection */
+	struct ip_ct_tcp_state seen[2];		/* connection parameters per direction */
 };
+
+/* For NAT, when it mangles the packet */
+extern void ip_conntrack_tcp_update(struct ip_conntrack *conntrack, int dir,   
+                                    struct iphdr *iph, size_t newlen,
+                                    struct tcphdr *tcph);
+                                                    
 
 #endif /* _IP_CONNTRACK_TCP_H */
diff -urN --exclude-from=diff.exclude linux-2.4.9/net/ipv4/netfilter.orig/ip_conntrack_proto_generic.c linux-2.4.9/net/ipv4/netfilter/ip_conntrack_proto_generic.c
--- linux-2.4.9/net/ipv4/netfilter.orig/ip_conntrack_proto_generic.c	Fri Apr 27 23:15:01 2001
+++ linux-2.4.9/net/ipv4/netfilter/ip_conntrack_proto_generic.c	Wed Sep 19 17:30:03 2001
@@ -4,7 +4,7 @@
 #include <linux/netfilter.h>
 #include <linux/netfilter_ipv4/ip_conntrack_protocol.h>
 
-#define GENERIC_TIMEOUT (600*HZ)
+unsigned long ip_ct_generic_timeout = 600*HZ;
 
 static int generic_pkt_to_tuple(const void *datah, size_t datalen,
 				struct ip_conntrack_tuple *tuple)
@@ -43,7 +43,7 @@
 		       struct iphdr *iph, size_t len,
 		       enum ip_conntrack_info conntrackinfo)
 {
-	ip_ct_refresh(conntrack, GENERIC_TIMEOUT);
+	ip_ct_refresh(conntrack, ip_ct_generic_timeout);
 	return NF_ACCEPT;
 }
 
diff -urN --exclude-from=diff.exclude linux-2.4.9/net/ipv4/netfilter.orig/ip_conntrack_proto_icmp.c linux-2.4.9/net/ipv4/netfilter/ip_conntrack_proto_icmp.c
--- linux-2.4.9/net/ipv4/netfilter.orig/ip_conntrack_proto_icmp.c	Fri Apr 27 23:15:01 2001
+++ linux-2.4.9/net/ipv4/netfilter/ip_conntrack_proto_icmp.c	Wed Sep 19 17:30:03 2001
@@ -6,7 +6,7 @@
 #include <linux/icmp.h>
 #include <linux/netfilter_ipv4/ip_conntrack_protocol.h>
 
-#define ICMP_TIMEOUT (30*HZ)
+unsigned long ip_ct_icmp_timeout = 30*HZ;
 
 #if 0
 #define DEBUGP printk
@@ -82,7 +82,7 @@
 			ct->timeout.function((unsigned long)ct);
 	} else {
 		atomic_inc(&ct->proto.icmp.count);
-		ip_ct_refresh(ct, ICMP_TIMEOUT);
+		ip_ct_refresh(ct, ip_ct_icmp_timeout);
 	}
 
 	return NF_ACCEPT;
diff -urN --exclude-from=diff.exclude linux-2.4.9/net/ipv4/netfilter.orig/ip_conntrack_proto_tcp.c linux-2.4.9/net/ipv4/netfilter/ip_conntrack_proto_tcp.c
--- linux-2.4.9/net/ipv4/netfilter.orig/ip_conntrack_proto_tcp.c	Fri Apr 27 23:15:01 2001
+++ linux-2.4.9/net/ipv4/netfilter/ip_conntrack_proto_tcp.c	Wed Oct  3 15:39:45 2001
@@ -1,3 +1,14 @@
+/*
+ * TCP connection tracking
+ */
+
+/*
+ * Changes:
+ *		Jozsef Kadlecsik:	Real stateful connection tracking
+ *					Modified state transitions table
+ *					Window scaling support
+ */
+
 #define __NO_VERSION__
 #include <linux/types.h>
 #include <linux/sched.h>
@@ -7,6 +18,8 @@
 #include <linux/in.h>
 #include <linux/ip.h>
 #include <linux/tcp.h>
+#include <linux/spinlock.h>
+#include <net/tcp.h>
 #include <linux/netfilter_ipv4/ip_conntrack.h>
 #include <linux/netfilter_ipv4/ip_conntrack_protocol.h>
 #include <linux/netfilter_ipv4/lockhelp.h>
@@ -20,6 +33,14 @@
 /* Protects conntrack->proto.tcp */
 static DECLARE_RWLOCK(tcp_lock);
 
+/* Logging options */ 
+int ip_ct_tcp_log_invalid_scale = 1;
+int ip_ct_tcp_log_out_of_window = 1;
+
+/* "Be conservative in what you do, 
+    be liberal in what you accept from others." */
+int ip_ct_tcp_be_liberal = 0;
+
 /* FIXME: Examine ipfilter's timeouts and conntrack transitions more
    closely.  They're more complex. --RR */
 
@@ -30,14 +51,14 @@
 
 static const char *tcp_conntrack_names[] = {
 	"NONE",
-	"ESTABLISHED",
 	"SYN_SENT",
 	"SYN_RECV",
+	"ESTABLISHED",
 	"FIN_WAIT",
-	"TIME_WAIT",
-	"CLOSE",
 	"CLOSE_WAIT",
 	"LAST_ACK",
+	"TIME_WAIT",
+	"CLOSE",
 	"LISTEN"
 };
 
@@ -46,50 +67,186 @@
 #define HOURS * 60 MINS
 #define DAYS * 24 HOURS
 
-
-static unsigned long tcp_timeouts[]
-= { 30 MINS, 	/*	TCP_CONNTRACK_NONE,	*/
-    5 DAYS,	/*	TCP_CONNTRACK_ESTABLISHED,	*/
-    2 MINS,	/*	TCP_CONNTRACK_SYN_SENT,	*/
-    60 SECS,	/*	TCP_CONNTRACK_SYN_RECV,	*/
-    2 MINS,	/*	TCP_CONNTRACK_FIN_WAIT,	*/
-    2 MINS,	/*	TCP_CONNTRACK_TIME_WAIT,	*/
-    10 SECS,	/*	TCP_CONNTRACK_CLOSE,	*/
-    60 SECS,	/*	TCP_CONNTRACK_CLOSE_WAIT,	*/
-    30 SECS,	/*	TCP_CONNTRACK_LAST_ACK,	*/
-    2 MINS,	/*	TCP_CONNTRACK_LISTEN,	*/
+unsigned long ip_ct_tcp_timeout_none =		30 MINS;
+unsigned long ip_ct_tcp_timeout_syn_sent =	 2 MINS;
+unsigned long ip_ct_tcp_timeout_syn_recv =	60 SECS;
+unsigned long ip_ct_tcp_timeout_established =	 5 DAYS;
+unsigned long ip_ct_tcp_timeout_fin_wait =	 2 MINS;
+unsigned long ip_ct_tcp_timeout_close_wait =	12 HOURS;
+unsigned long ip_ct_tcp_timeout_last_ack =	30 SECS;
+unsigned long ip_ct_tcp_timeout_time_wait =	 2 MINS;
+unsigned long ip_ct_tcp_timeout_close =		10 SECS;
+unsigned long ip_ct_tcp_timeout_listen =	 2 MINS;
+
+static unsigned long * tcp_timeouts[]
+= { &ip_ct_tcp_timeout_none, 		/*	TCP_CONNTRACK_NONE,	*/
+    &ip_ct_tcp_timeout_syn_sent,	/*	TCP_CONNTRACK_SYN_SENT,	*/
+    &ip_ct_tcp_timeout_syn_recv,	/*	TCP_CONNTRACK_SYN_RECV,	*/
+    &ip_ct_tcp_timeout_established,	/*	TCP_CONNTRACK_ESTABLISHED,	*/
+    &ip_ct_tcp_timeout_fin_wait,	/*	TCP_CONNTRACK_FIN_WAIT,	*/
+    &ip_ct_tcp_timeout_close_wait,	/*	TCP_CONNTRACK_CLOSE_WAIT,	*/
+    &ip_ct_tcp_timeout_last_ack,	/*	TCP_CONNTRACK_LAST_ACK,	*/
+    &ip_ct_tcp_timeout_time_wait,	/*	TCP_CONNTRACK_TIME_WAIT,	*/
+    &ip_ct_tcp_timeout_close,		/*	TCP_CONNTRACK_CLOSE,	*/
+    &ip_ct_tcp_timeout_listen,		/*	TCP_CONNTRACK_LISTEN,	*/
 };
 
 #define sNO TCP_CONNTRACK_NONE
-#define sES TCP_CONNTRACK_ESTABLISHED
 #define sSS TCP_CONNTRACK_SYN_SENT
 #define sSR TCP_CONNTRACK_SYN_RECV
+#define sES TCP_CONNTRACK_ESTABLISHED
 #define sFW TCP_CONNTRACK_FIN_WAIT
-#define sTW TCP_CONNTRACK_TIME_WAIT
-#define sCL TCP_CONNTRACK_CLOSE
 #define sCW TCP_CONNTRACK_CLOSE_WAIT
 #define sLA TCP_CONNTRACK_LAST_ACK
+#define sTW TCP_CONNTRACK_TIME_WAIT
+#define sCL TCP_CONNTRACK_CLOSE
 #define sLI TCP_CONNTRACK_LISTEN
 #define sIV TCP_CONNTRACK_MAX
 
-static enum tcp_conntrack tcp_conntracks[2][5][TCP_CONNTRACK_MAX] = {
+/*
+ * The TCP state transition table needs a few words...
+ *
+ * We are the man in the middle. All the packets go through us
+ * but might get lost in transit to the destination.
+ * It is assumed that the destinations can't receive segments 
+ * we haven't seen.
+ *
+ * The checked segment is in window.
+ *
+ * The meaning of the states are:
+ *
+ * NONE:	initial state
+ * SYN_SENT:	SYN-only packet seen 
+ * SYN_RECV:	SYN-ACK packet seen
+ * ESTABLISHED:	ACK packet seen
+ * FIN_WAIT:	FIN packet seen
+ * CLOSE_WAIT:	ACK seen (after FIN) 
+ * LAST_ACK:	FIN seen (after FIN)
+ * TIME_WAIT:	last ACK seen
+ * CLOSE:	closed connection
+ *
+ * LISTEN state is not used.
+ *
+ */
+static enum tcp_conntrack tcp_conntracks[2][6][TCP_CONNTRACK_MAX] = {
 	{
-/*	ORIGINAL */
-/* 	  sNO, sES, sSS, sSR, sFW, sTW, sCL, sCW, sLA, sLI 	*/
-/*syn*/	{sSS, sES, sSS, sSR, sSS, sSS, sSS, sSS, sSS, sLI },
-/*fin*/	{sTW, sFW, sSS, sTW, sFW, sTW, sCL, sTW, sLA, sLI },
-/*ack*/	{sES, sES, sSS, sES, sFW, sTW, sCL, sCW, sLA, sES },
-/*rst*/ {sCL, sCL, sSS, sCL, sCL, sTW, sCL, sCL, sCL, sCL },
-/*none*/{sIV, sIV, sIV, sIV, sIV, sIV, sIV, sIV, sIV, sIV }
+/* ORIGINAL */
+/* 	     sNO, sSS, sSR, sES, sFW, sCW, sLA, sTW, sCL, sLI	*/
+/*syn*/	   { sSS, sSS, sCL, sCL, sCL, sCL, sCL, sSS, sSS, sIV },
+/*
+ *	sNO -> sSS	Initialize a new connection
+ *	sSS -> sSS	Retransmitted SYN
+ *	sSR -> sCL	Error: SYNs in window outside the SYN_SENT state 
+ *			are errors. Receiver will either go back to the 
+ *			LISTEN state or reply with RST.
+ *	sES -> sCL
+ *	sFW -> sCL
+ *	sCW -> sCL
+ *	sLA -> sCL
+ *	sTW -> sSS	Reopened connection (RFC 1122).
+ *	sCL -> sSS
+ */
+/* 	     sNO, sSS, sSR, sES, sFW, sCW, sLA, sTW, sCL, sLI	*/
+/*synack*/ { sSR, sSR, sES, sCL, sCL, sCL, sCL, sCL, sCL, sIV },
+/*
+ *	sNO -> sSR	Assumed: hey, we've just started up!
+ *	sSS -> sSR	Simultaneous open.
+ *	sSR -> sES	Ditto.
+ *	sES -> sCL	Error.
+ *	sFW -> sCL
+ *	sCW -> sCL
+ *	sLA -> sCL
+ *	sTW -> sCL
+ *	sCL -> sCL
+ */
+/* 	     sNO, sSS, sSR, sES, sFW, sCW, sLA, sTW, sCL, sLI	*/
+/*fin*/    { sTW, sIV, sFW, sFW, sLA, sLA, sLA, sTW, sCL, sIV },
+/*
+ *	sNO -> sTW	We assume TIME-WAIT state.
+ *	sSS -> sIV	Client migth not send FIN in this state.
+ *	sSR -> sFW	Close started.
+ *	sES -> sFW	
+ *	sFW -> sLA	FIN seen in both directions, waiting for
+ *			the last ACK. 
+ *			Migth be a retransmitted FIN as well...
+ *	sCW -> sLA
+ *	sLA -> sLA	Retransmitted FIN. Remain in the same state.
+ *	sTW -> sTW
+ *	sCL -> sCL
+ */
+/* 	     sNO, sSS, sSR, sES, sFW, sCW, sLA, sTW, sCL, sLI	*/
+/*ack*/	   { sES, sIV, sES, sES, sCW, sCW, sTW, sTW, sCL, sIV },
+/*
+ *	sNO -> sES	Assumed.
+ *	sSS -> sIV	ACK is invalid: we haven't seen a SYN/ACK yet.
+ *	sSR -> sES	Established state is reached.
+ *	sES -> sES	:-)
+ *	sFW -> sCW	Normal close request answered by ACK.
+ *	sCW -> sCW
+ *	sLA -> sTW	Last ACK detected.
+ *	sTW -> sTW	Retransmitted last ACK. Remain in the same state.
+ *	sCL -> sCL
+ */
+/* 	     sNO, sSS, sSR, sES, sFW, sCW, sLA, sTW, sCL, sLI	*/
+/*rst*/    { sCL, sCL, sCL, sCL, sCL, sCL, sCL, sCL, sCL, sIV },
+/*none*/   { sIV, sIV, sIV, sIV, sIV, sIV, sIV, sIV, sIV, sIV }
 	},
 	{
-/*	REPLY */
-/* 	  sNO, sES, sSS, sSR, sFW, sTW, sCL, sCW, sLA, sLI 	*/
-/*syn*/	{sSR, sES, sSR, sSR, sSR, sSR, sSR, sSR, sSR, sSR },
-/*fin*/	{sCL, sCW, sSS, sTW, sTW, sTW, sCL, sCW, sLA, sLI },
-/*ack*/	{sCL, sES, sSS, sSR, sFW, sTW, sCL, sCW, sCL, sLI },
-/*rst*/ {sCL, sCL, sCL, sCL, sCL, sCL, sCL, sCL, sLA, sLI },
-/*none*/{sIV, sIV, sIV, sIV, sIV, sIV, sIV, sIV, sIV, sIV }
+/* REPLY */
+/* 	     sNO, sSS, sSR, sES, sFW, sCW, sLA, sTW, sCL, sLI	*/
+/*syn*/	   { sIV, sSS, sSR, sCL, sCL, sCL, sCL, sSS, sSS, sIV },
+/*
+ *	sNO -> sIV	Never reached.
+ *	sSS -> sSS	Simultaneous open.
+ *	sSR -> sSR	Simultaneous open, retransmitted SYN.
+ *			We have seen a SYN/ACK, but it seems 
+ *			it is delayed or got lost.
+ *	sES -> sCL	Error.
+ *	sFW -> sCL
+ *	sCW -> sCL
+ *	sLA -> sCL
+ *	sTW -> sSS	Reopened connection.
+ *	sCL -> sSS
+ */
+/* 	     sNO, sSS, sSR, sES, sFW, sCW, sLA, sTW, sCL, sLI	*/
+/*synack*/ { sIV, sSR, sES, sCL, sCL, sCL, sCL, sCL, sCL, sIV },
+/*
+ *	sSS -> sSR	Standard open.
+ *	sSR -> sES	Simultaneous open.
+ *	sES -> sCL	Error.
+ *	sFW -> sCL
+ *	sCW -> sCL
+ *	sLA -> sCL
+ *	sTW -> sCL
+ *	sCL -> sCL
+ */
+/* 	     sNO, sSS, sSR, sES, sFW, sCW, sLA, sTW, sCL, sLI	*/
+/*fin*/    { sIV, sIV, sFW, sFW, sLA, sLA, sLA, sTW, sCL, sIV },
+/*
+ *	sSS -> sIV	Server might not send FIN in this state.
+ *	sSR -> sFW	Close started.
+ *	sES -> sFW
+ *	sFW -> sLA	FIN seen in both directions.
+ *	sCW -> sLA
+ *	sLA -> sLA	Retransmitted FIN.
+ *	sTW -> sTW
+ *	sCL -> sCL
+ */
+/* 	     sNO, sSS, sSR, sES, sFW, sCW, sLA, sTW, sCL, sLI	*/
+/*ack*/	   { sIV, sIV, sES, sES, sCW, sCW, sTW, sTW, sCL, sIV },
+/*
+ *	sSS -> sIV	ACK is invalid: we haven't seen a SYN/ACK yet.
+ *	sSR -> sES	Simultaneous open.
+ *	sES -> sES	:-)
+ *	sFW -> sCW	Normal close request answered by ACK.
+ *	sCW -> sCW
+ *	sLA -> sTW	Last ACK detected.
+ *	sTW -> sTW	Retransmitted last ACK.
+ *	sCL -> sCL
+ */
+/* 	     sNO, sSS, sSR, sES, sFW, sCW, sLA, sTW, sCL, sLI	*/
+/*rst*/    { sIV, sCL, sCL, sCL, sCL, sIV, sCL, sCL, sCL, sIV },
+/*none*/   { sIV, sIV, sIV, sIV, sIV, sIV, sIV, sIV, sIV, sIV }
 	}
 };
 
@@ -136,19 +293,322 @@
 
 static unsigned int get_conntrack_index(const struct tcphdr *tcph)
 {
-	if (tcph->rst) return 3;
-	else if (tcph->syn) return 0;
-	else if (tcph->fin) return 1;
-	else if (tcph->ack) return 2;
-	else return 4;
+	if (tcph->rst) return 4;
+	else if (tcph->syn) return (tcph->ack ? 1 : 0);
+	else if (tcph->fin) return 2;
+	else if (tcph->ack) return 3;
+	else return 5;
 }
 
+/* From ipt_LOG.c... */
+/* Use lock to serialize, so printks don't overlap */
+static spinlock_t log_lock = SPIN_LOCK_UNLOCKED;
+
+static void log_packet(struct iphdr *iph, struct tcphdr *tcph)
+{
+	/* Important fields:
+	 * TOS, len, DF/MF, fragment offset, TTL, src, dst, options. */
+	/* Max length: 40 "SRC=255.255.255.255 DST=255.255.255.255 " */
+	printk("SRC=%u.%u.%u.%u DST=%u.%u.%u.%u ",
+	       NIPQUAD(iph->saddr), NIPQUAD(iph->daddr));
+
+	/* Max length: 46 "LEN=65535 TOS=0xFF PREC=0xFF TTL=255 ID=65535 " */
+	printk("LEN=%u TOS=0x%02X PREC=0x%02X TTL=%u ID=%u ",
+	       ntohs(iph->tot_len), iph->tos & IPTOS_TOS_MASK,
+	       iph->tos & IPTOS_PREC_MASK, iph->ttl, ntohs(iph->id));
+
+	/* Max length: 6 "CE DF MF " */
+	if (ntohs(iph->frag_off) & IP_CE)
+		printk("CE ");
+	if (ntohs(iph->frag_off) & IP_DF)
+		printk("DF ");
+	/* ... but conntrack don't see fragments */
+
+	if (iph->ihl * 4 != sizeof(struct iphdr)) {
+		unsigned int i;
+
+		/* Max length: 127 "OPT (" 15*4*2chars ") " */
+		printk("OPT (");
+		for (i = sizeof(struct iphdr); i < iph->ihl * 4; i++)
+			printk("%02X", ((u_int8_t *)iph)[i]);
+		printk(") ");
+	}
+
+	/* Max length: 10 "PROTO=TCP " */
+	printk("PROTO=TCP ");
+
+	/* Max length: 20 "SPT=65535 DPT=65535 " */
+	printk("SPT=%u DPT=%u ",
+	       ntohs(tcph->source), ntohs(tcph->dest));
+	/* Max length: 30 "SEQ=4294967295 ACK=4294967295 " */
+	printk("SEQ=%u ACK=%u ",
+	       ntohl(tcph->seq), ntohl(tcph->ack_seq));
+	/* Max length: 13 "WINDOW=65535 " */
+	printk("WINDOW=%u ", ntohs(tcph->window));
+	/* Max length: 9 "RES=0x3F " */
+	printk("RES=0x%02x ", (u_int8_t)(ntohl(tcp_flag_word(tcph) & TCP_RESERVED_BITS) >> 22));
+	/* Max length: 36 "URG ACK PSH RST SYN FIN " */
+	if (tcph->urg)
+		printk("URG ");
+	if (tcph->ack)
+		printk("ACK ");
+	if (tcph->psh)
+		printk("PSH ");
+	if (tcph->rst)
+		printk("RST ");
+	if (tcph->syn)
+		printk("SYN ");
+	if (tcph->fin)
+		printk("FIN ");
+	/* Max length: 11 "URGP=65535 " */
+	printk("URGP=%u ", ntohs(tcph->urg_ptr));
+
+	if (tcph->doff * 4 != sizeof(struct tcphdr)) {
+		unsigned int i;
+
+		/* Max length: 127 "OPT (" 15*4*2chars ") " */
+		printk("OPT (");
+		for (i =sizeof(struct tcphdr); i < tcph->doff * 4; i++)
+			printk("%02X", ((u_int8_t *)tcph)[i]);
+		printk(") ");
+	}
+
+	/* Proto    Max log string length */
+	/* IP:      40+46+6+11+127 = 230 */
+	/* TCP:     10+max(25,20+30+13+9+36+11+127) = 256 */
+}
+
+#define log_invalid_packet(iph, tcph, format, arg...) 	\
+do { 							\
+	spin_lock_bh(&log_lock); 			\
+	log_packet(iph, tcph); 				\
+	printk(format, ## arg); 			\
+	spin_unlock_bh(&log_lock); 			\
+} while (0);
+
+/* TCP connection tracking based on 'Real Stateful TCP Packet Filtering
+   in IP Filter' by Guido van Rooij.
+   
+   http://www.nluug.nl/events/sane2000/papers.html
+   http://www.iae.nl/users/guido/papers/tcp_filtering.ps.gz
+   
+   The boundaries according to the article:
+   
+   	td_maxend = max(ack + max(win,1)) seen in reply packets
+	td_maxwin = max(max(win, 1)) seen in sent packets
+	td_end    = max(seq + len) seen in sent packets
+   
+   I. 	Upper bound for valid data:	seq + len <= sender.td_maxend
+   II. 	Lower bound for valid data:	seq >= sender.td_end - receiver.td_maxwin
+   III.	Upper bound for valid ack:	ack <= receiver.td_end
+   IV.	Lower bound for valid ack:	ack >= receiver.td_end - MAXACKWINDOW
+   	
+   The upper bound limit for a valid ack is not ignored - 
+   we doesn't have to deal with fragments. 
+*/
+
+#define SEGMENT_SEQ_PLUS_LEN(seq, len, iph, tcph)	(seq + len - (iph->ihl + tcph->doff)*4 \
+							 + (tcph->syn ? 1 : 0) + (tcph->fin ? 1 : 0))
+
+/* Fixme: what about big packets? */
+#define MAXACKWINDOW	66000
+
+/*
+ * Simplified tcp_parse_options routine from tcp_input.c
+ */
+static u_int8_t tcp_scale_option(struct iphdr *iph, struct tcphdr *tcph)
+{
+	unsigned char *ptr;
+	int length = (tcph->doff*4) - sizeof(struct tcphdr);
+	
+	ptr = (unsigned char *)(tcph + 1);
+	
+	while (length > 0) {
+		int opcode=*ptr++;
+		int opsize;
+		
+		switch (opcode) {
+			case TCPOPT_EOL:
+				return 0;
+			case TCPOPT_NOP:	/* Ref: RFC 793 section 3.1 */
+				length--;
+				continue;
+			default:
+				opsize=*ptr++;
+				if (opsize < 2) /* "silly options" */
+					return 0;
+				if (opsize > length)
+					break;	/* don't parse partial options */
+					
+				if (opcode == TCPOPT_WINDOW && opsize == TCPOLEN_WINDOW) {
+					u_int8_t scale = *(u_int8_t *)ptr;
+					
+					if (scale > 14) {
+						/* See RFC1323 for an explanation of the limit to 14 */
+						if (ip_ct_tcp_log_invalid_scale && net_ratelimit())
+							log_invalid_packet(iph, tcph, "Illegal window scaling value %u > 14 ignored\n",
+									   scale);
+						scale = 14;
+					}
+					return scale;
+				}
+				ptr += opsize - 2;
+				length -= opsize;
+		}
+	}
+	return 0;
+}
+
+static int tcp_in_window(struct ip_ct_tcp_state *sender, 
+                         struct ip_ct_tcp_state *receiver,
+                         struct iphdr *iph, size_t len,
+                         struct tcphdr *tcph)
+{
+	__u32 seq, ack, end, swin;
+	__u16 win;
+	int res;
+	
+	/*
+	 * Get the required data from the packet.
+	 */
+	seq = ntohl(tcph->seq);
+	ack = ntohl(tcph->ack_seq);
+	win = ntohs(tcph->window);
+	end = SEGMENT_SEQ_PLUS_LEN(seq, len, iph, tcph);
+	
+	DEBUGP("tcp_in_window: src=%u.%u.%u.%u:%hu dst=%u.%u.%u.%u:%hu seq=%u ack=%u win=%u end=%u\n",
+		NIPQUAD(iph->saddr), ntohs(tcph->source), NIPQUAD(iph->daddr), ntohs(tcph->dest),
+		seq, ack, win, end);
+	DEBUGP("tcp_in_window: sender end=%u maxend=%u maxwin=%u scale=%i receiver end=%u maxend=%u maxwin=%u scale=%i\n",
+		sender->td_end, sender->td_maxend, sender->td_maxwin, sender->td_scale, 
+		receiver->td_end, receiver->td_maxend, receiver->td_maxwin, receiver->td_scale);
+		
+	if (sender->td_end == 0) {
+		/*
+		 * Initialize sender data.
+		 */
+		if (tcph->syn && tcph->ack) {
+			/*
+			 * Outgoing SYN-ACK in reply to a SYN.
+			 *
+			 * Fixme: here we loose supporting simultaneous open...
+			 */
+			sender->td_end = 
+			sender->td_maxend = end;
+			sender->td_maxwin = (win == 0 ? 1 : win);
+			sender->td_scale = tcp_scale_option(iph, tcph);
+		} else {
+			/*
+			 * We are in the middle of a connection,
+			 * its history is lost for us.
+			 * Let's try to use the data from the packet.
+		 	 */
+			sender->td_end = end;
+			sender->td_maxwin = (win == 0 ? 1 : win);
+			sender->td_maxend = end + sender->td_maxwin;
+			sender->td_scale = 0;
+		}
+	}
+	
+	if (!(tcph->ack)) {
+		/*
+		 * If there is no ACK, just pretend it was set and OK.
+		 */
+		ack = receiver->td_end;
+	} else if (((tcp_flag_word(tcph) & (TCP_FLAG_ACK|TCP_FLAG_RST)) == (TCP_FLAG_ACK|TCP_FLAG_RST)) 
+		   && (ack == 0)) {
+		/*
+		 * Broken TCP stacks, that set ACK in RST packets as well
+		 * with zero ack value.
+		 */
+		ack = receiver->td_end;
+	}
+	
+	if (seq == end)
+		/*
+		 * Packets contains no data: we assume it is valid
+		 * and check the ack value only.
+		 */
+		seq = end = sender->td_end;
+
+	DEBUGP("tcp_in_window: I=%i II=%i III=%i IV=%i\n",
+		before(end, sender->td_maxend + 1),
+	    	after(seq, sender->td_end - receiver->td_maxwin - 1),
+	    	before(ack, receiver->td_end + 1),
+	    	after(ack, receiver->td_end - MAXACKWINDOW));
+		
+	if (before(end, sender->td_maxend + 1) &&
+	    after(seq, sender->td_end - receiver->td_maxwin - 1) &&
+	    before(ack, receiver->td_end + 1) &&
+	    after(ack, receiver->td_end - MAXACKWINDOW)) {
+	    	/*
+		 * Take into account window scaling (RFC 1323).
+		 */
+
+		swin = win;
+		
+		if (tcph->ack && sender->td_scale && receiver->td_scale)
+			swin <<= sender->td_scale;
+		/*
+		 * Update sender data.
+		 */
+		if (sender->td_maxwin < swin)
+			sender->td_maxwin = swin;
+		if (after(end, sender->td_end))
+			sender->td_end = end;
+		if (after(ack + swin, receiver->td_maxend - 1)) {
+			receiver->td_maxend = ack + swin;
+			if (win == 0)
+				receiver->td_maxend++;
+		}
+		res = 1;
+	} else {
+		if (ip_ct_tcp_log_out_of_window && net_ratelimit())
+			log_invalid_packet(iph, tcph, "Out of window data: %s\n",
+				before(end, sender->td_maxend + 1) ?
+					after(seq, sender->td_end - receiver->td_maxwin - 1) ?
+						before(ack, receiver->td_end + 1) ?
+							after(ack, receiver->td_end - MAXACKWINDOW) ? "BUG"
+							: "ACK is under the lower bound (possibly overly delayed ACK)"
+						: "ACK is over the upper bound (ACKed data has never seen yet)"
+					: "SEQ is under the lower bound (retransmitted already ACKed data)"
+				: "SEQ is over the upper bound (over the window of the receiver)");
+		res = ip_ct_tcp_be_liberal && !tcph->rst;
+	}
+
+	DEBUGP("tcp_in_window: res=%i sender end=%u maxend=%u maxwin=%u receiver end=%u maxend=%u maxwin=%u\n",
+		res, sender->td_end, sender->td_maxend, sender->td_maxwin, 
+		receiver->td_end, receiver->td_maxend, receiver->td_maxwin);
+
+	return res;
+}
+
+/* Update sender->td_end after NAT successfully mangled the packet */
+void ip_conntrack_tcp_update(struct ip_conntrack *conntrack, int dir,
+                             struct iphdr *iph, size_t newlen,
+                             struct tcphdr *tcph)
+{
+	__u32 end;
+
+	end = SEGMENT_SEQ_PLUS_LEN(ntohl(tcph->seq), newlen, iph, tcph);
+	
+	WRITE_LOCK(&tcp_lock);
+	/*
+	 * We have to worry for the ack in the reply packet only...
+	 */
+	if (after(end, conntrack->proto.tcp.seen[dir].td_end))
+		conntrack->proto.tcp.seen[dir].td_end = end;
+	WRITE_UNLOCK(&tcp_lock);
+}
+
+
 /* Returns verdict for packet, or -1 for invalid. */
 static int tcp_packet(struct ip_conntrack *conntrack,
 		      struct iphdr *iph, size_t len,
 		      enum ip_conntrack_info ctinfo)
 {
-	enum tcp_conntrack newconntrack, oldtcpstate;
+	enum tcp_conntrack new_state, old_state;
+	enum ip_conntrack_dir dir;
 	struct tcphdr *tcph = (struct tcphdr *)((u_int32_t *)iph + iph->ihl);
 
 	/* We're guaranteed to have the base header, but maybe not the
@@ -159,29 +619,43 @@
 	}
 
 	WRITE_LOCK(&tcp_lock);
-	oldtcpstate = conntrack->proto.tcp.state;
-	newconntrack
+	old_state = conntrack->proto.tcp.state;
+	dir = CTINFO2DIR(ctinfo);
+	
+	new_state
 		= tcp_conntracks
-		[CTINFO2DIR(ctinfo)]
-		[get_conntrack_index(tcph)][oldtcpstate];
+		[dir]
+		[get_conntrack_index(tcph)][old_state];
+
+	if (new_state == TCP_CONNTRACK_SYN_SENT
+	    && old_state >= TCP_CONNTRACK_TIME_WAIT) {
+		/* Attempt to reopen a closed connection.
+		 * Delete this connection and look up again. */
+		WRITE_UNLOCK(&tcp_lock);
+		if (del_timer(&conntrack->timeout))
+			conntrack->timeout.function((unsigned long)conntrack);
+		return NF_REPEAT;
+	} else if (!(new_state == TCP_CONNTRACK_MAX
+	           || tcp_in_window(&conntrack->proto.tcp.seen[dir],
+				    &conntrack->proto.tcp.seen[!dir],
+				    iph, len, tcph)))
+		new_state = TCP_CONNTRACK_MAX;
+
+	DEBUGP("tcp_conntracks: src=%u.%u.%u.%u:%hu dst=%u.%u.%u.%u:%hu syn=%i ack=%i fin=%i rst=%i old=%i new=%i\n",
+		NIPQUAD(iph->saddr), ntohs(tcph->source), NIPQUAD(iph->daddr), ntohs(tcph->dest),
+		(tcph->syn ? 1 : 0), (tcph->ack ? 1 : 0), (tcph->fin ? 1 : 0), (tcph->rst ? 1 : 0),
+		old_state, new_state);
 
 	/* Invalid */
-	if (newconntrack == TCP_CONNTRACK_MAX) {
+	if (new_state == TCP_CONNTRACK_MAX) {
 		DEBUGP("ip_conntrack_tcp: Invalid dir=%i index=%u conntrack=%u\n",
-		       CTINFO2DIR(ctinfo), get_conntrack_index(tcph),
-		       conntrack->proto.tcp.state);
+		       dir, get_conntrack_index(tcph),
+		       old_state);
 		WRITE_UNLOCK(&tcp_lock);
 		return -1;
 	}
 
-	conntrack->proto.tcp.state = newconntrack;
-
-	/* Poor man's window tracking: record SYN/ACK for handshake check */
-	if (oldtcpstate == TCP_CONNTRACK_SYN_SENT
-	    && CTINFO2DIR(ctinfo) == IP_CT_DIR_REPLY
-	    && tcph->syn && tcph->ack)
-		conntrack->proto.tcp.handshake_ack
-			= htonl(ntohl(tcph->seq) + 1);
+	conntrack->proto.tcp.state = new_state;
 	WRITE_UNLOCK(&tcp_lock);
 
 	/* If only reply is a RST, we can consider ourselves not to
@@ -193,13 +667,11 @@
 			conntrack->timeout.function((unsigned long)conntrack);
 	} else {
 		/* Set ASSURED if we see see valid ack in ESTABLISHED after SYN_RECV */
-		if (oldtcpstate == TCP_CONNTRACK_SYN_RECV
-		    && CTINFO2DIR(ctinfo) == IP_CT_DIR_ORIGINAL
-		    && tcph->ack && !tcph->syn
-		    && tcph->ack_seq == conntrack->proto.tcp.handshake_ack)
+		if (old_state == TCP_CONNTRACK_SYN_RECV
+		    && new_state == TCP_CONNTRACK_ESTABLISHED)
 			set_bit(IPS_ASSURED_BIT, &conntrack->status);
 
-		ip_ct_refresh(conntrack, tcp_timeouts[newconntrack]);
+		ip_ct_refresh(conntrack, *tcp_timeouts[new_state]);
 	}
 
 	return NF_ACCEPT;
@@ -209,23 +681,57 @@
 static int tcp_new(struct ip_conntrack *conntrack,
 		   struct iphdr *iph, size_t len)
 {
-	enum tcp_conntrack newconntrack;
+	enum tcp_conntrack new_state;
 	struct tcphdr *tcph = (struct tcphdr *)((u_int32_t *)iph + iph->ihl);
 
 	/* Don't need lock here: this conntrack not in circulation yet */
-	newconntrack
+	new_state
 		= tcp_conntracks[0][get_conntrack_index(tcph)]
 		[TCP_CONNTRACK_NONE];
 
 	/* Invalid: delete conntrack */
-	if (newconntrack == TCP_CONNTRACK_MAX) {
+	if (new_state == TCP_CONNTRACK_MAX) {
 		DEBUGP("ip_conntrack_tcp: invalid new deleting.\n");
 		return 0;
 	}
 
-	conntrack->proto.tcp.state = newconntrack;
+	if (new_state == TCP_CONNTRACK_SYN_SENT) {
+		conntrack->proto.tcp.seen[0].td_end =
+			SEGMENT_SEQ_PLUS_LEN(ntohl(tcph->seq), len, iph, tcph);
+		conntrack->proto.tcp.seen[0].td_maxwin = ntohs(tcph->window);
+		if (conntrack->proto.tcp.seen[0].td_maxwin == 0)
+			conntrack->proto.tcp.seen[0].td_maxwin = 1;
+		conntrack->proto.tcp.seen[0].td_maxend =
+			conntrack->proto.tcp.seen[0].td_end;
+		conntrack->proto.tcp.seen[0].td_scale = tcp_scale_option(iph, tcph);
+	} else {
+		/*
+		 * We are in the middle of a connection,
+		 * its history is lost for us.
+		 * Let's try to use the data from the packet.
+		 */
+		conntrack->proto.tcp.seen[0].td_end =
+			SEGMENT_SEQ_PLUS_LEN(ntohl(tcph->seq), len, iph, tcph);
+		conntrack->proto.tcp.seen[0].td_maxwin = ntohs(tcph->window);
+		if (conntrack->proto.tcp.seen[0].td_maxwin == 0)
+			conntrack->proto.tcp.seen[0].td_maxwin = 1;
+		conntrack->proto.tcp.seen[0].td_maxend =
+			conntrack->proto.tcp.seen[0].td_end + 
+			conntrack->proto.tcp.seen[0].td_maxwin;
+		conntrack->proto.tcp.seen[0].td_scale = 0;
+	}
+    
+	conntrack->proto.tcp.seen[1].td_end = 0;
+	conntrack->proto.tcp.seen[1].td_maxend = 0;
+	conntrack->proto.tcp.seen[1].td_maxwin = 1;
+	conntrack->proto.tcp.seen[1].td_scale = 0;      
+
+	conntrack->proto.tcp.state = new_state;
+ 
 	return 1;
 }
+
+EXPORT_SYMBOL(ip_conntrack_tcp_update);
 
 struct ip_conntrack_protocol ip_conntrack_protocol_tcp
 = { { NULL, NULL }, IPPROTO_TCP, "tcp",
diff -urN --exclude-from=diff.exclude linux-2.4.9/net/ipv4/netfilter.orig/ip_conntrack_proto_udp.c linux-2.4.9/net/ipv4/netfilter/ip_conntrack_proto_udp.c
--- linux-2.4.9/net/ipv4/netfilter.orig/ip_conntrack_proto_udp.c	Fri Apr 27 23:15:01 2001
+++ linux-2.4.9/net/ipv4/netfilter/ip_conntrack_proto_udp.c	Thu Sep 20 14:37:24 2001
@@ -6,8 +6,8 @@
 #include <linux/udp.h>
 #include <linux/netfilter_ipv4/ip_conntrack_protocol.h>
 
-#define UDP_TIMEOUT (30*HZ)
-#define UDP_STREAM_TIMEOUT (180*HZ)
+unsigned long ip_ct_udp_timeout = 30*HZ;
+unsigned long ip_ct_udp_timeout_stream = 180*HZ;
 
 static int udp_pkt_to_tuple(const void *datah, size_t datalen,
 			    struct ip_conntrack_tuple *tuple)
@@ -52,11 +52,11 @@
 	/* If we've seen traffic both ways, this is some kind of UDP
 	   stream.  Extend timeout. */
 	if (conntrack->status & IPS_SEEN_REPLY) {
-		ip_ct_refresh(conntrack, UDP_STREAM_TIMEOUT);
+		ip_ct_refresh(conntrack, ip_ct_udp_timeout_stream);
 		/* Also, more likely to be important, and not a probe */
 		set_bit(IPS_ASSURED_BIT, &conntrack->status);
 	} else
-		ip_ct_refresh(conntrack, UDP_TIMEOUT);
+		ip_ct_refresh(conntrack, ip_ct_udp_timeout);
 
 	return NF_ACCEPT;
 }
diff -urN --exclude-from=diff.exclude linux-2.4.9/net/ipv4/netfilter.orig/ip_conntrack_standalone.c linux-2.4.9/net/ipv4/netfilter/ip_conntrack_standalone.c
--- linux-2.4.9/net/ipv4/netfilter.orig/ip_conntrack_standalone.c	Fri Apr 27 23:15:01 2001
+++ linux-2.4.9/net/ipv4/netfilter/ip_conntrack_standalone.c	Wed Oct  3 14:19:51 2001
@@ -7,6 +7,7 @@
 /* (c) 1999 Paul `Rusty' Russell.  Licenced under the GNU General
    Public Licence. */
 
+#include <linux/config.h>
 #include <linux/types.h>
 #include <linux/ip.h>
 #include <linux/netfilter.h>
@@ -15,6 +16,9 @@
 #include <linux/skbuff.h>
 #include <linux/proc_fs.h>
 #include <linux/version.h>
+#ifdef CONFIG_SYSCTL
+#include <linux/sysctl.h>
+#endif
 #include <net/checksum.h>
 
 #define ASSERT_READ_LOCK(x) MUST_BE_READ_LOCKED(&ip_conntrack_lock)
@@ -224,6 +228,108 @@
 static struct nf_hook_ops ip_conntrack_local_in_ops
 = { { NULL, NULL }, ip_confirm, PF_INET, NF_IP_LOCAL_IN, NF_IP_PRI_LAST-1 };
 
+/* Sysctl support */
+
+#ifdef CONFIG_SYSCTL
+
+/* From ip_conntrack_proto_tcp.c */
+extern unsigned long ip_ct_tcp_timeout_none;
+extern unsigned long ip_ct_tcp_timeout_syn_sent;
+extern unsigned long ip_ct_tcp_timeout_syn_recv;
+extern unsigned long ip_ct_tcp_timeout_established;
+extern unsigned long ip_ct_tcp_timeout_fin_wait;
+extern unsigned long ip_ct_tcp_timeout_close_wait;
+extern unsigned long ip_ct_tcp_timeout_last_ack;
+extern unsigned long ip_ct_tcp_timeout_time_wait;
+extern unsigned long ip_ct_tcp_timeout_close;
+extern unsigned long ip_ct_tcp_timeout_listen;
+extern int ip_ct_tcp_log_invalid_scale;
+extern int ip_ct_tcp_log_out_of_window;
+extern int ip_ct_tcp_be_liberal;
+
+/* From ip_conntrack_proto_udp.c */
+extern unsigned long ip_ct_udp_timeout;
+extern unsigned long ip_ct_udp_timeout_stream;
+
+/* From ip_conntrack_proto_icmp.c */
+extern unsigned long ip_ct_icmp_timeout;
+
+/* From ip_conntrack_proto_icmp.c */
+extern unsigned long ip_ct_generic_timeout;
+
+static struct ctl_table_header *ip_ct_sysctl_header;
+
+static ctl_table ip_ct_sysctl_table[18] = {
+	{NET_IPV4_NF_CT_TCP_TIMEOUT_NONE, "ip_ct_tcp_timeout_none",
+	 &ip_ct_tcp_timeout_none, sizeof(unsigned long), 0644, NULL,
+	 &proc_doulongvec_minmax},
+	{NET_IPV4_NF_CT_TCP_TIMEOUT_SYN_SENT, "ip_ct_tcp_timeout_syn_sent",
+	 &ip_ct_tcp_timeout_syn_sent, sizeof(unsigned long), 0644, NULL,
+	 &proc_doulongvec_minmax},
+	{NET_IPV4_NF_CT_TCP_TIMEOUT_SYN_RECV, "ip_ct_tcp_timeout_syn_recv",
+	 &ip_ct_tcp_timeout_syn_recv, sizeof(unsigned long), 0644, NULL,
+	 &proc_doulongvec_minmax},
+	{NET_IPV4_NF_CT_TCP_TIMEOUT_ESTABLISHED, "ip_ct_tcp_timeout_established",
+	 &ip_ct_tcp_timeout_established, sizeof(unsigned long), 0644, NULL,
+	 &proc_doulongvec_minmax},
+	{NET_IPV4_NF_CT_TCP_TIMEOUT_FIN_WAIT, "ip_ct_tcp_timeout_fin_wait",
+	 &ip_ct_tcp_timeout_fin_wait, sizeof(unsigned long), 0644, NULL,
+	 &proc_doulongvec_minmax},
+	{NET_IPV4_NF_CT_TCP_TIMEOUT_CLOSE_WAIT, "ip_ct_tcp_timeout_close_wait",
+	 &ip_ct_tcp_timeout_close_wait, sizeof(unsigned long), 0644, NULL,
+	 &proc_doulongvec_minmax},
+	{NET_IPV4_NF_CT_TCP_TIMEOUT_LAST_ACK, "ip_ct_tcp_timeout_last_ack",
+	 &ip_ct_tcp_timeout_last_ack, sizeof(unsigned long), 0644, NULL,
+	 &proc_doulongvec_minmax},
+	{NET_IPV4_NF_CT_TCP_TIMEOUT_TIME_WAIT, "ip_ct_tcp_timeout_time_wait",
+	 &ip_ct_tcp_timeout_time_wait, sizeof(unsigned long), 0644, NULL,
+	 &proc_doulongvec_minmax},
+	{NET_IPV4_NF_CT_TCP_TIMEOUT_CLOSE, "ip_ct_tcp_timeout_close",
+	 &ip_ct_tcp_timeout_close, sizeof(unsigned long), 0644, NULL,
+	 &proc_doulongvec_minmax},
+	{NET_IPV4_NF_CT_TCP_TIMEOUT_LISTEN, "ip_ct_tcp_timeout_listen",
+	 &ip_ct_tcp_timeout_listen, sizeof(unsigned long), 0644, NULL,
+	 &proc_doulongvec_minmax},
+	{NET_IPV4_NF_CT_TCP_LOG_INVALID_SCALE, "ip_ct_tcp_log_invalid_scale",
+	 &ip_ct_tcp_log_invalid_scale, sizeof(unsigned long), 0644, NULL,
+	 &proc_dointvec},
+	{NET_IPV4_NF_CT_TCP_LOG_OUT_OF_WINDOW, "ip_ct_tcp_log_out_of_window",
+	 &ip_ct_tcp_log_out_of_window, sizeof(unsigned long), 0644, NULL,
+	 &proc_dointvec},
+	{NET_IPV4_NF_CT_TCP_BE_LIBERAL, "ip_ct_tcp_be_liberal",
+	 &ip_ct_tcp_be_liberal, sizeof(unsigned long), 0644, NULL,
+	 &proc_dointvec},
+	{NET_IPV4_NF_CT_UDP_TIMEOUT, "ip_ct_udp_timeout",
+	 &ip_ct_udp_timeout, sizeof(unsigned long), 0644, NULL,
+	 &proc_doulongvec_minmax},
+	{NET_IPV4_NF_CT_UDP_TIMEOUT_STREAM, "ip_ct_udp_timeout_stream",
+	 &ip_ct_udp_timeout_stream, sizeof(unsigned long), 0644, NULL,
+	 &proc_doulongvec_minmax},
+	{NET_IPV4_NF_CT_ICMP_TIMEOUT, "ip_ct_icmp_timeout",
+	 &ip_ct_icmp_timeout, sizeof(unsigned long), 0644, NULL,
+	 &proc_doulongvec_minmax},
+	{NET_IPV4_NF_CT_GENERIC_TIMEOUT, "ip_ct_generic_timeout",
+	 &ip_ct_generic_timeout, sizeof(unsigned long), 0644, NULL,
+	 &proc_doulongvec_minmax},
+	{0}
+};
+
+static ctl_table ip_ct_netfilter_table[] = {
+	{NET_IPV4_NETFILTER, "netfilter", NULL, 0, 0555, ip_ct_sysctl_table, 0, 0, 0, 0, 0},
+	{0}
+};
+
+static ctl_table ip_ct_ipv4_table[] = {
+	{NET_IPV4, "ipv4", NULL, 0, 0555, ip_ct_netfilter_table, 0, 0, 0, 0, 0},
+	{0}
+};
+
+static ctl_table ip_ct_net_table[] = {
+	{CTL_NET, "net", NULL, 0, 0555, ip_ct_ipv4_table, 0, 0, 0, 0, 0},
+	{0}
+};
+#endif
+
 static int init_or_cleanup(int init)
 {
 	int ret = 0;
@@ -255,10 +361,20 @@
 		printk("ip_conntrack: can't register local in hook.\n");
 		goto cleanup_inoutandlocalops;
 	}
+#ifdef CONFIG_SYSCTL
+	ip_ct_sysctl_header = register_sysctl_table(ip_ct_net_table, 0);
+	if (ip_ct_sysctl_header == NULL) {
+		printk("ip_conntrack: can't register to sysctl.\n");
+		goto cleanup;
+	}
+#endif
 
 	return ret;
 
  cleanup:
+#ifdef CONFIG_SYSCTL
+ 	unregister_sysctl_table(ip_ct_sysctl_header);
+#endif
 	nf_unregister_hook(&ip_conntrack_local_in_ops);
  cleanup_inoutandlocalops:
 	nf_unregister_hook(&ip_conntrack_out_ops);
diff -urN --exclude-from=diff.exclude linux-2.4.9/net/ipv4/netfilter.orig/ip_nat_helper.c linux-2.4.9/net/ipv4/netfilter/ip_nat_helper.c
--- linux-2.4.9/net/ipv4/netfilter.orig/ip_nat_helper.c	Fri Apr 27 23:15:01 2001
+++ linux-2.4.9/net/ipv4/netfilter/ip_nat_helper.c	Wed Sep 19 17:30:03 2001
@@ -100,6 +100,8 @@
 	DEBUGP("ip_nat_resize_packet: Seq_offset after: ");
 	DUMP_OFFSET(this_way);
 	
+	ip_conntrack_tcp_update(ct, dir, iph, new_size, tcph);
+
 	return 1;
 }
 
--- linux-2.4.9/include/linux/sysctl.h.orig	Wed Sep 19 17:32:11 2001
+++ linux-2.4.9/include/linux/sysctl.h	Mon Oct  1 14:21:15 2001
@@ -224,6 +224,7 @@
 	NET_IPV4_NEIGH=17,
 	NET_IPV4_ROUTE=18,
 	NET_IPV4_FIB_HASH=19,
+	NET_IPV4_NETFILTER=20,
 
 	NET_IPV4_TCP_TIMESTAMPS=33,
 	NET_IPV4_TCP_WINDOW_SCALING=34,
@@ -326,6 +327,27 @@
 	NET_IPV4_CONF_LOG_MARTIANS=11,
 	NET_IPV4_CONF_TAG=12,
 	NET_IPV4_CONF_ARPFILTER=13
+};
+
+enum
+{
+	NET_IPV4_NF_CT_TCP_TIMEOUT_NONE=1,
+	NET_IPV4_NF_CT_TCP_TIMEOUT_SYN_SENT=2,
+	NET_IPV4_NF_CT_TCP_TIMEOUT_SYN_RECV=3,
+	NET_IPV4_NF_CT_TCP_TIMEOUT_ESTABLISHED=4,
+	NET_IPV4_NF_CT_TCP_TIMEOUT_FIN_WAIT=5,
+	NET_IPV4_NF_CT_TCP_TIMEOUT_CLOSE_WAIT=6,
+	NET_IPV4_NF_CT_TCP_TIMEOUT_LAST_ACK=7,
+	NET_IPV4_NF_CT_TCP_TIMEOUT_TIME_WAIT=8,
+	NET_IPV4_NF_CT_TCP_TIMEOUT_CLOSE=9,
+	NET_IPV4_NF_CT_TCP_TIMEOUT_LISTEN=10,
+	NET_IPV4_NF_CT_TCP_LOG_INVALID_SCALE=11,
+	NET_IPV4_NF_CT_TCP_LOG_OUT_OF_WINDOW=12,
+	NET_IPV4_NF_CT_TCP_BE_LIBERAL=13,
+	NET_IPV4_NF_CT_UDP_TIMEOUT=14,
+	NET_IPV4_NF_CT_UDP_TIMEOUT_STREAM=15,
+	NET_IPV4_NF_CT_ICMP_TIMEOUT=16,
+	NET_IPV4_NF_CT_GENERIC_TIMEOUT=17
 };
 
 /* /proc/sys/net/ipv6 */
