[netfilter-cvslog] r3260 - in trunk/patch-o-matic-ng/nf_conntrack: . linux-2.6/include/linux/netfilter linux-2.6/include/linux/netfilter_ipv4 linux-2.6/include/linux/netfilter_ipv6 linux-2.6/net/ipv4/netfilter linux-2.6/net/ipv6/netfilter linux-2.6/net/netfilter

yasuyuki at netfilter.org yasuyuki at netfilter.org
Mon Nov 1 08:39:00 CET 2004


Author: yasuyuki at netfilter.org
Date: 2004-11-01 08:38:59 +0100 (Mon, 01 Nov 2004)
New Revision: 3260

Added:
   trunk/patch-o-matic-ng/nf_conntrack/linux-2.6/include/linux/netfilter/nf_conntrack_sctp.h
   trunk/patch-o-matic-ng/nf_conntrack/linux-2.6/net/ipv6/netfilter/nf_conntrack_reasm.c
   trunk/patch-o-matic-ng/nf_conntrack/linux-2.6/net/netfilter/nf_conntrack_l3proto_generic.c
   trunk/patch-o-matic-ng/nf_conntrack/linux-2.6/net/netfilter/nf_conntrack_proto_sctp.c
Removed:
   trunk/patch-o-matic-ng/nf_conntrack/linux-2.6/include/linux/netfilter_ipv6/nf_conntrack_frag6.h
   trunk/patch-o-matic-ng/nf_conntrack/linux-2.6/net/ipv4/netfilter/Kconfig.ladd
   trunk/patch-o-matic-ng/nf_conntrack/linux-2.6/net/ipv4/netfilter/Makefile.ladd
   trunk/patch-o-matic-ng/nf_conntrack/linux-2.6/net/ipv6/netfilter/Kconfig.ladd
   trunk/patch-o-matic-ng/nf_conntrack/linux-2.6/net/ipv6/netfilter/Makefile.ladd
   trunk/patch-o-matic-ng/nf_conntrack/linux-2.6/net/ipv6/netfilter/nf_conntrack_proto_frag6.c
Modified:
   trunk/patch-o-matic-ng/nf_conntrack/help
   trunk/patch-o-matic-ng/nf_conntrack/info
   trunk/patch-o-matic-ng/nf_conntrack/linux-2.6.patch
   trunk/patch-o-matic-ng/nf_conntrack/linux-2.6/include/linux/netfilter/nf_conntrack.h
   trunk/patch-o-matic-ng/nf_conntrack/linux-2.6/include/linux/netfilter/nf_conntrack_core.h
   trunk/patch-o-matic-ng/nf_conntrack/linux-2.6/include/linux/netfilter/nf_conntrack_l3proto.h
   trunk/patch-o-matic-ng/nf_conntrack/linux-2.6/include/linux/netfilter/nf_conntrack_protocol.h
   trunk/patch-o-matic-ng/nf_conntrack/linux-2.6/include/linux/netfilter/nf_conntrack_tcp.h
   trunk/patch-o-matic-ng/nf_conntrack/linux-2.6/include/linux/netfilter/nf_conntrack_tuple.h
   trunk/patch-o-matic-ng/nf_conntrack/linux-2.6/include/linux/netfilter_ipv4/nf_conntrack_icmp.h
   trunk/patch-o-matic-ng/nf_conntrack/linux-2.6/include/linux/netfilter_ipv4/nf_conntrack_ipv4.h
   trunk/patch-o-matic-ng/nf_conntrack/linux-2.6/include/linux/netfilter_ipv6/nf_conntrack_icmpv6.h
   trunk/patch-o-matic-ng/nf_conntrack/linux-2.6/net/ipv4/netfilter/nf_conntrack_l3proto_ipv4.c
   trunk/patch-o-matic-ng/nf_conntrack/linux-2.6/net/ipv4/netfilter/nf_conntrack_proto_icmp.c
   trunk/patch-o-matic-ng/nf_conntrack/linux-2.6/net/ipv6/netfilter/nf_conntrack_l3proto_ipv6.c
   trunk/patch-o-matic-ng/nf_conntrack/linux-2.6/net/ipv6/netfilter/nf_conntrack_proto_icmpv6.c
   trunk/patch-o-matic-ng/nf_conntrack/linux-2.6/net/netfilter/Kconfig
   trunk/patch-o-matic-ng/nf_conntrack/linux-2.6/net/netfilter/Makefile
   trunk/patch-o-matic-ng/nf_conntrack/linux-2.6/net/netfilter/nf_conntrack_core.c
   trunk/patch-o-matic-ng/nf_conntrack/linux-2.6/net/netfilter/nf_conntrack_ftp.c
   trunk/patch-o-matic-ng/nf_conntrack/linux-2.6/net/netfilter/nf_conntrack_proto_generic.c
   trunk/patch-o-matic-ng/nf_conntrack/linux-2.6/net/netfilter/nf_conntrack_proto_tcp.c
   trunk/patch-o-matic-ng/nf_conntrack/linux-2.6/net/netfilter/nf_conntrack_proto_udp.c
   trunk/patch-o-matic-ng/nf_conntrack/linux-2.6/net/netfilter/nf_conntrack_standalone.c
Log:

- synchronized nf_conntrack with ip_conntrack in linux 2.6.10-rc1.
- deleted the processing which re-allocate conntrack from BASIC slab cache
  and destroys HELPER slab cache when unloading all helper modules. All slab
  caches are destroyed when unloading nf_conntrack.ko.
- re-implemented handling fragmented IPv6 packets.



Modified: trunk/patch-o-matic-ng/nf_conntrack/help
===================================================================
--- trunk/patch-o-matic-ng/nf_conntrack/help	2004-10-24 22:27:31 UTC (rev 3259)
+++ trunk/patch-o-matic-ng/nf_conntrack/help	2004-11-01 07:38:59 UTC (rev 3260)
@@ -6,15 +6,15 @@
 Note:
 	The new IPv4 connection tracking module (nf_conntrack_ipv4) cannot
 	work with ip_conntrack. If you want to try nf_conntrack_ipv4,
-	please check off "Connection tracking (required for masq/NAT)".
+	please unload ip_conntrack.
 
 	On the other hand, you can use new IPv6 connection tracking module
 	(nf_conntrack_ipv6) with ip_conntrack.
 
 TODO:
 	- allocate conntrack with various size dependent on features. (done)
-	- handling fragmented packets (done)
-	- getorigdst() (is this needed ?)
-	- backport recent fixes to ip_conntrack. (done (from linux 2.4.7)).
+	- handling fragmented packets (implemented, but under discussion again)
+	- getorigdst() (done)
+	- backport recent fixes to ip_conntrack. (done (from linux 2.6.10-rc1)).
 	- wrapper for current ip_conntrack and ip_nat
 	- matches and targets working with connection tracking

Modified: trunk/patch-o-matic-ng/nf_conntrack/info
===================================================================
--- trunk/patch-o-matic-ng/nf_conntrack/info	2004-10-24 22:27:31 UTC (rev 3259)
+++ trunk/patch-o-matic-ng/nf_conntrack/info	2004-11-01 07:38:59 UTC (rev 3260)
@@ -1,5 +1,5 @@
 Author:	Yasuyuki KOZAKAI @ USAGI/WIDE Project <yasuyuki.kozakai at toshiba.co.jp>
 Status:	Testing
 Repository: extra
-Requires: linux >= 2.6.7
+Requires: linux >= 2.6.10-rc1
 Recompile: kernel

Modified: trunk/patch-o-matic-ng/nf_conntrack/linux-2.6/include/linux/netfilter/nf_conntrack.h
===================================================================
--- trunk/patch-o-matic-ng/nf_conntrack/linux-2.6/include/linux/netfilter/nf_conntrack.h	2004-10-24 22:27:31 UTC (rev 3259)
+++ trunk/patch-o-matic-ng/nf_conntrack/linux-2.6/include/linux/netfilter/nf_conntrack.h	2004-11-01 07:38:59 UTC (rev 3260)
@@ -63,15 +63,15 @@
 #include <linux/netfilter/nf_conntrack_tcp.h>
 #include <linux/netfilter_ipv4/nf_conntrack_icmp.h>
 #include <linux/netfilter_ipv6/nf_conntrack_icmpv6.h>
-#include <linux/netfilter_ipv6/nf_conntrack_frag6.h>
+#include <linux/netfilter/nf_conntrack_sctp.h>
 
 /* per conntrack: protocol private data */
 union nf_conntrack_proto {
 	/* insert conntrack proto private data here */
+	struct nf_ct_sctp sctp;
 	struct nf_ct_tcp tcp;
 	struct nf_ct_icmp icmp;
 	struct nf_ct_icmpv6 icmpv6;
-	struct nf_ct_frag6 frag6;
 };
 
 union nf_conntrack_expect_proto {
@@ -104,7 +104,7 @@
 #include <linux/types.h>
 #include <linux/skbuff.h>
 
-#ifdef CONFIG_NF_DEBUG
+#ifdef CONFIG_NETFILTER_DEBUG
 #define NF_CT_ASSERT(x)							\
 do {									\
 	if (!(x))							\
@@ -157,6 +157,12 @@
 	union nf_conntrack_expect_help help;
 };
 
+struct nf_conntrack_counter
+{
+	u_int64_t packets;
+	u_int64_t bytes;
+};
+
 struct nf_conntrack_helper;
 
 #include <linux/netfilter_ipv4/nf_conntrack_ipv4.h>
@@ -166,6 +172,7 @@
            plus 1 for any connection(s) we are `master' for */
 	struct nf_conntrack ct_general;
 
+	/* XXX should I move this to the tail ? - Y.K */
 	/* These are my tuples; original and reply */
 	struct nf_conntrack_tuple_hash tuplehash[NF_CT_DIR_MAX];
 
@@ -175,6 +182,11 @@
 	/* Timer function; drops refcnt when it goes off. */
 	struct timer_list timeout;
 
+#ifdef CONFIG_NF_CT_ACCT
+	/* Accounting Information (same cache line as other written members) */
+	struct nf_conntrack_counter counters[NF_CT_DIR_MAX];
+#endif
+
 	/* If we're expecting another related connection, this will be
            in expected linked list */
 	struct list_head sibling_list;
@@ -188,10 +200,6 @@
 	/* Helper. if any */
 	struct nf_conntrack_helper *helper;
 
-	/* Our various nf_ct_info structs specify *what* relation this
-           packet has to the conntrack */
-	struct nf_ct_info infos[NF_CT_NUMBER];
-
 	/* features - nat, helper, ... used by allocating system */
 	u_int32_t features;
 
@@ -199,6 +207,10 @@
 
 	union nf_conntrack_proto proto;
 
+#if defined(CONFIG_NF_CONNTRACK_MARK)
+	unsigned long mark;
+#endif
+
 	/* These members are dynamically allocated. */
 
 	union nf_conntrack_help *help;
@@ -226,8 +238,12 @@
 			 const struct nf_conn *ignored_conntrack);
 
 /* Return conntrack_info and tuple hash for given skb. */
-extern struct nf_conn *
-nf_ct_get(struct sk_buff *skb, enum nf_conntrack_info *ctinfo);
+static inline struct nf_conn *
+nf_ct_get(struct sk_buff *skb, enum nf_conntrack_info *ctinfo)
+{
+	*ctinfo = skb->nfctinfo;
+	return (struct nf_conn *)skb->nfct;
+}
 
 /* decrement reference count on a conntrack */
 extern inline void nf_ct_put(struct nf_conn *ct);
@@ -246,8 +262,10 @@
 				const struct nf_conntrack_tuple *orig);
 
 /* Refresh conntrack for this many jiffies */
-extern void nf_ct_refresh(struct nf_conn *ct,
-			  unsigned long extra_jiffies);
+extern void nf_ct_refresh_acct(struct nf_conn *ct,
+			       enum nf_conntrack_info ctinfo,
+			       const struct sk_buff *skb,
+			       unsigned long extra_jiffies);
 
 /* These are for NAT.  Icky. */
 /* Call me when a conntrack is destroyed. */
@@ -256,9 +274,7 @@
 /* Fake conntrack entry for untracked connections */
 extern struct nf_conn nf_conntrack_untracked;
 
-/* Returns new sk_buff, or NULL */
-struct sk_buff *
-nf_ct_gather_frags(struct sk_buff *skb);
+extern int nf_ct_no_defrag;
 
 /* Delete all conntracks which match. */
 extern void
@@ -273,6 +289,27 @@
 
 extern unsigned int nf_conntrack_htable_size;
 
+struct nf_conntrack_stat
+{
+	unsigned int searched;
+	unsigned int found;
+	unsigned int new;
+	unsigned int invalid;
+	unsigned int ignore;
+	unsigned int delete;
+	unsigned int delete_list;
+	unsigned int insert;
+	unsigned int insert_failed;
+	unsigned int drop;
+	unsigned int early_drop;
+	unsigned int error;
+	unsigned int expect_new;
+	unsigned int expect_create;
+	unsigned int expect_delete;
+};
+
+#define NF_CT_STAT_INC(count) (__get_cpu_var(nf_conntrack_stat).count++)
+
 /* eg. PROVIDES_CONNTRACK(ftp); */
 #define PROVIDES_CONNTRACK(name)                        \
         int needs_nf_conntrack_##name;                  \
@@ -293,9 +330,7 @@
 
 extern int
 nf_conntrack_register_cache(u_int32_t features, const char *name, size_t size,
-			    int (*init_conntrack)(struct nf_conn *, u_int32_t),
-			    int (*copy_conntrack)(struct nf_conn *,
-						  const struct nf_conn *));
+			    int (*init_conntrack)(struct nf_conn *, u_int32_t));
 extern void
 nf_conntrack_unregister_cache(u_int32_t features);
 

Modified: trunk/patch-o-matic-ng/nf_conntrack/linux-2.6/include/linux/netfilter/nf_conntrack_core.h
===================================================================
--- trunk/patch-o-matic-ng/nf_conntrack/linux-2.6/include/linux/netfilter/nf_conntrack_core.h	2004-10-24 22:27:31 UTC (rev 3259)
+++ trunk/patch-o-matic-ng/nf_conntrack/linux-2.6/include/linux/netfilter/nf_conntrack_core.h	2004-11-01 07:38:59 UTC (rev 3260)
@@ -23,14 +23,6 @@
 extern int nf_conntrack_init(void);
 extern void nf_conntrack_cleanup(void);
 
-struct nf_conntrack_protocol;
-extern struct nf_conntrack_protocol *
-nf_ct_find_proto(u_int16_t l3proto, u_int8_t protocol);
-
-/* Like above, but you already have conntrack read lock. */
-extern struct nf_conntrack_protocol *
-__nf_ct_find_proto(u_int16_t l3proto, u_int8_t protocol);
-
 extern struct list_head protocol_list;
 
 struct nf_conntrack_l3proto;
@@ -39,6 +31,8 @@
 extern struct nf_conntrack_l3proto *__nf_ct_find_l3proto(u_int16_t l3proto);
 extern struct list_head l3proto_list;
 
+struct nf_conntrack_protocol;
+
 extern int nf_ct_get_tuple(const struct sk_buff *skb,
 			   unsigned int nhoff,
 			   unsigned int dataoff,
@@ -58,9 +52,18 @@
 nf_conntrack_find_get(const struct nf_conntrack_tuple *tuple,
 		      const struct nf_conn *ignored_conntrack);
 
+extern int __nf_conntrack_confirm(struct sk_buff *skb);
+
 /* Confirm a connection: returns NF_DROP if packet must be dropped. */
-extern int nf_conntrack_confirm(struct sk_buff *skb);
+static inline int nf_conntrack_confirm(struct sk_buff *skb)
+{
+	if (skb->nfct
+	    && !is_confirmed((struct nf_conn *)skb->nfct))
+		return __nf_conntrack_confirm(skb);
 
+	return NF_ACCEPT;
+}
+
 extern struct list_head *nf_conntrack_hash;
 extern struct list_head nf_conntrack_expect_list;
 DECLARE_RWLOCK_EXTERN(nf_conntrack_lock);

Modified: trunk/patch-o-matic-ng/nf_conntrack/linux-2.6/include/linux/netfilter/nf_conntrack_l3proto.h
===================================================================
--- trunk/patch-o-matic-ng/nf_conntrack/linux-2.6/include/linux/netfilter/nf_conntrack_l3proto.h	2004-10-24 22:27:31 UTC (rev 3259)
+++ trunk/patch-o-matic-ng/nf_conntrack/linux-2.6/include/linux/netfilter/nf_conntrack_l3proto.h	2004-11-01 07:38:59 UTC (rev 3260)
@@ -26,6 +26,7 @@
 /* Header for use in defining a given L3 protocol for connection tracking. */
 #ifndef _NF_CONNTRACK_L3PROTO_H
 #define _NF_CONNTRACK_L3PROTO_H
+#include <linux/seq_file.h>
 #include <linux/netfilter/nf_conntrack.h>
 
 struct nf_conntrack_l3proto
@@ -39,12 +40,6 @@
 	/* Protocol name */
 	const char *name;
 
-	/* Initialization. ex) register hooks */
-	int (*init)(void);
-
-	/* Finalization. ex) unregister hooks */
-	void  (*fini)(void);
-
 	/*
 	 * Try to fill in the third arg: nhoff is offset of l3 proto
          * hdr.  Return true if possible.
@@ -60,12 +55,11 @@
 			    const struct nf_conntrack_tuple *orig);
 
 	/* Print out the per-protocol part of the tuple. */
-	unsigned int (*print_tuple)(char *buffer,
-				    const struct nf_conntrack_tuple *);
+	int (*print_tuple)(struct seq_file *s,
+			   const struct nf_conntrack_tuple *);
 
 	/* Print out the private part of the conntrack. */
-	unsigned int (*print_conntrack)(char *buffer,
-					const struct nf_conn *);
+	int (*print_conntrack)(struct seq_file *s, const struct nf_conn *);
 
 	/* Returns verdict for packet, or -1 for invalid. */
 	int (*packet)(struct nf_conn *conntrack,
@@ -95,11 +89,20 @@
 	struct module *me;
 };
 
+extern struct nf_conntrack_l3proto *nf_ct_l3protos[AF_MAX];
+
 /* Protocol registration. */
 extern int nf_conntrack_l3proto_register(struct nf_conntrack_l3proto *proto);
 extern void nf_conntrack_l3proto_unregister(struct nf_conntrack_l3proto *proto);
 
+static inline struct nf_conntrack_l3proto *
+nf_ct_find_l3proto(u_int16_t l3proto)
+{
+	return nf_ct_l3protos[l3proto];
+}
+
 /* Existing built-in protocols */
 extern struct nf_conntrack_l3proto nf_conntrack_l3proto_ipv4;
 extern struct nf_conntrack_l3proto nf_conntrack_l3proto_ipv6;
+extern struct nf_conntrack_l3proto nf_conntrack_generic_l3proto;
 #endif /*_NF_CONNTRACK_L3PROTO_H*/

Modified: trunk/patch-o-matic-ng/nf_conntrack/linux-2.6/include/linux/netfilter/nf_conntrack_protocol.h
===================================================================
--- trunk/patch-o-matic-ng/nf_conntrack/linux-2.6/include/linux/netfilter/nf_conntrack_protocol.h	2004-10-24 22:27:31 UTC (rev 3259)
+++ trunk/patch-o-matic-ng/nf_conntrack/linux-2.6/include/linux/netfilter/nf_conntrack_protocol.h	2004-11-01 07:38:59 UTC (rev 3260)
@@ -14,6 +14,8 @@
 #define _NF_CONNTRACK_PROTOCOL_H
 #include <linux/netfilter/nf_conntrack.h>
 
+struct seq_file;
+
 struct nf_conntrack_protocol
 {
 	/* Next pointer. */
@@ -31,8 +33,8 @@
 	/* Try to fill in the third arg: dataoff is offset past NF
            hdr.  Return true if possible. */
 	int (*pkt_to_tuple)(const struct sk_buff *skb,
-			   unsigned int dataoff,
-			   struct nf_conntrack_tuple *tuple);
+			    unsigned int dataoff,
+			    struct nf_conntrack_tuple *tuple);
 
 	/* Invert the per-proto part of the tuple: ie. turn xmit into reply.
 	 * Some packets can't be inverted: return 0 in that case.
@@ -40,19 +42,19 @@
 	int (*invert_tuple)(struct nf_conntrack_tuple *inverse,
 			    const struct nf_conntrack_tuple *orig);
 
-	/* Print out the per-protocol part of the tuple. */
-	unsigned int (*print_tuple)(char *buffer,
-				    const struct nf_conntrack_tuple *);
+	/* Print out the per-protocol part of the tuple. Return like seq_* */
+	int (*print_tuple)(struct seq_file *s,
+			   const struct nf_conntrack_tuple *);
 
 	/* Print out the private part of the conntrack. */
-	unsigned int (*print_conntrack)(char *buffer,
-					const struct nf_conn *);
+	int (*print_conntrack)(struct seq_file *s, const struct nf_conn *);
 
 	/* Returns verdict for packet, or -1 for invalid. */
 	int (*packet)(struct nf_conn *conntrack,
 		      const struct sk_buff *skb,
 		      unsigned int dataoff,
 		      enum nf_conntrack_info ctinfo,
+		      int pf,
 		      unsigned int hooknum);
 
 	/* Called when a new connection for this protocol found;
@@ -68,15 +70,44 @@
 			       const struct sk_buff *skb,
 			       unsigned int dataoff);
 
+	int (*error)(struct sk_buff *skb, unsigned int dataoff,
+		     enum nf_conntrack_info *ctinfo,
+		     int pf, unsigned int hooknum);
+
 	/* Module (if any) which this is connected to. */
 	struct module *me;
 };
 
+/* Existing built-in protocols */
+extern struct nf_conntrack_protocol nf_conntrack_protocol_tcp6;
+extern struct nf_conntrack_protocol nf_conntrack_protocol_udp4;
+extern struct nf_conntrack_protocol nf_conntrack_protocol_udp6;
+extern struct nf_conntrack_protocol nf_conntrack_generic_protocol;
+
+#define MAX_NF_CT_PROTO 256
+extern struct nf_conntrack_protocol **nf_ct_protos[PF_MAX];
+
+extern struct nf_conntrack_protocol *
+nf_ct_find_proto(u_int16_t l3proto, u_int8_t protocol);
+
 /* Protocol registration. */
 extern int nf_conntrack_protocol_register(struct nf_conntrack_protocol *proto);
 extern void nf_conntrack_protocol_unregister(struct nf_conntrack_protocol *proto);
 
-/* Existing built-in protocols */
-extern struct nf_conntrack_protocol nf_conntrack_protocol_tcp;
-extern struct nf_conntrack_protocol nf_conntrack_protocol_udp;
+/* Log invalid packets */
+extern unsigned int nf_ct_log_invalid;
+
+#ifdef CONFIG_SYSCTL
+#ifdef DEBUG_INVALID_PACKETS
+#define LOG_INVALID(proto) \
+	(nf_ct_log_invalid == (proto) || nf_ct_log_invalid == IPPROTO_RAW)
+#else
+#define LOG_INVALID(proto) \
+	((nf_ct_log_invalid == (proto) || nf_ct_log_invalid == IPPROTO_RAW) \
+	 && net_ratelimit())
+#endif
+#else
+#define LOG_INVALID(proto) 0
+#endif /* CONFIG_SYSCTL */
+
 #endif /*_NF_CONNTRACK_PROTOCOL_H*/

Added: trunk/patch-o-matic-ng/nf_conntrack/linux-2.6/include/linux/netfilter/nf_conntrack_sctp.h
===================================================================
--- trunk/patch-o-matic-ng/nf_conntrack/linux-2.6/include/linux/netfilter/nf_conntrack_sctp.h	2004-10-24 22:27:31 UTC (rev 3259)
+++ trunk/patch-o-matic-ng/nf_conntrack/linux-2.6/include/linux/netfilter/nf_conntrack_sctp.h	2004-11-01 07:38:59 UTC (rev 3260)
@@ -0,0 +1,25 @@
+#ifndef _NF_CONNTRACK_SCTP_H
+#define _NF_CONNTRACK_SCTP_H
+/* SCTP tracking. */
+
+enum sctp_conntrack {
+	SCTP_CONNTRACK_NONE,
+	SCTP_CONNTRACK_CLOSED,
+	SCTP_CONNTRACK_COOKIE_WAIT,
+	SCTP_CONNTRACK_COOKIE_ECHOED,
+	SCTP_CONNTRACK_ESTABLISHED,
+	SCTP_CONNTRACK_SHUTDOWN_SENT,
+	SCTP_CONNTRACK_SHUTDOWN_RECD,
+	SCTP_CONNTRACK_SHUTDOWN_ACK_SENT,
+	SCTP_CONNTRACK_MAX
+};
+
+struct nf_ct_sctp
+{
+	enum sctp_conntrack state;
+
+	u_int32_t vtag[NF_CT_DIR_MAX];
+	u_int32_t ttag[NF_CT_DIR_MAX];
+};
+
+#endif /* _NF_CONNTRACK_SCTP_H */

Modified: trunk/patch-o-matic-ng/nf_conntrack/linux-2.6/include/linux/netfilter/nf_conntrack_tcp.h
===================================================================
--- trunk/patch-o-matic-ng/nf_conntrack/linux-2.6/include/linux/netfilter/nf_conntrack_tcp.h	2004-10-24 22:27:31 UTC (rev 3259)
+++ trunk/patch-o-matic-ng/nf_conntrack/linux-2.6/include/linux/netfilter/nf_conntrack_tcp.h	2004-11-01 07:38:59 UTC (rev 3260)
@@ -15,25 +15,50 @@
 
 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_CLOSE_WAIT,
+	TCP_CONNTRACK_LAST_ACK,
 	TCP_CONNTRACK_TIME_WAIT,
 	TCP_CONNTRACK_CLOSE,
-	TCP_CONNTRACK_CLOSE_WAIT,
-	TCP_CONNTRACK_LAST_ACK,
 	TCP_CONNTRACK_LISTEN,
-	TCP_CONNTRACK_MAX
+	TCP_CONNTRACK_MAX,
+	TCP_CONNTRACK_IGNORE
 };
 
+/* Window scaling is advertised by the sender */
+#define NF_CT_TCP_STATE_FLAG_WINDOW_SCALE      0x01
+
+/* SACK is permitted by the sender */
+#define NF_CT_TCP_FLAG_SACK_PERM               0x02
+
+struct nf_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 */
+	u_int8_t        loose;          /* used when connection picked up from the middle */
+	u_int8_t        flags;          /* per direction state flags */
+ };
+
 struct nf_ct_tcp
 {
-	enum tcp_conntrack state;
-
-	/* Poor man's window tracking: sequence number of valid ACK
-           handshake completion packet */
-	u_int32_t handshake_ack;
+	struct nf_ct_tcp_state seen[2]; /* connection parameters per direction */
+	u_int8_t	state;          /* state of the connection (enum tcp_conntrack) */
+	/* For detecting stale connections */
+	u_int8_t	last_dir;	/* Direction of the last packet (enum nf_conntrack_dir) */
+	u_int8_t	retrans;	/* Number of retransmitted packets */
+	u_int8_t	last_index;	/* Index of the last packet */
+	u_int32_t	last_seq;	/* Last sequence number seen in dir */
+	u_int32_t	last_end;	/* Last seq + len */
 };
 
+/* Update TCP window tracking data when NAT mangles the packet */
+extern int nf_conntrack_tcp_update(struct sk_buff *skb,
+				   unsigned int dataoff,
+				   struct nf_conn *conntrack,
+				   int dir);
+
 #endif /* _NF_CONNTRACK_TCP_H */

Modified: trunk/patch-o-matic-ng/nf_conntrack/linux-2.6/include/linux/netfilter/nf_conntrack_tuple.h
===================================================================
--- trunk/patch-o-matic-ng/nf_conntrack/linux-2.6/include/linux/netfilter/nf_conntrack_tuple.h	2004-10-24 22:27:31 UTC (rev 3259)
+++ trunk/patch-o-matic-ng/nf_conntrack/linux-2.6/include/linux/netfilter/nf_conntrack_tuple.h	2004-11-01 07:38:59 UTC (rev 3260)
@@ -47,8 +47,8 @@
 		u_int16_t id;
 	} icmp;
 	struct {
-		u_int32_t id;
-	} frag6;
+		u_int16_t port;
+	} sctp;
 };
 
 /* The manipulable part of the tuple. */
@@ -86,8 +86,8 @@
 				u_int8_t type, code;
 			} icmp;
 			struct {
-				u_int16_t orig;
-			} frag6;
+				u_int16_t port;
+			} sctp;
 		} u;
 
 		/* The protocol. */

Modified: trunk/patch-o-matic-ng/nf_conntrack/linux-2.6/include/linux/netfilter_ipv4/nf_conntrack_icmp.h
===================================================================
--- trunk/patch-o-matic-ng/nf_conntrack/linux-2.6/include/linux/netfilter_ipv4/nf_conntrack_icmp.h	2004-10-24 22:27:31 UTC (rev 3259)
+++ trunk/patch-o-matic-ng/nf_conntrack/linux-2.6/include/linux/netfilter_ipv4/nf_conntrack_icmp.h	2004-11-01 07:38:59 UTC (rev 3260)
@@ -20,5 +20,4 @@
 	atomic_t count;
 };
 
-extern struct nf_conntrack_protocol nf_conntrack_protocol_icmp;
 #endif /* _NF_CONNTRACK_ICMP_H */

Modified: trunk/patch-o-matic-ng/nf_conntrack/linux-2.6/include/linux/netfilter_ipv4/nf_conntrack_ipv4.h
===================================================================
--- trunk/patch-o-matic-ng/nf_conntrack/linux-2.6/include/linux/netfilter_ipv4/nf_conntrack_ipv4.h	2004-10-24 22:27:31 UTC (rev 3259)
+++ trunk/patch-o-matic-ng/nf_conntrack/linux-2.6/include/linux/netfilter_ipv4/nf_conntrack_ipv4.h	2004-11-01 07:38:59 UTC (rev 3260)
@@ -35,4 +35,8 @@
 #endif
 };
 
+/* Returns new sk_buff, or NULL */
+struct sk_buff *
+nf_ct_ipv4_ct_gather_frags(struct sk_buff *skb);
+
 #endif /*_NF_CONNTRACK_IPV4_H*/

Deleted: trunk/patch-o-matic-ng/nf_conntrack/linux-2.6/include/linux/netfilter_ipv6/nf_conntrack_frag6.h
===================================================================
--- trunk/patch-o-matic-ng/nf_conntrack/linux-2.6/include/linux/netfilter_ipv6/nf_conntrack_frag6.h	2004-10-24 22:27:31 UTC (rev 3259)
+++ trunk/patch-o-matic-ng/nf_conntrack/linux-2.6/include/linux/netfilter_ipv6/nf_conntrack_frag6.h	2004-11-01 07:38:59 UTC (rev 3260)
@@ -1,53 +0,0 @@
-/*
- * nf_conntrack_frag6.h
- *
- * Definitions and Declarations for Fragment Header tracking.
- *
- * Derived from include/linux/netfiter_ipv4/ip_conntrack_frag6.h
- *
- * 16 Dec 2003: Yasuyuki Kozakai @ USAGI <yasuyuki.kozakai at toshiba.co.jp>
- *	- enable working with L3 protocol independent connection tracking.
- */
-
-#ifndef _NF_CONNTRACK_FRAG6_H
-#define _NF_CONNTRACK_FRAG6_H
-/* Fragment Header tracking. */
-
-/* used by last_in */
-#define NF_CT_FRAG6_FIRST_IN	1	/* first fragment is already received */
-#define NF_CT_FRAG6_LAST_IN	2	/* last fragment is already received */
-#define NF_CT_FRAG6_COMPLETE	4	/* no need to wait fragments any more */
-
-struct nf_ct_frag6
-{
-	/* fragmented packets */
-	struct sk_buff *fragments;
-	struct sk_buff *orig_list;
-
-	/* protect fragments list */
-	spinlock_t lock;
-
-	/* reassembled packet */
-	struct sk_buff *reasm;
-	int len;
-	int meat;
-	int iif;
-	struct timeval stamp;
-	unsigned int csum;
-	__u8 last_in;
-};
-
-/* get reassembled packet */
-extern struct sk_buff *nf_ct_frag6_get_reasm(const struct sk_buff *skb);
-
-/* pass the reserved fragmented packets to next hook */
-extern unsigned int
-nf_ct_frag6_confirm(struct sk_buff *skb);
-
-extern unsigned int
-nf_ct_frag6_reinject(unsigned int hooknum, struct sk_buff *skb,
-                     struct net_device *in, struct net_device *out,
-                     int (*okfn)(struct sk_buff *));
-
-extern struct nf_conntrack_protocol nf_conntrack_protocol_frag6;
-#endif /* _NF_CONNTRACK_FRAG6_H */

Modified: trunk/patch-o-matic-ng/nf_conntrack/linux-2.6/include/linux/netfilter_ipv6/nf_conntrack_icmpv6.h
===================================================================
--- trunk/patch-o-matic-ng/nf_conntrack/linux-2.6/include/linux/netfilter_ipv6/nf_conntrack_icmpv6.h	2004-10-24 22:27:31 UTC (rev 3259)
+++ trunk/patch-o-matic-ng/nf_conntrack/linux-2.6/include/linux/netfilter_ipv6/nf_conntrack_icmpv6.h	2004-11-01 07:38:59 UTC (rev 3260)
@@ -15,10 +15,10 @@
 #include <asm/atomic.h>
 
 #ifndef ICMPV6_NI_QUERY
-#define ICMPV6_NI_QUERY			139
+#define ICMPV6_NI_QUERY 139
 #endif
 #ifndef ICMPV6_NI_REPLY
-#define ICMPV6_NI_REPLY			140
+#define ICMPV6_NI_REPLY 140
 #endif
 
 struct nf_ct_icmpv6
@@ -27,5 +27,4 @@
 	atomic_t count;
 };
 
-extern struct nf_conntrack_protocol nf_conntrack_protocol_icmpv6;
 #endif /* _NF_CONNTRACK_ICMPV6_H */

Deleted: trunk/patch-o-matic-ng/nf_conntrack/linux-2.6/net/ipv4/netfilter/Kconfig.ladd
===================================================================
--- trunk/patch-o-matic-ng/nf_conntrack/linux-2.6/net/ipv4/netfilter/Kconfig.ladd	2004-10-24 22:27:31 UTC (rev 3259)
+++ trunk/patch-o-matic-ng/nf_conntrack/linux-2.6/net/ipv4/netfilter/Kconfig.ladd	2004-11-01 07:38:59 UTC (rev 3260)
@@ -1,29 +0,0 @@
-config NF_CONNTRACK_IPV4
-	tristate "IPv4 support on new connection tracking (EXPERIMENTAL)"
-	depends on EXPERIMENTAL && !IP_NF_CONNTRACK
-	select NF_CONNTRACK
-	---help---
-	  Connection tracking keeps a record of what packets have passed
-	  through your machine, in order to figure out how they are related
-	  into connections.
-
-	  This is IPv4 support on Layer 3 independent connection tracking.
-	  Layer 3 independent connection tracking is experimental scheme
-	  which generalize ip_conntrack to support other layer 3 protocols.
-
-	  To compile it as a module, choose M here.  If unsure, say N.
-
-config NF_CONNTRACK_IPV4_FTP
-	tristate "FTP support on new connection tracking (EXPERIMENTAL)"
-	depends on EXPERIMENTAL && NF_CONNTRACK_IPV4
-	select NF_CONNTRACK_FTP
-	help
-	  Tracking FTP connections is problematic: special helpers are
-	  required for tracking them, and doing masquerading and other forms
-	  of Network Address Translation on them.
-
-	  This is FTP support on Layer 3 independent connection tracking.
-	  Layer 3 independent connection tracking is experimental scheme
-	  which generalize ip_conntrack to support other layer 3 protocols.
-
-	  To compile it as a module, choose M here.  If unsure, say N.

Deleted: trunk/patch-o-matic-ng/nf_conntrack/linux-2.6/net/ipv4/netfilter/Makefile.ladd
===================================================================
--- trunk/patch-o-matic-ng/nf_conntrack/linux-2.6/net/ipv4/netfilter/Makefile.ladd	2004-10-24 22:27:31 UTC (rev 3259)
+++ trunk/patch-o-matic-ng/nf_conntrack/linux-2.6/net/ipv4/netfilter/Makefile.ladd	2004-11-01 07:38:59 UTC (rev 3260)
@@ -1,7 +0,0 @@
-obj-$(CONFIG_IP_NF_QUEUE) += ip_queue.o
-
-# objects for l3 independent conntrack
-nf_conntrack_ipv4-objs  :=  nf_conntrack_l3proto_ipv4.o nf_conntrack_proto_icmp.o
-
-# l3 independent conntrack
-obj-$(CONFIG_NF_CONNTRACK_IPV4) += nf_conntrack_ipv4.o

Modified: trunk/patch-o-matic-ng/nf_conntrack/linux-2.6/net/ipv4/netfilter/nf_conntrack_l3proto_ipv4.c
===================================================================
--- trunk/patch-o-matic-ng/nf_conntrack/linux-2.6/net/ipv4/netfilter/nf_conntrack_l3proto_ipv4.c	2004-10-24 22:27:31 UTC (rev 3259)
+++ trunk/patch-o-matic-ng/nf_conntrack/linux-2.6/net/ipv4/netfilter/nf_conntrack_l3proto_ipv4.c	2004-11-01 07:38:59 UTC (rev 3260)
@@ -28,7 +28,7 @@
 #include <linux/netfilter/nf_conntrack_protocol.h>
 #include <linux/netfilter/nf_conntrack_l3proto.h>
 #include <linux/netfilter/nf_conntrack_core.h>
-#include <linux/netfilter_ipv4/nf_conntrack_icmp.h>
+#include <linux/netfilter_ipv4/nf_conntrack_ipv4.h>
 
 #if 0
 #define DEBUGP printk
@@ -36,10 +36,11 @@
 #define DEBUGP(format, args...)
 #endif
 
+DECLARE_PER_CPU(struct nf_conntrack_stat, nf_conntrack_stat);
+
 static int ipv4_pkt_to_tuple(const struct sk_buff *skb, unsigned int nhoff,
 			     struct nf_conntrack_tuple *tuple)
 {
-
 	if (skb_copy_bits(skb, nhoff + offsetof(struct iphdr, saddr),
 			  &tuple->src.u3.ip, sizeof(tuple->src.u3.ip)))
 		return 0;
@@ -60,31 +61,36 @@
 	return 1;
 }
 
-static unsigned int
-ipv4_print_tuple(char *buffer, const struct nf_conntrack_tuple *tuple)
+static int ipv4_print_tuple(struct seq_file *s,
+			    const struct nf_conntrack_tuple *tuple)
 {
-	int len;
-
-	len = sprintf(buffer, "src=%u.%u.%u.%u dst=%u.%u.%u.%u ",
-		      NIPQUAD(tuple->src.u3.ip), NIPQUAD(tuple->dst.u3.ip));
-
-	return len;
+	return seq_printf(s, "src=%u.%u.%u.%u dst=%u.%u.%u.%u ",
+		          NIPQUAD(tuple->src.u3.ip),
+			  NIPQUAD(tuple->dst.u3.ip));
 }
 
-static unsigned int
-ipv4_print_conntrack(char *buffer, const struct nf_conn *conntrack)
+static int ipv4_print_conntrack(struct seq_file *s,
+				const struct nf_conn *conntrack)
 {
 	return 0;
 }
 
+static int nf_ct_ipv4_no_defrag;
+
 /* Returns new sk_buff, or NULL */
-struct sk_buff *
-ip_ct_gather_frags(struct sk_buff *skb)
+static struct sk_buff *
+nf_ct_ipv4_gather_frags(struct sk_buff *skb)
 {
         struct sock *sk = skb->sk;
 #ifdef CONFIG_NETFILTER_DEBUG
         unsigned int olddebug = skb->nf_debug;
 #endif
+
+	if (unlikely(nf_ct_ipv4_no_defrag)) {
+		kfree_skb(skb);
+		return NULL;
+	}
+
         if (sk) {
                 sock_hold(sk);
                 skb_orphan(skb);
@@ -114,100 +120,16 @@
         return skb;
 }
 
-struct nf_conntrack_l3proto nf_conntrack_l3proto_ipv4;
-/* Returns conntrack if it dealt with ICMP, and filled in skb fields */
-struct nf_conn *
-icmp_error_track(struct sk_buff *skb,
-                 enum nf_conntrack_info *ctinfo,
-                 unsigned int hooknum)
-{
-	struct nf_conntrack_tuple innertuple, origtuple;
-	struct {
-		struct icmphdr icmp;
-		struct iphdr ip;
-	} inside;
-	struct nf_conntrack_protocol *innerproto;
-	struct nf_conntrack_tuple_hash *h;
-	int dataoff;
-
-	NF_CT_ASSERT(skb->nfct == NULL);
-
-	/* Not enough header? */
-	if (skb_copy_bits(skb, skb->nh.iph->ihl*4, &inside, sizeof(inside))!=0)
-		return NULL;
-
-	if (inside.icmp.type != ICMP_DEST_UNREACH
-	    && inside.icmp.type != ICMP_SOURCE_QUENCH
-	    && inside.icmp.type != ICMP_TIME_EXCEEDED
-	    && inside.icmp.type != ICMP_PARAMETERPROB
-	    && inside.icmp.type != ICMP_REDIRECT)
-		return NULL;
-
-	/* Ignore ICMP's containing fragments (shouldn't happen) */
-	if (inside.ip.frag_off & htons(IP_OFFSET)) {
-		DEBUGP("icmp_error_track: fragment of proto %u\n",
-		       inside.ip.protocol);
-		return NULL;
-	}
-
-	innerproto = nf_ct_find_proto(PF_INET, inside.ip.protocol);
-	dataoff = skb->nh.iph->ihl*4 + sizeof(inside.icmp);
-	/* Are they talking about one of our connections? */
-	if (!nf_ct_get_tuple(skb, dataoff, dataoff + inside.ip.ihl*4, PF_INET,
-			     inside.ip.protocol, &origtuple,
-			     &nf_conntrack_l3proto_ipv4, innerproto)) {
-		DEBUGP("icmp_error: ! get_tuple p=%u", inside.ip.protocol);
-		return NULL;
-	}
-
-        /* Ordinarily, we'd expect the inverted tupleproto, but it's
-           been preserved inside the ICMP. */
-        if (!nf_ct_invert_tuple(&innertuple, &origtuple,
-				&nf_conntrack_l3proto_ipv4, innerproto)) {
-                DEBUGP("icmp_error_track: Can't invert tuple\n");
-                return NULL;
-        }
-
-        *ctinfo = NF_CT_RELATED;
-
-	h = nf_conntrack_find_get(&innertuple, NULL);
-	if (!h) {
-		/* Locally generated ICMPs will match inverted if they
-		   haven't been SNAT'ed yet */
-		/* FIXME: NAT code has to handle half-done double NAT --RR */
-		if (hooknum == NF_IP_LOCAL_OUT)
-			h = nf_conntrack_find_get(&origtuple, NULL);
-
-		if (!h) {
-			DEBUGP("icmp_error_track: no match\n");
-			return NULL;
-		}
-		/* Reverse direction from that found */
-		if (NF_CT_DIRECTION(h) != NF_CT_DIR_REPLY)
-			*ctinfo += NF_CT_IS_REPLY;
-	} else {
-		if (NF_CT_DIRECTION(h) == NF_CT_DIR_REPLY)
-			*ctinfo += NF_CT_IS_REPLY;
-	}
-
-	/* Update skb to refer to this connection */
-	skb->nfct = &h->ctrack->infos[*ctinfo];
-	return h->ctrack;
-}
-
 static int
 ipv4_prepare(struct sk_buff **pskb, unsigned int hooknum, unsigned int *dataoff,
 	     u_int8_t *protonum, int *ret)
 {
-	enum nf_conntrack_info ctinfo;
-
 	/* Never happen */
 	if ((*pskb)->nh.iph->frag_off & htons(IP_OFFSET)) {
 		if (net_ratelimit()) {
 			printk(KERN_ERR "ipv4_prepare: Frag of proto %u (hook=%u)\n",
 			(*pskb)->nh.iph->protocol, hooknum);
 		}
-
 		*ret = NF_DROP;
 		return 0;
 	}
@@ -215,20 +137,6 @@
 	/* FIXME: Do this right please. --RR */
 	(*pskb)->nfcache |= NFC_UNKNOWN;
 
-	/* Previously seen (loopback or untracked)?  Ignore. */
-	if ((*pskb)->nfct) {
-		DEBUGP("loopback or untracked, nfct=0x%p\n", (*pskb)->nfct);
-		*ret = NF_ACCEPT;
-		return 0;
-	}
-
-        /* It may be an icmp error... */
-        if ((*pskb)->nh.iph->protocol == IPPROTO_ICMP
-            && icmp_error_track(*pskb, &ctinfo, hooknum)) {
-		*ret = NF_ACCEPT;
-		return 0;
-	}
-
 	*dataoff = (*pskb)->nh.raw - (*pskb)->data + (*pskb)->nh.iph->ihl*4;
 	*protonum = (*pskb)->nh.iph->protocol;
 
@@ -245,10 +153,10 @@
 }
 
 static unsigned int ipv4_confirm(unsigned int hooknum,
-			       struct sk_buff **pskb,
-			       const struct net_device *in,
-			       const struct net_device *out,
-			       int (*okfn)(struct sk_buff *))
+				 struct sk_buff **pskb,
+				 const struct net_device *in,
+				 const struct net_device *out,
+				 int (*okfn)(struct sk_buff *))
 {
 	/* We've seen it coming out the other side: confirm it */
 	DEBUGP("ipv4_confirm\n");
@@ -256,10 +164,10 @@
 }
 
 static unsigned int ipv4_conntrack_defrag(unsigned int hooknum,
-                                        struct sk_buff **pskb,
-                                        const struct net_device *in,
-                                        const struct net_device *out,
-                                        int (*okfn)(struct sk_buff *))
+					  struct sk_buff **pskb,
+					  const struct net_device *in,
+					  const struct net_device *out,
+					  int (*okfn)(struct sk_buff *))
 {
 	/* Previously seen (loopback)?  Ignore.  Do this before
 	   fragment check. */
@@ -269,7 +177,7 @@
 
 	/* Gather fragments. */
 	if ((*pskb)->nh.iph->frag_off & htons(IP_MF|IP_OFFSET)) {
-		*pskb = ip_ct_gather_frags(*pskb);
+		*pskb = nf_ct_ipv4_gather_frags(*pskb);
 		if (!*pskb)
 			return NF_STOLEN;
 	}
@@ -293,7 +201,8 @@
 	/* Local packets are never produced too large for their
 	   interface.  We degfragment them at LOCAL_OUT, however,
 	   so we have to refragment them here. */
-	if ((*pskb)->len > dst_pmtu(&rt->u.dst)) {
+	if ((*pskb)->len > dst_pmtu(&rt->u.dst) &&
+	    !skb_shinfo(*pskb)->tso_size) {
 		/* No hook can be after us, so this should be OK. */
 		ip_fragment(*pskb, okfn);
 		return NF_STOLEN;
@@ -346,6 +255,14 @@
 	.priority	= NF_IP_PRI_CONNTRACK,
 };
 
+static struct nf_hook_ops ipv4_conntrack_defrag_local_out_ops = {
+	.hook           = ipv4_conntrack_defrag,
+	.owner          = THIS_MODULE,
+	.pf             = PF_INET,
+	.hooknum        = NF_IP_LOCAL_OUT,
+	.priority       = NF_IP_PRI_CONNTRACK_DEFRAG,
+};
+
 static struct nf_hook_ops ipv4_conntrack_local_out_ops = {
 	.hook		= ipv4_conntrack_local,
 	.owner		= THIS_MODULE,
@@ -363,14 +280,6 @@
 	.priority	= NF_IP_PRI_LAST,
 };
 
-static struct nf_hook_ops ipv4_conntrack_defrag_local_out_ops = {
-	.hook           = ipv4_conntrack_defrag,
-	.owner          = THIS_MODULE,
-	.pf             = PF_INET,
-	.hooknum        = NF_IP_LOCAL_OUT,
-	.priority       = NF_IP_PRI_CONNTRACK_DEFRAG,
-};
-
 static struct nf_hook_ops ipv4_conntrack_local_in_ops = {
 	.hook		= ipv4_confirm,
 	.owner		= THIS_MODULE,
@@ -416,8 +325,8 @@
 	{ .ctl_name = 0 }
 };
 #endif
-/* Fast function for those who don't want to parse /proc (and I don't
 
+/* Fast function for those who don't want to parse /proc (and I don't
    blame them). */
 /* Reversing the socket's dst/src point of view gives us the reply
    mapping. */
@@ -480,49 +389,61 @@
 	.get		= &getorigdst,
 };
 
-static struct nf_conntrack_protocol tcp, udp, icmp;
+struct nf_conntrack_l3proto nf_conntrack_l3proto_ipv4 = {
+	.l3proto	 = PF_INET,
+	.name		 = "ipv4",
+	.pkt_to_tuple	 = ipv4_pkt_to_tuple,
+	.invert_tuple	 = ipv4_invert_tuple,
+	.print_tuple	 = ipv4_print_tuple,
+	.print_conntrack = ipv4_print_conntrack,
+	.prepare	 = ipv4_prepare,
+	.get_features	 = ipv4_get_features,
+	.me		 = THIS_MODULE,
+};
 
+extern struct nf_conntrack_protocol nf_conntrack_protocol_tcp4;
+extern struct nf_conntrack_protocol nf_conntrack_protocol_udp4;
+extern struct nf_conntrack_protocol nf_conntrack_protocol_icmp;
 static int init_or_cleanup(int init)
 {
 	int ret = 0;
-	struct nf_conntrack_protocol *proto;
 
 	if (!init) goto cleanup;
 
 	ret = nf_register_sockopt(&so_getorigdst);
-	if (ret != 0) {
+	if (ret < 0) {
 		printk(KERN_ERR "Unable to register netfilter socket option\n");
 		goto cleanup_nothing;
 	}
 
-	memcpy(&tcp, &nf_conntrack_protocol_tcp, sizeof(tcp));
-	tcp.l3proto = PF_INET;
-	ret = nf_conntrack_protocol_register(&tcp);
+	ret = nf_conntrack_protocol_register(&nf_conntrack_protocol_tcp4);
 	if (ret < 0) {
 		printk("nf_conntrack_ipv4: can't register tcp.\n");
 		goto cleanup_sockopt;
 	}
 
-	memcpy(&udp, &nf_conntrack_protocol_udp, sizeof(udp));
-	udp.l3proto = PF_INET;
-	ret = nf_conntrack_protocol_register(&udp);
+	ret = nf_conntrack_protocol_register(&nf_conntrack_protocol_udp4);
 	if (ret < 0) {
 		printk("nf_conntrack_ipv4: can't register udp.\n");
 		goto cleanup_tcp;
 	}
 
-	memcpy(&icmp, &nf_conntrack_protocol_icmp, sizeof(icmp));
-	icmp.l3proto = PF_INET;
-	ret = nf_conntrack_protocol_register(&icmp);
+	ret = nf_conntrack_protocol_register(&nf_conntrack_protocol_icmp);
 	if (ret < 0) {
 		printk("nf_conntrack_ipv4: can't register icmp.\n");
 		goto cleanup_udp;
 	}
 
+	ret = nf_conntrack_l3proto_register(&nf_conntrack_l3proto_ipv4);
+	if (ret < 0) {
+		printk("nf_conntrack_ipv4: can't register ipv4\n");
+		goto cleanup_icmp;
+	}
+
 	ret = nf_register_hook(&ipv4_conntrack_defrag_ops);
 	if (ret < 0) {
 		printk("nf_conntrack_ipv4: can't register pre-routing defrag hook.\n");
-		goto cleanup_icmp;
+		goto cleanup_ipv4;
 	}
 	ret = nf_register_hook(&ipv4_conntrack_defrag_local_out_ops);
 	if (ret < 0) {
@@ -565,13 +486,11 @@
 
 	return ret;
 
-
  cleanup:
 #ifdef CONFIG_SYSCTL
  	unregister_sysctl_table(nf_ct_ipv4_sysctl_header);
-#endif
-
  cleanup_localinops:
+#endif
 	nf_unregister_hook(&ipv4_conntrack_local_in_ops);
  cleanup_inoutandlocalops:
 	nf_unregister_hook(&ipv4_conntrack_out_ops);
@@ -582,81 +501,42 @@
  cleanup_defraglocalops:
 	nf_unregister_hook(&ipv4_conntrack_defrag_local_out_ops);
  cleanup_defragops:
+	/* Frag queues may hold fragments with skb->dst == NULL */
+	nf_ct_ipv4_no_defrag = 1;
+	synchronize_net();
+	local_bh_disable();
+	ipfrag_flush();
+	local_bh_enable();
 	nf_unregister_hook(&ipv4_conntrack_defrag_ops);
+ cleanup_ipv4:
+	nf_conntrack_l3proto_unregister(&nf_conntrack_l3proto_ipv4);
  cleanup_icmp:
-	READ_LOCK(&nf_conntrack_lock);
-	proto = __nf_ct_find_proto(PF_INET, IPPROTO_ICMP);
-	READ_UNLOCK(&nf_conntrack_lock);
-	if (proto)
-		nf_conntrack_protocol_unregister(proto);
+	nf_conntrack_protocol_unregister(&nf_conntrack_protocol_icmp);
  cleanup_udp:
-	READ_LOCK(&nf_conntrack_lock);
-	proto = __nf_ct_find_proto(PF_INET, IPPROTO_UDP);
-	READ_UNLOCK(&nf_conntrack_lock);
-	if (proto)
-		nf_conntrack_protocol_unregister(proto);
+	nf_conntrack_protocol_unregister(&nf_conntrack_protocol_udp4);
  cleanup_tcp:
-	READ_LOCK(&nf_conntrack_lock);
-	proto = __nf_ct_find_proto(PF_INET, IPPROTO_TCP);
-	READ_UNLOCK(&nf_conntrack_lock);
-	if (proto)
-		nf_conntrack_protocol_unregister(proto);
+	nf_conntrack_protocol_unregister(&nf_conntrack_protocol_tcp4);
  cleanup_sockopt:
 	nf_unregister_sockopt(&so_getorigdst);
  cleanup_nothing:
 	return ret;
 }
 
-static int ipv4_init(void)
-{
-	need_nf_conntrack();
-	return init_or_cleanup(1);
-}
-
-static void ipv4_fini(void)
-{
-	init_or_cleanup(0);
-}
-
-struct nf_conntrack_l3proto nf_conntrack_l3proto_ipv4 = {
-	{ NULL, NULL },		/* list */
-	PF_INET,		/* l3proto */
-	"ipv4",			/* name */
-	ipv4_init,		/* init */
-	ipv4_fini,		/* fini */
-	ipv4_pkt_to_tuple, 	/* pkt_to_tuple */
-	ipv4_invert_tuple, 	/* invert_tuple */
-	ipv4_print_tuple,	/* print_tuple */
-	ipv4_print_conntrack,	/* print_conntrack */
-	NULL,			/* packet */
-	NULL,			/* new */
-	NULL,			/* destroy */
-	ipv4_prepare,		/* prepare */
-	ipv4_get_features,	/* get_features */
-	THIS_MODULE		/* me */
-};
-
 MODULE_LICENSE("GPL");
 
 static int __init init(void)
 {
-	int ret;
-
 	need_nf_conntrack();
-	ret = nf_conntrack_l3proto_register(&nf_conntrack_l3proto_ipv4);
-	if (ret < 0) {
-		printk("nf_conntrack_ipv4: can't register l3 proto module\n");
-	}
-	return ret;
+	return init_or_cleanup(1);
 }
 
 static void __exit fini(void)
 {
-	nf_conntrack_l3proto_unregister(&nf_conntrack_l3proto_ipv4);
+	init_or_cleanup(0);
 }
 
 module_init(init);
 module_exit(fini);
 
 PROVIDES_CONNTRACK(ipv4);
-EXPORT_SYMBOL(ip_ct_gather_frags);
+EXPORT_SYMBOL(nf_ct_ipv4_gather_frags);

Modified: trunk/patch-o-matic-ng/nf_conntrack/linux-2.6/net/ipv4/netfilter/nf_conntrack_proto_icmp.c
===================================================================
--- trunk/patch-o-matic-ng/nf_conntrack/linux-2.6/net/ipv4/netfilter/nf_conntrack_proto_icmp.c	2004-10-24 22:27:31 UTC (rev 3259)
+++ trunk/patch-o-matic-ng/nf_conntrack/linux-2.6/net/ipv4/netfilter/nf_conntrack_proto_icmp.c	2004-11-01 07:38:59 UTC (rev 3260)
@@ -15,12 +15,16 @@
 #include <linux/types.h>
 #include <linux/sched.h>
 #include <linux/timer.h>
-#include <linux/module.h>
 #include <linux/netfilter.h>
 #include <linux/in.h>
 #include <linux/icmp.h>
+#include <linux/seq_file.h>
+#include <net/ip.h>
+#include <net/checksum.h>
+#include <linux/netfilter_ipv4.h>
 #include <linux/netfilter/nf_conntrack_tuple.h>
 #include <linux/netfilter/nf_conntrack_protocol.h>
+#include <linux/netfilter/nf_conntrack_core.h>
 
 unsigned long nf_ct_icmp_timeout = 30*HZ;
 
@@ -34,14 +38,15 @@
 			     unsigned int dataoff,
 			     struct nf_conntrack_tuple *tuple)
 {
-	struct icmphdr hdr;
+	struct icmphdr _hdr, *hp;
 
-	if (skb_copy_bits(skb, dataoff, &hdr, sizeof(hdr)) != 0)
+	hp = skb_header_pointer(skb, dataoff, sizeof(_hdr), &_hdr);
+	if (hp == NULL)
 		return 0;
 
-	tuple->dst.u.icmp.type = hdr.type;
-	tuple->src.u.icmp.id = hdr.un.echo.id;
-	tuple->dst.u.icmp.code = hdr.code;
+	tuple->dst.u.icmp.type = hp->type;
+	tuple->src.u.icmp.id = hp->un.echo.id;
+	tuple->dst.u.icmp.code = hp->code;
 
 	return 1;
 }
@@ -71,18 +76,18 @@
 }
 
 /* Print out the per-protocol part of the tuple. */
-static unsigned int icmp_print_tuple(char *buffer,
-				     const struct nf_conntrack_tuple *tuple)
+static int icmp_print_tuple(struct seq_file *s,
+			    const struct nf_conntrack_tuple *tuple)
 {
-	return sprintf(buffer, "type=%u code=%u id=%u ",
-		       tuple->dst.u.icmp.type,
-		       tuple->dst.u.icmp.code,
-		       ntohs(tuple->src.u.icmp.id));
+	return seq_printf(s, "type=%u code=%u id=%u ",
+			  tuple->dst.u.icmp.type,
+			  tuple->dst.u.icmp.code,
+			  ntohs(tuple->src.u.icmp.id));
 }
 
 /* Print out the private part of the conntrack. */
-static unsigned int icmp_print_conntrack(char *buffer,
-				     const struct nf_conn *conntrack)
+static int icmp_print_conntrack(struct seq_file *s,
+				const struct nf_conn *conntrack)
 {
 	return 0;
 }
@@ -92,6 +97,7 @@
 		       const struct sk_buff *skb,
 		       unsigned int dataoff,
 		       enum nf_conntrack_info ctinfo,
+		       int pf,
 		       unsigned int hooknum)
 {
 	/* Try to delete connection immediately after all replies:
@@ -104,7 +110,7 @@
 			ct->timeout.function((unsigned long)ct);
 	} else {
 		atomic_inc(&ct->proto.icmp.count);
-		nf_ct_refresh(ct, nf_ct_icmp_timeout);
+		nf_ct_refresh_acct(ct, ctinfo, skb, nf_ct_icmp_timeout);
 	}
 
 	return NF_ACCEPT;
@@ -132,9 +138,164 @@
 	return 1;
 }
 
-struct nf_conntrack_protocol nf_conntrack_protocol_icmp
-= { { NULL, NULL }, PF_INET, IPPROTO_ICMP, "icmp",
-    icmp_pkt_to_tuple, icmp_invert_tuple, icmp_print_tuple,
-    icmp_print_conntrack, icmp_packet, icmp_new, NULL, NULL, NULL };
+extern struct nf_conntrack_l3proto nf_conntrack_l3proto_ipv4;
+/* Returns conntrack if it dealt with ICMP, and filled in skb fields */
+static int
+icmp_error_message(struct sk_buff *skb,
+                 enum nf_conntrack_info *ctinfo,
+                 unsigned int hooknum)
+{
+	struct nf_conntrack_tuple innertuple, origtuple;
+	struct {
+		struct icmphdr icmp;
+		struct iphdr ip;
+	} _in, *inside;
+	struct nf_conntrack_protocol *innerproto;
+	struct nf_conntrack_tuple_hash *h;
+	int dataoff;
 
+	NF_CT_ASSERT(skb->nfct == NULL);
+
+	/* Not enough header? */
+	inside = skb_header_pointer(skb, skb->nh.iph->ihl*4, sizeof(_in), &_in);
+	if (inside == NULL)
+		return NF_ACCEPT;
+
+	/* Ignore ICMP's containing fragments (shouldn't happen) */
+	if (inside->ip.frag_off & htons(IP_OFFSET)) {
+		DEBUGP("icmp_error_message: fragment of proto %u\n",
+		       inside->ip.protocol);
+		return NF_ACCEPT;
+	}
+
+	innerproto = nf_ct_find_proto(PF_INET, inside->ip.protocol);
+	dataoff = skb->nh.iph->ihl*4 + sizeof(inside->icmp);
+	/* Are they talking about one of our connections? */
+	if (!nf_ct_get_tuple(skb, dataoff, dataoff + inside->ip.ihl*4, PF_INET,
+			     inside->ip.protocol, &origtuple,
+			     &nf_conntrack_l3proto_ipv4, innerproto)) {
+		DEBUGP("icmp_error_message: ! get_tuple p=%u",
+		       inside->ip.protocol);
+		return NF_ACCEPT;
+	}
+
+        /* Ordinarily, we'd expect the inverted tupleproto, but it's
+           been preserved inside the ICMP. */
+        if (!nf_ct_invert_tuple(&innertuple, &origtuple,
+				&nf_conntrack_l3proto_ipv4, innerproto)) {
+		DEBUGP("icmp_error_message: no match\n");
+		return NF_ACCEPT;
+	}
+
+	*ctinfo = NF_CT_RELATED;
+
+	h = nf_conntrack_find_get(&innertuple, NULL);
+	if (!h) {
+		/* Locally generated ICMPs will match inverted if they
+		   haven't been SNAT'ed yet */
+		/* FIXME: NAT code has to handle half-done double NAT --RR */
+		if (hooknum == NF_IP_LOCAL_OUT)
+			h = nf_conntrack_find_get(&origtuple, NULL);
+
+		if (!h) {
+			DEBUGP("icmp_error_message: no match\n");
+			return NF_ACCEPT;
+		}
+
+		/* Reverse direction from that found */
+		if (NF_CT_DIRECTION(h) == NF_CT_DIR_REPLY)
+			*ctinfo += NF_CT_IS_REPLY;
+	} else {
+		if (NF_CT_DIRECTION(h) == NF_CT_DIR_REPLY)
+			*ctinfo += NF_CT_IS_REPLY;
+	}
+
+        /* Update skb to refer to this connection */
+        skb->nfct = &h->ctrack->ct_general;
+        skb->nfctinfo = *ctinfo;
+        return -NF_ACCEPT;
+}
+
+/* Small and modified version of icmp_rcv */
+static int
+icmp_error(struct sk_buff *skb, unsigned int dataoff,
+	   enum nf_conntrack_info *ctinfo, int pf, unsigned int hooknum)
+{
+	struct icmphdr _ih, *icmph;
+
+	/* Not enough header? */
+	icmph = skb_header_pointer(skb, skb->nh.iph->ihl*4, sizeof(_ih), &_ih);
+	if (icmph == NULL) {
+		if (LOG_INVALID(IPPROTO_ICMP))
+			nf_log_packet(PF_INET, 0, skb, NULL, NULL,
+				      "nf_ct_icmp: short packet ");
+		return -NF_ACCEPT;
+	}
+
+	/* See ip_conntrack_proto_tcp.c */
+	if (hooknum != NF_IP_PRE_ROUTING)
+		goto checksum_skipped;
+
+	switch (skb->ip_summed) {
+	case CHECKSUM_HW:
+		if (!(u16)csum_fold(skb->csum))
+			break;
+		if (LOG_INVALID(IPPROTO_ICMP))
+			nf_log_packet(PF_INET, 0, skb, NULL, NULL,
+				      "nf_ct_icmp: bad HW ICMP checksum ");
+		return -NF_ACCEPT;
+	case CHECKSUM_NONE:
+		if ((u16)csum_fold(skb_checksum(skb, 0, skb->len, 0))) {
+			if (LOG_INVALID(IPPROTO_ICMP))
+				nf_log_packet(PF_INET, 0, skb, NULL, NULL,
+					      "nf_ct_icmp: bad ICMP checksum ");
+			return -NF_ACCEPT;
+		}
+	default:
+		break;
+	}
+
+checksum_skipped:
+	/*
+	 *	18 is the highest 'known' ICMP type. Anything else is a mystery
+	 *
+	 *	RFC 1122: 3.2.2  Unknown ICMP messages types MUST be silently
+	 *		  discarded.
+	 */
+	if (icmph->type > NR_ICMP_TYPES) {
+		if (LOG_INVALID(IPPROTO_ICMP))
+			nf_log_packet(PF_INET, 0, skb, NULL, NULL,
+				      "nf_ct_icmp: invalid ICMP type ");
+		return -NF_ACCEPT;
+	}
+
+	/* Need to track icmp error message? */
+	if (icmph->type != ICMP_DEST_UNREACH
+	    && icmph->type != ICMP_SOURCE_QUENCH
+	    && icmph->type != ICMP_TIME_EXCEEDED
+	    && icmph->type != ICMP_PARAMETERPROB
+	    && icmph->type != ICMP_REDIRECT)
+		return NF_ACCEPT;
+
+	return icmp_error_message(skb, ctinfo, hooknum);
+}
+
+struct nf_conntrack_protocol nf_conntrack_protocol_icmp =
+{
+	.list			= { NULL, NULL },
+	.l3proto		= PF_INET,
+	.proto			= IPPROTO_ICMP,
+	.name			= "icmp",
+	.pkt_to_tuple		= icmp_pkt_to_tuple,
+	.invert_tuple		= icmp_invert_tuple,
+	.print_tuple		= icmp_print_tuple,
+	.print_conntrack	= icmp_print_conntrack,
+	.packet			= icmp_packet,
+	.new			= icmp_new,
+	.error			= icmp_error,
+	.destroy		= NULL,
+	.exp_matches_pkt	= NULL,
+	.me			= NULL
+};
+
 EXPORT_SYMBOL(nf_conntrack_protocol_icmp);

Deleted: trunk/patch-o-matic-ng/nf_conntrack/linux-2.6/net/ipv6/netfilter/Kconfig.ladd
===================================================================
--- trunk/patch-o-matic-ng/nf_conntrack/linux-2.6/net/ipv6/netfilter/Kconfig.ladd	2004-10-24 22:27:31 UTC (rev 3259)
+++ trunk/patch-o-matic-ng/nf_conntrack/linux-2.6/net/ipv6/netfilter/Kconfig.ladd	2004-11-01 07:38:59 UTC (rev 3260)
@@ -1,29 +0,0 @@
-config NF_CONNTRACK_IPV6
-	tristate "IPv6 support on new connection tracking (EXPERIMENTAL)"
-	depends on EXPERIMENTAL
-	select NF_CONNTRACK
-	---help---
-	  Connection tracking keeps a record of what packets have passed
-	  through your machine, in order to figure out how they are related
-	  into connections.
-
-	  This is IPv6 support on Layer 3 independent connection tracking.
-	  Layer 3 independent connection tracking is experimental scheme
-	  which generalize ip_conntrack to support other layer 3 protocols.
-
-	  To compile it as a module, choose M here.  If unsure, say N.
-
-config NF_CONNTRACK_IPV6_FTP
-	tristate "FTP support on new connection tracking (EXPERIMENTAL)"
-	depends on EXPERIMENTAL && NF_CONNTRACK_IPV6
-	select NF_CONNTRACK_FTP
-	help
-	  Tracking FTP connections is problematic: special helpers are
-	  required for tracking them, and doing masquerading and other forms
-	  of Network Address Translation on them.
-
-	  This is FTP support on Layer 3 independent connection tracking.
-	  Layer 3 independent connection tracking is experimental scheme
-	  which generalize ip_conntrack to support other layer 3 protocols.
-
-	  To compile it as a module, choose M here.  If unsure, say N.

Deleted: trunk/patch-o-matic-ng/nf_conntrack/linux-2.6/net/ipv6/netfilter/Makefile.ladd
===================================================================
--- trunk/patch-o-matic-ng/nf_conntrack/linux-2.6/net/ipv6/netfilter/Makefile.ladd	2004-10-24 22:27:31 UTC (rev 3259)
+++ trunk/patch-o-matic-ng/nf_conntrack/linux-2.6/net/ipv6/netfilter/Makefile.ladd	2004-11-01 07:38:59 UTC (rev 3260)
@@ -1,7 +0,0 @@
-obj-$(CONFIG_IP6_NF_MATCH_HL) += ip6t_hl.o
-
-# objects for l3 independent conntrack
-nf_conntrack_ipv6-objs  :=  nf_conntrack_l3proto_ipv6.o nf_conntrack_proto_icmpv6.o nf_conntrack_proto_frag6.o
-
-# l3 independent conntrack
-obj-$(CONFIG_NF_CONNTRACK_IPV6) += nf_conntrack_ipv6.o

Modified: trunk/patch-o-matic-ng/nf_conntrack/linux-2.6/net/ipv6/netfilter/nf_conntrack_l3proto_ipv6.c
===================================================================
--- trunk/patch-o-matic-ng/nf_conntrack/linux-2.6/net/ipv6/netfilter/nf_conntrack_l3proto_ipv6.c	2004-10-24 22:27:31 UTC (rev 3259)
+++ trunk/patch-o-matic-ng/nf_conntrack/linux-2.6/net/ipv6/netfilter/nf_conntrack_l3proto_ipv6.c	2004-11-01 07:38:59 UTC (rev 3260)
@@ -33,15 +33,12 @@
 #include <linux/icmp.h>
 #include <linux/sysctl.h>
 #include <net/ipv6.h>
-#include <net/checksum.h>
 
 #include <linux/netfilter_ipv6.h>
 #include <linux/netfilter/nf_conntrack.h>
 #include <linux/netfilter/nf_conntrack_protocol.h>
 #include <linux/netfilter/nf_conntrack_l3proto.h>
 #include <linux/netfilter/nf_conntrack_core.h>
-#include <linux/netfilter_ipv6/nf_conntrack_icmpv6.h>
-#include <linux/netfilter_ipv6/nf_conntrack_frag6.h>
 
 #if 0
 #define DEBUGP printk
@@ -49,6 +46,8 @@
 #define DEBUGP(format, args...)
 #endif
 
+DECLARE_PER_CPU(struct nf_conntrack_stat, nf_conntrack_stat);
+
 static int ipv6_pkt_to_tuple(const struct sk_buff *skb, unsigned int nhoff,
 			     struct nf_conntrack_tuple *tuple)
 {
@@ -66,26 +65,24 @@
 static int ipv6_invert_tuple(struct nf_conntrack_tuple *tuple,
 			     const struct nf_conntrack_tuple *orig)
 {
-	memcpy(&tuple->src.u3.ip6, &orig->dst.u3.ip6, sizeof(tuple->src.u3.ip6));
-	memcpy(&tuple->dst.u3.ip6, &orig->src.u3.ip6, sizeof(tuple->dst.u3.ip6));
+	memcpy(&tuple->src.u3.ip6, &orig->dst.u3.ip6,
+	       sizeof(tuple->src.u3.ip6));
+	memcpy(&tuple->dst.u3.ip6, &orig->src.u3.ip6,
+	       sizeof(tuple->dst.u3.ip6));
 
 	return 1;
 }
 
-static unsigned int
-ipv6_print_tuple(char *buffer, const struct nf_conntrack_tuple *tuple)
+static int ipv6_print_tuple(struct seq_file *s,
+			    const struct nf_conntrack_tuple *tuple)
 {
-	int len;
-
-	len = sprintf(buffer, "src=%x:%x:%x:%x:%x:%x:%x:%x dst=%x:%x:%x:%x:%x:%x:%x:%x ",
-		      NIP6(*((struct in6_addr *)tuple->src.u3.ip6)),
-		      NIP6(*((struct in6_addr *)tuple->dst.u3.ip6)));
-
-	return len;
+	return seq_printf(s, "src=%x:%x:%x:%x:%x:%x:%x:%x dst=%x:%x:%x:%x:%x:%x:%x:%x ",
+			  NIP6(*((struct in6_addr *)tuple->src.u3.ip6)),
+			  NIP6(*((struct in6_addr *)tuple->dst.u3.ip6)));
 }
 
-static unsigned int
-ipv6_print_conntrack(char *buffer, const struct nf_conn *conntrack)
+static int ipv6_print_conntrack(struct seq_file *s,
+				const struct nf_conn *conntrack)
 {
 	return 0;
 }
@@ -111,8 +108,8 @@
  *        - Note also special handling of AUTH header. Thanks to IPsec wizards.
  */
 
-static int ip6_ct_skip_exthdr(struct sk_buff *skb, int start, u8 *nexthdrp,
-			      int len)
+int nf_ct_ipv6_skip_exthdr(struct sk_buff *skb, int start, u8 *nexthdrp,
+			   int len)
 {
 	u8 nexthdr = *nexthdrp;
 
@@ -142,98 +139,6 @@
 	return start;
 }
 
-struct nf_conntrack_l3proto nf_conntrack_l3proto_ipv6;
-struct nf_conn *
-icmpv6_error_track(struct sk_buff *skb,
-		  unsigned int icmp6off,
-		  enum nf_conntrack_info *ctinfo,
-		  unsigned int hooknum)
-{
-	struct nf_conntrack_tuple intuple, origtuple;
-	struct nf_conntrack_tuple_hash *h;
-	struct ipv6hdr *ip6h;
-	struct icmp6hdr hdr;
-	unsigned int inip6off;
-	struct nf_conntrack_protocol *inproto;
-	u_int8_t inprotonum;
-	unsigned int inprotoff;
-
-	NF_CT_ASSERT(skb->nfct == NULL);
-
-	ip6h = skb->nh.ipv6h;
-	if (skb_copy_bits(skb, icmp6off, &hdr, sizeof(hdr)) != 0) {
-		DEBUGP("icmpv6_error_track: Can't copy ICMPv6 hdr.\n");
-		return NULL;
-	}
-
-	if (hdr.icmp6_type >= 128)
-		return NULL;
-
-	/*
-	 * Should I ignore invalid ICMPv6 error here ?
-	 * ex) ICMPv6 error in ICMPv6 error, Fragmented packet, and so on.
-	 * - YK
-	 */
-
-	/* Ignore it if the checksum's bogus. */
-	if (csum_ipv6_magic(&ip6h->saddr, &ip6h->daddr, skb->len - icmp6off,
-			    IPPROTO_ICMPV6,
-			    skb_checksum(skb, icmp6off,
-					 skb->len - icmp6off, 0))) {
-		DEBUGP("icmpv6_error_track: ICMPv6 checksum failed\n");
-		return NULL;
-	}
-
-	inip6off = icmp6off + sizeof(hdr);
-	if (skb_copy_bits(skb, inip6off+offsetof(struct ipv6hdr, nexthdr),
-			  &inprotonum, sizeof(inprotonum)) != 0) {
-		DEBUGP("icmpv6_error_track: Can't copy nexthdr field in inner IPv6 hdr.\n");
-		return NULL;
-	}
-	inprotoff = ip6_ct_skip_exthdr(skb, inip6off + sizeof(struct ipv6hdr),
-				       &inprotonum,
-				       skb->len - inip6off
-						- sizeof(struct ipv6hdr));
-
-	if ((inprotoff < 0) || (inprotoff > skb->len) ||
-	    (inprotonum == NEXTHDR_FRAGMENT)) {
-		DEBUGP("icmpv6_error_track: Can't find protocol header in ICMPv6 payload.\n");
-		return NULL;
-	}
-
-	inproto = nf_ct_find_proto(PF_INET6, inprotonum);
-
-	/* Are they talking about one of our connections? */
-	if (!nf_ct_get_tuple(skb, inip6off, inprotoff, PF_INET6, inprotonum,
-			     &origtuple, &nf_conntrack_l3proto_ipv6, inproto)) {
-		DEBUGP("icmpv6_error_track: Can't get tuple\n");
-		return NULL;
-	}
-
-	/* Ordinarily, we'd expect the inverted tupleproto, but it's
-	   been preserved inside the ICMP. */
-	if (!nf_ct_invert_tuple(&intuple, &origtuple,
-				&nf_conntrack_l3proto_ipv6, inproto)) {
-		DEBUGP("icmpv6_error_track: Can't invert tuple\n");
-		return NULL;
-	}
-
-	*ctinfo = NF_CT_RELATED;
-
-	h = nf_conntrack_find_get(&intuple, NULL);
-	if (!h) {
-		DEBUGP("icmpv6_error_track: no match\n");
-		return NULL;
-	} else {
-		if (NF_CT_DIRECTION(h) == NF_CT_DIR_REPLY)
-			*ctinfo += NF_CT_IS_REPLY;
-	}
-
-	/* Update skb to refer to this connection */
-	skb->nfct = &h->ctrack->infos[*ctinfo];
-	return h->ctrack;
-}
-
 static int
 ipv6_prepare(struct sk_buff **pskb, unsigned int hooknum, unsigned int *dataoff,
 	     u_int8_t *protonum, int *ret)
@@ -241,13 +146,12 @@
 	unsigned int extoff;
 	unsigned char pnum;
 	int protoff;
-	enum nf_conntrack_info ctinfo;
 
-	extoff = (u8*)((*pskb)->nh.ipv6h+1) - (*pskb)->data;
+	extoff = (u8*)((*pskb)->nh.ipv6h + 1) - (*pskb)->data;
 	pnum = (*pskb)->nh.ipv6h->nexthdr;
 
-	protoff = ip6_ct_skip_exthdr(*pskb, extoff, &pnum,
-				     (*pskb)->len - extoff);
+	protoff = nf_ct_ipv6_skip_exthdr(*pskb, extoff, &pnum,
+					 (*pskb)->len - extoff);
 
 	/*
 	 * (protoff == (*pskb)->len) mean that the packet doesn't have no data
@@ -255,16 +159,14 @@
 	 */
 	if ((protoff < 0) || (protoff > (*pskb)->len)) {
 		DEBUGP("ip6_conntrack_core: can't find proto in pkt\n");
+		NF_CT_STAT_INC(error);
+		NF_CT_STAT_INC(invalid);
 		*ret =  NF_ACCEPT;
 		return 0;
 	}
 
-	/* It may be an icmp error... */
-	if ((pnum == IPPROTO_ICMPV6) &&
-	    icmpv6_error_track(*pskb, protoff, &ctinfo, hooknum)) {
-		*ret = NF_ACCEPT;
-		return 0;
-	}
+	/* FIXME: Do this right please. --RR */
+	(*pskb)->nfcache |= NFC_UNKNOWN;
 
 	*dataoff = protoff;
 	*protonum = pnum;
@@ -282,33 +184,66 @@
 				 const struct net_device *out,
 				 int (*okfn)(struct sk_buff *))
 {
-	DEBUGP("ipv6_confirm, %p\n", (*pskb));
-
 	/* We've seen it coming out the other side: confirm it */
-	return nf_ct_frag6_confirm(*pskb);
+	return nf_conntrack_confirm(*pskb);
 }
 
+extern struct sk_buff *nf_ct_frag6_gather(struct sk_buff *skb);
+extern void nf_ct_frag6_output(unsigned int hooknum, struct sk_buff *skb,
+			       struct net_device *in,
+			       struct net_device *out,
+			       int (*okfn)(struct sk_buff *));
+static unsigned int ipv6_defrag(unsigned int hooknum,
+				struct sk_buff **pskb,
+				const struct net_device *in,
+				const struct net_device *out,
+				int (*okfn)(struct sk_buff *))
+{
+	struct sk_buff *reasm;
+
+	/* Previously seen (loopback)?  */
+	if ((*pskb)->nfct)
+		return NF_ACCEPT;
+
+	reasm = nf_ct_frag6_gather(*pskb);
+
+	/* queued */
+	if (reasm == NULL)
+		return NF_STOLEN;
+
+	/* error occured or not fragmented */
+	if (reasm == *pskb)
+		return NF_ACCEPT;
+
+	nf_ct_frag6_output(hooknum, reasm, in, out, okfn);
+
+	return NF_STOLEN;
+}
+
 static unsigned int ipv6_conntrack_in(unsigned int hooknum,
 				      struct sk_buff **pskb,
 				      const struct net_device *in,
 				      const struct net_device *out,
 				      int (*okfn)(struct sk_buff *))
 {
-	unsigned int ret;
+	struct sk_buff *reasm = (*pskb)->reasm;
 
-	DEBUGP("ipv6_conntrack_in, %p\n", (*pskb));
+	/* This packet is fragmented and has reassembled packet. */
+	if (reasm) {
+		/* Reassembled packet isn't parsed yet ? */
+		if (!reasm->nfct) {
+			unsigned int ret;
 
-	if ((*pskb)->nfct)
+			ret = nf_conntrack_in(PF_INET6, hooknum, &reasm);
+			if (ret != NF_ACCEPT)
+				return ret;
+		}
+		nf_conntrack_get(reasm->nfct);
+		(*pskb)->nfct = reasm->nfct;
 		return NF_ACCEPT;
+	}
 
-	ret = nf_conntrack_in(PF_INET6, hooknum, pskb);
-
-	if (ret != NF_ACCEPT || (*pskb)->nfct == NULL)
-		return ret;
-
-	/* XXX discards qualifiers */
-	return nf_ct_frag6_reinject(hooknum, (*pskb), (struct net_device *)in,
-				    (struct net_device *)out, okfn);
+	return nf_conntrack_in(PF_INET6, hooknum, pskb);
 }
 
 static unsigned int ipv6_conntrack_local(unsigned int hooknum,
@@ -317,8 +252,6 @@
 					 const struct net_device *out,
 					 int (*okfn)(struct sk_buff *))
 {
-	DEBUGP("ipv6_conntrack_local, %p\n", (*pskb));
-
 	/* root is playing with raw sockets. */
 	if ((*pskb)->len < sizeof(struct ipv6hdr)) {
 		if (net_ratelimit())
@@ -330,6 +263,14 @@
 
 /* Connection tracking may drop packets, but never alters them, so
    make it the first hook. */
+static struct nf_hook_ops ipv6_conntrack_defrag_ops = {
+	.hook		= ipv6_defrag,
+	.owner		= THIS_MODULE,
+	.pf		= PF_INET6,
+	.hooknum	= NF_IP6_PRE_ROUTING,
+	.priority	= NF_IP6_PRI_CONNTRACK_DEFRAG,
+};
+
 static struct nf_hook_ops ipv6_conntrack_in_ops = {
 	.hook		= ipv6_conntrack_in,
 	.owner		= THIS_MODULE,
@@ -346,6 +287,14 @@
 	.priority	= NF_IP6_PRI_CONNTRACK,
 };
 
+static struct nf_hook_ops ipv6_conntrack_defrag_local_out_ops = {
+	.hook		= ipv6_defrag,
+	.owner		= THIS_MODULE,
+	.pf		= PF_INET6,
+	.hooknum	= NF_IP6_LOCAL_OUT,
+	.priority	= NF_IP6_PRI_CONNTRACK_DEFRAG,
+};
+
 /* Refragmenter; last chance. */
 static struct nf_hook_ops ipv6_conntrack_out_ops = {
 	.hook		= ipv6_confirm,
@@ -368,7 +317,7 @@
 /* From nf_conntrack_proto_icmpv6.c */
 extern unsigned long nf_ct_icmpv6_timeout;
 
-/* From nf_conntrack_proto_frag6.c */
+/* From nf_conntrack_frag6.c */
 extern unsigned long nf_ct_frag6_timeout;
 extern unsigned long nf_ct_frag6_low_thresh;
 extern unsigned long nf_ct_frag6_high_thresh;
@@ -432,51 +381,145 @@
 };
 #endif
 
-static struct nf_conntrack_protocol tcp, udp, icmpv6, frag6;
+/* Fast function for those who don't want to parse /proc (and I don't
+   blame them). */
+/* Reversing the socket's dst/src point of view gives us the reply
+   mapping. */
+static int
+getorigdst(struct sock *sk, int optval, void *user, int *len)
+{
+	struct inet_opt *inet = inet_sk(sk);
+	struct ipv6_pinfo *np = inet6_sk(sk);
+	struct nf_conntrack_tuple_hash *h;
+	struct nf_conntrack_tuple tuple;
 
+	memset(&tuple, 0, sizeof(tuple));
+	ipv6_addr_copy((struct in6_addr *)tuple.src.u3.ip6, &np->rcv_saddr);
+	ipv6_addr_copy((struct in6_addr *)tuple.dst.u3.ip6, &np->daddr);
+	tuple.src.u.tcp.port = inet->sport;
+	tuple.dst.u.tcp.port = inet->dport;
+	tuple.dst.protonum = IPPROTO_TCP;
+
+	/* We only do TCP at the moment: is there a better way? */
+	if (strcmp(sk->sk_prot->name, "TCP")) {
+		DEBUGP("IPV6 ORIGINAL_DST: Not a TCP socket\n");
+		return -ENOPROTOOPT;
+	}
+
+	if ((unsigned int) *len < sizeof(struct sockaddr_in)) {
+		DEBUGP("IPV6 ORIGINAL_DST: len %u not %u\n",
+		       *len, sizeof(struct sockaddr_in));
+		return -EINVAL;
+	}
+
+	h = nf_conntrack_find_get(&tuple, NULL);
+	if (h) {
+		struct sockaddr_in6 sin;
+
+		sin.sin6_family = AF_INET6;
+		sin.sin6_port = h->ctrack->tuplehash[NF_CT_DIR_ORIGINAL]
+				.tuple.dst.u.tcp.port;
+		ipv6_addr_copy(&sin.sin6_addr,
+			       (struct in6_addr *)h->ctrack->tuplehash[NF_CT_DIR_ORIGINAL].tuple.dst.u3.ip6);
+
+		DEBUGP("IPV6 ORIGINAL_DST: %x:%x:%x:%x:%x:%x:%x:%x %u\n",
+		       NIP6(sin.sin6_addr), ntohs(sin.sin6_port));
+		nf_ct_put(h->ctrack);
+		if (copy_to_user(user, &sin, sizeof(sin)) != 0)
+			return -EFAULT;
+		else
+			return 0;
+	}
+	DEBUGP("IPV6 ORIGINAL_DST: Can't find %x:%x:%x:%x:%x:%x:%x:%x/%u-%x:%x:%x:%x:%x:%x:%x:%x/%u.\n",
+	       NIP6((struct in6_addr *)tuple.src.u3.ip6),
+	       ntohs(tuple.src.u.tcp.port),
+	       NIP6((struct in6_addr *)tuple.dst.u3.ip6),
+	       ntohs(tuple.dst.u.tcp.port));
+	return -ENOENT;
+}
+
+static struct nf_sockopt_ops so_getorigdst = {
+	.pf		= PF_INET6,
+	.get_optmin	= SO_ORIGINAL_DST,
+	.get_optmax	= SO_ORIGINAL_DST+1,
+	.get		= &getorigdst,
+};
+
+struct nf_conntrack_l3proto nf_conntrack_l3proto_ipv6 = {
+	.l3proto		= PF_INET6,
+	.name			= "ipv6",
+	.pkt_to_tuple		= ipv6_pkt_to_tuple,
+	.invert_tuple		= ipv6_invert_tuple,
+	.print_tuple		= ipv6_print_tuple,
+	.print_conntrack	= ipv6_print_conntrack,
+	.prepare		= ipv6_prepare,
+	.get_features		= ipv6_get_features,
+	.me			= THIS_MODULE,
+};
+
+extern struct nf_conntrack_protocol nf_conntrack_protocol_tcp6;
+extern struct nf_conntrack_protocol nf_conntrack_protocol_udp6;
+extern struct nf_conntrack_protocol nf_conntrack_protocol_icmpv6;
+extern int nf_ct_frag6_init(void);
+extern void nf_ct_frag6_cleanup(void);
 static int init_or_cleanup(int init)
 {
 	int ret = 0;
-	struct nf_conntrack_protocol *proto;
 
 	if (!init) goto cleanup;
 
-	memcpy(&tcp, &nf_conntrack_protocol_tcp, sizeof(tcp));
-	tcp.l3proto = PF_INET6;
-	ret = nf_conntrack_protocol_register(&tcp);
+	ret = nf_register_sockopt(&so_getorigdst);
 	if (ret < 0) {
-		printk("nf_conntrack_ipv6: can't register tcp.\n");
+		printk(KERN_ERR "Unable to register netfilter socket option\n");
 		goto cleanup_nothing;
 	}
+	ret = nf_ct_frag6_init();
+	if (ret < 0) {
+		printk("nf_conntrack_ipv6: can't initialize frag6.\n");
+		goto cleanup_sockopt;
+	}
+	ret = nf_conntrack_protocol_register(&nf_conntrack_protocol_tcp6);
+	if (ret < 0) {
+		printk("nf_conntrack_ipv6: can't register tcp.\n");
+		goto cleanup_frag6;
+	}
 
-	memcpy(&udp, &nf_conntrack_protocol_udp, sizeof(udp));
-	udp.l3proto = PF_INET6;
-	ret = nf_conntrack_protocol_register(&udp);
+	ret = nf_conntrack_protocol_register(&nf_conntrack_protocol_udp6);
 	if (ret < 0) {
 		printk("nf_conntrack_ipv6: can't register udp.\n");
 		goto cleanup_tcp;
 	}
 
-	memcpy(&icmpv6, &nf_conntrack_protocol_icmpv6, sizeof(icmpv6));
-	icmpv6.l3proto = PF_INET6;
-	ret = nf_conntrack_protocol_register(&icmpv6);
+	ret = nf_conntrack_protocol_register(&nf_conntrack_protocol_icmpv6);
 	if (ret < 0) {
 		printk("nf_conntrack_ipv6: can't register icmpv6.\n");
 		goto cleanup_udp;
 	}
 
-	memcpy(&frag6, &nf_conntrack_protocol_frag6, sizeof(frag6));
-	frag6.l3proto = PF_INET6;
-	ret = nf_conntrack_protocol_register(&frag6);
+	ret = nf_conntrack_l3proto_register(&nf_conntrack_l3proto_ipv6);
 	if (ret < 0) {
-		printk("nf_conntrack_ipv6: can't register frag6.\n");
+		printk("nf_conntrack_ipv6: can't register ipv6\n");
 		goto cleanup_icmpv6;
 	}
 
+	ret = nf_register_hook(&ipv6_conntrack_defrag_ops);
+	if (ret < 0) {
+		printk("nf_conntrack_ipv6: can't register pre-routing defrag "
+		       "hook.\n");
+		goto cleanup_ipv6;
+	}
+
+	ret = nf_register_hook(&ipv6_conntrack_defrag_local_out_ops);
+	if (ret < 0) {
+		printk("nf_conntrack_ipv6: can't register local_out defrag "
+		       "hook.\n");
+		goto cleanup_defragops;
+	}
+
 	ret = nf_register_hook(&ipv6_conntrack_in_ops);
 	if (ret < 0) {
 		printk("nf_conntrack_ipv6: can't register pre-routing hook.\n");
-		goto cleanup_frag6;
+		goto cleanup_defraglocalops;
 	}
 
 	ret = nf_register_hook(&ipv6_conntrack_local_out_ops);
@@ -505,14 +548,13 @@
 		goto cleanup_localinops;
 	}
 #endif
-
 	return ret;
 
  cleanup:
 #ifdef CONFIG_SYSCTL
  	unregister_sysctl_table(nf_ct_ipv6_sysctl_header);
-#endif
  cleanup_localinops:
+#endif
 	nf_unregister_hook(&ipv6_conntrack_local_in_ops);
  cleanup_inoutandlocalops:
 	nf_unregister_hook(&ipv6_conntrack_out_ops);
@@ -520,80 +562,37 @@
 	nf_unregister_hook(&ipv6_conntrack_local_out_ops);
  cleanup_inops:
 	nf_unregister_hook(&ipv6_conntrack_in_ops);
- cleanup_frag6:
-	READ_LOCK(&nf_conntrack_lock);
-	proto = __nf_ct_find_proto(PF_INET6, NEXTHDR_FRAGMENT);
-	READ_UNLOCK(&nf_conntrack_lock);
-	if (proto)
-		nf_conntrack_protocol_unregister(proto);
+ cleanup_defraglocalops:
+	nf_unregister_hook(&ipv6_conntrack_defrag_local_out_ops);
+ cleanup_defragops:
+	nf_unregister_hook(&ipv6_conntrack_defrag_ops);
+ cleanup_ipv6:
+	nf_conntrack_l3proto_unregister(&nf_conntrack_l3proto_ipv6);
  cleanup_icmpv6:
-	READ_LOCK(&nf_conntrack_lock);
-	proto = __nf_ct_find_proto(PF_INET6, IPPROTO_ICMPV6);
-	READ_UNLOCK(&nf_conntrack_lock);
-	if (proto)
-		nf_conntrack_protocol_unregister(proto);
+	nf_conntrack_protocol_unregister(&nf_conntrack_protocol_icmpv6);
  cleanup_udp:
-	READ_LOCK(&nf_conntrack_lock);
-	proto = __nf_ct_find_proto(PF_INET6, IPPROTO_UDP);
-	READ_UNLOCK(&nf_conntrack_lock);
-	if (proto)
-		nf_conntrack_protocol_unregister(proto);
+	nf_conntrack_protocol_unregister(&nf_conntrack_protocol_udp6);
  cleanup_tcp:
-	READ_LOCK(&nf_conntrack_lock);
-	proto = __nf_ct_find_proto(PF_INET6, IPPROTO_TCP);
-	READ_UNLOCK(&nf_conntrack_lock);
-	if (proto)
-		nf_conntrack_protocol_unregister(proto);
+	nf_conntrack_protocol_unregister(&nf_conntrack_protocol_tcp6);
+ cleanup_frag6:
+	nf_ct_frag6_cleanup();
+ cleanup_sockopt:
+	nf_unregister_sockopt(&so_getorigdst);
  cleanup_nothing:
 	return ret;
 }
 
-static int ipv6_init(void)
-{
-	return init_or_cleanup(1);
-}
-
-static void ipv6_fini(void)
-{
-	init_or_cleanup(0);
-}
-
-struct nf_conntrack_l3proto nf_conntrack_l3proto_ipv6 = {
-	{ NULL, NULL },		/* list */
-	PF_INET6,		/* l3proto */
-	"ipv6",			/* name */
-	ipv6_init,		/* init */
-	ipv6_fini,		/* fini */
-	ipv6_pkt_to_tuple, 	/* pkt_to_tuple */
-	ipv6_invert_tuple, 	/* invert_tuple */
-	ipv6_print_tuple,	/* print_tuple */
-	ipv6_print_conntrack,	/* print_conntrack */
-	NULL,			/* packet */
-	NULL,			/* new */
-	NULL,			/* destroy */
-	ipv6_prepare,		/* prepare */
-	ipv6_get_features,	/* get_features */
-	THIS_MODULE		/* me */
-};
-
 MODULE_LICENSE("GPL");
 
 static int __init init(void)
 {
-	int ret;
-
 	need_nf_conntrack();
-	ret = nf_conntrack_l3proto_register(&nf_conntrack_l3proto_ipv6);
-	if (ret < 0) {
-		printk("nf_conntrack_ipv6: can't register l3 proto module\n");
-	}
-
-	return ret;
+	return init_or_cleanup(1);
 }
 
 static void __exit fini(void)
 {
-	nf_conntrack_l3proto_unregister(&nf_conntrack_l3proto_ipv6);
+	init_or_cleanup(0);
 }
 
 module_init(init);

Deleted: trunk/patch-o-matic-ng/nf_conntrack/linux-2.6/net/ipv6/netfilter/nf_conntrack_proto_frag6.c
===================================================================
--- trunk/patch-o-matic-ng/nf_conntrack/linux-2.6/net/ipv6/netfilter/nf_conntrack_proto_frag6.c	2004-10-24 22:27:31 UTC (rev 3259)
+++ trunk/patch-o-matic-ng/nf_conntrack/linux-2.6/net/ipv6/netfilter/nf_conntrack_proto_frag6.c	2004-11-01 07:38:59 UTC (rev 3260)
@@ -1,845 +0,0 @@
-/*
- * Copyright (C)2004 USAGI/WIDE Project
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
- *
- * some parts are derived from net/ipv6/reassembly.c
- *
- * Authors:
- *	Yasuyuki Kozakai	<yasuyuki.kozakai at toshiba.co.jp>
- *
- */
-
-/* handling fragmented IPv6 packets */
-
-#include <linux/types.h>
-#include <linux/sched.h>
-#include <linux/timer.h>
-#include <linux/module.h>
-#include <linux/random.h>
-#include <linux/netfilter.h>
-#include <net/ipv6.h>
-#include <net/checksum.h>
-#include <linux/icmpv6.h>
-#include <linux/netfilter.h>
-#include <linux/netfilter_ipv6.h>
-
-/* This rwlock protects the main hash table, protocol/helper/expected
-   registrations, conntrack timers*/
-#define ASSERT_READ_LOCK(x) MUST_BE_READ_LOCKED(&nf_conntrack_lock)
-#define ASSERT_WRITE_LOCK(x) MUST_BE_WRITE_LOCKED(&nf_conntrack_lock)
-
-#include <linux/netfilter/nf_conntrack.h>
-#include <linux/netfilter/nf_conntrack_core.h>
-#include <linux/netfilter/nf_conntrack_tuple.h>
-#include <linux/netfilter/nf_conntrack_protocol.h>
-#include <linux/netfilter_ipv4/listhelp.h>
-#include <linux/netfilter_ipv4/lockhelp.h>
-#include <linux/netfilter_ipv6/nf_conntrack_frag6.h>
-
-#if 0
-#define DEBUGP printk
-#else
-#define DEBUGP(format, args...)
-#endif
-
-
-unsigned long nf_ct_frag6_timeout = 30*HZ;
-unsigned long nf_ct_frag6_high_thresh = 256*1024;
-unsigned long nf_ct_frag6_low_thresh = 192*1024;
-
-struct frag6_skb_cb
-{
-	struct inet6_skb_parm h;
-	int offset;
-	struct sk_buff *orig;
-};
-
-struct orig_skb_cb
-{
-	struct inet6_skb_parm h;
-	struct sk_buff *next;
-};
-
-#define ORIG_CB(skb)	((struct orig_skb_cb*)((skb)->cb))
-#define FRAG6_CB(skb)	((struct frag6_skb_cb*)((skb)->cb))
-
-static atomic_t frag6_mem = ATOMIC_INIT(0);
-
-/* Memory Tracking Functions. */
-static inline void frag6_kfree_skb(struct sk_buff *skb)
-{
-	atomic_sub(skb->truesize, &frag6_mem);
-	if (FRAG6_CB(skb)->orig)
-		kfree_skb(FRAG6_CB(skb)->orig);
-
-	kfree_skb(skb);
-}
-
-static int
-evictor_candidate(const struct nf_conntrack_tuple_hash *i,
-		  const struct nf_conntrack_tuple *ignored)
-{
-
-	return (i->tuple.dst.protonum == NEXTHDR_FRAGMENT) &&
-		(!(i->ctrack->proto.frag6.last_in & NF_CT_FRAG6_COMPLETE)) &&
-		(!nf_ct_tuple_equal(&i->tuple, ignored));
-}
-
-static void frag6_evictor(const struct nf_conntrack_tuple *ignored)
-{
-	struct nf_conntrack_tuple_hash *h;
-	unsigned int hash;
-	unsigned int i;
-
-	DEBUGP("frag6_evictor\n");
-	get_random_bytes(&hash, 4);
-	hash %= nf_conntrack_htable_size;
-
-	i = hash;
-	if ((++i) >= nf_conntrack_htable_size)
-		i = 0;
-
-	while (atomic_read(&frag6_mem) > nf_ct_frag6_low_thresh) {
-		WRITE_LOCK(&nf_conntrack_lock);
-
-		h = LIST_FIND_B(&nf_conntrack_hash[i], evictor_candidate,
-				struct nf_conntrack_tuple_hash *, ignored);
-
-		if (!h) {
-			if ((++i) >= nf_conntrack_htable_size)
-				i = 0;
-
-			if (i == hash) {
-				WRITE_UNLOCK(&nf_conntrack_lock);
-				break;
-			}
-			WRITE_UNLOCK(&nf_conntrack_lock);
-			continue;
-		}
-
-		nf_conntrack_get(&h->ctrack->infos[0]);
-		WRITE_UNLOCK(&nf_conntrack_lock);
-
-		if (del_timer(&h->ctrack->timeout))
-			h->ctrack->timeout.function((unsigned long)h->ctrack);
-		nf_conntrack_put(&h->ctrack->infos[0]);
-	}
-}
-
-/*
- * find the header just before Fragment Header.
- *
- * if success return 0 and set ...
- * (*prevhdrp): the value of "Next Header Field" in the header
- *	      just before Fragment Header.
- * (*prevhoff): the offset of "Next Header Field" in the header
- *	      just before Fragment Header.
- * (*fhoff)   : the offset of Fragment Header.
- *
- * Based on ipv6_skip_hdr() in net/ipv6/exthdr.c
- *
- */
-static int
-frag6_find_prev_fhdr(struct sk_buff *skb, u8 *prevhdrp, int *prevhoff,
-		     int *fhoff)
-{
-	u8 nexthdr = skb->nh.ipv6h->nexthdr;
-	int prev_nhoff = (u8 *)&skb->nh.ipv6h->nexthdr - skb->data;
-	int start = (u8 *)(skb->nh.ipv6h+1) - skb->data;
-	int len = skb->len - start;
-	u8 prevhdr = NEXTHDR_IPV6;
-
-	while (nexthdr != NEXTHDR_FRAGMENT) {
-		struct ipv6_opt_hdr hdr;
-		int hdrlen;
-
-		if (!ipv6_ext_hdr(nexthdr)) {
-			return -1;
-		}
-		if (len < (int)sizeof(struct ipv6_opt_hdr)) {
-			DEBUGP("too short\n");
-			return -1;
-		}
-		if (nexthdr == NEXTHDR_NONE) {
-			DEBUGP("next header is none\n");
-			return -1;
-		}
-		if (skb_copy_bits(skb, start, &hdr, sizeof(hdr)))
-			BUG();
-		if (nexthdr == NEXTHDR_AUTH)
-			hdrlen = (hdr.hdrlen+2)<<2;
-		else
-			hdrlen = ipv6_optlen(&hdr);
-
-		prevhdr = nexthdr;
-		prev_nhoff = start;
-
-		nexthdr = hdr.nexthdr;
-		len -= hdrlen;
-		start += hdrlen;
-	}
-
-	if (len < 0)
-		return -1;
-
-	*prevhdrp = prevhdr;
-	*prevhoff = prev_nhoff;
-	*fhoff = start;
-
-	return 0;
-}
-
-/* derived from net/ipv6/reassembly.c */
-
-static int
-frag6_queue(struct nf_conn *conntrack, struct sk_buff *skb,
-	    struct frag_hdr *fhdr)
-{
-	struct sk_buff *prev, *next;
-	int offset, end;
-	struct nf_ct_frag6 *fq = &conntrack->proto.frag6;
-
-	offset = ntohs(fhdr->frag_off) & ~0x7;
-	end = offset + (ntohs(skb->nh.ipv6h->payload_len) -
-			((u8 *) (fhdr + 1) - (u8 *) (skb->nh.ipv6h + 1)));
-
-	DEBUGP("frag6_queue: end = %d\n", end);
-
-	if ((unsigned int)end > IPV6_MAXPLEN) {
-
-		DEBUGP("frag6_queue: offset too big. offset = %u, payload_len = %u, end = %u\n",
-		       offset, ntohs(skb->nh.ipv6h->payload_len), end);
- 		return -1;
-	}
-
- 	if (skb->ip_summed == CHECKSUM_HW)
- 		skb->csum = csum_sub(skb->csum,
- 				     csum_partial(skb->nh.raw,
-						  (u8*)(fhdr + 1) - skb->nh.raw,
-						  0));
-
-	/* Is this the final fragment? */
-	if (!(fhdr->frag_off & htons(IP6_MF))) {
-		/* If we already have some bits beyond end
-		 * or have different end, the segment is corrupted.
-		 */
-		if (end < fq->len ||
-		    ((fq->last_in & NF_CT_FRAG6_LAST_IN) && end != fq->len)) {
-			DEBUGP("frag6_queue: final fragment. but bad offset\n");
-			goto err;
-		}
-		fq->last_in |= NF_CT_FRAG6_LAST_IN;
-		fq->len = end;
-	} else {
-		/* Check if the fragment is rounded to 8 bytes.
-		 * Required by the RFC.
-		 */
-		if (end & 0x7) {
-			DEBUGP("frag6_queue: bad offset\n");
-			return -1;
-		}
-		if (end > fq->len) {
-			/* Some bits beyond end -> corruption. */
-			if (fq->last_in & NF_CT_FRAG6_LAST_IN) {
-				DEBUGP("frag6_queue: end of payload is too big\n");
-				goto err;
-			}
-			fq->len = end;
-		}
-	}
-
-	if (end == offset) {
-		DEBUGP("frag6_queue: invaild payload length or offset=%d\n",
-		       offset);
-		goto err;
-	}
-
-	/* Point into the IP datagram 'data' part. */
-	if (!pskb_pull(skb, (u8 *)(fhdr + 1) - skb->data)) {
-		DEBUGP("frag6_queue: too short\n");
-		goto err;
-	}
-	if (end - offset < skb->len) {
-		if (pskb_trim(skb, end - offset)) {
-			DEBUGP("can't trim\n");
-			goto err;
-		}
-		if (skb->ip_summed != CHECKSUM_UNNECESSARY)
-			skb->ip_summed = CHECKSUM_NONE;
-	}
-
-	/* Find out which fragments are in front and at the back of us
-	 * in the chain of fragments so far.  We must know where to put
-	 * this fragment, right?
-	 */
-	prev = NULL;
-	for(next = fq->fragments; next != NULL; next = next->next) {
-		if (FRAG6_CB(next)->offset >= offset)
-			break;	/* bingo! */
-		prev = next;
-	}
-
-	/* We found where to put this one.  Check for overlap with
-	 * preceding fragment, and, if needed, align things so that
-	 * any overlaps are eliminated.
-	 */
-	if (prev) {
-		int i = (FRAG6_CB(prev)->offset + prev->len) - offset;
-
-		if (i > 0) {
-			offset += i;
-			if (end <= offset) {
-				DEBUGP("this packet is coverd with other packets in queue\n");
-				goto err;
-			}
-			if (!pskb_pull(skb, i)) {
-				DEBUGP("overrupped and can't pull\n");
-				goto err;
-			}
-			if (skb->ip_summed != CHECKSUM_UNNECESSARY)
-				skb->ip_summed = CHECKSUM_NONE;
-		}
-	}
-
-	/* Look for overlap with succeeding segments.
-	 * If we can merge fragments, do it.
-	 */
-	while (next && FRAG6_CB(next)->offset < end) {
-		int i = end - FRAG6_CB(next)->offset; /* overlap is 'i' bytes */
-
-		if (i < next->len) {
-			/* Eat head of the next overlapped fragment
-			 * and leave the loop. The next ones cannot overlap.
-			 */
-			if (!pskb_pull(next, i)) {
-				DEBUGP("can't pull\n");
-				goto err;
-			}
-			FRAG6_CB(next)->offset += i;	/* next fragment */
-			fq->meat -= i;
-			if (next->ip_summed != CHECKSUM_UNNECESSARY)
-				next->ip_summed = CHECKSUM_NONE;
-			break;
-		} else {
-			struct sk_buff *free_it = next;
-
-			/* Old fragment is completely overridden with
-			 * new one drop it.
-			 */
-			next = next->next;
-
-			if (prev)
-				prev->next = next;
-			else
-				fq->fragments = next;
-
-			fq->meat -= free_it->len;
-			frag6_kfree_skb(free_it);
-		}
-	}
-
-	FRAG6_CB(skb)->offset = offset;
-
-	/* Insert this fragment in the chain of fragments. */
-	skb->next = next;
-	if (prev)
-		prev->next = skb;
-	else
-		fq->fragments = skb;
-
-	if (skb->dev)
-		fq->iif = skb->dev->ifindex;
-	fq->stamp = skb->stamp;
-	fq->meat += skb->len;
-	atomic_add(skb->truesize, &frag6_mem);
-
-	if (offset == 0)
-		fq->last_in |= NF_CT_FRAG6_FIRST_IN;
-
-	return 0;
-
-err:
-	return -1;
-}
-
-static int
-frag6_reasm(struct nf_conn *conntrack, struct net_device *dev)
-{
-	struct nf_ct_frag6 *fq = &conntrack->proto.frag6;
-	struct sk_buff *fp, *head = fq->fragments;
-	int    payload_len;
-	u8 prevhdr;
-	int prevnhoff, fhoff;
-
-	BUG_TRAP(head != NULL);
-	BUG_TRAP(FRAG6_CB(head)->offset == 0);
-
-	/* Unfragmented part is taken from the first segment. */
-	payload_len = (head->data - head->nh.raw) - sizeof(struct ipv6hdr)
-		      + fq->len - sizeof(struct frag_hdr);
-	if (payload_len > IPV6_MAXPLEN)
-		goto out_oversize;
-
-	if (frag6_find_prev_fhdr(head, &prevhdr, &prevnhoff, &fhoff) < 0)
-		goto out_fail;
-
-	/* Head of list must not be cloned. */
-	if (skb_cloned(head) && pskb_expand_head(head, 0, 0, GFP_ATOMIC)) {
-		DEBUGP("can't copy head of cloned skb\n");
-		goto out_oom;
-	}
-
-	/* If the first fragment is fragmented itself, we split
-	 * it to two chunks: the first with data and paged part
-	 * and the second, holding only fragments. */
-	if (skb_shinfo(head)->frag_list) {
-		struct sk_buff *clone;
-		int i, plen = 0;
-
-		if ((clone = alloc_skb(0, GFP_ATOMIC)) == NULL)
-			goto out_oom;
-
-		clone->next = head->next;
-		head->next = clone;
-		skb_shinfo(clone)->frag_list = skb_shinfo(head)->frag_list;
-		skb_shinfo(head)->frag_list = NULL;
-		for (i=0; i < skb_shinfo(head)->nr_frags; i++)
-			plen += skb_shinfo(head)->frags[i].size;
-		clone->len = clone->data_len = head->data_len - plen;
-		head->data_len -= clone->len;
-		head->len -= clone->len;
-		clone->csum = 0;
-		clone->ip_summed = head->ip_summed;
-		atomic_add(clone->truesize, &frag6_mem);
-	}
-
-	/* We have to remove fragment header from datagram and to relocate
-	 * header in order to calculate ICV correctly. */
-
-	head->data[prevnhoff] = head->data[fhoff];
-	memmove(head->head + sizeof(struct frag_hdr), head->head, 
-		(head->data - head->head) - sizeof(struct frag_hdr));
-	head->mac.raw += sizeof(struct frag_hdr);
-	head->nh.raw += sizeof(struct frag_hdr);
-	if (head->h.raw && head->h.raw < head->data - sizeof(struct frag_hdr))
-		head->h.raw += sizeof(struct frag_hdr);
-
-	skb_shinfo(head)->frag_list = head->next;
-	skb_push(head, head->data - head->nh.raw);
-	atomic_sub(head->truesize, &frag6_mem);
-
-	for (fp=head->next; fp; fp = fp->next) {
-		head->data_len += fp->len;
-		head->len += fp->len;
-		if (head->ip_summed != fp->ip_summed)
-			head->ip_summed = CHECKSUM_NONE;
-		else if (head->ip_summed == CHECKSUM_HW)
-			head->csum = csum_add(head->csum, fp->csum);
-		head->truesize += fp->truesize;
-		atomic_sub(fp->truesize, &frag6_mem);
-	}
-
-	head->next = NULL;
-	head->dev = dev;
-	head->stamp = fq->stamp;
-	head->nh.ipv6h->payload_len = ntohs(payload_len);
-
-	/* Yes, and fold redundant checksum back. 8) */
-	if (head->ip_summed == CHECKSUM_HW)
-		head->csum = csum_partial(head->nh.raw,
-					  head->h.raw - head->nh.raw,
-					  head->csum);
-
-	fq->fragments = NULL;
-	fq->reasm = head;
-	return 0;
-
-out_oversize:
-	if (net_ratelimit())
-		printk(KERN_DEBUG "ip6_frag_reasm: payload len = %d\n",
-		       payload_len);
-	goto out_fail;
-out_oom:
-	if (net_ratelimit())
-		printk(KERN_DEBUG "ip6_frag_reasm: no memory for reassembly\n");
-out_fail:
-	return -1;
-}
-
-/*
- * delete fragment header and change "Next Header" field in the previous
- * header
- */
-static int
-ip6_del_fraghdr(struct sk_buff *skb)
-{
-	u8 prevhdr;
-	int prevnhoff, fhoff;
-
-	if (frag6_find_prev_fhdr(skb, &prevhdr, &prevnhoff, &fhoff) < 0)
-		return -1;
-
-	if (!pskb_may_pull(skb, fhoff + sizeof(struct frag_hdr))) {
-		DEBUGP("packet too short\n");
-		return -1;
-	}
-
-	if (skb_cloned(skb) && pskb_expand_head(skb, 0, 0, GFP_ATOMIC) < 0) {
-		DEBUGP("can't copy head of cloned skb\n");
-		return -1;
-	}
-
-	skb->data[prevnhoff] = skb->data[fhoff];
-	memmove(skb->head + sizeof(struct frag_hdr), skb->head, 
-		(skb->data - skb->head) + fhoff);
-	skb->mac.raw += sizeof(struct frag_hdr);
-	skb->nh.raw += sizeof(struct frag_hdr);
-	if (skb->h.raw && skb->h.raw < skb->data - sizeof(struct frag_hdr))
-		skb->h.raw += sizeof(struct frag_hdr);
-
-	return 0;
-}
-
-unsigned int
-nf_ct_frag6_confirm(struct sk_buff *skb)
-{
-	struct nf_conn *conntrack;
-	enum nf_conntrack_info ctinfo;
-
-	conntrack = nf_ct_get(skb, &ctinfo);
-
-	if (conntrack == NULL ||
-	    conntrack->tuplehash[NF_CT_DIR_ORIGINAL].tuple.dst.protonum
-	     != NEXTHDR_FRAGMENT)
-		/* this is not fragmented packet. */
-		return nf_conntrack_confirm(skb);
-	else
-		return nf_conntrack_confirm(conntrack->proto.frag6.reasm);
-}
-
-/* pass the reserved fragmented packets to next hook */
-unsigned int
-nf_ct_frag6_reinject(unsigned int hooknum, struct sk_buff *skb,
-		     struct net_device *in, struct net_device *out,
-		     int (*okfn)(struct sk_buff *))
-{
-	struct nf_conn *conntrack;
-	struct nf_ct_frag6 *fq;
-	struct sk_buff *frag, *frag_list;
-	enum nf_conntrack_info ctinfo;
-
-	conntrack = nf_ct_get(skb, &ctinfo);
-
-	if (conntrack == NULL ||
-	    conntrack->tuplehash[NF_CT_DIR_ORIGINAL].tuple.dst.protonum
-	     != NEXTHDR_FRAGMENT)
-		/* this is not fragmented packet. */
-		return NF_ACCEPT;
-
-	fq = &conntrack->proto.frag6;
-
-	spin_lock(&fq->lock);
-	frag_list = fq->orig_list;
-	fq->orig_list = NULL;
-	spin_unlock(&fq->lock);
-
-	if (frag_list== NULL) {
-		if (net_ratelimit())
-			printk("already out ?\n");
-
-		return NF_STOLEN;
-	}
-
-	for (frag = frag_list; frag;) {
-		struct sk_buff *next = ORIG_CB(frag)->next;
-		NF_HOOK_THRESH(PF_INET6, hooknum, frag, in, out, okfn,
-			       NF_IP6_PRI_CONNTRACK+1);
-		frag = next;
-	}
-
-	return NF_STOLEN;
-}
-
-/* return reassembled packet */
-struct sk_buff *
-nf_ct_frag6_get_reasm(const struct sk_buff *skb)
-{
-	if (skb->nfct && (struct nf_conn *)skb->nfct->master)
-		return ((struct nf_conn *)skb->nfct->master)->proto.frag6.reasm;
-
-	return NULL;
-}
-
-/* protocol module definitions */
-
-static int
-frag6_pkt_to_tuple(const struct sk_buff *skb, unsigned int dataoff,
-		   struct nf_conntrack_tuple *tuple)
-{
-	struct frag_hdr hdr;
-
-	/* Actually only need first 8 bytes. */
-	if (skb_copy_bits(skb, dataoff, &hdr, 8) != 0)
-		return 0;
-
-	tuple->src.u.frag6.id = hdr.identification;
-	tuple->dst.u.frag6.orig = 1;
-
-	return 1;
-}
-
-static int
-frag6_invert_tuple(struct nf_conntrack_tuple *tuple,
-		   const struct nf_conntrack_tuple *orig)
-{
-	tuple->src.u.frag6.id = orig->src.u.frag6.id;
-	tuple->dst.u.frag6.orig = !orig->dst.u.frag6.orig;
-	return 1;
-}
-
-/* Print out the per-protocol part of the tuple. */
-static unsigned int
-frag6_print_tuple(char *buffer, const struct nf_conntrack_tuple *tuple)
-{
-	if (tuple->dst.u.frag6.orig)
-		return sprintf(buffer, "id=%08x", tuple->src.u.frag6.id);
-	else
-		return 0;
-}
-
-/* Print out the private part of the conntrack. */
-static unsigned int
-frag6_print_conntrack(char *buffer, const struct nf_conn *conntrack)
-{
-	return 0;
-}
-
-/* gather fragmented packets, reassemble them, and track reassembled packet. */
-static int
-frag6_packet(struct nf_conn *conntrack,
-	     const struct sk_buff *skb,
-	     unsigned int dataoff,
-	     enum nf_conntrack_info ctinfo,
-	     unsigned int hooknum)
-{
-	struct sk_buff *orig = (struct sk_buff *)skb;	/* XXX */
-	struct sk_buff *clone;
-	struct frag_hdr *fhdr;
-	struct nf_ct_frag6 *fq = &conntrack->proto.frag6;
-	struct net_device *dev = orig->dev;
-	struct sk_buff *frag;
-	int ret = -1;
-
-	if (NFCTINFO2DIR(ctinfo) == NF_CT_DIR_REPLY) {
-		DEBUGP("frag6_packet: unexpected reply packet\n");
-		return -1;
-	}
-
-	clone = skb_clone(orig, GFP_ATOMIC);
-	if (clone == NULL) {
-		DEBUGP("can't clone skb\n");
-		return -1;
-	}
-	FRAG6_CB(clone)->orig = orig;
-	nf_conntrack_put(clone->nfct);
-	clone->nfct = NULL;
-
-	/* Jumbo payload inhibits frag. header */
-	if (clone->nh.ipv6h->payload_len==0) {
-		DEBUGP("frag6_packet: Jumbo payload\n");
-		goto free_clone;
-	}
-	if (!pskb_may_pull(clone, dataoff + sizeof(struct frag_hdr))) {
-		DEBUGP("frag6_packet: packet too short\n");
-		goto free_clone;
-	}
-
-	if (atomic_read(&frag6_mem) > nf_ct_frag6_high_thresh)
-		frag6_evictor(&conntrack->tuplehash[NF_CT_DIR_ORIGINAL].tuple);
-
-	spin_lock(&fq->lock);
-
-	if (fq->last_in & NF_CT_FRAG6_COMPLETE) {
-		spin_unlock(&fq->lock);
-		DEBUGP("already all fragmented packets are gathered\n");
-		goto free_clone;
-	}
-
-	fhdr = (struct frag_hdr *)(clone->data + dataoff);
-	DEBUGP("fra6_packet: offset = %u\n", ntohs(fhdr->frag_off) & ~0x7);
-
-	if (!(fhdr->frag_off & htons(0xFFF9)) && fq->fragments == NULL) {
-		/*
-		 * this packet is not fragmented. but we handle it as fragmented
-		 * packet.
-		 */
-
-		if (ip6_del_fraghdr(clone) < 0) {
-			spin_unlock(&fq->lock);
-			goto free_clone;
-		}
-			
-		fq->orig_list = orig;
-		ORIG_CB(orig)->next = NULL;
-		fq->reasm = clone;
-		fq->meat = fq->len = skb->len;
-		fq->last_in |= NF_CT_FRAG6_FIRST_IN | NF_CT_FRAG6_LAST_IN |
-			       NF_CT_FRAG6_COMPLETE;
-
-	} else {
-		ret = frag6_queue(conntrack, clone, fhdr);
-		if (ret == -1) {
-			spin_unlock(&fq->lock);
-			goto free_clone;
-		}
-
-		DEBUGP("frag6_packet: meat = %d, len = %d\n", fq->meat, fq->len);
-
-		nf_conntrack_confirm(orig);
-
-		if (fq->last_in != (NF_CT_FRAG6_FIRST_IN | NF_CT_FRAG6_LAST_IN)
-		    || fq->meat != fq->len) {
-			/* not gathered all packet yet */
-			spin_unlock(&fq->lock);
-			/* put conntrack so that it is destroyed when timeout */
-			nf_conntrack_put(orig->nfct);
-			orig->nfct = NULL;
-			ret = NF_STOLEN;
-			goto out;
-		}
-
-		/* all packets have be gatherd. let's reassemble */
-		fq->last_in |= NF_CT_FRAG6_COMPLETE;
-
-		/* at first, reserve original packets. */
-		for (frag = fq->fragments; frag; frag = frag->next) {
-			struct sk_buff *tmp_skb = FRAG6_CB(frag)->orig;
-
-			ORIG_CB(tmp_skb)->next = fq->orig_list;
-			fq->orig_list = tmp_skb;
-		}
-
-		ret = frag6_reasm(conntrack, dev);
-
-		/*
-		 * timer is not needed any more.
-		 * conntrack is not destroyed yet because orig still grub it.
-		 */
-		if (del_timer(&conntrack->timeout))
-			conntrack->timeout.function((unsigned long)conntrack);
-
-		if (ret == -1) {
-			spin_unlock(&fq->lock);
-			nf_conntrack_put(orig->nfct);
-			orig->nfct = NULL;
-			ret = NF_STOLEN;
-			goto out;
-		}
-	}
-	fq->fragments = NULL;
-
-	spin_unlock(&fq->lock);
-
-	set_bit(NF_S_ASSURED_BIT, &conntrack->status);
-	ret = nf_conntrack_in(PF_INET6, hooknum, &fq->reasm);
-
-	spin_lock(&fq->lock);
-	if (ret == NF_ACCEPT) {
-		for (frag = fq->orig_list; frag;) {
-			struct sk_buff *next = ORIG_CB(frag)->next;
-
-			frag->nfct = &conntrack->infos[NF_S_ASSURED];
-			nf_conntrack_get(frag->nfct);
-			frag = next;
-		}
-
-		/* this skb grub conntrack twice. so put once */
-		nf_conntrack_put(orig->nfct);
-	} else {
-		for (frag = fq->orig_list; frag;) {
-			struct sk_buff *next = ORIG_CB(frag)->next;
-
-			if (orig != frag)
-				kfree_skb(frag);
-			frag = next;
-		}
-		fq->orig_list = NULL;
-		ret = NF_DROP;
-	}
-	spin_unlock(&fq->lock);
-
-	return ret;
-
-free_clone:
-	kfree_skb(clone);
-out:
-	return ret;
-}
-
-/* Called when a new connection for this protocol found. */
-static int
-frag6_new(struct nf_conn *conntrack, const struct sk_buff *skb,
-	  unsigned int dataoff)
-{
-	spin_lock_init(&conntrack->proto.frag6.lock);
-	nf_ct_refresh(conntrack, nf_ct_frag6_timeout);
-	return 1;
-}
-
-/* destroy all fragmented packets in this conntrack */
-static void
-frag6_destroy(struct nf_conn *conntrack)
-{
-	struct sk_buff *skb;
-	struct nf_ct_frag6 *fq = &conntrack->proto.frag6;
-
-	/* Send error only if the first segment arrived. */
-	if ((fq->last_in & NF_CT_FRAG6_FIRST_IN) &&
-	    (!(fq->last_in & NF_CT_FRAG6_COMPLETE)) && fq->fragments) {
-		struct net_device *dev = dev_get_by_index(fq->iif);
-
-		/*
-		   But use as source device on which LAST ARRIVED
-		   segment was received.
-		 */
-		if (dev) {
-			fq->fragments->dev = dev;
-			icmpv6_send(fq->fragments, ICMPV6_TIME_EXCEED,
-				    ICMPV6_EXC_FRAGTIME, 0, dev);
-			dev_put(dev);
-		}
-	}
-	skb = fq->fragments;
-	while (skb) {
-		struct sk_buff *next = skb->next;
-
-		frag6_kfree_skb(skb);
-		skb = next;
-	}
-
-	if (fq->reasm)
-		kfree_skb(fq->reasm);
-}
-
-struct nf_conntrack_protocol nf_conntrack_protocol_frag6
-= { { NULL, NULL }, PF_UNSPEC, NEXTHDR_FRAGMENT, "fragment",
-    frag6_pkt_to_tuple, frag6_invert_tuple, frag6_print_tuple,
-    frag6_print_conntrack, frag6_packet, frag6_new, frag6_destroy, NULL, NULL };
-
-EXPORT_SYMBOL(nf_conntrack_protocol_frag6);
-EXPORT_SYMBOL(nf_ct_frag6_get_reasm);
-EXPORT_SYMBOL(nf_ct_frag6_confirm);
-EXPORT_SYMBOL(nf_ct_frag6_reinject);

Modified: trunk/patch-o-matic-ng/nf_conntrack/linux-2.6/net/ipv6/netfilter/nf_conntrack_proto_icmpv6.c
===================================================================
--- trunk/patch-o-matic-ng/nf_conntrack/linux-2.6/net/ipv6/netfilter/nf_conntrack_proto_icmpv6.c	2004-10-24 22:27:31 UTC (rev 3259)
+++ trunk/patch-o-matic-ng/nf_conntrack/linux-2.6/net/ipv6/netfilter/nf_conntrack_proto_icmpv6.c	2004-11-01 07:38:59 UTC (rev 3260)
@@ -19,8 +19,13 @@
 #include <linux/netfilter.h>
 #include <linux/in6.h>
 #include <linux/icmpv6.h>
+#include <linux/ipv6.h>
+#include <net/ipv6.h>
+#include <linux/seq_file.h>
+#include <linux/netfilter_ipv6.h>
 #include <linux/netfilter/nf_conntrack_tuple.h>
 #include <linux/netfilter/nf_conntrack_protocol.h>
+#include <linux/netfilter/nf_conntrack_core.h>
 #include <linux/netfilter_ipv6/nf_conntrack_icmpv6.h>
 
 unsigned long nf_ct_icmpv6_timeout = 30*HZ;
@@ -35,13 +40,14 @@
 			       unsigned int dataoff,
 			       struct nf_conntrack_tuple *tuple)
 {
-	struct icmp6hdr hdr;
+	struct icmp6hdr _hdr, *hp;
 
-	if (skb_copy_bits(skb, dataoff, &hdr, sizeof(hdr)) != 0)
+	hp = skb_header_pointer(skb, dataoff, sizeof(_hdr), &_hdr);
+	if (hp == NULL)
 		return 0;
-	tuple->dst.u.icmp.type = hdr.icmp6_type;
-	tuple->src.u.icmp.id = hdr.icmp6_identifier;
-	tuple->dst.u.icmp.code = hdr.icmp6_code;
+	tuple->dst.u.icmp.type = hp->icmp6_type;
+	tuple->src.u.icmp.id = hp->icmp6_identifier;
+	tuple->dst.u.icmp.code = hp->icmp6_code;
 
 	return 1;
 }
@@ -68,18 +74,18 @@
 }
 
 /* Print out the per-protocol part of the tuple. */
-static unsigned int icmpv6_print_tuple(char *buffer,
-				     const struct nf_conntrack_tuple *tuple)
+static int icmpv6_print_tuple(struct seq_file *s,
+			      const struct nf_conntrack_tuple *tuple)
 {
-	return sprintf(buffer, "type=%u code=%u id=%u ",
-		       tuple->dst.u.icmp.type,
-		       tuple->dst.u.icmp.code,
-		       ntohs(tuple->src.u.icmp.id));
+	return seq_printf(s, "type=%u code=%u id=%u ",
+			  tuple->dst.u.icmp.type,
+			  tuple->dst.u.icmp.code,
+			  ntohs(tuple->src.u.icmp.id));
 }
 
 /* Print out the private part of the conntrack. */
-static unsigned int icmpv6_print_conntrack(char *buffer,
-				     const struct nf_conn *conntrack)
+static int icmpv6_print_conntrack(struct seq_file *s,
+				  const struct nf_conn *conntrack)
 {
 	return 0;
 }
@@ -89,6 +95,7 @@
 		       const struct sk_buff *skb,
 		       unsigned int dataoff,
 		       enum nf_conntrack_info ctinfo,
+		       int pf,
 		       unsigned int hooknum)
 {
 	/* Try to delete connection immediately after all replies:
@@ -101,13 +108,12 @@
 			ct->timeout.function((unsigned long)ct);
 	} else {
 		atomic_inc(&ct->proto.icmp.count);
-		nf_ct_refresh(ct, nf_ct_icmpv6_timeout);
+		nf_ct_refresh_acct(ct, ctinfo, skb, nf_ct_icmpv6_timeout);
 	}
 
 	return NF_ACCEPT;
 }
 
-
 /* Called when a new connection for this protocol found. */
 static int icmpv6_new(struct nf_conn *conntrack,
 		      const struct sk_buff *skb,
@@ -130,9 +136,131 @@
 	return 1;
 }
 
-struct nf_conntrack_protocol nf_conntrack_protocol_icmpv6
-= { { NULL, NULL }, PF_INET6, IPPROTO_ICMPV6, "icmpv6",
-    icmpv6_pkt_to_tuple, icmpv6_invert_tuple, icmpv6_print_tuple,
-    icmpv6_print_conntrack, icmpv6_packet, icmpv6_new, NULL, NULL, NULL };
+extern int
+nf_ct_ipv6_skip_exthdr(struct sk_buff *skb, int start, u8 *nexthdrp, int len);
+extern struct nf_conntrack_l3proto nf_conntrack_l3proto_ipv6;
+static int
+icmpv6_error_message(struct sk_buff *skb,
+		     unsigned int icmp6off,
+		     enum nf_conntrack_info *ctinfo,
+		     unsigned int hooknum)
+{
+	struct nf_conntrack_tuple intuple, origtuple;
+	struct nf_conntrack_tuple_hash *h;
+	struct icmp6hdr _hdr, *hp;
+	unsigned int inip6off;
+	struct nf_conntrack_protocol *inproto;
+	u_int8_t inprotonum;
+	unsigned int inprotoff;
 
+	NF_CT_ASSERT(skb->nfct == NULL);
+
+	hp = skb_header_pointer(skb, icmp6off, sizeof(_hdr), &_hdr);
+	if (hp == NULL) {
+		DEBUGP("icmpv6_error: Can't get ICMPv6 hdr.\n");
+		return NF_ACCEPT;
+	}
+
+	inip6off = icmp6off + sizeof(_hdr);
+	if (skb_copy_bits(skb, inip6off+offsetof(struct ipv6hdr, nexthdr),
+			  &inprotonum, sizeof(inprotonum)) != 0) {
+		DEBUGP("icmpv6_error: Can't get nexthdr in inner IPv6 header.\n");
+		return NF_ACCEPT;
+	}
+	inprotoff = nf_ct_ipv6_skip_exthdr(skb,
+					   inip6off + sizeof(struct ipv6hdr),
+					   &inprotonum,
+					   skb->len - inip6off
+						    - sizeof(struct ipv6hdr));
+
+	if ((inprotoff < 0) || (inprotoff > skb->len) ||
+	    (inprotonum == NEXTHDR_FRAGMENT)) {
+		DEBUGP("icmpv6_error: Can't get protocol header in ICMPv6 payload.\n");
+		return NF_ACCEPT;
+	}
+
+	inproto = nf_ct_find_proto(PF_INET6, inprotonum);
+
+	/* Are they talking about one of our connections? */
+	if (!nf_ct_get_tuple(skb, inip6off, inprotoff, PF_INET6, inprotonum,
+			     &origtuple, &nf_conntrack_l3proto_ipv6, inproto)) {
+		DEBUGP("icmpv6_error: Can't get tuple\n");
+		return NF_ACCEPT;
+	}
+
+	/* Ordinarily, we'd expect the inverted tupleproto, but it's
+	   been preserved inside the ICMP. */
+	if (!nf_ct_invert_tuple(&intuple, &origtuple,
+				&nf_conntrack_l3proto_ipv6, inproto)) {
+		DEBUGP("icmpv6_error: Can't invert tuple\n");
+		return NF_ACCEPT;
+	}
+
+	*ctinfo = NF_CT_RELATED;
+
+	h = nf_conntrack_find_get(&intuple, NULL);
+	if (!h) {
+		DEBUGP("icmpv6_error: no match\n");
+		return NF_ACCEPT;
+	} else {
+		if (NF_CT_DIRECTION(h) == NF_CT_DIR_REPLY)
+			*ctinfo += NF_CT_IS_REPLY;
+	}
+
+	/* Update skb to refer to this connection */
+	skb->nfct = &h->ctrack->ct_general;
+	skb->nfctinfo = *ctinfo;
+	return -NF_ACCEPT;
+}
+
+static int
+icmpv6_error(struct sk_buff *skb, unsigned int dataoff,
+	     enum nf_conntrack_info *ctinfo, int pf, unsigned int hooknum)
+{
+	struct icmp6hdr _ih, *icmp6h;
+
+	icmp6h = skb_header_pointer(skb, dataoff, sizeof(_ih), &_ih);
+	if (icmp6h == NULL) {
+		if (LOG_INVALID(IPPROTO_ICMPV6))
+		nf_log_packet(PF_INET6, 0, skb, NULL, NULL,
+			      "nf_ct_icmpv6: short packet ");
+		return -NF_ACCEPT;
+	}
+
+	if (hooknum != NF_IP6_PRE_ROUTING)
+		goto skipped;
+
+	/* Ignore it if the checksum's bogus. */
+	if (csum_ipv6_magic(&skb->nh.ipv6h->saddr, &skb->nh.ipv6h->daddr,
+			    skb->len - dataoff, IPPROTO_ICMPV6,
+			    skb_checksum(skb, dataoff,
+					 skb->len - dataoff, 0))) {
+		nf_log_packet(PF_INET6, 0, skb, NULL, NULL,
+			      "nf_ct_icmpv6: ICMPv6 checksum failed\n");
+		return -NF_ACCEPT;
+	}
+
+skipped:
+
+	/* is not error message ? */
+	if (icmp6h->icmp6_type >= 128)
+		return NF_ACCEPT;
+
+	return icmpv6_error_message(skb, dataoff, ctinfo, hooknum);
+}
+
+struct nf_conntrack_protocol nf_conntrack_protocol_icmpv6 =
+{
+	.l3proto		= PF_INET6,
+	.proto			= IPPROTO_ICMPV6,
+	.name			= "icmpv6",
+	.pkt_to_tuple		= icmpv6_pkt_to_tuple,
+	.invert_tuple		= icmpv6_invert_tuple,
+	.print_tuple		= icmpv6_print_tuple,
+	.print_conntrack	= icmpv6_print_conntrack,
+	.packet			= icmpv6_packet,
+	.new			= icmpv6_new,
+	.error			= icmpv6_error,
+};
+
 EXPORT_SYMBOL(nf_conntrack_protocol_icmpv6);

Added: trunk/patch-o-matic-ng/nf_conntrack/linux-2.6/net/ipv6/netfilter/nf_conntrack_reasm.c
===================================================================
--- trunk/patch-o-matic-ng/nf_conntrack/linux-2.6/net/ipv6/netfilter/nf_conntrack_reasm.c	2004-10-24 22:27:31 UTC (rev 3259)
+++ trunk/patch-o-matic-ng/nf_conntrack/linux-2.6/net/ipv6/netfilter/nf_conntrack_reasm.c	2004-11-01 07:38:59 UTC (rev 3260)
@@ -0,0 +1,890 @@
+/*
+ * IPv6 fragment reassembly for connection tracking
+ * Linux INET6 implementation
+ *
+ * Copyright (C)2003 USAGI/WIDE Project
+ *
+ * Authors:
+ *	Yasuyuki Kozakai	<yasuyuki.kozakai at toshiba.co.jp>
+ *
+ * Based on: net/ipv6/reassembly.c
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+#include <linux/config.h>
+#include <linux/errno.h>
+#include <linux/types.h>
+#include <linux/string.h>
+#include <linux/socket.h>
+#include <linux/sockios.h>
+#include <linux/jiffies.h>
+#include <linux/net.h>
+#include <linux/list.h>
+#include <linux/netdevice.h>
+#include <linux/in6.h>
+#include <linux/ipv6.h>
+#include <linux/icmpv6.h>
+#include <linux/random.h>
+#include <linux/jhash.h>
+
+#include <net/sock.h>
+#include <net/snmp.h>
+
+#include <net/ipv6.h>
+#include <net/protocol.h>
+#include <net/transp_v6.h>
+#include <net/rawv6.h>
+#include <net/ndisc.h>
+#include <net/addrconf.h>
+#include <linux/sysctl.h>
+#include <linux/netfilter.h>
+#include <linux/netfilter_ipv6.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+
+#if 0
+#define DEBUGP printk
+#else
+#define DEBUGP(format, args...)
+#endif
+
+#define NF_CT_FRAG6_HIGH_THRESH 262144 /* == 256*1024 */
+#define NF_CT_FRAG6_LOW_THRESH 196608  /* == 192*1024 */
+#define NF_CT_FRAG6_TIMEOUT IPV6_FRAG_TIMEOUT
+
+int nf_ct_frag6_high_thresh = 256*1024;
+int nf_ct_frag6_low_thresh = 192*1024;
+int nf_ct_frag6_timeout = IPV6_FRAG_TIMEOUT;
+
+struct nf_ct_frag6_skb_cb
+{
+	struct inet6_skb_parm	h;
+	int			offset;
+	struct sk_buff		*orig;
+};
+
+#define NFCT_FRAG6_CB(skb)	((struct nf_ct_frag6_skb_cb*)((skb)->cb))
+
+struct nf_ct_frag6_queue
+{
+	struct nf_ct_frag6_queue	*next;
+	struct list_head lru_list;		/* lru list member	*/
+
+	__u32			id;		/* fragment id		*/
+	struct in6_addr		saddr;
+	struct in6_addr		daddr;
+
+	spinlock_t		lock;
+	atomic_t		refcnt;
+	struct timer_list	timer;		/* expire timer		*/
+	struct sk_buff		*fragments;
+	int			len;
+	int			meat;
+	struct timeval		stamp;
+	unsigned int		csum;
+	__u8			last_in;	/* has first/last segment arrived? */
+#define COMPLETE		4
+#define FIRST_IN		2
+#define LAST_IN			1
+	__u16			nhoffset;
+	struct nf_ct_frag6_queue	**pprev;
+};
+
+/* Hash table. */
+
+#define FRAG6Q_HASHSZ	64
+
+static struct nf_ct_frag6_queue *nf_ct_frag6_hash[FRAG6Q_HASHSZ];
+static rwlock_t nf_ct_frag6_lock = RW_LOCK_UNLOCKED;
+static u32 nf_ct_frag6_hash_rnd;
+static LIST_HEAD(nf_ct_frag6_lru_list);
+int nf_ct_frag6_nqueues = 0;
+
+static __inline__ void __fq_unlink(struct nf_ct_frag6_queue *fq)
+{
+	if (fq->next)
+		fq->next->pprev = fq->pprev;
+	*fq->pprev = fq->next;
+	list_del(&fq->lru_list);
+	nf_ct_frag6_nqueues--;
+}
+
+static __inline__ void fq_unlink(struct nf_ct_frag6_queue *fq)
+{
+	write_lock(&nf_ct_frag6_lock);
+	__fq_unlink(fq);
+	write_unlock(&nf_ct_frag6_lock);
+}
+
+static unsigned int ip6qhashfn(u32 id, struct in6_addr *saddr,
+			       struct in6_addr *daddr)
+{
+	u32 a, b, c;
+
+	a = saddr->s6_addr32[0];
+	b = saddr->s6_addr32[1];
+	c = saddr->s6_addr32[2];
+
+	a += JHASH_GOLDEN_RATIO;
+	b += JHASH_GOLDEN_RATIO;
+	c += nf_ct_frag6_hash_rnd;
+	__jhash_mix(a, b, c);
+
+	a += saddr->s6_addr32[3];
+	b += daddr->s6_addr32[0];
+	c += daddr->s6_addr32[1];
+	__jhash_mix(a, b, c);
+
+	a += daddr->s6_addr32[2];
+	b += daddr->s6_addr32[3];
+	c += id;
+	__jhash_mix(a, b, c);
+
+	return c & (FRAG6Q_HASHSZ - 1);
+}
+
+static struct timer_list nf_ct_frag6_secret_timer;
+int nf_ct_frag6_secret_interval = 10 * 60 * HZ;
+
+static void nf_ct_frag6_secret_rebuild(unsigned long dummy)
+{
+	unsigned long now = jiffies;
+	int i;
+
+	write_lock(&nf_ct_frag6_lock);
+	get_random_bytes(&nf_ct_frag6_hash_rnd, sizeof(u32));
+	for (i = 0; i < FRAG6Q_HASHSZ; i++) {
+		struct nf_ct_frag6_queue *q;
+
+		q = nf_ct_frag6_hash[i];
+		while (q) {
+			struct nf_ct_frag6_queue *next = q->next;
+			unsigned int hval = ip6qhashfn(q->id,
+						       &q->saddr,
+						       &q->daddr);
+
+			if (hval != i) {
+				/* Unlink. */
+				if (q->next)
+					q->next->pprev = q->pprev;
+				*q->pprev = q->next;
+
+				/* Relink to new hash chain. */
+				if ((q->next = nf_ct_frag6_hash[hval]) != NULL)
+					q->next->pprev = &q->next;
+				nf_ct_frag6_hash[hval] = q;
+				q->pprev = &nf_ct_frag6_hash[hval];
+			}
+
+			q = next;
+		}
+	}
+	write_unlock(&nf_ct_frag6_lock);
+
+	mod_timer(&nf_ct_frag6_secret_timer, now + nf_ct_frag6_secret_interval);
+}
+
+atomic_t nf_ct_frag6_mem = ATOMIC_INIT(0);
+
+/* Memory Tracking Functions. */
+static inline void frag_kfree_skb(struct sk_buff *skb)
+{
+	atomic_sub(skb->truesize, &nf_ct_frag6_mem);
+	if (NFCT_FRAG6_CB(skb)->orig)
+		kfree_skb(NFCT_FRAG6_CB(skb)->orig);
+
+	kfree_skb(skb);
+}
+
+static inline void frag_free_queue(struct nf_ct_frag6_queue *fq)
+{
+	atomic_sub(sizeof(struct nf_ct_frag6_queue), &nf_ct_frag6_mem);
+	kfree(fq);
+}
+
+static inline struct nf_ct_frag6_queue *frag_alloc_queue(void)
+{
+	struct nf_ct_frag6_queue *fq = kmalloc(sizeof(struct nf_ct_frag6_queue), GFP_ATOMIC);
+
+	if (!fq)
+		return NULL;
+	atomic_add(sizeof(struct nf_ct_frag6_queue), &nf_ct_frag6_mem);
+	return fq;
+}
+
+/* Destruction primitives. */
+
+/* Complete destruction of fq. */
+static void nf_ct_frag6_destroy(struct nf_ct_frag6_queue *fq)
+{
+	struct sk_buff *fp;
+
+	BUG_TRAP(fq->last_in&COMPLETE);
+	BUG_TRAP(del_timer(&fq->timer) == 0);
+
+	/* Release all fragment data. */
+	fp = fq->fragments;
+	while (fp) {
+		struct sk_buff *xp = fp->next;
+
+		frag_kfree_skb(fp);
+		fp = xp;
+	}
+
+	frag_free_queue(fq);
+}
+
+static __inline__ void fq_put(struct nf_ct_frag6_queue *fq)
+{
+	if (atomic_dec_and_test(&fq->refcnt))
+		nf_ct_frag6_destroy(fq);
+}
+
+/* Kill fq entry. It is not destroyed immediately,
+ * because caller (and someone more) holds reference count.
+ */
+static __inline__ void fq_kill(struct nf_ct_frag6_queue *fq)
+{
+	if (del_timer(&fq->timer))
+		atomic_dec(&fq->refcnt);
+
+	if (!(fq->last_in & COMPLETE)) {
+		fq_unlink(fq);
+		atomic_dec(&fq->refcnt);
+		fq->last_in |= COMPLETE;
+	}
+}
+
+static void nf_ct_frag6_evictor(void)
+{
+	struct nf_ct_frag6_queue *fq;
+	struct list_head *tmp;
+
+	for (;;) {
+		if (atomic_read(&nf_ct_frag6_mem) <= nf_ct_frag6_low_thresh)
+			return;
+		read_lock(&nf_ct_frag6_lock);
+		if (list_empty(&nf_ct_frag6_lru_list)) {
+			read_unlock(&nf_ct_frag6_lock);
+			return;
+		}
+		tmp = nf_ct_frag6_lru_list.next;
+		fq = list_entry(tmp, struct nf_ct_frag6_queue, lru_list);
+		atomic_inc(&fq->refcnt);
+		read_unlock(&nf_ct_frag6_lock);
+
+		spin_lock(&fq->lock);
+		if (!(fq->last_in&COMPLETE))
+			fq_kill(fq);
+		spin_unlock(&fq->lock);
+
+		fq_put(fq);
+	}
+}
+
+static void nf_ct_frag6_expire(unsigned long data)
+{
+	struct nf_ct_frag6_queue *fq = (struct nf_ct_frag6_queue *) data;
+
+	spin_lock(&fq->lock);
+
+	if (fq->last_in & COMPLETE)
+		goto out;
+
+	fq_kill(fq);
+
+out:
+	spin_unlock(&fq->lock);
+	fq_put(fq);
+}
+
+/* Creation primitives. */
+
+
+static struct nf_ct_frag6_queue *nf_ct_frag6_intern(unsigned int hash,
+					  struct nf_ct_frag6_queue *fq_in)
+{
+	struct nf_ct_frag6_queue *fq;
+
+	write_lock(&nf_ct_frag6_lock);
+#ifdef CONFIG_SMP
+	for (fq = nf_ct_frag6_hash[hash]; fq; fq = fq->next) {
+		if (fq->id == fq_in->id && 
+		    !ipv6_addr_cmp(&fq_in->saddr, &fq->saddr) &&
+		    !ipv6_addr_cmp(&fq_in->daddr, &fq->daddr)) {
+			atomic_inc(&fq->refcnt);
+			write_unlock(&nf_ct_frag6_lock);
+			fq_in->last_in |= COMPLETE;
+			fq_put(fq_in);
+			return fq;
+		}
+	}
+#endif
+	fq = fq_in;
+
+	if (!mod_timer(&fq->timer, jiffies + nf_ct_frag6_timeout))
+		atomic_inc(&fq->refcnt);
+
+	atomic_inc(&fq->refcnt);
+	if ((fq->next = nf_ct_frag6_hash[hash]) != NULL)
+		fq->next->pprev = &fq->next;
+	nf_ct_frag6_hash[hash] = fq;
+	fq->pprev = &nf_ct_frag6_hash[hash];
+	INIT_LIST_HEAD(&fq->lru_list);
+	list_add_tail(&fq->lru_list, &nf_ct_frag6_lru_list);
+	nf_ct_frag6_nqueues++;
+	write_unlock(&nf_ct_frag6_lock);
+	return fq;
+}
+
+
+static struct nf_ct_frag6_queue *
+nf_ct_frag6_create(unsigned int hash, u32 id, struct in6_addr *src,				   struct in6_addr *dst)
+{
+	struct nf_ct_frag6_queue *fq;
+
+	if ((fq = frag_alloc_queue()) == NULL) {
+		DEBUGP("Can't alloc new queue\n");
+		goto oom;
+	}
+
+	memset(fq, 0, sizeof(struct nf_ct_frag6_queue));
+
+	fq->id = id;
+	ipv6_addr_copy(&fq->saddr, src);
+	ipv6_addr_copy(&fq->daddr, dst);
+
+	init_timer(&fq->timer);
+	fq->timer.function = nf_ct_frag6_expire;
+	fq->timer.data = (long) fq;
+	fq->lock = SPIN_LOCK_UNLOCKED;
+	atomic_set(&fq->refcnt, 1);
+
+	return nf_ct_frag6_intern(hash, fq);
+
+oom:
+	return NULL;
+}
+
+static __inline__ struct nf_ct_frag6_queue *
+fq_find(u32 id, struct in6_addr *src, struct in6_addr *dst)
+{
+	struct nf_ct_frag6_queue *fq;
+	unsigned int hash = ip6qhashfn(id, src, dst);
+
+	read_lock(&nf_ct_frag6_lock);
+	for (fq = nf_ct_frag6_hash[hash]; fq; fq = fq->next) {
+		if (fq->id == id && 
+		    !ipv6_addr_cmp(src, &fq->saddr) &&
+		    !ipv6_addr_cmp(dst, &fq->daddr)) {
+			atomic_inc(&fq->refcnt);
+			read_unlock(&nf_ct_frag6_lock);
+			return fq;
+		}
+	}
+	read_unlock(&nf_ct_frag6_lock);
+
+	return nf_ct_frag6_create(hash, id, src, dst);
+}
+
+
+static int nf_ct_frag6_queue(struct nf_ct_frag6_queue *fq, struct sk_buff *skb, 
+			     struct frag_hdr *fhdr, int nhoff)
+{
+	struct sk_buff *prev, *next;
+	int offset, end;
+
+	if (fq->last_in & COMPLETE) {
+		DEBUGP("Allready completed\n");
+		goto err;
+	}
+
+	offset = ntohs(fhdr->frag_off) & ~0x7;
+	end = offset + (ntohs(skb->nh.ipv6h->payload_len) -
+			((u8 *) (fhdr + 1) - (u8 *) (skb->nh.ipv6h + 1)));
+
+	if ((unsigned int)end > IPV6_MAXPLEN) {
+		DEBUGP("offset is too large.\n");
+ 		return -1;
+	}
+
+ 	if (skb->ip_summed == CHECKSUM_HW)
+ 		skb->csum = csum_sub(skb->csum,
+ 				     csum_partial(skb->nh.raw,
+						  (u8*)(fhdr + 1) - skb->nh.raw,
+						  0));
+
+	/* Is this the final fragment? */
+	if (!(fhdr->frag_off & htons(IP6_MF))) {
+		/* If we already have some bits beyond end
+		 * or have different end, the segment is corrupted.
+		 */
+		if (end < fq->len ||
+		    ((fq->last_in & LAST_IN) && end != fq->len)) {
+			DEBUGP("already received last fragment\n");
+			goto err;
+		}
+		fq->last_in |= LAST_IN;
+		fq->len = end;
+	} else {
+		/* Check if the fragment is rounded to 8 bytes.
+		 * Required by the RFC.
+		 */
+		if (end & 0x7) {
+			/* RFC2460 says always send parameter problem in
+			 * this case. -DaveM
+			 */
+			DEBUGP("the end of this fragment is not rounded to 8 bytes.\n");
+			return -1;
+		}
+		if (end > fq->len) {
+			/* Some bits beyond end -> corruption. */
+			if (fq->last_in & LAST_IN) {
+				DEBUGP("last packet already reached.\n");
+				goto err;
+			}
+			fq->len = end;
+		}
+	}
+
+	if (end == offset)
+		goto err;
+
+	/* Point into the IP datagram 'data' part. */
+	if (!pskb_pull(skb, (u8 *) (fhdr + 1) - skb->data)) {
+		DEBUGP("queue: message is too short.\n");
+		goto err;
+	}
+	if (end-offset < skb->len) {
+		if (pskb_trim(skb, end - offset)) {
+			DEBUGP("Can't trim\n");
+			goto err;
+		}
+		if (skb->ip_summed != CHECKSUM_UNNECESSARY)
+			skb->ip_summed = CHECKSUM_NONE;
+	}
+
+	/* Find out which fragments are in front and at the back of us
+	 * in the chain of fragments so far.  We must know where to put
+	 * this fragment, right?
+	 */
+	prev = NULL;
+	for (next = fq->fragments; next != NULL; next = next->next) {
+		if (NFCT_FRAG6_CB(next)->offset >= offset)
+			break;	/* bingo! */
+		prev = next;
+	}
+
+	/* We found where to put this one.  Check for overlap with
+	 * preceding fragment, and, if needed, align things so that
+	 * any overlaps are eliminated.
+	 */
+	if (prev) {
+		int i = (NFCT_FRAG6_CB(prev)->offset + prev->len) - offset;
+
+		if (i > 0) {
+			offset += i;
+			if (end <= offset) {
+				DEBUGP("overlap\n");
+				goto err;
+			}
+			if (!pskb_pull(skb, i)) {
+				DEBUGP("Can't pull\n");
+				goto err;
+			}
+			if (skb->ip_summed != CHECKSUM_UNNECESSARY)
+				skb->ip_summed = CHECKSUM_NONE;
+		}
+	}
+
+	/* Look for overlap with succeeding segments.
+	 * If we can merge fragments, do it.
+	 */
+	while (next && NFCT_FRAG6_CB(next)->offset < end) {
+		/* overlap is 'i' bytes */
+		int i = end - NFCT_FRAG6_CB(next)->offset;
+
+		if (i < next->len) {
+			/* Eat head of the next overlapped fragment
+			 * and leave the loop. The next ones cannot overlap.
+			 */
+			DEBUGP("Eat head of the overlapped parts.: %d", i);
+			if (!pskb_pull(next, i))
+				goto err;
+
+			/* next fragment */
+			NFCT_FRAG6_CB(next)->offset += i;
+			fq->meat -= i;
+			if (next->ip_summed != CHECKSUM_UNNECESSARY)
+				next->ip_summed = CHECKSUM_NONE;
+			break;
+		} else {
+			struct sk_buff *free_it = next;
+
+			/* Old fragmnet is completely overridden with
+			 * new one drop it.
+			 */
+			next = next->next;
+
+			if (prev)
+				prev->next = next;
+			else
+				fq->fragments = next;
+
+			fq->meat -= free_it->len;
+			frag_kfree_skb(free_it);
+		}
+	}
+
+	NFCT_FRAG6_CB(skb)->offset = offset;
+
+	/* Insert this fragment in the chain of fragments. */
+	skb->next = next;
+	if (prev)
+		prev->next = skb;
+	else
+		fq->fragments = skb;
+
+	skb->dev = NULL;
+	fq->stamp = skb->stamp;
+	fq->meat += skb->len;
+	atomic_add(skb->truesize, &nf_ct_frag6_mem);
+
+	/* The first fragment.
+	 * nhoffset is obtained from the first fragment, of course.
+	 */
+	if (offset == 0) {
+		fq->nhoffset = nhoff;
+		fq->last_in |= FIRST_IN;
+	}
+	write_lock(&nf_ct_frag6_lock);
+	list_move_tail(&fq->lru_list, &nf_ct_frag6_lru_list);
+	write_unlock(&nf_ct_frag6_lock);
+	return 0;
+
+err:
+	return -1;
+}
+
+/*
+ *	Check if this packet is complete.
+ *	Returns NULL on failure by any reason, and pointer
+ *	to current nexthdr field in reassembled frame.
+ *
+ *	It is called with locked fq, and caller must check that
+ *	queue is eligible for reassembly i.e. it is not COMPLETE,
+ *	the last and the first frames arrived and all the bits are here.
+ */
+static struct sk_buff *
+nf_ct_frag6_reasm(struct nf_ct_frag6_queue *fq, struct net_device *dev)
+{
+	struct sk_buff *fp, *op, *head = fq->fragments;
+	int    payload_len;
+
+	fq_kill(fq);
+
+	BUG_TRAP(head != NULL);
+	BUG_TRAP(NFCT_FRAG6_CB(head)->offset == 0);
+
+	/* Unfragmented part is taken from the first segment. */
+	payload_len = (head->data - head->nh.raw) - sizeof(struct ipv6hdr) + fq->len - sizeof(struct frag_hdr);
+	if (payload_len > IPV6_MAXPLEN) {
+		DEBUGP("payload len is too large.\n");
+		goto out_oversize;
+	}
+
+	/* Head of list must not be cloned. */
+	if (skb_cloned(head) && pskb_expand_head(head, 0, 0, GFP_ATOMIC)) {
+		DEBUGP("skb is cloned but can't expand head");
+		goto out_oom;
+	}
+
+	/* If the first fragment is fragmented itself, we split
+	 * it to two chunks: the first with data and paged part
+	 * and the second, holding only fragments. */
+	if (skb_shinfo(head)->frag_list) {
+		struct sk_buff *clone;
+		int i, plen = 0;
+
+		if ((clone = alloc_skb(0, GFP_ATOMIC)) == NULL) {
+			DEBUGP("Can't alloc skb\n");
+			goto out_oom;
+		}
+		clone->next = head->next;
+		head->next = clone;
+		skb_shinfo(clone)->frag_list = skb_shinfo(head)->frag_list;
+		skb_shinfo(head)->frag_list = NULL;
+		for (i=0; i<skb_shinfo(head)->nr_frags; i++)
+			plen += skb_shinfo(head)->frags[i].size;
+		clone->len = clone->data_len = head->data_len - plen;
+		head->data_len -= clone->len;
+		head->len -= clone->len;
+		clone->csum = 0;
+		clone->ip_summed = head->ip_summed;
+
+		NFCT_FRAG6_CB(clone)->orig = NULL;
+		atomic_add(clone->truesize, &nf_ct_frag6_mem);
+	}
+
+	/* We have to remove fragment header from datagram and to relocate
+	 * header in order to calculate ICV correctly. */
+	head->nh.raw[fq->nhoffset] = head->h.raw[0];
+	memmove(head->head + sizeof(struct frag_hdr), head->head, 
+		(head->data - head->head) - sizeof(struct frag_hdr));
+	head->mac.raw += sizeof(struct frag_hdr);
+	head->nh.raw += sizeof(struct frag_hdr);
+
+	skb_shinfo(head)->frag_list = head->next;
+	head->h.raw = head->data;
+	skb_push(head, head->data - head->nh.raw);
+	atomic_sub(head->truesize, &nf_ct_frag6_mem);
+
+	for (fp=head->next; fp; fp = fp->next) {
+		head->data_len += fp->len;
+		head->len += fp->len;
+		if (head->ip_summed != fp->ip_summed)
+			head->ip_summed = CHECKSUM_NONE;
+		else if (head->ip_summed == CHECKSUM_HW)
+			head->csum = csum_add(head->csum, fp->csum);
+		head->truesize += fp->truesize;
+		atomic_sub(fp->truesize, &nf_ct_frag6_mem);
+	}
+
+	head->next = NULL;
+	head->dev = dev;
+	head->stamp = fq->stamp;
+	head->nh.ipv6h->payload_len = htons(payload_len);
+
+	/* Yes, and fold redundant checksum back. 8) */
+	if (head->ip_summed == CHECKSUM_HW)
+		head->csum = csum_partial(head->nh.raw, head->h.raw-head->nh.raw, head->csum);
+
+	fq->fragments = NULL;
+
+	/* all original skbs are linked into the NFCT_FRAG6_CB(head).orig */
+	fp = skb_shinfo(head)->frag_list;
+	if (NFCT_FRAG6_CB(fp)->orig == NULL)
+		/* at above code, head skb is divided into two skbs. */
+		fp = fp->next;
+
+	op = NFCT_FRAG6_CB(head)->orig;
+	for (; fp; fp = fp->next) {
+		struct sk_buff *orig = NFCT_FRAG6_CB(fp)->orig;
+
+		op->next = orig;
+		op = orig;
+		NFCT_FRAG6_CB(fp)->orig = NULL;
+	}
+
+	return head;
+
+out_oversize:
+	if (net_ratelimit())
+		printk(KERN_DEBUG "nf_ct_frag6_reasm: payload len = %d\n", payload_len);
+	goto out_fail;
+out_oom:
+	if (net_ratelimit())
+		printk(KERN_DEBUG "nf_ct_frag6_reasm: no memory for reassembly\n");
+out_fail:
+	return NULL;
+}
+
+/*
+ * find the header just before Fragment Header.
+ *
+ * if success return 0 and set ...
+ * (*prevhdrp): the value of "Next Header Field" in the header
+ *		just before Fragment Header.
+ * (*prevhoff): the offset of "Next Header Field" in the header
+ *		just before Fragment Header.
+ * (*fhoff)   : the offset of Fragment Header.
+ *
+ * Based on ipv6_skip_hdr() in net/ipv6/exthdr.c
+ *
+ */
+static int
+find_prev_fhdr(struct sk_buff *skb, u8 *prevhdrp, int *prevhoff, int *fhoff)
+{
+        u8 nexthdr = skb->nh.ipv6h->nexthdr;
+	u8 prev_nhoff = (u8 *)&skb->nh.ipv6h->nexthdr - skb->data;
+	int start = (u8 *)(skb->nh.ipv6h+1) - skb->data;
+	int len = skb->len - start;
+	u8 prevhdr = NEXTHDR_IPV6;
+
+        while (nexthdr != NEXTHDR_FRAGMENT) {
+                struct ipv6_opt_hdr hdr;
+                int hdrlen;
+
+		if (!ipv6_ext_hdr(nexthdr)) {
+			return -1;
+		}
+                if (len < (int)sizeof(struct ipv6_opt_hdr)) {
+			DEBUGP("too short\n");
+			return -1;
+		}
+                if (nexthdr == NEXTHDR_NONE) {
+			DEBUGP("next header is none\n");
+			return -1;
+		}
+                if (skb_copy_bits(skb, start, &hdr, sizeof(hdr)))
+                        BUG();
+                if (nexthdr == NEXTHDR_AUTH)
+                        hdrlen = (hdr.hdrlen+2)<<2;
+                else
+                        hdrlen = ipv6_optlen(&hdr);
+
+		prevhdr = nexthdr;
+		prev_nhoff = start;
+
+                nexthdr = hdr.nexthdr;
+                len -= hdrlen;
+                start += hdrlen;
+        }
+
+	if (len < 0)
+		return -1;
+
+	*prevhdrp = prevhdr;
+	*prevhoff = prev_nhoff;
+	*fhoff = start;
+
+	return 0;
+}
+
+struct sk_buff *nf_ct_frag6_gather(struct sk_buff *skb)
+{
+	struct sk_buff *clone; 
+	struct net_device *dev = skb->dev;
+	struct frag_hdr *fhdr;
+	struct nf_ct_frag6_queue *fq;
+	struct ipv6hdr *hdr;
+	int fhoff, nhoff;
+	u8 prevhdr;
+	struct sk_buff *ret_skb = NULL;
+
+	/* Jumbo payload inhibits frag. header */
+	if (skb->nh.ipv6h->payload_len == 0) {
+		DEBUGP("payload len = 0\n");
+		return skb;
+	}
+
+	if (find_prev_fhdr(skb, &prevhdr, &nhoff, &fhoff) < 0)
+		return skb;
+
+	clone = skb_clone(skb, GFP_ATOMIC);
+	if (clone == NULL) {
+		DEBUGP("Can't clone skb\n");
+		return skb;
+	}
+
+	NFCT_FRAG6_CB(clone)->orig = skb;
+
+	if (!pskb_may_pull(clone, fhoff + sizeof(*fhdr))) {
+		DEBUGP("message is too short.\n");
+		goto ret_orig;
+	}
+
+	clone->h.raw = clone->data + fhoff;
+	hdr = clone->nh.ipv6h;
+	fhdr = (struct frag_hdr *)clone->h.raw;
+
+	if (!(fhdr->frag_off & htons(0xFFF9))) {
+		DEBUGP("Invalid fragment offset\n");
+		/* It is not a fragmented frame */
+		goto ret_orig;
+	}
+
+	if (atomic_read(&nf_ct_frag6_mem) > nf_ct_frag6_high_thresh)
+		nf_ct_frag6_evictor();
+
+	fq = fq_find(fhdr->identification, &hdr->saddr, &hdr->daddr);
+	if (fq == NULL) {
+		DEBUGP("Can't find and can't create new queue\n");
+		goto ret_orig;
+	}
+
+	spin_lock(&fq->lock);
+
+	if (nf_ct_frag6_queue(fq, clone, fhdr, nhoff) < 0) {
+		spin_unlock(&fq->lock);
+		DEBUGP("Can't insert skb to queue\n");
+		fq_put(fq);
+		goto ret_orig;
+	}
+
+	if (fq->last_in == (FIRST_IN|LAST_IN) && fq->meat == fq->len) {
+		ret_skb = nf_ct_frag6_reasm(fq, dev);
+		if (ret_skb == NULL)
+			DEBUGP("Can't reassemble fragmented packets\n");
+	}
+	spin_unlock(&fq->lock);
+
+	fq_put(fq);
+	return ret_skb;
+
+ret_orig:
+	kfree_skb(clone);
+	return skb;
+}
+
+void nf_ct_frag6_output(unsigned int hooknum, struct sk_buff *skb,
+			struct net_device *in, struct net_device *out,
+			int (*okfn)(struct sk_buff *))
+{
+	struct sk_buff *s, *s2;
+
+	for (s = NFCT_FRAG6_CB(skb)->orig; s;) {
+		nf_conntrack_put(s->nfct);
+		nf_conntrack_get(skb->nfct);
+		s->nfct = skb->nfct;
+		s->nfcache = skb->nfcache;
+
+		nf_conntrack_put_reasm(s->reasm);
+		nf_conntrack_get_reasm(skb);
+		s->reasm = skb;
+
+		s2 = s->next;
+		NF_HOOK_THRESH(PF_INET6, hooknum, s, in, out, okfn,
+			       NF_IP6_PRI_CONNTRACK_DEFRAG + 1);
+		s = s2;
+	}
+}
+
+int nf_ct_frag6_kfree_frags(struct sk_buff *skb)
+{
+	struct sk_buff *s, *s2;
+
+	for (s = NFCT_FRAG6_CB(skb)->orig; s; s = s2) {
+
+		s2 = s->next;
+		kfree_skb(s);
+	}
+
+	kfree_skb(skb);
+
+	return 0;
+}
+
+int nf_ct_frag6_init(void)
+{
+	nf_ct_frag6_hash_rnd = (u32) ((num_physpages ^ (num_physpages>>7)) ^
+				   (jiffies ^ (jiffies >> 6)));
+
+	init_timer(&nf_ct_frag6_secret_timer);
+	nf_ct_frag6_secret_timer.function = nf_ct_frag6_secret_rebuild;
+	nf_ct_frag6_secret_timer.expires = jiffies
+					   + nf_ct_frag6_secret_interval;
+	add_timer(&nf_ct_frag6_secret_timer);
+
+	return 0;
+}
+
+void nf_ct_frag6_cleanup(void)
+{
+	del_timer(&nf_ct_frag6_secret_timer);
+	nf_ct_frag6_evictor();
+}

Modified: trunk/patch-o-matic-ng/nf_conntrack/linux-2.6/net/netfilter/Kconfig
===================================================================
--- trunk/patch-o-matic-ng/nf_conntrack/linux-2.6/net/netfilter/Kconfig	2004-10-24 22:27:31 UTC (rev 3259)
+++ trunk/patch-o-matic-ng/nf_conntrack/linux-2.6/net/netfilter/Kconfig	2004-11-01 07:38:59 UTC (rev 3260)
@@ -2,12 +2,66 @@
 # protocol independent part of netfilter configuration
 #
 
+menu "Generic Netfilter Configuration"
+	depends on INET && NETFILTER
+
 config NF_CONNTRACK
-	tristate
+	tristate "Layer 3 Independent Connection tracking (EXPERIMENTAL)"
 	depends on EXPERIMENTAL && NET
 	default n
+	---help---
+	  Connection tracking keeps a record of what packets have passed
+	  through your machine, in order to figure out how they are related
+	  into connections.
 
-config NF_CONNTRACK_FTP
-	tristate
+	  Layer 3 independent connection tracking is experimental scheme
+	  which generalize ip_conntrack to support other layer 3 protocols.
+
+	  To compile it as a module, choose M here.  If unsure, say N.
+
+config NF_CT_ACCT
+	bool "Connection tracking flow accounting"
+	depends on NF_CONNTRACK
+	help
+	  If this option is enabled, the connection tracking code will
+	  keep per-flow packet and byte counters.
+
+	  Those counters can be used for flow-based accounting or the
+	  `connbytes' match.
+
+	  If unsure, say `N'.
+
+config IP_NF_CONNTRACK_MARK
+	bool  'Connection mark tracking support'
+	help
+	  This option enables support for connection marks, used by the
+	  `CONNMARK' target and `connmark' match. Similar to the mark value
+	  of packets, but this mark value is kept in the conntrack session
+	  instead of the individual packets.
+
+config NF_CT_PROTO_SCTP
+	tristate 'SCTP protocol on new connection tracking support (EXPERIMENTAL)'
 	depends on EXPERIMENTAL && NET && NF_CONNTRACK
 	default n
+	help
+	  With this option enabled, the layer 3 independent connection
+	  tracking code will be able to do state tracking on SCTP connections.
+
+	  If you want to compile it as a module, say M here and read
+	  Documentation/modules.txt.  If unsure, say `N'.
+
+config NF_CONNTRACK_FTP
+	tristate "FTP support on new connection tracking (EXPERIMENTAL)"
+	depends on EXPERIMENTAL && NF_CONNTRACK
+	help
+	  Tracking FTP connections is problematic: special helpers are
+	  required for tracking them, and doing masquerading and other forms
+	  of Network Address Translation on them.
+
+	  This is FTP support on Layer 3 independent connection tracking.
+	  Layer 3 independent connection tracking is experimental scheme
+	  which generalize ip_conntrack to support other layer 3 protocols.
+
+	  To compile it as a module, choose M here.  If unsure, say N.
+
+endmenu

Modified: trunk/patch-o-matic-ng/nf_conntrack/linux-2.6/net/netfilter/Makefile
===================================================================
--- trunk/patch-o-matic-ng/nf_conntrack/linux-2.6/net/netfilter/Makefile	2004-10-24 22:27:31 UTC (rev 3259)
+++ trunk/patch-o-matic-ng/nf_conntrack/linux-2.6/net/netfilter/Makefile	2004-11-01 07:38:59 UTC (rev 3260)
@@ -1,4 +1,7 @@
-nf_conntrack-objs	:= nf_conntrack_core.o nf_conntrack_standalone.o nf_conntrack_proto_generic.o nf_conntrack_proto_tcp.o nf_conntrack_proto_udp.o
+nf_conntrack-objs	:= nf_conntrack_core.o nf_conntrack_standalone.o nf_conntrack_l3proto_generic.o nf_conntrack_proto_generic.o nf_conntrack_proto_tcp.o nf_conntrack_proto_udp.o
 
 obj-$(CONFIG_NF_CONNTRACK) += nf_conntrack.o
 obj-$(CONFIG_NF_CONNTRACK_FTP) += nf_conntrack_ftp.o
+
+# SCTP protocol connection tracking
+obj-$(CONFIG_NF_CT_PROTO_SCTP) += nf_conntrack_proto_sctp.o

Modified: trunk/patch-o-matic-ng/nf_conntrack/linux-2.6/net/netfilter/nf_conntrack_core.c
===================================================================
--- trunk/patch-o-matic-ng/nf_conntrack/linux-2.6/net/netfilter/nf_conntrack_core.c	2004-10-24 22:27:31 UTC (rev 3259)
+++ trunk/patch-o-matic-ng/nf_conntrack/linux-2.6/net/netfilter/nf_conntrack_core.c	2004-11-01 07:38:59 UTC (rev 3260)
@@ -31,12 +31,16 @@
 #include <linux/skbuff.h>
 #include <linux/proc_fs.h>
 #include <linux/vmalloc.h>
-#include <net/checksum.h>
 #include <linux/stddef.h>
 #include <linux/slab.h>
 #include <linux/random.h>
 #include <linux/jhash.h>
 #include <linux/err.h>
+#include <linux/percpu.h>
+#include <linux/moduleparam.h>
+#include <linux/kernel.h>
+#include <linux/netdevice.h>
+#include <linux/socket.h>
 
 /* This rwlock protects the main hash table, protocol/helper/expected
    registrations, conntrack timers*/
@@ -61,18 +65,23 @@
 DECLARE_RWLOCK(nf_conntrack_lock);
 DECLARE_RWLOCK(nf_conntrack_expect_tuple_lock);
 
+/* nf_conntrack_standalone needs this */
+atomic_t nf_conntrack_count = ATOMIC_INIT(0);
+
 void (*nf_conntrack_destroyed)(struct nf_conn *conntrack) = NULL;
 LIST_HEAD(nf_conntrack_expect_list);
-LIST_HEAD(l3proto_list);
-LIST_HEAD(protocol_list);
+struct nf_conntrack_protocol **nf_ct_protos[PF_MAX];
+struct nf_conntrack_l3proto *nf_ct_l3protos[PF_MAX];
 static LIST_HEAD(helpers);
 unsigned int nf_conntrack_htable_size = 0;
 int nf_conntrack_max;
-static atomic_t nf_conntrack_count = ATOMIC_INIT(0);
 struct list_head *nf_conntrack_hash;
+static kmem_cache_t *nf_conntrack_expect_cachep;
 struct nf_conn nf_conntrack_untracked;
+unsigned int nf_ct_log_invalid;
 
-extern struct nf_conntrack_protocol nf_conntrack_generic_protocol;
+DEFINE_PER_CPU(struct nf_conntrack_stat, nf_conntrack_stat);
+EXPORT_PER_CPU_SYMBOL(nf_conntrack_stat);
 
 /*
  * This scheme offers various size of "struct nf_conn" dependent on
@@ -91,87 +100,34 @@
 	kmem_cache_t *cachep;
 
 	/* allocated slab cache + modules which uses this slab cache */
-	atomic_t use;
+	int use;
 
 	/* Initialization */
 	int (*init_conntrack)(struct nf_conn *, u_int32_t);
 
-	/* copying */
-	int (*copy_conntrack)(struct nf_conn *, const struct nf_conn *);
 } nf_ct_cache[NF_CT_F_NUM];
 
 /* protect members of nf_ct_cache except of "use" */
 DECLARE_RWLOCK(nf_ct_cache_lock);
 
-static inline int
-proto_cmpfn(const struct nf_conntrack_protocol *curr, u_int16_t l3proto,
-	    u_int8_t protocol)
-{
-	return ((l3proto == curr->l3proto) && (protocol == curr->proto));
-}
+/* This avoids calling kmem_cache_create() with same name simultaneously */
+DECLARE_MUTEX(nf_ct_cache_mutex);
 
+extern struct nf_conntrack_protocol nf_conntrack_generic_protocol;
 struct nf_conntrack_protocol *
-__nf_ct_find_proto(u_int16_t l3proto, u_int8_t protocol)
-{
-	struct nf_conntrack_protocol *p;
-
-	MUST_BE_READ_LOCKED(&nf_conntrack_lock);
-	p = LIST_FIND(&protocol_list, proto_cmpfn,
-		      struct nf_conntrack_protocol *, l3proto, protocol);
-	if (!p)
-		p = &nf_conntrack_generic_protocol;
-
-	return p;
-}
-
-struct nf_conntrack_protocol *
 nf_ct_find_proto(u_int16_t l3proto, u_int8_t protocol)
 {
-	struct nf_conntrack_protocol *p;
+	if (unlikely(nf_ct_protos[l3proto] == NULL))
+		return &nf_conntrack_generic_protocol;
 
-	READ_LOCK(&nf_conntrack_lock);
-	p = __nf_ct_find_proto(l3proto, protocol);
-	READ_UNLOCK(&nf_conntrack_lock);
-	return p;
+	return nf_ct_protos[l3proto][protocol];
 }
 
-static inline int l3proto_cmpfn(const struct nf_conntrack_l3proto *curr,
-			      u_int16_t protocol)
-{
-	return protocol == curr->l3proto;
-}
-
-struct nf_conntrack_l3proto *
-__nf_ct_find_l3proto(u_int16_t protocol)
-{
-	struct nf_conntrack_l3proto *p;
-
-	MUST_BE_READ_LOCKED(&nf_conntrack_lock);
-	p = LIST_FIND(&l3proto_list, l3proto_cmpfn,
-		      struct nf_conntrack_l3proto *, protocol);
-
-	return p;
-}
-
-struct nf_conntrack_l3proto *
-nf_ct_find_l3proto(u_int16_t protocol)
-{
-	struct nf_conntrack_l3proto *p;
-
-	READ_LOCK(&nf_conntrack_lock);
-	p = __nf_ct_find_l3proto(protocol);
-	READ_UNLOCK(&nf_conntrack_lock);
-	return p;
-}
-
 inline void 
 nf_ct_put(struct nf_conn *ct)
 {
 	NF_CT_ASSERT(ct);
-	NF_CT_ASSERT(ct->infos[0].master);
-	/* nf_conntrack_put wants to go via an info struct, so feed it
-	   one at random. */
-	nf_conntrack_put(&ct->infos[0]);
+	nf_conntrack_put(&ct->ct_general);
 }
 
 static int nf_conntrack_hash_rnd_initted;
@@ -193,7 +149,7 @@
 
 	READ_LOCK(&nf_ct_cache_lock);
 
-	if (!nf_ct_cache[features].cachep) {
+	if (!nf_ct_cache[features].use) {
 		DEBUGP("alloc_conntrack: not supported features = 0x%x\n",
 			features);
 		goto out;
@@ -229,212 +185,6 @@
 	kmem_cache_free(nf_ct_cache[features].cachep, conntrack);
 }
 
-/* copy conntrack. don't set timer and don't copy sibling list */
-static int
-copy_conntrack(struct nf_conn *dst, const struct nf_conn *src)
-{
-	int i;
-
-	DEBUGP("copy_conntarck: src=%p, dst=%p\n", src, dst);
-
-	atomic_set(&dst->ct_general.use, atomic_read(&src->ct_general.use));
-	dst->ct_general.destroy = src->ct_general.destroy;
-	memcpy(&dst->tuplehash[NF_CT_DIR_ORIGINAL].tuple,
-	       &src->tuplehash[NF_CT_DIR_ORIGINAL].tuple,
-	       sizeof(struct nf_conntrack_tuple));	
-	dst->tuplehash[NF_CT_DIR_ORIGINAL].ctrack = dst;
-	memcpy(&dst->tuplehash[NF_CT_DIR_REPLY].tuple,
-	       &src->tuplehash[NF_CT_DIR_REPLY].tuple,
-	       sizeof(struct nf_conntrack_tuple));
-	dst->tuplehash[NF_CT_DIR_REPLY].ctrack = dst;
-	dst->status = src->status;
-	init_timer(&dst->timeout);
-	dst->timeout.data = (unsigned long)dst;
-	dst->timeout.function = src->timeout.function;
-	INIT_LIST_HEAD(&dst->sibling_list);
-	dst->expecting = src->expecting;
-	dst->master = src->master;
-	dst->helper = src->helper;
-	for (i=0; i < NF_CT_NUMBER; i++)
-		dst->infos[i].master = &dst->ct_general;
-	dst->proto = src->proto;
-
-	/* copy other parts if needed */
-	if (nf_ct_cache[dst->features].copy_conntrack)
-		return nf_ct_cache[dst->features].copy_conntrack(dst, src);
-
-	return 0;
-}
-
-/* meaningful in only reallocate_conntrack() */
-static int
-replace_conntrack(struct nf_conn *to, struct nf_conn *from)
-{
-	u_int32_t ho, hr;
-	struct list_head *exp_entry;
-	struct nf_conntrack_expect *exp;
-
-	MUST_BE_READ_LOCKED(&nf_conntrack_lock);
-
-	DEBUGP("replace_conntrack: from = %p, to = %p\n", from, to);
-
-	to->timeout.expires = from->timeout.expires;
-
-	/* destroyed soon ? */
-	if (!del_timer(&from->timeout)) {
-		DEBUGP("replace_conntrack: already timer has be expired\n");
-		return -1;
-	}
-
-	ho = hash_conntrack(&from->tuplehash[NF_CT_DIR_ORIGINAL].tuple);
-	hr = hash_conntrack(&from->tuplehash[NF_CT_DIR_REPLY].tuple);
-	LIST_DELETE(&nf_conntrack_hash[ho],
-		    &from->tuplehash[NF_CT_DIR_ORIGINAL]);
-	LIST_DELETE(&nf_conntrack_hash[hr],
-		    &from->tuplehash[NF_CT_DIR_REPLY]);
-
-	WRITE_LOCK(&nf_conntrack_expect_tuple_lock);
-
-	list_for_each(exp_entry, &from->sibling_list) {
-		exp = list_entry(exp_entry, struct nf_conntrack_expect,
-				 expected_list);
-		if (exp->expectant)
-			exp->expectant = to;
-	}
-
-	list_add(&to->sibling_list, &from->sibling_list);
-	list_del(&from->sibling_list);
-
-	if (from->master) {
-		to->master = from->master;
-		to->master->sibling = to;
-	}
-
-	WRITE_UNLOCK(&nf_conntrack_expect_tuple_lock);
-
-	add_timer(&to->timeout);
-
-	list_prepend(&nf_conntrack_hash[ho],
-		     &to->tuplehash[NF_CT_DIR_ORIGINAL]);
-	list_prepend(&nf_conntrack_hash[hr],
-		     &to->tuplehash[NF_CT_DIR_REPLY]);
-
-	return 0;
-}
-
-/* reallocate conntrack from other slab */
-static int reallocate_conntrack(struct nf_conn *conntrack)
-{
-	struct nf_conntrack_l3proto *l3proto;
-	struct nf_conn *tmp_conntrack;
-	u_int32_t features;
-
-	MUST_BE_READ_LOCKED(&nf_conntrack_lock);
-
-	DEBUGP("reallocate_conntrack: conntrack=%p\n", conntrack);
-	/*  find features needed by this conntrack. */
-	l3proto = __nf_ct_find_l3proto(conntrack->tuplehash[NF_CT_DIR_ORIGINAL]
-				       .tuple.src.l3num);
-	NF_CT_ASSERT(l3proto);
-	features = l3proto->get_features(
-			&conntrack->tuplehash[NF_CT_DIR_ORIGINAL].tuple);
-
-	if (nf_ct_find_helper(&conntrack->tuplehash[NF_CT_DIR_REPLY].tuple))
-		features |= NF_CT_F_HELP;
-
-	if (conntrack->features == features) {
-		DEBUGP("reallocate_conntrack: same features required\n");
-		return -1;
-	}
-
-	tmp_conntrack = alloc_conntrack(features);
-	if (!tmp_conntrack) {
-		DEBUGP("reallocate_conntrack: Can't allocate conntrack.\n");
-		return -1;
-	}
-
-	if (copy_conntrack(tmp_conntrack, conntrack) < 0) {
-		DEBUGP("reallocate_conntrack: Can't copy conntrack.\n");
-		free_conntrack(tmp_conntrack);
-		return -1;
-	}
-
-	if (replace_conntrack(tmp_conntrack, conntrack) < 0) {
-		DEBUGP("reallocate_conntrack: Can't replace conntrack.\n");
-		free_conntrack(tmp_conntrack);
-		return -1;
-	}
-
-	free_conntrack(conntrack);
-
-	return 0;
-}
-
-static void
-nf_conntrack_destroy_cache(u_int32_t features)
-{
-	kmem_cache_t *cachep;
-	char *name;
-	int i;
-	struct nf_conntrack_tuple_hash *hash, *next_hash;
-	struct list_head *hash_entry, *next;
-
-	DEBUGP("nf_conntrack_destroy_cache: features=0x%x\n", features);
-	NF_CT_ASSERT(atomic_read(&nf_ct_cache[features].use));
-
-	synchronize_net();
-
-	/* reallocate conntrack from other slab caches. */
-	WRITE_LOCK(&nf_conntrack_lock);
-
-	for (i = 0; i < nf_conntrack_htable_size; i++) {
-		list_for_each_safe(hash_entry, next, &nf_conntrack_hash[i]) {
-			hash = list_entry(hash_entry,
-					  struct nf_conntrack_tuple_hash,
-					  list);
-
-			/* prevent "next" from grabbing inverse tuple_hash of
-			   the same conntrack. */
-			next_hash = list_entry(next,
-					       struct nf_conntrack_tuple_hash,
-					       list);
-			if (hash->ctrack == next_hash->ctrack) {
-				DEBUGP("next conntrack is same as this one.\n");
-				next = next->next;
-			}
-
-			if (hash->ctrack->features != features)
-				continue;
-
-			if (reallocate_conntrack(hash->ctrack) < 0) {
-				DEBUGP("failed to realloc\n");
-				nf_ct_put(hash->ctrack);
-			}
-		}
-	}
-	WRITE_UNLOCK(&nf_conntrack_lock);
-
-	WRITE_LOCK(&nf_ct_cache_lock);
-	cachep = nf_ct_cache[features].cachep;
-	name = nf_ct_cache[features].name;
-	nf_ct_cache[features].cachep = NULL;
-	nf_ct_cache[features].name = NULL;
-	nf_ct_cache[features].init_conntrack = NULL;
-	nf_ct_cache[features].copy_conntrack = NULL;
-	nf_ct_cache[features].size = 0;
-	WRITE_UNLOCK(&nf_ct_cache_lock);
-
-	kmem_cache_destroy(cachep);
-	kfree(name);
-}
-
-static void
-nf_conntrack_put_cache(u_int32_t features)
-{
-	if (atomic_dec_and_test(&nf_ct_cache[features].use))
-		nf_conntrack_destroy_cache(features);
-}
-
 /* Initialize "struct nf_conn" which has spaces for helper */
 static int
 init_conntrack_for_helper(struct nf_conn *conntrack, u_int32_t features)
@@ -447,27 +197,9 @@
 	return 0;
 }
 
-/* copy "struct nf_conn" which has spaces for helper */
-static int
-copy_conntrack_for_helper(struct nf_conn *dst, const struct nf_conn *src)
-{
-	NF_CT_ASSERT(dst->help);
-
-	if (!src->help) {
-		DEBUGP("copy_conntrack_for_helper: helper dependent data is none.\n");
-		return 0;
-	}
-
-	memcpy(dst->help, src->help, sizeof(union nf_conntrack_help));
-	
-	return 0;
-}
-
 int nf_conntrack_register_cache(u_int32_t features, const char *name,
 				size_t size,
-				int (*init)(struct nf_conn *, u_int32_t),
-				int (*copy)(struct nf_conn *,
-					    const struct nf_conn *))
+				int (*init)(struct nf_conn *, u_int32_t))
 {
 	int ret = 0;
 	char *cache_name;
@@ -479,84 +211,108 @@
 	if (features < NF_CT_F_BASIC || features >= NF_CT_F_NUM) {
 		DEBUGP("nf_conntrack_register_cache: invalid features.: 0x%x\n",
 			features);
-		ret = -EINVAL;
-		goto out;
+		return -EINVAL;
 	}
 
+	down(&nf_ct_cache_mutex);
+
+	WRITE_LOCK(&nf_ct_cache_lock);
+	/* e.g: multiple helpers are loaded */
+	if (nf_ct_cache[features].use > 0) {
+		DEBUGP("nf_conntrack_register_cache: already resisterd.\n");
+		if ((!strncmp(nf_ct_cache[features].name, name,
+			      NF_CT_FEATURES_NAMELEN))
+		    && nf_ct_cache[features].size == size
+		    && nf_ct_cache[features].init_conntrack == init) {
+			DEBUGP("nf_conntrack_register_cache: reusing.\n");
+			nf_ct_cache[features].use++;
+			ret = 0;
+		} else
+			ret = -EBUSY;
+
+		WRITE_UNLOCK(&nf_ct_cache_lock);
+		up(&nf_ct_cache_mutex);
+		return ret;
+	}
+	WRITE_UNLOCK(&nf_ct_cache_lock);
+
 	/*
-	 * There are some restrictions to call kmem_cache_create().
-	 * 1. The memory space for name of slab cache must be alive until
-	 *    cache is destroyed.
-	 * 2. kmem_cache_create() can't be called while locking since it may
-	 *    sleep.
+	 * The memory space for name of slab cache must be alive until
+	 * cache is destroyed.
 	 */
 	cache_name = kmalloc(sizeof(char)*NF_CT_FEATURES_NAMELEN, GFP_ATOMIC);
 	if (cache_name == NULL) {
-		DEBUGP("can't alloc for cache_name\n");
+		DEBUGP("nf_conntrack_register_cache: can't alloc cache_name\n");
 		ret = -ENOMEM;
-		goto out;
+		goto out_up_mutex;
 	}
 
-	strlcpy(cache_name, name, NF_CT_FEATURES_NAMELEN);
+	if (strlcpy(cache_name, name, NF_CT_FEATURES_NAMELEN)
+						>= NF_CT_FEATURES_NAMELEN) {
+		printk("nf_conntrack_register_cache: name too long\n");
+		ret = -EINVAL;
+		goto out_free_name;
+	}
+
 	cachep = kmem_cache_create(cache_name, size, 0, SLAB_HWCACHE_ALIGN,
 				   NULL, NULL);
 	if (!cachep) {
-		printk("Can't create slab cache for the features = 0x%x\n",
-			features);
+		printk("nf_conntrack_register_cache: Can't create slab cache "
+		       "for the features = 0x%x\n", features);
 		ret = -ENOMEM;
 		goto out_free_name;
 	}
 
 	WRITE_LOCK(&nf_ct_cache_lock);
-	/* e.g: multiple helpers are loaded */
-	if (nf_ct_cache[features].cachep) {
-		DEBUGP("nf_conntrack_register_cache: already resisterd.\n");
-		if (strncmp(nf_ct_cache[features].name, name,
-			    NF_CT_FEATURES_NAMELEN)
-		    && nf_ct_cache[features].size == size
-		    && nf_ct_cache[features].init_conntrack == init
-		    && nf_ct_cache[features].copy_conntrack == copy) {
-			DEBUGP("nf_conntrack_register_cache: reusing.\n");
-			atomic_inc(&nf_ct_cache[features].use);
-			ret = 0;
-			goto out_unlock;
-		}
-		ret = -EBUSY;
-		goto out_unlock;
-	}
-
-	atomic_set(&nf_ct_cache[features].use, 1);
+	nf_ct_cache[features].use = 1;
 	nf_ct_cache[features].size = size;
 	nf_ct_cache[features].init_conntrack = init;
-	nf_ct_cache[features].copy_conntrack = copy;
 	nf_ct_cache[features].cachep = cachep;
 	nf_ct_cache[features].name = cache_name;
-
 	WRITE_UNLOCK(&nf_ct_cache_lock);
-	return ret;
 
-out_unlock:
-	WRITE_UNLOCK(&nf_ct_cache_lock);
-	kmem_cache_destroy(cachep);
+	goto out_up_mutex;
+
 out_free_name:
 	kfree(cache_name);
-out:
+out_up_mutex:
+	up(&nf_ct_cache_mutex);
 	return ret;
 }
 
-/* slab cache may not be destroyed bacause some conntracks are still alive. */
-void
-nf_conntrack_unregister_cache(u_int32_t features)
+/* FIXME: In the current, only nf_conntrack_cleanup() can call this function. */
+void nf_conntrack_unregister_cache(u_int32_t features)
 {
-	READ_LOCK(&nf_ct_cache_lock);
-	if (!nf_ct_cache[features].cachep) {
-		DEBUGP("nf_conntrack_unregister_cache: not registered features. : 0x%x\n",
-			features);
-		READ_UNLOCK(&nf_ct_cache_lock);
+	kmem_cache_t *cachep;
+	char *name;
+
+	/*
+	 * This assures that kmem_cache_create() isn't called before destroying
+	 * slab cache.
+	 */
+	DEBUGP("nf_conntrack_unregister_cache: 0x%04x\n", features);
+	down(&nf_ct_cache_mutex);
+
+	WRITE_LOCK(&nf_ct_cache_lock);
+	if (--nf_ct_cache[features].use > 0) {
+		WRITE_UNLOCK(&nf_ct_cache_lock);
+		up(&nf_ct_cache_mutex);
 		return;
 	}
-	READ_UNLOCK(&nf_ct_cache_lock);
-	nf_conntrack_put_cache(features);
+	cachep = nf_ct_cache[features].cachep;
+	name = nf_ct_cache[features].name;
+	nf_ct_cache[features].cachep = NULL;
+	nf_ct_cache[features].name = NULL;
+	nf_ct_cache[features].init_conntrack = NULL;
+	nf_ct_cache[features].size = 0;
+	WRITE_UNLOCK(&nf_ct_cache_lock);
+
+	synchronize_net();
+
+	kmem_cache_destroy(cachep);
+	kfree(name);
+
+	up(&nf_ct_cache_mutex);
 }
 
 int
@@ -595,7 +351,6 @@
 	return protocol->invert_tuple(inverse, orig);
 }
 
-
 /* nf_conntrack_expect helper functions */
 
 /* Compare tuple parts depending on mask. */
@@ -610,13 +365,13 @@
 destroy_expect(struct nf_conntrack_expect *exp)
 {
 	DEBUGP("destroy_expect(%p) use=%d\n", exp, atomic_read(&exp->use));
-	NF_CT_ASSERT(atomic_read(&exp->use));
+	NF_CT_ASSERT(atomic_read(&exp->use) == 0);
 	NF_CT_ASSERT(!timer_pending(&exp->timeout));
 
-	kfree(exp);
+	kmem_cache_free(nf_conntrack_expect_cachep, exp);
+	NF_CT_STAT_INC(expect_delete);
 }
 
-
 inline void nf_conntrack_expect_put(struct nf_conntrack_expect *exp)
 {
 	NF_CT_ASSERT(exp);
@@ -684,8 +439,7 @@
 	/* if we are supposed to have a timer, but we can't delete
 	 * it: race condition.  __unexpect_related will
 	 * be calledd by timeout function */
-	if (expect->expectant->helper->timeout
-	    && !del_timer(&expect->timeout))
+	if (expect->expectant->helper->timeout && !del_timer(&expect->timeout))
 		return;
 
 	__unexpect_related(expect);
@@ -779,7 +533,7 @@
 			list_del(&ct->master->expected_list);
 			master = ct->master->expectant;
 		}
-		kfree(ct->master);
+		kmem_cache_free(nf_conntrack_expect_cachep, ct->master);
 	}
 	WRITE_UNLOCK(&nf_conntrack_lock);
 
@@ -789,13 +543,18 @@
 	DEBUGP("destroy_conntrack: returning ct=%p to slab\n", ct);
 	free_conntrack(ct);
 	atomic_dec(&nf_conntrack_count);
+	NF_CT_STAT_INC(delete);
 }
 
 static void death_by_timeout(unsigned long ul_conntrack)
 {
 	struct nf_conn *ct = (void *)ul_conntrack;
 
+
 	WRITE_LOCK(&nf_conntrack_lock);
+	/* Inside lock so preempt is disabled on module removal path.
+	 * Otherwise we can get spurious warnings. */
+	NF_CT_STAT_INC(delete_list);
 	clean_from_lists(ct);
 	WRITE_UNLOCK(&nf_conntrack_lock);
 	nf_ct_put(ct);
@@ -817,13 +576,19 @@
 {
 	struct nf_conntrack_tuple_hash *h;
 	unsigned int hash = hash_conntrack(tuple);
+	/* use per_cpu() to avoid multiple calls to smp_processor_id() */
+	unsigned int cpu = smp_processor_id();
 
 	MUST_BE_READ_LOCKED(&nf_conntrack_lock);
-	h = LIST_FIND(&nf_conntrack_hash[hash],
-		      conntrack_tuple_cmp,
-		      struct nf_conntrack_tuple_hash *,
-		      tuple, ignored_conntrack);
-	return h;
+	list_for_each_entry(h, &nf_conntrack_hash[hash], list) {
+		if (conntrack_tuple_cmp(h, tuple, ignored_conntrack)) {
+			per_cpu(nf_conntrack_stat, cpu).found++;
+			return h;
+		}
+		per_cpu(nf_conntrack_stat, cpu).searched++;
+	}
+
+	return NULL;
 }
 
 /* Find a connection corresponding to a tuple. */
@@ -842,36 +607,15 @@
 	return h;
 }
 
-static inline struct nf_conn *
-__nf_ct_get(struct nf_ct_info *nfct, enum nf_conntrack_info *ctinfo)
+/* Confirm a connection given skb; places it in hash table */
+int
+__nf_conntrack_confirm(struct sk_buff *skb)
 {
-	struct nf_conn *ct
-		= (struct nf_conn *)nfct->master;
-
-	/* ctinfo is the index of the nfct inside the conntrack */
-	*ctinfo = nfct - ct->infos;
-	NF_CT_ASSERT(*ctinfo >= 0 && *ctinfo < NF_CT_NUMBER);
-	return ct;
-}
-
-/* Return conntrack and conntrack_info given skb->nfct->master */
-struct nf_conn *
-nf_ct_get(struct sk_buff *skb, enum nf_conntrack_info *ctinfo)
-{
-	if (skb->nfct) 
-		return __nf_ct_get(skb->nfct, ctinfo);
-	return NULL;
-}
-
-/* Confirm a connection given skb->nfct; places it in hash table */
-static int
-__nf_conntrack_confirm(struct nf_ct_info *nfct)
-{
 	unsigned int hash, repl_hash;
 	struct nf_conn *ct;
 	enum nf_conntrack_info ctinfo;
 
-	ct = __nf_ct_get(nfct, &ctinfo);
+	ct = nf_ct_get(skb, &ctinfo);
 
 	/* ipt_REJECT uses nf_conntrack_attach to attach related
 	   ICMP/TCP RST packets in other direction.  Actual packet
@@ -919,23 +663,16 @@
 		atomic_inc(&ct->ct_general.use);
 		set_bit(NF_S_CONFIRMED_BIT, &ct->status);
 		WRITE_UNLOCK(&nf_conntrack_lock);
+		NF_CT_STAT_INC(insert);
 		return NF_ACCEPT;
 	}
 
 	WRITE_UNLOCK(&nf_conntrack_lock);
+	NF_CT_STAT_INC(insert_failed);
 	DEBUGP("__nf_conntrack_confirm: duplicated conntrack\n");
 	return NF_DROP;
 }
 
-int nf_conntrack_confirm(struct sk_buff *skb)
-{
-
-	if (skb->nfct
-		&& !is_confirmed((struct nf_conn *)skb->nfct->master))
-		return __nf_conntrack_confirm(skb->nfct);
-	return NF_ACCEPT;
-}
-
 /* Returns true if a connection correspondings to the tuple (required
    for NAT). */
 int
@@ -976,6 +713,7 @@
 	if (del_timer(&h->ctrack->timeout)) {
 		death_by_timeout((unsigned long)h->ctrack);
 		dropped = 1;
+		NF_CT_STAT_INC(early_drop);
 	}
 	nf_ct_put(h->ctrack);
 	return dropped;
@@ -987,7 +725,8 @@
 	return nf_ct_tuple_mask_cmp(rtuple, &i->tuple, &i->mask);
 }
 
-struct nf_conntrack_helper *nf_ct_find_helper(const struct nf_conntrack_tuple *tuple)
+struct nf_conntrack_helper *
+nf_ct_find_helper(const struct nf_conntrack_tuple *tuple)
 {
 	return LIST_FIND(&helpers, helper_cmp,
 			 struct nf_conntrack_helper *,
@@ -1007,10 +746,8 @@
 	struct nf_conntrack_tuple repl_tuple;
 	size_t hash;
 	struct nf_conntrack_expect *expected;
-	int i;
-	static unsigned int drop_next;
 	u_int32_t features = 0;
-	struct nf_conntrack_helper *helper;
+	int helper_used = 0;
 
 	if (!nf_conntrack_hash_rnd_initted) {
 		get_random_bytes(&nf_conntrack_hash_rnd, 4);
@@ -1019,15 +756,10 @@
 
 	hash = hash_conntrack(tuple);
 
-	if (nf_conntrack_max &&
-	    atomic_read(&nf_conntrack_count) >= nf_conntrack_max) {
-		/* Try dropping from random chain, or else from the
-		   chain about to put into (in case they're trying to
-		   bomb one hash chain). */
-		unsigned int next = (drop_next++)%nf_conntrack_htable_size;
-
-		if (!early_drop(&nf_conntrack_hash[next])
-		    && !early_drop(&nf_conntrack_hash[hash])) {
+	if (nf_conntrack_max
+	    && atomic_read(&nf_conntrack_count) >= nf_conntrack_max) {
+		/* Try dropping from this hash chain. */
+		if (!early_drop(&nf_conntrack_hash[hash])) {
 			if (net_ratelimit())
 				printk(KERN_WARNING
 				       "nf_conntrack: table full, dropping"
@@ -1043,9 +775,12 @@
 
 	/*  find features needed by this conntrack. */
 	features = l3proto->get_features(tuple);
-	helper = nf_ct_find_helper(&repl_tuple);
-	if (helper != NULL)
+	READ_LOCK(&nf_conntrack_lock);
+	if (nf_ct_find_helper(&repl_tuple) != NULL) {
 		features |= NF_CT_F_HELP;
+		helper_used = 1;
+	}
+	READ_UNLOCK(&nf_conntrack_lock);
 
 	conntrack = alloc_conntrack(features);
 	if (!conntrack) {
@@ -1059,8 +794,6 @@
 	conntrack->tuplehash[NF_CT_DIR_ORIGINAL].ctrack = conntrack;
 	conntrack->tuplehash[NF_CT_DIR_REPLY].tuple = repl_tuple;
 	conntrack->tuplehash[NF_CT_DIR_REPLY].ctrack = conntrack;
-	for (i=0; i < NF_CT_NUMBER; i++)
-		conntrack->infos[i].master = &conntrack->ct_general;
 
 	if (!protocol->new(conntrack, skb, dataoff)) {
 		free_conntrack(conntrack);
@@ -1081,42 +814,62 @@
 			     struct nf_conntrack_expect *, tuple);
 	READ_UNLOCK(&nf_conntrack_expect_tuple_lock);
 
-	/* If master is not in hash table yet (ie. packet hasn't left
-	   this machine yet), how can other end know about expected?
-	   Hence these are not the droids you are looking for (if
-	   master ct never got confirmed, we'd hold a reference to it
-	   and weird things would happen to future packets). */
-	if (expected && !is_confirmed(expected->expectant))
-		expected = NULL;
+	if (expected) {
+		/* If master is not in hash table yet (ie. packet hasn't left
+		   this machine yet), how can other end know about expected?
+		   Hence these are not the droids you are looking for (if
+		   master ct never got confirmed, we'd hold a reference to it
+		   and weird things would happen to future packets). */
+		if (!is_confirmed(expected->expectant)) {
+			/* This avoids timing problem. helper may be unloaded
+			   after allocating conntrack */
+			if (helper_used)
+				conntrack->helper =
+					nf_ct_find_helper(&repl_tuple);
+			goto end;
+		}
 
-	/* Look up the conntrack helper for master connections only */
-	if (!expected)
-		conntrack->helper = helper;
+		/* Expectation is dying... */
+		if (expected->expectant->helper->timeout
+		    && !del_timer(&expected->timeout))
+			goto end;
 
-	/* If the expectation is dying, then this is a loser. */
-	if (expected
-	    && expected->expectant->helper->timeout
-	    && ! del_timer(&expected->timeout))
-		expected = NULL;
-
-	if (expected) {
 		DEBUGP("conntrack: expectation arrives ct=%p exp=%p\n",
 			conntrack, expected);
 		/* Welcome, Mr. Bond.  We've been expecting you... */
-		NF_CT_ASSERT(master_ct(conntrack));
+		NF_CT_ASSERT(expected->expectant);
 		__set_bit(NF_S_EXPECTED_BIT, &conntrack->status);
 		conntrack->master = expected;
 		expected->sibling = conntrack;
+#if CONFIG_NF_CONNTRACK_MARK
+		conntrack->mark = expected->expectant->mark;
+#endif
 		LIST_DELETE(&nf_conntrack_expect_list, expected);
 		expected->expectant->expecting--;
-		nf_conntrack_get(&master_ct(conntrack)->infos[0]);
-	}
+		nf_conntrack_get(&master_ct(conntrack)->ct_general);
+
+		/* this is a braindead... --pablo */
+		atomic_inc(&nf_conntrack_count);
+		WRITE_UNLOCK(&nf_conntrack_lock);
+
+		if (expected->expectfn)
+			expected->expectfn(conntrack);
+
+		NF_CT_STAT_INC(expect_new);
+
+		goto ret;
+	} else  {
+		/* This avoids timing problem. helper may be unloaded
+		   after allocating conntrack */
+		if (helper_used)
+			conntrack->helper = nf_ct_find_helper(&repl_tuple);
+        }
+
+end:	NF_CT_STAT_INC(new);
 	atomic_inc(&nf_conntrack_count);
 	WRITE_UNLOCK(&nf_conntrack_lock);
 
-	if (expected && expected->expectfn)
-		expected->expectfn(conntrack);
-	return &conntrack->tuplehash[NF_CT_DIR_ORIGINAL];
+ret:	return &conntrack->tuplehash[NF_CT_DIR_ORIGINAL];
 }
 
 /* On success, returns conntrack ptr, sets skb->nfct and ctinfo */
@@ -1134,7 +887,8 @@
 	struct nf_conntrack_tuple_hash *h;
 
 	if (!nf_ct_get_tuple(skb, (unsigned int)(skb->nh.raw - skb->data),
-			     dataoff, l3num, protonum, &tuple, l3proto, proto)) {
+			     dataoff, l3num, protonum, &tuple, l3proto,
+			     proto)) {
 		DEBUGP("resolve_normal_ct: Can't get tuple\n");
 		return NULL;
 	}
@@ -1175,7 +929,8 @@
 		}
 		*set_reply = 0;
 	}
-	skb->nfct = &h->ctrack->infos[*ctinfo];
+	skb->nfct = &h->ctrack->ct_general;
+	skb->nfctinfo = *ctinfo;
 	return h->ctrack;
 }
 
@@ -1191,12 +946,15 @@
 	int set_reply;
 	int ret;
 
-	l3proto = nf_ct_find_l3proto((u_int16_t)pf);
-	if (l3proto == NULL) {
-		DEBUGP("Can't find l3 module. pf=%d\n", pf);
+	/* Previously seen (loopback or untracked)?  Ignore. */
+	if ((*pskb)->nfct) {
+		DEBUGP("loopback or untracked, nfct=0x%p\n", (*pskb)->nfct);
+		NF_CT_STAT_INC(ignore);
 		return NF_ACCEPT;
 	}
 
+	l3proto = nf_ct_find_l3proto((u_int16_t)pf);
+	DEBUGP("l3proto = %u\n", pf);
 	if (l3proto->prepare(pskb, hooknum, &dataoff, &protonum, &ret) == 0) {
 		DEBUGP("not prepared to track yet or error occured\n");
 		return ret;
@@ -1205,35 +963,50 @@
 	proto = nf_ct_find_proto((u_int16_t)pf, protonum);
 	DEBUGP("protonum = %u\n", protonum);
 
+	/* It may be an special packet, error, unclean...
+	 * inverse of the return code tells to the netfilter
+	 * core what to do with the packet. */
+	if (proto->error != NULL &&
+	    (ret = proto->error(*pskb, dataoff, &ctinfo, pf, hooknum)) <= 0) {
+		NF_CT_STAT_INC(error);
+		NF_CT_STAT_INC(invalid);
+		return -ret;
+	}
+
 	ct = resolve_normal_ct(*pskb, dataoff, pf, protonum, l3proto, proto,
 			       &set_reply, &ctinfo);
 	if (!ct) {
 		/* Not valid part of a connection */
-		DEBUGP("Not valid part of a connection\n");
+		DEBUGP("nf_conntrack_in: Not valid part of a connection\n");
+		NF_CT_STAT_INC(invalid);
 		return NF_ACCEPT;
 	}
 
 	if (IS_ERR(ct)) {
 		/* Too stressed to deal. */
-		DEBUGP("Can't resolve normal connection\n");
+		DEBUGP("nf_conntrack_in: Can't resolve normal connection\n");
+		NF_CT_STAT_INC(drop);
 		return NF_DROP;
 	}
 
 	NF_CT_ASSERT((*pskb)->nfct);
 
-	ret = proto->packet(ct, *pskb, dataoff, ctinfo, hooknum);
-	if (ret == -1) {
-		/* Invalid */
-		DEBUGP("can't track with proto module\n");
+	ret = proto->packet(ct, *pskb, dataoff, ctinfo, pf, hooknum);
+	if (ret < 0) {
+		/* Invalid: inverse of the return code tells
+		 * the netfilter core what to do */
+		DEBUGP("nf_conntrack_in: Can't track with proto module\n");
 		nf_conntrack_put((*pskb)->nfct);
 		(*pskb)->nfct = NULL;
-		return NF_ACCEPT;
+		NF_CT_STAT_INC(invalid);
+		return -ret;
 	}
 
 	if (ret != NF_DROP && ct->helper) {
 		ret = ct->helper->help(*pskb, dataoff, ct, ctinfo);
 		if (ret == -1) {
 			/* Invalid */
+			NF_CT_STAT_INC(invalid);
 			nf_conntrack_put((*pskb)->nfct);
 			(*pskb)->nfct = NULL;
 			return NF_ACCEPT;
@@ -1318,8 +1091,7 @@
 {
 	struct nf_conntrack_expect *new;
 
-	new = (struct nf_conntrack_expect *)
-		kmalloc(sizeof(struct nf_conntrack_expect), GFP_ATOMIC);
+	new = kmem_cache_alloc(nf_conntrack_expect_cachep, GFP_ATOMIC);
 	if (!new) {
 		DEBUGP("expect_related: OOM allocating expect\n");
 		return NULL;
@@ -1327,6 +1099,7 @@
 
 	/* tuple_cmp compares whole union, we have to initialized cleanly */
 	memset(new, 0, sizeof(struct nf_conntrack_expect));
+	atomic_set(&new->use, 1);
 
 	return new;
 }
@@ -1338,12 +1111,10 @@
 	DEBUGP("new expectation %p of conntrack %p\n", new, related_to);
 	new->expectant = related_to;
 	new->sibling = NULL;
-	atomic_set(&new->use, 1);
 
 	/* add to expected list for this connection */
-	list_add(&new->expected_list, &related_to->sibling_list);
+	list_add_tail(&new->expected_list, &related_to->sibling_list);
 	/* add to global list of expectations */
-
 	list_prepend(&nf_conntrack_expect_list, &new->list);
 	/* add and start timer if required */
 	if (related_to->helper->timeout) {
@@ -1392,12 +1163,12 @@
 		}
 
 		WRITE_UNLOCK(&nf_conntrack_lock);
-		kfree(expect);
+		/* This expectation is not inserted so no need to lock */
+		kmem_cache_free(nf_conntrack_expect_cachep, expect);
 		return -EEXIST;
 
 	} else if (related_to->helper->max_expected && 
 		   related_to->expecting >= related_to->helper->max_expected) {
-		struct list_head *cur_item;
 		/* old == NULL */
 		if (!(related_to->helper->flags & 
 		      NF_CT_HELPER_F_REUSE_EXPECT)) {
@@ -1413,7 +1184,7 @@
 				       related_to->tuplehash[NF_CT_DIR_ORIGINAL].tuple.src.l3num,
 		    	       	       NIP6(*(struct in6_addr *)related_to->tuplehash[NF_CT_DIR_ORIGINAL].tuple.src.u3.all),
 		    	       	       NIP6(*(struct in6_addr *)related_to->tuplehash[NF_CT_DIR_ORIGINAL].tuple.dst.u3.all));
-			kfree(expect);
+			kmem_cache_free(nf_conntrack_expect_cachep, expect);
 			return -EPERM;
 		}
 		DEBUGP("nf_conntrack: max number of expected "
@@ -1426,21 +1197,14 @@
 		       NIP6(*(struct in6_addr *)related_to->tuplehash[NF_CT_DIR_ORIGINAL].tuple.dst.u3.all));
 
 		/* choose the the oldest expectation to evict */
-		list_for_each(cur_item, &related_to->sibling_list) { 
-			struct nf_conntrack_expect *cur;
-
-			cur = list_entry(cur_item, 
-					 struct nf_conntrack_expect,
-					 expected_list);
-			if (cur->sibling == NULL) {
-				old = cur;
+		list_for_each_entry(old, &related_to->sibling_list,
+				    expected_list)
+			if (old->sibling == NULL)
 				break;
-			}
-		}
 
-		/* (!old) cannot happen, since related_to->expecting is the
-		 * number of unconfirmed expects */
-		NF_CT_ASSERT(old);
+		/* We cannot fail since related_to->expecting is the number
+		 * of unconfirmed expectations */
+		NF_CT_ASSERT(old && old->sibling == NULL);
 
 		/* newnat14 does not reuse the real allocated memory
 		 * structures but rather unexpects the old and
@@ -1455,13 +1219,14 @@
 		WRITE_UNLOCK(&nf_conntrack_lock);
 		DEBUGP("expect_related: busy!\n");
 
-		kfree(expect);
+		kmem_cache_free(nf_conntrack_expect_cachep, expect);
 		return -EBUSY;
 	}
 
 out:	nf_conntrack_expect_insert(expect, related_to);
 
 	WRITE_UNLOCK(&nf_conntrack_lock);
+	NF_CT_STAT_INC(expect_create);
 
 	return ret;
 }
@@ -1538,8 +1303,7 @@
 					  sizeof(struct nf_conn)
 					  + sizeof(union nf_conntrack_help)
 					  + __alignof__(union nf_conntrack_help),
-					  init_conntrack_for_helper,
-					  copy_conntrack_for_helper);
+					  init_conntrack_for_helper);
 	if (ret < 0) {
 		printk(KERN_ERR "nf_conntrack_helper_reigster: Unable to create slab cache for conntracks\n");
 		return ret;
@@ -1577,49 +1341,66 @@
 			    struct nf_conntrack_tuple_hash *, me);
 	WRITE_UNLOCK(&nf_conntrack_lock);
 
-	nf_conntrack_unregister_cache(NF_CT_F_HELP);
-
 	/* Someone could be still looking at the helper in a bh. */
 	synchronize_net();
 }
 
-/* Refresh conntrack for this many jiffies. */
-void nf_ct_refresh(struct nf_conn *ct, unsigned long extra_jiffies)
+static inline void ct_add_counters(struct nf_conn *ct,
+				   enum nf_conntrack_info ctinfo,
+				   const struct sk_buff *skb)
 {
+#ifdef CONFIG_NF_CT_ACCT
+	if (skb) {
+		ct->counters[NFCTINFO2DIR(ctinfo)].packets++;
+		/* XXX totlen should be used ? - kozakai */
+		ct->counters[NFCTINFO2DIR(ctinfo)].bytes +=
+			ntohs(skb->len - (unsigned int)(skb->nh.raw
+							- skb->data));
+	}
+#endif
+}
+
+/* Refresh conntrack for this many jiffies and do accounting (if skb != NULL) */
+void nf_ct_refresh_acct(struct nf_conn *ct,
+			enum nf_conntrack_info ctinfo,
+			const struct sk_buff *skb,
+			unsigned long extra_jiffies)
+{
 	NF_CT_ASSERT(ct->timeout.data == (unsigned long)ct);
 
 	/* If not in hash table, timer will not be active yet */
-	if (!is_confirmed(ct))
+	if (!is_confirmed(ct)) {
 		ct->timeout.expires = extra_jiffies;
-	else {
+		ct_add_counters(ct, ctinfo, skb);
+	} else {
 		WRITE_LOCK(&nf_conntrack_lock);
 		/* Need del_timer for race avoidance (may already be dying). */
 		if (del_timer(&ct->timeout)) {
 			ct->timeout.expires = jiffies + extra_jiffies;
 			add_timer(&ct->timeout);
 		}
+		ct_add_counters(ct, ctinfo, skb);
 		WRITE_UNLOCK(&nf_conntrack_lock);
 	}
 }
 
-/* Used by ipt_REJECT. */
-static void __nf_conntrack_attach(struct sk_buff *nskb, struct nf_ct_info *nfct)
+/* Used by ipt_REJECT and ip6t_REJECT. */
+static void __nf_conntrack_attach(struct sk_buff *nskb, struct sk_buff *skb)
 {
 	struct nf_conn *ct;
 	enum nf_conntrack_info ctinfo;
 
-	ct = __nf_ct_get(nfct, &ctinfo);
-
-	/* This ICMP is in reverse direction to the packet which
-	   caused it */
+	/* This ICMP is in reverse direction to the packet which caused it */
+	ct = nf_ct_get(skb, &ctinfo);
 	if (NFCTINFO2DIR(ctinfo) == NF_CT_DIR_ORIGINAL)
 		ctinfo = NF_CT_RELATED + NF_CT_IS_REPLY;
 	else
 		ctinfo = NF_CT_RELATED;
 
-	/* Attach new skbuff, and increment count */
-	nskb->nfct = &ct->infos[ctinfo];
-	atomic_inc(&ct->ct_general.use);
+	/* Attach to new skbuff, and increment count */
+	nskb->nfct = &ct->ct_general;
+	nskb->nfctinfo = ctinfo;
+	nf_conntrack_get(nskb->nfct);
 }
 
 static inline int
@@ -1675,6 +1456,8 @@
    supposed to kill the mall. */
 void nf_conntrack_cleanup(void)
 {
+	int i;
+
 	nf_ct_attach = NULL;
 	/* This makes sure all current packets have passed through
 	   netfilter framework.  Roll on, two-stage module
@@ -1688,12 +1471,20 @@
 		goto i_see_dead_people;
 	}
 
-	nf_conntrack_destroy_cache(NF_CT_F_BASIC);
+	for (i = 0; i < NF_CT_F_NUM; i++) {
+		if (nf_ct_cache[i].use == 0)
+			continue;
+
+		NF_CT_ASSERT(nf_ct_cache[i].use == 1);
+		nf_ct_cache[i].use = 1;
+		nf_conntrack_unregister_cache(i);
+	}
+	kmem_cache_destroy(nf_conntrack_expect_cachep);
 	vfree(nf_conntrack_hash);
 }
 
 static int hashsize;
-MODULE_PARM(hashsize, "i");
+module_param(hashsize, int, 0400);
 
 int __init nf_conntrack_init(void)
 {
@@ -1727,13 +1518,26 @@
 	}
 
 	ret = nf_conntrack_register_cache(NF_CT_F_BASIC, "nf_conntrack:basic",
-					  sizeof(struct nf_conn),
-					  NULL, NULL);
+					  sizeof(struct nf_conn), NULL);
 	if (ret < 0) {
 		printk(KERN_ERR "Unable to create nf_conn slab cache\n");
 		goto err_free_hash;
 	}
 
+	nf_conntrack_expect_cachep = kmem_cache_create("nf_conntrack_expect",
+					sizeof(struct nf_conntrack_expect),
+					0, SLAB_HWCACHE_ALIGN, NULL, NULL);
+	if (!nf_conntrack_expect_cachep) {
+		printk(KERN_ERR "Unable to create nf_expect slab cache\n");
+		goto err_free_conntrack_slab;
+	}
+
+	/* Don't NEED lock here, but good form anyway. */
+	WRITE_LOCK(&nf_conntrack_lock);
+        for (i = 0; i < PF_MAX; i++)
+		nf_ct_l3protos[i] = &nf_conntrack_generic_l3proto;
+        WRITE_UNLOCK(&nf_conntrack_lock);
+
 	for (i = 0; i < nf_conntrack_htable_size; i++)
 		INIT_LIST_HEAD(&nf_conntrack_hash[i]);
 
@@ -1745,14 +1549,11 @@
 	atomic_set(&nf_conntrack_untracked.ct_general.use, 1);
 	/*  - and look it like as a confirmed connection */
 	set_bit(NF_S_CONFIRMED_BIT, &nf_conntrack_untracked.status);
-	/*  - and prepare the ctinfo field for REJECT & NAT. */
-	nf_conntrack_untracked.infos[NF_CT_NEW].master =
-	nf_conntrack_untracked.infos[NF_CT_RELATED].master =
-	nf_conntrack_untracked.infos[NF_CT_RELATED + NF_CT_IS_REPLY].master =
-			&nf_conntrack_untracked.ct_general;
 
 	return ret;
 
+err_free_conntrack_slab:
+	nf_conntrack_unregister_cache(NF_CT_F_BASIC);
 err_free_hash:
 	vfree(nf_conntrack_hash);
 err_unreg_sockopt:

Modified: trunk/patch-o-matic-ng/nf_conntrack/linux-2.6/net/netfilter/nf_conntrack_ftp.c
===================================================================
--- trunk/patch-o-matic-ng/nf_conntrack/linux-2.6/net/netfilter/nf_conntrack_ftp.c	2004-10-24 22:27:31 UTC (rev 3259)
+++ trunk/patch-o-matic-ng/nf_conntrack/linux-2.6/net/netfilter/nf_conntrack_ftp.c	2004-11-01 07:38:59 UTC (rev 3260)
@@ -17,13 +17,13 @@
 
 #include <linux/config.h>
 #include <linux/module.h>
+#include <linux/moduleparam.h>
 #include <linux/netfilter.h>
 #include <linux/ip.h>
 #include <linux/ipv6.h>
 #include <linux/ctype.h>
 #include <net/checksum.h>
 #include <net/tcp.h>
-#include <linux/kernel.h>
 
 #include <linux/netfilter/nf_conntrack.h>
 #include <linux/netfilter_ipv4/lockhelp.h>
@@ -37,18 +37,16 @@
 /* This is slow, but it's simple. --RR */
 static char ftp_buffer[65536];
 
-DECLARE_LOCK(nf_ftp_lock);
+static DECLARE_LOCK(nf_ftp_lock);
 struct module *nf_conntrack_ftp = THIS_MODULE;
 
 #define MAX_PORTS 8
 static int ports[MAX_PORTS];
 static int ports_c;
-#ifdef MODULE_PARM
-MODULE_PARM(ports, "1-" __MODULE_STRING(MAX_PORTS) "i");
-#endif
+module_param_array(ports, int, &ports_c, 0400);
 
 static int loose;
-MODULE_PARM(loose, "i");
+module_param(loose, int, 0600);
 
 #if 0
 #define DEBUGP printk
@@ -247,16 +245,16 @@
 	u_int16_t tmp_port = 0;
 	int i;
 
-	for(i = start; i < dlen; i++) {
+	for (i = start; i < dlen; i++) {
 		/* Finished? */
-		if(data[i] == delim){
+		if (data[i] == delim) {
 			if (tmp_port == 0)
 				break;
 			*port = htons(tmp_port);
 			DEBUGP("get_port: return %d\n", tmp_port);
 			return i + 1;
 		}
-		else if(data[i] >= '0' && data[i] <= '9')
+		else if (data[i] >= '0' && data[i] <= '9')
 			tmp_port = tmp_port*10 + data[i] - '0';
 		else { /* Some other crap */
 			DEBUGP("get_port: invalid char.\n");
@@ -266,7 +264,7 @@
 	return 0;
 }
 
-/* Returns 0, or length of numbers: |1|3ffe::1|6275| or |2|132.235.1.2|6275| */
+/* Returns 0, or length of numbers: |1|132.235.1.2|6275| or |2|3ffe::1|6275| */
 static int try_eprt(const char *data, size_t dlen, struct nf_conntrack_man *cmd,
 		    char term)
 {
@@ -393,7 +391,8 @@
 		enum nf_conntrack_info ctinfo)
 {
 	unsigned int dataoff, datalen;
-	struct tcphdr tcph;
+	struct tcphdr _tcph, *th;
+	char *fb_ptr;
 	u_int32_t old_seq_aft_nl;
 	int old_seq_aft_nl_set, ret;
 	int dir = NFCTINFO2DIR(ctinfo);
@@ -414,10 +413,11 @@
 		return NF_ACCEPT;
 	}
 
-	if (skb_copy_bits(skb, protoff, &tcph, sizeof(tcph)) != 0) 
+	th = skb_header_pointer(skb, protoff, sizeof(_tcph), &_tcph);
+	if (th == NULL)
 		return NF_ACCEPT;
 
-	dataoff = protoff + tcph.doff * 4;
+	dataoff = protoff + th->doff * 4;
 	/* No data? */
 	if (dataoff >= skb->len) {
 		DEBUGP("ftp: dataoff(%u) >= skblen(%u)\n", dataoff, skb->len);
@@ -426,26 +426,27 @@
 	datalen = skb->len - dataoff;
 
 	LOCK_BH(&nf_ftp_lock);
-	skb_copy_bits(skb, dataoff, ftp_buffer, datalen);
+	fb_ptr = skb_header_pointer(skb, dataoff, datalen, ftp_buffer);
+	BUG_ON(fb_ptr == NULL);
 
 	old_seq_aft_nl_set = ct_ftp_info->seq_aft_nl_set[dir];
 	old_seq_aft_nl = ct_ftp_info->seq_aft_nl[dir];
 
 	DEBUGP("conntrack_ftp: datalen %u\n", datalen);
-	if (ftp_buffer[datalen - 1] == '\n') {
+	if (fb_ptr[datalen - 1] == '\n') {
 		DEBUGP("conntrack_ftp: datalen %u ends in \\n\n", datalen);
 		if (!old_seq_aft_nl_set
-		    || after(ntohl(tcph.seq) + datalen, old_seq_aft_nl)) {
+		    || after(ntohl(th->seq) + datalen, old_seq_aft_nl)) {
 			DEBUGP("conntrack_ftp: updating nl to %u\n",
-			       ntohl(tcph.seq) + datalen);
+			       ntohl(th->seq) + datalen);
 			ct_ftp_info->seq_aft_nl[dir] = 
-						ntohl(tcph.seq) + datalen;
+						ntohl(th->seq) + datalen;
 			ct_ftp_info->seq_aft_nl_set[dir] = 1;
 		}
 	}
 
 	if(!old_seq_aft_nl_set ||
-			(ntohl(tcph.seq) != old_seq_aft_nl)) {
+			(ntohl(th->seq) != old_seq_aft_nl)) {
 		DEBUGP("nf_conntrack_ftp_help: wrong seq pos %s(%u)\n",
 		       old_seq_aft_nl_set ? "":"(UNSET) ", old_seq_aft_nl);
 		ret = NF_ACCEPT;
@@ -461,7 +462,7 @@
 	for (i = 0; i < ARRAY_SIZE(search); i++) {
 		if (search[i].dir != dir) continue;
 
-		found = find_pattern(ftp_buffer, datalen,
+		found = find_pattern(fb_ptr, datalen,
 				     search[i].pattern,
 				     search[i].plen,
 				     search[i].skip,
@@ -474,12 +475,12 @@
 	if (found == -1) {
 		/* We don't usually drop packets.  After all, this is
 		   connection tracking, not packet filtering.
-		   However, it is neccessary for accurate tracking in
+		   However, it is necessary for accurate tracking in
 		   this case. */
 		if (net_ratelimit())
 			printk("conntrack_ftp: partial %s %u+%u\n",
 			       search[i].pattern,
-			       ntohl(tcph.seq), datalen);
+			       ntohl(th->seq), datalen);
 		ret = NF_DROP;
 		goto out;
 	} else if (found == 0) { /* No match */
@@ -488,8 +489,8 @@
 	}
 
 	DEBUGP("conntrack_ftp: match `%.*s' (%u bytes at %u)\n",
-	       (int)matchlen, ftp_buffer + matchoff,
-	       matchlen, ntohl(tcph.seq) + matchoff);
+	       (int)matchlen, fb_ptr + matchoff,
+	       matchlen, ntohl(th->seq) + matchoff);
 
 	exp = nf_conntrack_expect_alloc();
 	if (exp == NULL) {
@@ -503,7 +504,7 @@
 	if ((cmd.l3num == ct->tuplehash[dir].tuple.src.l3num) &&
 	    (!memcmp(&cmd.u3.all, &ct->tuplehash[dir].tuple.src.u3.all,
 		     sizeof(cmd.u3.all)))) {
-		exp->seq = ntohl(tcph.seq) + matchoff;
+		exp->seq = ntohl(th->seq) + matchoff;
 		exp_ftp_info->len = matchlen;
 		exp_ftp_info->ftptype = search[i].ftptype;
 		exp_ftp_info->port = ntohs(cmd.u.tcp.port);
@@ -565,66 +566,79 @@
 	return ret;
 }
 
-static struct nf_conntrack_helper ftp[MAX_PORTS];
-static char ftp_names[MAX_PORTS][10];
+static struct nf_conntrack_helper ftp[MAX_PORTS][2];
+static char ftp_names[MAX_PORTS][2][10];
 
-/* Not __exit: called from init() */
-static void fini(void)
+static void __exit fini(void)
 {
-	int i;
+	int i, j;
 	for (i = 0; i < ports_c; i++) {
-		DEBUGP("nf_ct_ftp: unregistering helper for port %d\n",
-				ports[i]);
-		nf_conntrack_helper_unregister(&ftp[i]);
+		for (j = 0; j < 2; j++) {
+			DEBUGP("nf_ct_ftp: unregistering helper for pf: %d "
+			       "port: %d\n",
+				ftp[i][j].tuple.src.l3num, ports[i]);
+			nf_conntrack_helper_unregister(&ftp[i][j]);
+		}
 	}
 }
 
 static int __init init(void)
 {
-	int i, ret;
+	int i, j = -1, ret = 0;
 	char *tmpname;
 
-	if (ports[0] == 0)
-		ports[0] = FTP_PORT;
+	if (ports_c == 0)
+		ports[ports_c++] = FTP_PORT;
 
 	/* FIXME should be configurable whether IPv4 and IPv6 FTP connections
 		 are tracked or not - YK */
-	for (i = 0; (i < MAX_PORTS) && ports[i]; i++) {
+	for (i = 0; i < ports_c; i++) {
 		memset(&ftp[i], 0, sizeof(struct nf_conntrack_helper));
-		ftp[i].tuple.src.u.tcp.port = htons(ports[i]);
-		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].timeout = 0;
-		ftp[i].flags = NF_CT_HELPER_F_REUSE_EXPECT;
-		ftp[i].me = nf_conntrack_ftp;
-		ftp[i].help = help;
 
-		tmpname = &ftp_names[i][0];
-		if (ports[i] == FTP_PORT)
-			sprintf(tmpname, "ftp");
-		else
-			sprintf(tmpname, "ftp-%d", ports[i]);
-		ftp[i].name = tmpname;
+		ftp[i][0].tuple.src.l3num = PF_INET;
+		ftp[i][1].tuple.src.l3num = PF_INET6;
+		for (j = 0; j < 2; j++) {
+			ftp[i][j].tuple.src.u.tcp.port = htons(ports[i]);
+			ftp[i][j].tuple.dst.protonum = IPPROTO_TCP;
+			ftp[i][j].mask.src.u.tcp.port = 0xFFFF;
+			ftp[i][j].mask.dst.protonum = 0xFFFF;
+			ftp[i][j].max_expected = 1;
+			ftp[i][j].timeout = 0;
+			ftp[i][j].flags = NF_CT_HELPER_F_REUSE_EXPECT;
+			ftp[i][j].me = nf_conntrack_ftp;
+			ftp[i][j].help = help;
+			tmpname = &ftp_names[i][j][0];
+			if (ports[i] == FTP_PORT)
+				sprintf(tmpname, "ftp");
+			else
+				sprintf(tmpname, "ftp-%d", ports[i]);
+			ftp[i][j].name = tmpname;
 
-		DEBUGP("nf_ct_ftp: registering helper for port %d\n", 
-				ports[i]);
-		ret = nf_conntrack_helper_register(&ftp[i]);
+			DEBUGP("nf_ct_ftp: registering helper for pf: %d "
+			       "port: %d\n",
+				ftp[i][j].tuple.src.l3num, ports[i]);
+			ret = nf_conntrack_helper_register(&ftp[i][j]);
+			if (ret) {
+				printk("nf_ct_ftp: failed to register helper "
+				       " for pf: %d port: %d\n",
+					ftp[i][j].tuple.src.l3num, ports[i]);
+				goto done;
+			}
+		}
+	}
 
-		if (ret) {
-			fini();
-			return ret;
+done:
+	if (i < ports_c) {
+		for (;i >= 0; i--) {
+			for (; j >= 0; j--)
+				nf_conntrack_helper_unregister(&ftp[i][j]);
+			j = 1;
 		}
-
-		ports_c++;
 	}
-	return 0;
+	return ret;
 }
 
-
 PROVIDES_CONNTRACK(ftp);
-EXPORT_SYMBOL(nf_ftp_lock);
 
 module_init(init);
 module_exit(fini);

Added: trunk/patch-o-matic-ng/nf_conntrack/linux-2.6/net/netfilter/nf_conntrack_l3proto_generic.c
===================================================================
--- trunk/patch-o-matic-ng/nf_conntrack/linux-2.6/net/netfilter/nf_conntrack_l3proto_generic.c	2004-10-24 22:27:31 UTC (rev 3259)
+++ trunk/patch-o-matic-ng/nf_conntrack/linux-2.6/net/netfilter/nf_conntrack_l3proto_generic.c	2004-11-01 07:38:59 UTC (rev 3260)
@@ -0,0 +1,96 @@
+/* (C) 1999-2001 Paul `Rusty' Russell
+ * (C) 2002-2004 Netfilter Core Team <coreteam at netfilter.org>
+ * (C) 2003-2004 USAGI/WIDE Project <http://www.linux-ipv6.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * 16 Dec 2003: Yasuyuki Kozakai <yasuyuki.kozakai at toshiba.co.jp>
+ *	- enable working with L3 protocol independent connection tracking.
+ *
+ * Derived from net/ipv4/netfilter/ip_conntrack_proto_generic.c
+ */
+
+#include <linux/config.h>
+#include <linux/types.h>
+#include <linux/ip.h>
+#include <linux/netfilter.h>
+#include <linux/module.h>
+#include <linux/skbuff.h>
+#include <linux/icmp.h>
+#include <linux/sysctl.h>
+#include <net/ip.h>
+
+#include <linux/netfilter_ipv4.h>
+#include <linux/netfilter/nf_conntrack.h>
+#include <linux/netfilter/nf_conntrack_protocol.h>
+#include <linux/netfilter/nf_conntrack_l3proto.h>
+#include <linux/netfilter/nf_conntrack_core.h>
+#include <linux/netfilter_ipv4/nf_conntrack_ipv4.h>
+
+#if 0
+#define DEBUGP printk
+#else
+#define DEBUGP(format, args...)
+#endif
+
+DECLARE_PER_CPU(struct nf_conntrack_stat, nf_conntrack_stat);
+
+static int generic_pkt_to_tuple(const struct sk_buff *skb, unsigned int nhoff,
+				struct nf_conntrack_tuple *tuple)
+{
+	memset(tuple->src.u3.all, 0, NF_CT_TUPLE_L3SIZE);
+	memset(tuple->dst.u3.all, 0, NF_CT_TUPLE_L3SIZE);
+
+	return 1;
+}
+
+static int generic_invert_tuple(struct nf_conntrack_tuple *tuple,
+			   const struct nf_conntrack_tuple *orig)
+{
+	memset(tuple->src.u3.all, 0, NF_CT_TUPLE_L3SIZE);
+	memset(tuple->dst.u3.all, 0, NF_CT_TUPLE_L3SIZE);
+
+	return 1;
+}
+
+static int generic_print_tuple(struct seq_file *s,
+			    const struct nf_conntrack_tuple *tuple)
+{
+	return 0;
+}
+
+static int generic_print_conntrack(struct seq_file *s,
+				const struct nf_conn *conntrack)
+{
+	return 0;
+}
+
+static int
+generic_prepare(struct sk_buff **pskb, unsigned int hooknum,
+		unsigned int *dataoff, u_int8_t *protonum, int *ret)
+{
+	/* Never track !!! */
+	*ret = NF_ACCEPT;
+	return 0;
+}
+
+
+static u_int32_t generic_get_features(const struct nf_conntrack_tuple *tuple)
+				
+{
+	return NF_CT_F_BASIC;
+}
+
+struct nf_conntrack_l3proto nf_conntrack_generic_l3proto = {
+	.l3proto	 = PF_UNSPEC,
+	.name		 = "unknown",
+	.pkt_to_tuple	 = generic_pkt_to_tuple,
+	.invert_tuple	 = generic_invert_tuple,
+	.print_tuple	 = generic_print_tuple,
+	.print_conntrack = generic_print_conntrack,
+	.prepare	 = generic_prepare,
+	.get_features	 = generic_get_features,
+	.me		 = THIS_MODULE,
+};

Modified: trunk/patch-o-matic-ng/nf_conntrack/linux-2.6/net/netfilter/nf_conntrack_proto_generic.c
===================================================================
--- trunk/patch-o-matic-ng/nf_conntrack/linux-2.6/net/netfilter/nf_conntrack_proto_generic.c	2004-10-24 22:27:31 UTC (rev 3259)
+++ trunk/patch-o-matic-ng/nf_conntrack/linux-2.6/net/netfilter/nf_conntrack_proto_generic.c	2004-11-01 07:38:59 UTC (rev 3260)
@@ -40,15 +40,15 @@
 }
 
 /* Print out the per-protocol part of the tuple. */
-static unsigned int generic_print_tuple(char *buffer,
-					const struct nf_conntrack_tuple *tuple)
+static int generic_print_tuple(struct seq_file *s,
+			       const struct nf_conntrack_tuple *tuple)
 {
 	return 0;
 }
 
 /* Print out the private part of the conntrack. */
-static unsigned int generic_print_conntrack(char *buffer,
-					    const struct nf_conn *state)
+static int generic_print_conntrack(struct seq_file *s,
+				   const struct nf_conn *state)
 {
 	return 0;
 }
@@ -57,10 +57,11 @@
 static int packet(struct nf_conn *conntrack,
 		  const struct sk_buff *skb,
 		  unsigned int dataoff,
-		  enum nf_conntrack_info conntrackinfo,
+		  enum nf_conntrack_info ctinfo,
+		  int pf,
 		  unsigned int hooknum)
 {
-	nf_ct_refresh(conntrack, nf_ct_generic_timeout);
+	nf_ct_refresh_acct(conntrack, ctinfo, skb, nf_ct_generic_timeout);
 	return NF_ACCEPT;
 }
 
@@ -71,7 +72,15 @@
 	return 1;
 }
 
-struct nf_conntrack_protocol nf_conntrack_generic_protocol
-= { { NULL, NULL }, PF_UNSPEC, 0, "unknown",
-    generic_pkt_to_tuple, generic_invert_tuple, generic_print_tuple,
-    generic_print_conntrack, packet, new, NULL, NULL, NULL };
+struct nf_conntrack_protocol nf_conntrack_generic_protocol =
+{
+	.l3proto		= PF_UNSPEC,
+	.proto			= 0,
+	.name			= "unknown",
+	.pkt_to_tuple		= generic_pkt_to_tuple,
+	.invert_tuple		= generic_invert_tuple,
+	.print_tuple		= generic_print_tuple,
+	.print_conntrack	= generic_print_conntrack,
+	.packet			= packet,
+	.new			= new,
+};

Added: trunk/patch-o-matic-ng/nf_conntrack/linux-2.6/net/netfilter/nf_conntrack_proto_sctp.c
===================================================================
--- trunk/patch-o-matic-ng/nf_conntrack/linux-2.6/net/netfilter/nf_conntrack_proto_sctp.c	2004-10-24 22:27:31 UTC (rev 3259)
+++ trunk/patch-o-matic-ng/nf_conntrack/linux-2.6/net/netfilter/nf_conntrack_proto_sctp.c	2004-11-01 07:38:59 UTC (rev 3260)
@@ -0,0 +1,675 @@
+/*
+ * Connection tracking protocol helper module for SCTP.
+ * 
+ * SCTP is defined in RFC 2960. References to various sections in this code 
+ * are to this RFC.
+ * 
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Derived from net/ipv4/ip_conntrack_sctp.c
+ */
+
+/*
+ * Added support for proc manipulation of timeouts.
+ */
+
+#include <linux/types.h>
+#include <linux/sched.h>
+#include <linux/timer.h>
+#include <linux/netfilter.h>
+#include <linux/module.h>
+#include <linux/in.h>
+#include <linux/ip.h>
+#include <linux/sctp.h>
+#include <linux/string.h>
+#include <linux/seq_file.h>
+
+#include <linux/netfilter/nf_conntrack.h>
+#include <linux/netfilter/nf_conntrack_protocol.h>
+#include <linux/netfilter_ipv4/lockhelp.h>
+
+#if 0
+#define DEBUGP(format, ...) printk(format, ## __VA_ARGS__)
+#else
+#define DEBUGP(format, args...)
+#endif
+
+/* Protects conntrack->proto.sctp */
+static DECLARE_RWLOCK(sctp_lock);
+
+/* FIXME: Examine ipfilter's timeouts and conntrack transitions more
+   closely.  They're more complex. --RR 
+
+   And so for me for SCTP :D -Kiran */
+
+static const char *sctp_conntrack_names[] = {
+	"NONE",
+	"CLOSED",
+	"COOKIE_WAIT",
+	"COOKIE_ECHOED",
+	"ESTABLISHED",
+	"SHUTDOWN_SENT",
+	"SHUTDOWN_RECD",
+	"SHUTDOWN_ACK_SENT",
+};
+
+#define SECS  * HZ
+#define MINS  * 60 SECS
+#define HOURS * 60 MINS
+#define DAYS  * 24 HOURS
+
+unsigned long nf_ct_sctp_timeout_closed            =  10 SECS;
+unsigned long nf_ct_sctp_timeout_cookie_wait       =   3 SECS;
+unsigned long nf_ct_sctp_timeout_cookie_echoed     =   3 SECS;
+unsigned long nf_ct_sctp_timeout_established       =   5 DAYS;
+unsigned long nf_ct_sctp_timeout_shutdown_sent     = 300 SECS / 1000;
+unsigned long nf_ct_sctp_timeout_shutdown_recd     = 300 SECS / 1000;
+unsigned long nf_ct_sctp_timeout_shutdown_ack_sent =   3 SECS;
+
+static unsigned long * sctp_timeouts[]
+= { NULL,                                  /* SCTP_CONNTRACK_NONE  */
+    &nf_ct_sctp_timeout_closed,	           /* SCTP_CONNTRACK_CLOSED */
+    &nf_ct_sctp_timeout_cookie_wait,       /* SCTP_CONNTRACK_COOKIE_WAIT */
+    &nf_ct_sctp_timeout_cookie_echoed,     /* SCTP_CONNTRACK_COOKIE_ECHOED */
+    &nf_ct_sctp_timeout_established,       /* SCTP_CONNTRACK_ESTABLISHED */
+    &nf_ct_sctp_timeout_shutdown_sent,     /* SCTP_CONNTRACK_SHUTDOWN_SENT */
+    &nf_ct_sctp_timeout_shutdown_recd,     /* SCTP_CONNTRACK_SHUTDOWN_RECD */
+    &nf_ct_sctp_timeout_shutdown_ack_sent  /* SCTP_CONNTRACK_SHUTDOWN_ACK_SENT */
+ };
+
+#define sNO SCTP_CONNTRACK_NONE
+#define	sCL SCTP_CONNTRACK_CLOSED
+#define	sCW SCTP_CONNTRACK_COOKIE_WAIT
+#define	sCE SCTP_CONNTRACK_COOKIE_ECHOED
+#define	sES SCTP_CONNTRACK_ESTABLISHED
+#define	sSS SCTP_CONNTRACK_SHUTDOWN_SENT
+#define	sSR SCTP_CONNTRACK_SHUTDOWN_RECD
+#define	sSA SCTP_CONNTRACK_SHUTDOWN_ACK_SENT
+#define	sIV SCTP_CONNTRACK_MAX
+
+/* 
+	These are the descriptions of the states:
+
+NOTE: These state names are tantalizingly similar to the states of an 
+SCTP endpoint. But the interpretation of the states is a little different,
+considering that these are the states of the connection and not of an end 
+point. Please note the subtleties. -Kiran
+
+NONE              - Nothing so far.
+COOKIE WAIT       - We have seen an INIT chunk in the original direction, or also 
+                    an INIT_ACK chunk in the reply direction.
+COOKIE ECHOED     - We have seen a COOKIE_ECHO chunk in the original direction.
+ESTABLISHED       - We have seen a COOKIE_ACK in the reply direction.
+SHUTDOWN_SENT     - We have seen a SHUTDOWN chunk in the original direction.
+SHUTDOWN_RECD     - We have seen a SHUTDOWN chunk in the reply directoin.
+SHUTDOWN_ACK_SENT - We have seen a SHUTDOWN_ACK chunk in the direction opposite
+                    to that of the SHUTDOWN chunk.
+CLOSED            - We have seen a SHUTDOWN_COMPLETE chunk in the direction of 
+                    the SHUTDOWN chunk. Connection is closed.
+*/
+
+/* TODO
+ - I have assumed that the first INIT is in the original direction. 
+ This messes things when an INIT comes in the reply direction in CLOSED
+ state.
+ - Check the error type in the reply dir before transitioning from 
+cookie echoed to closed.
+ - Sec 5.2.4 of RFC 2960
+ - Multi Homing support.
+*/
+
+/* SCTP conntrack state transitions */
+static enum sctp_conntrack sctp_conntracks[2][9][SCTP_CONNTRACK_MAX] = {
+	{
+/*	ORIGINAL	*/
+/*                  sNO, sCL, sCW, sCE, sES, sSS, sSR, sSA */
+/* init         */ {sCW, sCW, sCW, sCE, sES, sSS, sSR, sSA},
+/* init_ack     */ {sCL, sCL, sCW, sCE, sES, sSS, sSR, sSA},
+/* abort        */ {sCL, sCL, sCL, sCL, sCL, sCL, sCL, sCL},
+/* shutdown     */ {sCL, sCL, sCW, sCE, sSS, sSS, sSR, sSA},
+/* shutdown_ack */ {sSA, sCL, sCW, sCE, sES, sSA, sSA, sSA},
+/* error        */ {sCL, sCL, sCW, sCE, sES, sSS, sSR, sSA},/* Cant have Stale cookie*/
+/* cookie_echo  */ {sCL, sCL, sCE, sCE, sES, sSS, sSR, sSA},/* 5.2.4 - Big TODO */
+/* cookie_ack   */ {sCL, sCL, sCW, sCE, sES, sSS, sSR, sSA},/* Cant come in orig dir */
+/* shutdown_comp*/ {sCL, sCL, sCW, sCE, sES, sSS, sSR, sCL}
+	},
+	{
+/*	REPLY	*/
+/*                  sNO, sCL, sCW, sCE, sES, sSS, sSR, sSA */
+/* init         */ {sIV, sCL, sCW, sCE, sES, sSS, sSR, sSA},/* INIT in sCL Big TODO */
+/* init_ack     */ {sIV, sCL, sCW, sCE, sES, sSS, sSR, sSA},
+/* abort        */ {sIV, sCL, sCL, sCL, sCL, sCL, sCL, sCL},
+/* shutdown     */ {sIV, sCL, sCW, sCE, sSR, sSS, sSR, sSA},
+/* shutdown_ack */ {sIV, sCL, sCW, sCE, sES, sSA, sSA, sSA},
+/* error        */ {sIV, sCL, sCW, sCL, sES, sSS, sSR, sSA},
+/* cookie_echo  */ {sIV, sCL, sCW, sCE, sES, sSS, sSR, sSA},/* Cant come in reply dir */
+/* cookie_ack   */ {sIV, sCL, sCW, sES, sES, sSS, sSR, sSA},
+/* shutdown_comp*/ {sIV, sCL, sCW, sCE, sES, sSS, sSR, sCL}
+	}
+};
+
+static int sctp_pkt_to_tuple(const struct sk_buff *skb,
+			     unsigned int dataoff,
+			     struct nf_conntrack_tuple *tuple)
+{
+	sctp_sctphdr_t _hdr, *hp;
+
+	DEBUGP(__FUNCTION__);
+	DEBUGP("\n");
+
+	/* Actually only need first 8 bytes. */
+	hp = skb_header_pointer(skb, dataoff, 8, &_hdr);
+	if (hp == NULL)
+		return 0;
+
+	tuple->src.u.sctp.port = hp->source;
+	tuple->dst.u.sctp.port = hp->dest;
+	return 1;
+}
+
+static int sctp_invert_tuple(struct nf_conntrack_tuple *tuple,
+			     const struct nf_conntrack_tuple *orig)
+{
+	DEBUGP(__FUNCTION__);
+	DEBUGP("\n");
+
+	tuple->src.u.sctp.port = orig->dst.u.sctp.port;
+	tuple->dst.u.sctp.port = orig->src.u.sctp.port;
+	return 1;
+}
+
+/* Print out the per-protocol part of the tuple. */
+static int sctp_print_tuple(struct seq_file *s,
+			    const struct nf_conntrack_tuple *tuple)
+{
+	DEBUGP(__FUNCTION__);
+	DEBUGP("\n");
+
+	return seq_printf(s, "sport=%hu dport=%hu ",
+			  ntohs(tuple->src.u.sctp.port),
+			  ntohs(tuple->dst.u.sctp.port));
+}
+
+/* Print out the private part of the conntrack. */
+static int sctp_print_conntrack(struct seq_file *s,
+				const struct nf_conn *conntrack)
+{
+	enum sctp_conntrack state;
+
+	DEBUGP(__FUNCTION__);
+	DEBUGP("\n");
+
+	READ_LOCK(&sctp_lock);
+	state = conntrack->proto.sctp.state;
+	READ_UNLOCK(&sctp_lock);
+
+	return seq_printf(s, "%s ", sctp_conntrack_names[state]);
+}
+
+#define for_each_sctp_chunk(skb, sch, _sch, offset, dataoff, count)	\
+for (offset = dataoff + sizeof(sctp_sctphdr_t), count = 0;		\
+	offset < skb->len &&						\
+	(sch = skb_header_pointer(skb, offset, sizeof(_sch), &_sch));	\
+	offset += (htons(sch->length) + 3) & ~3, count++)
+
+/* Some validity checks to make sure the chunks are fine */
+static int do_basic_checks(struct nf_conn *conntrack,
+			   const struct sk_buff *skb,
+			   unsigned int dataoff,
+			   char *map)
+{
+	u_int32_t offset, count;
+	sctp_chunkhdr_t _sch, *sch;
+	int flag;
+
+	DEBUGP(__FUNCTION__);
+	DEBUGP("\n");
+
+	flag = 0;
+
+	for_each_sctp_chunk (skb, sch, _sch, offset, dataoff, count) {
+		DEBUGP("Chunk Num: %d  Type: %d\n", count, sch->type);
+
+		if (sch->type == SCTP_CID_INIT 
+			|| sch->type == SCTP_CID_INIT_ACK
+			|| sch->type == SCTP_CID_SHUTDOWN_COMPLETE) {
+			flag = 1;
+		}
+
+		/* Cookie Ack/Echo chunks not the first OR 
+		   Init / Init Ack / Shutdown compl chunks not the only chunks */
+		if ((sch->type == SCTP_CID_COOKIE_ACK 
+			|| sch->type == SCTP_CID_COOKIE_ECHO
+			|| flag)
+		     && count !=0 ) {
+			DEBUGP("Basic checks failed\n");
+			return 1;
+		}
+
+		if (map) {
+			set_bit(sch->type, (void *)map);
+		}
+	}
+
+	DEBUGP("Basic checks passed\n");
+	return 0;
+}
+
+static int new_state(enum nf_conntrack_dir dir,
+		     enum sctp_conntrack cur_state,
+		     int chunk_type)
+{
+	int i;
+
+	DEBUGP(__FUNCTION__);
+	DEBUGP("\n");
+
+	DEBUGP("Chunk type: %d\n", chunk_type);
+
+	switch (chunk_type) {
+		case SCTP_CID_INIT: 
+			DEBUGP("SCTP_CID_INIT\n");
+			i = 0; break;
+		case SCTP_CID_INIT_ACK: 
+			DEBUGP("SCTP_CID_INIT_ACK\n");
+			i = 1; break;
+		case SCTP_CID_ABORT: 
+			DEBUGP("SCTP_CID_ABORT\n");
+			i = 2; break;
+		case SCTP_CID_SHUTDOWN: 
+			DEBUGP("SCTP_CID_SHUTDOWN\n");
+			i = 3; break;
+		case SCTP_CID_SHUTDOWN_ACK: 
+			DEBUGP("SCTP_CID_SHUTDOWN_ACK\n");
+			i = 4; break;
+		case SCTP_CID_ERROR: 
+			DEBUGP("SCTP_CID_ERROR\n");
+			i = 5; break;
+		case SCTP_CID_COOKIE_ECHO: 
+			DEBUGP("SCTP_CID_COOKIE_ECHO\n");
+			i = 6; break;
+		case SCTP_CID_COOKIE_ACK: 
+			DEBUGP("SCTP_CID_COOKIE_ACK\n");
+			i = 7; break;
+		case SCTP_CID_SHUTDOWN_COMPLETE: 
+			DEBUGP("SCTP_CID_SHUTDOWN_COMPLETE\n");
+			i = 8; break;
+		default:
+			/* Other chunks like DATA, SACK, HEARTBEAT and
+			its ACK do not cause a change in state */
+			DEBUGP("Unknown chunk type, Will stay in %s\n", 
+						sctp_conntrack_names[cur_state]);
+			return cur_state;
+	}
+
+	DEBUGP("dir: %d   cur_state: %s  chunk_type: %d  new_state: %s\n", 
+			dir, sctp_conntrack_names[cur_state], chunk_type,
+			sctp_conntrack_names[sctp_conntracks[dir][i][cur_state]]);
+
+	return sctp_conntracks[dir][i][cur_state];
+}
+
+/* Returns verdict for packet, or -1 for invalid. */
+static int sctp_packet(struct nf_conn *conntrack,
+		       const struct sk_buff *skb,
+		       unsigned int dataoff,
+		       enum nf_conntrack_info ctinfo,
+		       int pf,
+		       unsigned int hooknum)
+{
+	enum sctp_conntrack newconntrack, oldsctpstate;
+	sctp_sctphdr_t _sctph, *sh;
+	sctp_chunkhdr_t _sch, *sch;
+	u_int32_t offset, count;
+	char map[256 / sizeof (char)] = {0};
+
+	DEBUGP(__FUNCTION__);
+	DEBUGP("\n");
+
+	sh = skb_header_pointer(skb, dataoff, sizeof(_sctph), &_sctph);
+	if (sh == NULL)
+		return -1;
+
+	if (do_basic_checks(conntrack, skb, dataoff, map) != 0)
+		return -1;
+
+	/* Check the verification tag (Sec 8.5) */
+	if (!test_bit(SCTP_CID_INIT, (void *)map)
+		&& !test_bit(SCTP_CID_SHUTDOWN_COMPLETE, (void *)map)
+		&& !test_bit(SCTP_CID_COOKIE_ECHO, (void *)map)
+		&& !test_bit(SCTP_CID_ABORT, (void *)map)
+		&& !test_bit(SCTP_CID_SHUTDOWN_ACK, (void *)map)
+		&& (sh->vtag != conntrack->proto.sctp.vtag[NFCTINFO2DIR(ctinfo)])) {
+		DEBUGP("Verification tag check failed\n");
+		return -1;
+	}
+
+	oldsctpstate = newconntrack = SCTP_CONNTRACK_MAX;
+	for_each_sctp_chunk (skb, sch, _sch, offset, dataoff, count) {
+		WRITE_LOCK(&sctp_lock);
+
+		/* Special cases of Verification tag check (Sec 8.5.1) */
+		if (sch->type == SCTP_CID_INIT) {
+			/* Sec 8.5.1 (A) */
+			if (sh->vtag != 0) {
+				WRITE_UNLOCK(&sctp_lock);
+				return -1;
+			}
+		} else if (sch->type == SCTP_CID_ABORT) {
+			/* Sec 8.5.1 (B) */
+			if (!(sh->vtag == conntrack->proto.sctp.vtag[NFCTINFO2DIR(ctinfo)])
+				&& !(sh->vtag == conntrack->proto.sctp.vtag
+							[1 - NFCTINFO2DIR(ctinfo)])) {
+				WRITE_UNLOCK(&sctp_lock);
+				return -1;
+			}
+		} else if (sch->type == SCTP_CID_SHUTDOWN_COMPLETE) {
+			/* Sec 8.5.1 (C) */
+			if (!(sh->vtag == conntrack->proto.sctp.vtag[NFCTINFO2DIR(ctinfo)])
+				&& !(sh->vtag == conntrack->proto.sctp.vtag
+							[1 - NFCTINFO2DIR(ctinfo)] 
+					&& (sch->flags & 1))) {
+				WRITE_UNLOCK(&sctp_lock);
+				return -1;
+			}
+		} else if (sch->type == SCTP_CID_COOKIE_ECHO) {
+			/* Sec 8.5.1 (D) */
+			if (!(sh->vtag == conntrack->proto.sctp.vtag[NFCTINFO2DIR(ctinfo)])) {
+				WRITE_UNLOCK(&sctp_lock);
+				return -1;
+			}
+		}
+
+		oldsctpstate = conntrack->proto.sctp.state;
+		newconntrack = new_state(NFCTINFO2DIR(ctinfo), oldsctpstate, sch->type);
+
+		/* Invalid */
+		if (newconntrack == SCTP_CONNTRACK_MAX) {
+			DEBUGP("nf_conntrack_sctp: Invalid dir=%i ctype=%u conntrack=%u\n",
+			       NFCTINFO2DIR(ctinfo), sch->type, oldsctpstate);
+			WRITE_UNLOCK(&sctp_lock);
+			return -1;
+		}
+
+		/* If it is an INIT or an INIT ACK note down the vtag */
+		if (sch->type == SCTP_CID_INIT 
+			|| sch->type == SCTP_CID_INIT_ACK) {
+			sctp_inithdr_t _inithdr, *ih;
+
+			ih = skb_header_pointer(skb, offset + sizeof(sctp_chunkhdr_t),
+			                        sizeof(_inithdr), &_inithdr);
+			if (ih == NULL) {
+					WRITE_UNLOCK(&sctp_lock);
+					return -1;
+			}
+			DEBUGP("Setting vtag %x for dir %d\n", 
+					ih->init_tag, NFCTINFO2DIR(ctinfo));
+			conntrack->proto.sctp.vtag[NF_CT_DIR_ORIGINAL] = ih->init_tag;
+		}
+
+		conntrack->proto.sctp.state = newconntrack;
+		WRITE_UNLOCK(&sctp_lock);
+	}
+
+	nf_ct_refresh_acct(conntrack, ctinfo, skb, *sctp_timeouts[newconntrack]);
+
+	if (oldsctpstate == SCTP_CONNTRACK_COOKIE_ECHOED
+		&& NFCTINFO2DIR(ctinfo) == NF_CT_DIR_REPLY
+		&& newconntrack == SCTP_CONNTRACK_ESTABLISHED) {
+		DEBUGP("Setting assured bit\n");
+		set_bit(NF_S_ASSURED_BIT, &conntrack->status);
+	}
+
+	return NF_ACCEPT;
+}
+
+/* Called when a new connection for this protocol found. */
+static int sctp_new(struct nf_conn *conntrack, const struct sk_buff *skb,
+		    unsigned int dataoff)
+{
+	enum sctp_conntrack newconntrack;
+	sctp_sctphdr_t _sctph, *sh;
+	sctp_chunkhdr_t _sch, *sch;
+	u_int32_t offset, count;
+	char map[256 / sizeof (char)] = {0};
+
+	DEBUGP(__FUNCTION__);
+	DEBUGP("\n");
+
+	sh = skb_header_pointer(skb, dataoff, sizeof(_sctph), &_sctph);
+	if (sh == NULL)
+		return 0;
+
+	if (do_basic_checks(conntrack, skb, dataoff, map) != 0)
+		return 0;
+
+	/* If an OOTB packet has any of these chunks discard (Sec 8.4) */
+	if ((test_bit (SCTP_CID_ABORT, (void *)map))
+		|| (test_bit (SCTP_CID_SHUTDOWN_COMPLETE, (void *)map))
+		|| (test_bit (SCTP_CID_COOKIE_ACK, (void *)map))) {
+		return 0;
+	}
+
+	newconntrack = SCTP_CONNTRACK_MAX;
+	for_each_sctp_chunk (skb, sch, _sch, offset, dataoff, count) {
+		/* Don't need lock here: this conntrack not in circulation yet */
+		newconntrack = new_state(NF_CT_DIR_ORIGINAL, 
+					 SCTP_CONNTRACK_NONE, sch->type);
+
+		/* Invalid: delete conntrack */
+		if (newconntrack == SCTP_CONNTRACK_MAX) {
+			DEBUGP("nf_conntrack_sctp: invalid new deleting.\n");
+			return 0;
+		}
+
+		/* Copy the vtag into the state info */
+		if (sch->type == SCTP_CID_INIT) {
+			if (sh->vtag == 0) {
+				sctp_inithdr_t _inithdr, *ih;
+
+				ih = skb_header_pointer(skb, offset + sizeof(sctp_chunkhdr_t),
+				                        sizeof(_inithdr), &_inithdr);
+				if (ih == NULL)
+					return 0;
+
+				DEBUGP("Setting vtag %x for new conn\n", 
+					ih->init_tag);
+
+				conntrack->proto.sctp.vtag[NF_CT_DIR_REPLY] = 
+								ih->init_tag;
+			} else {
+				/* Sec 8.5.1 (A) */
+				return 0;
+			}
+		}
+		/* If it is a shutdown ack OOTB packet, we expect a return
+		   shutdown complete, otherwise an ABORT Sec 8.4 (5) and (8) */
+		else {
+			DEBUGP("Setting vtag %x for new conn OOTB\n", 
+				sh->vtag);
+			conntrack->proto.sctp.vtag[NF_CT_DIR_REPLY] = sh->vtag;
+		}
+
+		conntrack->proto.sctp.state = newconntrack;
+	}
+
+	return 1;
+}
+
+static int sctp_exp_matches_pkt(struct nf_conntrack_expect *exp,
+				const struct sk_buff *skb,
+				unsigned int dataoff)
+{
+	/* To be implemented */
+	return 0;
+}
+
+struct nf_conntrack_protocol nf_conntrack_protocol_sctp4 = { 
+	.l3proto	 = PF_INET,
+	.proto 		 = IPPROTO_SCTP, 
+	.name 		 = "sctp",
+	.pkt_to_tuple 	 = sctp_pkt_to_tuple, 
+	.invert_tuple 	 = sctp_invert_tuple, 
+	.print_tuple 	 = sctp_print_tuple, 
+	.print_conntrack = sctp_print_conntrack,
+	.packet 	 = sctp_packet, 
+	.new 		 = sctp_new, 
+	.destroy 	 = NULL, 
+	.exp_matches_pkt = sctp_exp_matches_pkt, 
+	.me 		 = THIS_MODULE 
+};
+
+struct nf_conntrack_protocol nf_conntrack_protocol_sctp6 = { 
+	.l3proto	 = PF_INET6,
+	.proto 		 = IPPROTO_SCTP, 
+	.name 		 = "sctp",
+	.pkt_to_tuple 	 = sctp_pkt_to_tuple, 
+	.invert_tuple 	 = sctp_invert_tuple, 
+	.print_tuple 	 = sctp_print_tuple, 
+	.print_conntrack = sctp_print_conntrack,
+	.packet 	 = sctp_packet, 
+	.new 		 = sctp_new, 
+	.destroy 	 = NULL, 
+	.exp_matches_pkt = sctp_exp_matches_pkt, 
+	.me 		 = THIS_MODULE 
+};
+
+#ifdef CONFIG_SYSCTL
+static ctl_table nf_ct_sysctl_table[] = {
+	{
+		.ctl_name	= NET_NF_CONNTRACK_SCTP_TIMEOUT_CLOSED,
+		.procname	= "nf_conntrack_sctp_timeout_closed",
+		.data		= &nf_ct_sctp_timeout_closed,
+		.maxlen		= sizeof(unsigned int),
+		.mode		= 0644,
+		.proc_handler	= &proc_dointvec_jiffies,
+	},
+	{
+		.ctl_name	= NET_NF_CONNTRACK_SCTP_TIMEOUT_COOKIE_WAIT,
+		.procname	= "nf_conntrack_sctp_timeout_cookie_wait",
+		.data		= &nf_ct_sctp_timeout_cookie_wait,
+		.maxlen		= sizeof(unsigned int),
+		.mode		= 0644,
+		.proc_handler	= &proc_dointvec_jiffies,
+	},
+	{
+		.ctl_name	= NET_NF_CONNTRACK_SCTP_TIMEOUT_COOKIE_ECHOED,
+		.procname	= "nf_conntrack_sctp_timeout_cookie_echoed",
+		.data		= &nf_ct_sctp_timeout_cookie_echoed,
+		.maxlen		= sizeof(unsigned int),
+		.mode		= 0644,
+		.proc_handler	= &proc_dointvec_jiffies,
+	},
+	{
+		.ctl_name	= NET_NF_CONNTRACK_SCTP_TIMEOUT_ESTABLISHED,
+		.procname	= "nf_conntrack_sctp_timeout_established",
+		.data		= &nf_ct_sctp_timeout_established,
+		.maxlen		= sizeof(unsigned int),
+		.mode		= 0644,
+		.proc_handler	= &proc_dointvec_jiffies,
+	},
+	{
+		.ctl_name	= NET_NF_CONNTRACK_SCTP_TIMEOUT_SHUTDOWN_SENT,
+		.procname	= "nf_conntrack_sctp_timeout_shutdown_sent",
+		.data		= &nf_ct_sctp_timeout_shutdown_sent,
+		.maxlen		= sizeof(unsigned int),
+		.mode		= 0644,
+		.proc_handler	= &proc_dointvec_jiffies,
+	},
+	{
+		.ctl_name	= NET_NF_CONNTRACK_SCTP_TIMEOUT_SHUTDOWN_RECD,
+		.procname	= "nf_conntrack_sctp_timeout_shutdown_recd",
+		.data		= &nf_ct_sctp_timeout_shutdown_recd,
+		.maxlen		= sizeof(unsigned int),
+		.mode		= 0644,
+		.proc_handler	= &proc_dointvec_jiffies,
+	},
+	{
+		.ctl_name	= NET_NF_CONNTRACK_SCTP_TIMEOUT_SHUTDOWN_ACK_SENT,
+		.procname	= "nf_conntrack_sctp_timeout_shutdown_ack_sent",
+		.data		= &nf_ct_sctp_timeout_shutdown_ack_sent,
+		.maxlen		= sizeof(unsigned int),
+		.mode		= 0644,
+		.proc_handler	= &proc_dointvec_jiffies,
+	},
+	{ .ctl_name = 0 }
+};
+
+static ctl_table nf_ct_netfilter_table[] = {
+	{
+		.ctl_name	= NET_NETFILTER,
+		.procname	= "netfilter",
+		.mode		= 0555,
+		.child		= nf_ct_sysctl_table,
+	},
+	{ .ctl_name = 0 }
+};
+
+static ctl_table nf_ct_net_table[] = {
+	{
+		.ctl_name	= CTL_NET,
+		.procname	= "net",
+		.mode		= 0555, 
+		.child		= nf_ct_netfilter_table,
+	},
+	{ .ctl_name = 0 }
+};
+
+static struct ctl_table_header *nf_ct_sysctl_header;
+#endif
+
+int __init init(void)
+{
+	int ret;
+
+	ret = nf_conntrack_protocol_register(&nf_conntrack_protocol_sctp4);
+	if (ret) {
+		printk("nf_conntrack_proto_sctp4: protocol register failed\n");
+		goto out;
+	}
+	ret = nf_conntrack_protocol_register(&nf_conntrack_protocol_sctp6);
+	if (ret) {
+		printk("nf_conntrack_proto_sctp6: protocol register failed\n");
+		goto cleanup_sctp4;
+	}
+
+#ifdef CONFIG_SYSCTL
+	nf_ct_sysctl_header = register_sysctl_table(nf_ct_net_table, 0);
+	if (nf_ct_sysctl_header == NULL) {
+		printk("nf_conntrack_proto_sctp: can't register to sysctl.\n");
+		goto cleanup;
+	}
+#endif
+
+	return ret;
+
+#ifdef CONFIG_SYSCTL
+ cleanup:
+	nf_conntrack_protocol_unregister(&nf_conntrack_protocol_sctp6);
+#endif
+ cleanup_sctp4:
+	nf_conntrack_protocol_unregister(&nf_conntrack_protocol_sctp4);
+ out:
+	DEBUGP("SCTP conntrack module loading %s\n", 
+					ret ? "failed": "succeeded");
+	return ret;
+}
+
+void __exit fini(void)
+{
+	nf_conntrack_protocol_unregister(&nf_conntrack_protocol_sctp6);
+	nf_conntrack_protocol_unregister(&nf_conntrack_protocol_sctp4);
+#ifdef CONFIG_SYSCTL
+ 	unregister_sysctl_table(nf_ct_sysctl_header);
+#endif
+	DEBUGP("SCTP conntrack module unloaded\n");
+}
+
+module_init(init);
+module_exit(fini);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Kiran Kumar Immidi");
+MODULE_DESCRIPTION("Netfilter connection tracking protocol helper for SCTP");

Modified: trunk/patch-o-matic-ng/nf_conntrack/linux-2.6/net/netfilter/nf_conntrack_proto_tcp.c
===================================================================
--- trunk/patch-o-matic-ng/nf_conntrack/linux-2.6/net/netfilter/nf_conntrack_proto_tcp.c	2004-10-24 22:27:31 UTC (rev 3259)
+++ trunk/patch-o-matic-ng/nf_conntrack/linux-2.6/net/netfilter/nf_conntrack_proto_tcp.c	2004-11-01 07:38:59 UTC (rev 3260)
@@ -1,33 +1,51 @@
 /* (C) 1999-2001 Paul `Rusty' Russell
  * (C) 2002-2004 Netfilter Core Team <coreteam at netfilter.org>
- * (C) 2003-2004 USAGI/WIDE Project <http://www.linux-ipv6.org>
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 as
  * published by the Free Software Foundation.
  *
- * 16 Dec 2003: Yasuyuki Kozakai <yasuyuki.kozakai at toshiba.co.jp>
- *	- generalize L3 protocol dependent part.
+ * Jozsef Kadlecsik <kadlec at blackhole.kfki.hu>:
+ *	- Real stateful connection tracking
+ *	- Modified state transitions table
+ *	- Window scaling support added
+ *	- SACK support added
  *
- * Derived from net/ipv4/netfilter/ip_conntrack_proto_tcp.c
+ * Willy Tarreau:
+ *	- State table bugfixes
+ *	- More robust state changes
+ *	- Tuning timer parameters
+ *
+ * Yasuyuki KOZAKAI:
+ *	- genelized Layer 3 protocol part.
+ *
+ * version 2.2
  */
 
+#include <linux/config.h>
 #include <linux/types.h>
 #include <linux/sched.h>
 #include <linux/timer.h>
 #include <linux/netfilter.h>
 #include <linux/module.h>
+#include <linux/in.h>
 #include <linux/tcp.h>
-#include <linux/string.h>
+#include <linux/spinlock.h>
+#include <linux/skbuff.h>
+#include <net/ip6_checksum.h>
 
 #include <net/tcp.h>
 
+#include <linux/netfilter.h>
+#include <linux/netfilter_ipv4.h>
+#include <linux/netfilter_ipv6.h>
 #include <linux/netfilter/nf_conntrack.h>
 #include <linux/netfilter/nf_conntrack_protocol.h>
 #include <linux/netfilter_ipv4/lockhelp.h>
 
 #if 0
 #define DEBUGP printk
+#define DEBUGP_VARS
 #else
 #define DEBUGP(format, args...)
 #endif
@@ -35,28 +53,40 @@
 /* Protects conntrack->proto.tcp */
 static DECLARE_RWLOCK(tcp_lock);
 
-/* FIXME: Examine ipfilter's timeouts and conntrack transitions more
-   closely.  They're more complex. --RR */
+/* "Be conservative in what you do, 
+    be liberal in what you accept from others." 
+    If it's non-zero, we mark only out of window RST segments as INVALID. */
+int nf_ct_tcp_be_liberal = 0;
 
-/* Actually, I believe that neither ipmasq (where this code is stolen
-   from) nor ipfilter do it exactly right.  A new conntrack machine taking
-   into account packet loss (which creates uncertainty as to exactly
-   the conntrack of the connection) is required.  RSN.  --RR */
+/* When connection is picked up from the middle, how many packets are required
+   to pass in each direction when we assume we are in sync - if any side uses
+   window scaling, we lost the game. 
+   If it is set to zero, we disable picking up already established 
+   connections. */
+int nf_ct_tcp_loose = 3;
 
+/* Max number of the retransmitted packets without receiving an (acceptable) 
+   ACK from the destination. If this number is reached, a shorter timer 
+   will be started. */
+int nf_ct_tcp_max_retrans = 3;
+
+  /* FIXME: Examine ipfilter's timeouts and conntrack transitions more
+     closely.  They're more complex. --RR */
+
 static const char *tcp_conntrack_names[] = {
 	"NONE",
-	"ESTABLISHED",
 	"SYN_SENT",
 	"SYN_RECV",
+	"ESTABLISHED",
 	"FIN_WAIT",
+	"CLOSE_WAIT",
+	"LAST_ACK",
 	"TIME_WAIT",
 	"CLOSE",
-	"CLOSE_WAIT",
-	"LAST_ACK",
 	"LISTEN"
 };
-
-#define SECS *HZ
+  
+#define SECS * HZ
 #define MINS * 60 SECS
 #define HOURS * 60 MINS
 #define DAYS * 24 HOURS
@@ -70,64 +100,214 @@
 unsigned long nf_ct_tcp_timeout_time_wait =     2 MINS;
 unsigned long nf_ct_tcp_timeout_close =        10 SECS;
 
+/* RFC1122 says the R2 limit should be at least 100 seconds.
+   Linux uses 15 packets as limit, which corresponds 
+   to ~13-30min depending on RTO. */
+unsigned long nf_ct_tcp_timeout_max_retrans =     5 MINS;
+ 
 static unsigned long * tcp_timeouts[]
-= { 0,                                 /*      TCP_CONNTRACK_NONE */
-    &nf_ct_tcp_timeout_established,    /*      TCP_CONNTRACK_ESTABLISHED,      */
-    &nf_ct_tcp_timeout_syn_sent,       /*      TCP_CONNTRACK_SYN_SENT, */
-    &nf_ct_tcp_timeout_syn_recv,       /*      TCP_CONNTRACK_SYN_RECV, */
-    &nf_ct_tcp_timeout_fin_wait,       /*      TCP_CONNTRACK_FIN_WAIT, */
-    &nf_ct_tcp_timeout_time_wait,      /*      TCP_CONNTRACK_TIME_WAIT,        */
-    &nf_ct_tcp_timeout_close,          /*      TCP_CONNTRACK_CLOSE,    */
-    &nf_ct_tcp_timeout_close_wait,     /*      TCP_CONNTRACK_CLOSE_WAIT,       */
-    &nf_ct_tcp_timeout_last_ack,       /*      TCP_CONNTRACK_LAST_ACK, */
-    0,                                 /*      TCP_CONNTRACK_LISTEN */
+= { NULL,                              /* TCP_CONNTRACK_NONE */
+    &nf_ct_tcp_timeout_syn_sent,       /* TCP_CONNTRACK_SYN_SENT, */
+    &nf_ct_tcp_timeout_syn_recv,       /* TCP_CONNTRACK_SYN_RECV, */
+    &nf_ct_tcp_timeout_established,    /* TCP_CONNTRACK_ESTABLISHED, */
+    &nf_ct_tcp_timeout_fin_wait,       /* TCP_CONNTRACK_FIN_WAIT, */
+    &nf_ct_tcp_timeout_close_wait,     /* TCP_CONNTRACK_CLOSE_WAIT, */
+    &nf_ct_tcp_timeout_last_ack,       /* TCP_CONNTRACK_LAST_ACK, */
+    &nf_ct_tcp_timeout_time_wait,      /* TCP_CONNTRACK_TIME_WAIT, */
+    &nf_ct_tcp_timeout_close,          /* TCP_CONNTRACK_CLOSE, */
+    NULL,                              /* 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 sCW TCP_CONNTRACK_CLOSE_WAIT
+#define sLA TCP_CONNTRACK_LAST_ACK
 #define sTW TCP_CONNTRACK_TIME_WAIT
 #define sCL TCP_CONNTRACK_CLOSE
-#define sCW TCP_CONNTRACK_CLOSE_WAIT
-#define sLA TCP_CONNTRACK_LAST_ACK
 #define sLI TCP_CONNTRACK_LISTEN
 #define sIV TCP_CONNTRACK_MAX
+#define sIG TCP_CONNTRACK_IGNORE
 
-static enum tcp_conntrack tcp_conntracks[2][5][TCP_CONNTRACK_MAX] = {
+/* What TCP flags are set from RST/SYN/FIN/ACK. */
+enum tcp_bit_set {
+	TCP_SYN_SET,
+	TCP_SYNACK_SET,
+	TCP_FIN_SET,
+	TCP_ACK_SET,
+	TCP_RST_SET,
+	TCP_NONE_SET,
+};
+  
+/*
+ * 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, but our windows are *not*
+ * equivalent with the ones of the sender/receiver. We always
+ * try to guess the state of the current sender.
+ *
+ * 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.
+ *
+ * Packets marked as IGNORED (sIG):
+ *	if they may be either invalid or valid 
+ *	and the receiver may send back a connection 
+ *	closing RST or a SYN/ACK.
+ *
+ * Packets marked as INVALID (sIV):
+ *	if they are invalid
+ *	or we do not support the request (simultaneous open)
+ */
+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, sIG, sIG, sIG, sIG, sIG, sSS, sSS, sIV },
+/*
+ *	sNO -> sSS	Initialize a new connection
+ *	sSS -> sSS	Retransmitted SYN
+ *	sSR -> sIG	Late retransmitted SYN?
+ *	sES -> sIG	Error: SYNs in window outside the SYN_SENT state
+ *			are errors. Receiver will reply with RST 
+ *			and close the connection.
+ *			Or we are not in sync and hold a dead connection.
+ *	sFW -> sIG
+ *	sCW -> sIG
+ *	sLA -> sIG
+ *	sTW -> sSS	Reopened connection (RFC 1122).
+ *	sCL -> sSS
+ */
+/* 	     sNO, sSS, sSR, sES, sFW, sCW, sLA, sTW, sCL, sLI	*/
+/*synack*/ { sIV, sIV, sIV, sIV, sIV, sIV, sIV, sIV, sIV, sIV },
+/*
+ * A SYN/ACK from the client is always invalid:
+ *	- either it tries to set up a simultaneous open, which is 
+ *	  not supported;
+ *	- or the firewall has just been inserted between the two hosts
+ *	  during the session set-up. The SYN will be retransmitted 
+ *	  by the true client (or it'll time out).
+ */
+/* 	     sNO, sSS, sSR, sES, sFW, sCW, sLA, sTW, sCL, sLI	*/
+/*fin*/    { sIV, sIV, sFW, sFW, sLA, sLA, sLA, sTW, sCL, sIV },
+/*
+ *	sNO -> sIV	Too late and no reason to do anything...
+ *	sSS -> sIV	Client migth not send FIN in this state:
+ *			we enforce waiting for a SYN/ACK reply first.
+ *	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*/    { sIV, 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, sIV, sIV, sIV, sIV, sIV, sIV, sIV, sIV, sIV },
+/*
+ *	sNO -> sIV	Never reached.
+ *	sSS -> sIV	Simultaneous open, not supported
+ *	sSR -> sIV	Simultaneous open, not supported.
+ *	sES -> sIV	Server may not initiate a connection.
+ *	sFW -> sIV
+ *	sCW -> sIV
+ *	sLA -> sIV
+ *	sTW -> sIV	Reopened connection, but server may not do it.
+ *	sCL -> sIV
+ */
+/* 	     sNO, sSS, sSR, sES, sFW, sCW, sLA, sTW, sCL, sLI	*/
+/*synack*/ { sIV, sSR, sSR, sIG, sIG, sIG, sIG, sIG, sIG, sIV },
+/*
+ *	sSS -> sSR	Standard open.
+ *	sSR -> sSR	Retransmitted SYN/ACK.
+ *	sES -> sIG	Late retransmitted SYN/ACK?
+ *	sFW -> sIG
+ *	sCW -> sIG
+ *	sLA -> sIG
+ *	sTW -> sIG
+ *	sCL -> sIG
+ */
+/* 	     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, sIV, sES, sCW, sCW, sTW, sTW, sCL, sIV },
+/*
+ *	sSS -> sIV	ACK is invalid: we haven't seen a SYN/ACK yet.
+ *	sSR -> sIV	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, sCL, sCL, sCL, sCL, sIV },
+/*none*/   { sIV, sIV, sIV, sIV, sIV, sIV, sIV, sIV, sIV, sIV }
+  	}
 };
 
 static int tcp_pkt_to_tuple(const struct sk_buff *skb,
-			     unsigned int dataoff,
-			     struct nf_conntrack_tuple *tuple)
+			    unsigned int dataoff,
+			    struct nf_conntrack_tuple *tuple)
 {
-	struct tcphdr hdr;
+	struct tcphdr _hdr, *hp;
 
 	/* Actually only need first 8 bytes. */
-	if (skb_copy_bits(skb, dataoff, &hdr, 8) != 0)
+	hp = skb_header_pointer(skb, dataoff, 8, &_hdr);
+	if (hp == NULL)
 		return 0;
 
-	tuple->src.u.tcp.port = hdr.source;
-	tuple->dst.u.tcp.port = hdr.dest;
+	tuple->src.u.tcp.port = hp->source;
+	tuple->dst.u.tcp.port = hp->dest;
 
 	return 1;
 }
@@ -141,17 +321,17 @@
 }
 
 /* Print out the per-protocol part of the tuple. */
-static unsigned int tcp_print_tuple(char *buffer,
-				    const struct nf_conntrack_tuple *tuple)
+static int tcp_print_tuple(struct seq_file *s,
+			   const struct nf_conntrack_tuple *tuple)
 {
-	return sprintf(buffer, "sport=%hu dport=%hu ",
-		       ntohs(tuple->src.u.tcp.port),
-		       ntohs(tuple->dst.u.tcp.port));
+	return seq_printf(s, "sport=%hu dport=%hu ",
+			  ntohs(tuple->src.u.tcp.port),
+			  ntohs(tuple->dst.u.tcp.port));
 }
 
 /* Print out the private part of the conntrack. */
-static unsigned int tcp_print_conntrack(char *buffer,
-					const struct nf_conn *conntrack)
+static int tcp_print_conntrack(struct seq_file *s,
+			       const struct nf_conn *conntrack)
 {
 	enum tcp_conntrack state;
 
@@ -159,124 +339,815 @@
 	state = conntrack->proto.tcp.state;
 	READ_UNLOCK(&tcp_lock);
 
-	return sprintf(buffer, "%s ", tcp_conntrack_names[state]);
+	return seq_printf(s, "%s ", tcp_conntrack_names[state]);
 }
 
 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 TCP_RST_SET;
+	else if (tcph->syn) return (tcph->ack ? TCP_SYNACK_SET : TCP_SYN_SET);
+	else if (tcph->fin) return TCP_FIN_SET;
+	else if (tcph->ack) return TCP_ACK_SET;
+	else return TCP_NONE_SET;
 }
 
+/* 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 and the conditions are slightly changed:
+   
+   	td_maxend = max(sack + max(win,1)) seen in reply packets
+	td_maxwin = max(max(win, 1)) + (sack - ack) 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:      sack <= receiver.td_end
+   IV.	Lower bound for valid ack:	ack >= receiver.td_end - MAXACKWINDOW
+
+   where sack is the highest right edge of sack block found in the packet.
+
+   The upper bound limit for a valid ack is not ignored - 
+   we doesn't have to deal with fragments. 
+*/
+
+static inline __u32 segment_seq_plus_len(__u32 seq,
+					 size_t len,
+					 unsigned int dataoff,
+					 struct tcphdr *tcph)
+{
+	/* XXX Should I use payload length field in IP/IPv6 header ?
+	 * - kozakai */
+	return (seq + len - dataoff - tcph->doff*4
+		+ (tcph->syn ? 1 : 0) + (tcph->fin ? 1 : 0));
+}
+  
+/* Fixme: what about big packets? */
+#define MAXACKWINCONST			66000
+#define MAXACKWINDOW(sender)						\
+	((sender)->td_maxwin > MAXACKWINCONST ? (sender)->td_maxwin	\
+					      : MAXACKWINCONST)
+  
+/*
+ * Simplified tcp_parse_options routine from tcp_input.c
+ */
+static void tcp_options(const struct sk_buff *skb,
+			unsigned int dataoff,
+			struct tcphdr *tcph, 
+			struct nf_ct_tcp_state *state)
+{
+	unsigned char buff[(15 * 4) - sizeof(struct tcphdr)];
+	unsigned char *ptr;
+	int length = (tcph->doff*4) - sizeof(struct tcphdr);
+
+	if (!length)
+		return;
+
+	ptr = skb_header_pointer(skb, dataoff + sizeof(struct tcphdr),
+				 length, buff);
+	BUG_ON(ptr == NULL);
+
+	state->td_scale = 
+	state->flags = 0;
+
+	while (length > 0) {
+		int opcode=*ptr++;
+		int opsize;
+
+		switch (opcode) {
+		case TCPOPT_EOL:
+			return;
+		case TCPOPT_NOP:	/* Ref: RFC 793 section 3.1 */
+			length--;
+			continue;
+		default:
+			opsize=*ptr++;
+			if (opsize < 2) /* "silly options" */
+				return;
+			if (opsize > length)
+				break;	/* don't parse partial options */
+
+			if (opcode == TCPOPT_SACK_PERM 
+			    && opsize == TCPOLEN_SACK_PERM)
+				state->flags |= NF_CT_TCP_FLAG_SACK_PERM;
+			else if (opcode == TCPOPT_WINDOW
+				 && opsize == TCPOLEN_WINDOW) {
+				state->td_scale = *(u_int8_t *)ptr;
+
+				if (state->td_scale > 14) {
+					/* See RFC1323 */
+					state->td_scale = 14;
+				}
+				state->flags |=
+					NF_CT_TCP_STATE_FLAG_WINDOW_SCALE;
+			}
+			ptr += opsize - 2;
+			length -= opsize;
+		}
+	}
+}
+
+static void tcp_sack(const struct sk_buff *skb, unsigned int dataoff,
+		     struct tcphdr *tcph, __u32 *sack)
+{
+	__u32 tmp;
+	unsigned char *ptr;
+        unsigned char buff[(15 * 4) - sizeof(struct tcphdr)] __attribute__((__aligned__(sizeof(__u32))));
+	int length = (tcph->doff*4) - sizeof(struct tcphdr);
+
+	ptr = skb_header_pointer(skb, dataoff + sizeof(struct tcphdr),
+				 length, buff);
+	BUG_ON(ptr == NULL);
+
+	/* Fast path for timestamp-only option */
+	if (length == TCPOLEN_TSTAMP_ALIGNED*4
+	    && *(__u32 *)ptr ==
+	        __constant_ntohl((TCPOPT_NOP << 24) 
+	        		 | (TCPOPT_NOP << 16)
+	        		 | (TCPOPT_TIMESTAMP << 8)
+	        		 | TCPOLEN_TIMESTAMP))
+		return;
+
+	while (length > 0) {
+		int opcode = *ptr++;
+		int opsize, i;
+
+		switch (opcode) {
+		case TCPOPT_EOL:
+			return;
+		case TCPOPT_NOP:	/* Ref: RFC 793 section 3.1 */
+			length--;
+			continue;
+		default:
+			opsize = *ptr++;
+			if (opsize < 2) /* "silly options" */
+				return;
+			if (opsize > length)
+				break;	/* don't parse partial options */
+
+			if (opcode == TCPOPT_SACK 
+			    && opsize >= (TCPOLEN_SACK_BASE 
+			    		  + TCPOLEN_SACK_PERBLOCK)
+			    && !((opsize - TCPOLEN_SACK_BASE) 
+			    	 % TCPOLEN_SACK_PERBLOCK)) {
+			    	for (i = 0;
+			    	     i < (opsize - TCPOLEN_SACK_BASE);
+			    	     i += TCPOLEN_SACK_PERBLOCK) {
+					tmp = ntohl(*((u_int32_t *)(ptr+i)+1));
+
+					if (after(tmp, *sack))
+						*sack = tmp;
+				}
+				return;
+			}
+			ptr += opsize - 2;
+			length -= opsize;
+		}
+	}
+}
+
+static int tcp_in_window(struct nf_ct_tcp *state, 
+                         enum nf_conntrack_dir dir,
+                         unsigned int *index,
+                         const struct sk_buff *skb,
+			 unsigned int dataoff,
+                         struct tcphdr *tcph,
+			 int pf)
+{
+	struct nf_ct_tcp_state *sender = &state->seen[dir];
+	struct nf_ct_tcp_state *receiver = &state->seen[!dir];
+	__u32 seq, ack, sack, end, win, swin;
+	int res;
+
+	/*
+	 * Get the required data from the packet.
+	 */
+	seq = ntohl(tcph->seq);
+	ack = sack = ntohl(tcph->ack_seq);
+	win = ntohs(tcph->window);
+	end = segment_seq_plus_len(seq, skb->len, dataoff, tcph);
+
+	if (receiver->flags & NF_CT_TCP_FLAG_SACK_PERM)
+		tcp_sack(skb, dataoff, tcph, &sack);
+
+	DEBUGP("tcp_in_window: START\n");
+	DEBUGP("tcp_in_window: src=%u.%u.%u.%u:%hu dst=%u.%u.%u.%u:%hu "
+	       "seq=%u ack=%u sack=%u win=%u end=%u\n",
+		NIPQUAD(iph->saddr), ntohs(tcph->source), 
+		NIPQUAD(iph->daddr), ntohs(tcph->dest),
+		seq, ack, sack, 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.
+			 */
+			sender->td_end = 
+			sender->td_maxend = end;
+			sender->td_maxwin = (win == 0 ? 1 : win);
+
+			tcp_options(skb, dataoff, tcph, sender);
+			/* 
+			 * RFC 1323:
+			 * Both sides must send the Window Scale option
+			 * to enable window scaling in either direction.
+			 */
+			if (!(sender->flags & NF_CT_TCP_STATE_FLAG_WINDOW_SCALE
+			      && receiver->flags & NF_CT_TCP_STATE_FLAG_WINDOW_SCALE))
+				sender->td_scale = 
+				receiver->td_scale = 0;
+		} 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;
+		}
+	} else if (state->state == TCP_CONNTRACK_SYN_SENT
+		   && dir == NF_CT_DIR_ORIGINAL
+		   && after(end, sender->td_end)) {
+		/*
+		 * RFC 793: "if a TCP is reinitialized ... then it need
+		 * not wait at all; it must only be sure to use sequence 
+		 * numbers larger than those recently used."
+		 */
+		sender->td_end =
+		sender->td_maxend = end;
+		sender->td_maxwin = (win == 0 ? 1 : win);
+
+		tcp_options(skb, dataoff, tcph, sender);
+	}
+
+	if (!(tcph->ack)) {
+		/*
+		 * If there is no ACK, just pretend it was set and OK.
+		 */
+		ack = sack = 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 = sack = 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: src=%u.%u.%u.%u:%hu dst=%u.%u.%u.%u:%hu "
+	       "seq=%u ack=%u sack =%u win=%u end=%u trim=%u\n",
+		NIPQUAD(iph->saddr), ntohs(tcph->source),
+		NIPQUAD(iph->daddr), ntohs(tcph->dest),
+		seq, ack, sack, win, end, 
+		after(end, sender->td_maxend) && before(seq, sender->td_maxend)
+		? sender->td_maxend : 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);
+
+	/* Ignore data over the right edge of the receiver's window. */
+	if (after(end, sender->td_maxend) &&
+	    before(seq, sender->td_maxend)) {
+		end = sender->td_maxend;
+		if (*index == TCP_FIN_SET)
+			*index = TCP_ACK_SET;
+	}
+	DEBUGP("tcp_in_window: I=%i II=%i III=%i IV=%i\n",
+		before(end, sender->td_maxend + 1) 
+		    || before(seq, sender->td_maxend + 1),
+	    	after(seq, sender->td_end - receiver->td_maxwin - 1) 
+	    	    || after(end, sender->td_end - receiver->td_maxwin - 1),
+	    	before(sack, receiver->td_end + 1),
+	    	after(ack, receiver->td_end - MAXACKWINDOW(sender)));
+
+	if (sender->loose || receiver->loose ||
+	    (before(end, sender->td_maxend + 1) &&
+	     after(seq, sender->td_end - receiver->td_maxwin - 1) &&
+	     before(sack, receiver->td_end + 1) &&
+	     after(ack, receiver->td_end - MAXACKWINDOW(sender)))) {
+	    	/*
+		 * Take into account window scaling (RFC 1323).
+		 */
+		if (!tcph->syn)
+			win <<= sender->td_scale;
+
+		/*
+		 * Update sender data.
+		 */
+		swin = win + (sack - ack);
+		if (sender->td_maxwin < swin)
+			sender->td_maxwin = swin;
+		if (after(end, sender->td_end))
+			sender->td_end = end;
+		if (after(sack + win, receiver->td_maxend - 1)) {
+			receiver->td_maxend = sack + win;
+			if (win == 0)
+				receiver->td_maxend++;
+		}
+
+		/* 
+		 * Check retransmissions.
+		 */
+		if (*index == TCP_ACK_SET) {
+			if (state->last_dir == dir
+			    && state->last_seq == seq
+			    && state->last_end == end)
+				state->retrans++;
+			else {
+				state->last_dir = dir;
+				state->last_seq = seq;
+				state->last_end = end;
+				state->retrans = 0;
+			}
+		}
+		/*
+		 * Close the window of disabled window tracking :-)
+		 */
+		if (sender->loose)
+			sender->loose--;
+
+		res = 1;
+	} else {
+		if (LOG_INVALID(IPPROTO_TCP))
+			nf_log_packet(pf, 0, skb, NULL, NULL,
+			"nf_ct_tcp: %s ",
+			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(sender)) ? "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 = nf_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;
+}
+
+#ifdef CONFIG_IP_NF_NAT_NEEDED
+/* Update sender->td_end after NAT successfully mangled the packet */
+/* Caller must linearize skb at tcp header. */
+int nf_conntrack_tcp_update(struct sk_buff *skb,
+			    unsigned int dataoff,
+			    struct nf_conn *conntrack, 
+			    int dir)
+{
+	struct tcphdr *tcph = (void *)skb->data + dataoff;
+	__u32 end;
+#ifdef DEBUGP_VARS
+	struct nf_ct_tcp_state *sender = &conntrack->proto.tcp.seen[dir];
+	struct nf_ct_tcp_state *receiver = &conntrack->proto.tcp.seen[!dir];
+#endif
+
+	end = segment_seq_plus_len(ntohl(tcph->seq), skb->len, dataoff, 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;
+	conntrack->proto.tcp.last_end = end;
+	WRITE_UNLOCK(&tcp_lock);
+	DEBUGP("tcp_update: 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);
+
+	return 1;
+}
+ 
+#endif
+
+#define	TH_FIN	0x01
+#define	TH_SYN	0x02
+#define	TH_RST	0x04
+#define	TH_PUSH	0x08
+#define	TH_ACK	0x10
+#define	TH_URG	0x20
+#define	TH_ECE	0x40
+#define	TH_CWR	0x80
+
+/* table of valid flag combinations - ECE and CWR are always valid */
+static u8 tcp_valid_flags[(TH_FIN|TH_SYN|TH_RST|TH_PUSH|TH_ACK|TH_URG) + 1] =
+{
+	[TH_SYN]			= 1,
+	[TH_SYN|TH_ACK]			= 1,
+	[TH_RST]			= 1,
+	[TH_RST|TH_ACK]			= 1,
+	[TH_RST|TH_ACK|TH_PUSH]		= 1,
+	[TH_FIN|TH_ACK]			= 1,
+	[TH_ACK]			= 1,
+	[TH_ACK|TH_PUSH]		= 1,
+	[TH_ACK|TH_URG]			= 1,
+	[TH_ACK|TH_URG|TH_PUSH]		= 1,
+	[TH_FIN|TH_ACK|TH_PUSH]		= 1,
+	[TH_FIN|TH_ACK|TH_URG]		= 1,
+	[TH_FIN|TH_ACK|TH_URG|TH_PUSH]	= 1,
+};
+
+/* Protect conntrack agaist broken packets. Code taken from ipt_unclean.c.  */
+static int tcp_error(struct sk_buff *skb,
+		     unsigned int dataoff,
+		     enum nf_conntrack_info *ctinfo,
+		     int pf,
+		     unsigned int hooknum,
+		     int(*csum)(const struct sk_buff *,unsigned int))
+{
+	struct tcphdr _tcph, *th;
+	unsigned int tcplen = skb->len - dataoff;
+	u_int8_t tcpflags;
+
+	/* Smaller that minimal TCP header? */
+	th = skb_header_pointer(skb, dataoff, sizeof(_tcph), &_tcph);
+	if (th == NULL) {
+		if (LOG_INVALID(IPPROTO_TCP))
+			nf_log_packet(pf, 0, skb, NULL, NULL, 
+				"nf_ct_tcp: short packet ");
+		return -NF_ACCEPT;
+  	}
+  
+	/* Not whole TCP header or malformed packet */
+	if (th->doff*4 < sizeof(struct tcphdr) || tcplen < th->doff*4) {
+		if (LOG_INVALID(IPPROTO_TCP))
+			nf_log_packet(pf, 0, skb, NULL, NULL, 
+				"nf_ct_tcp: truncated/malformed packet ");
+		return -NF_ACCEPT;
+	}
+  
+	/* Checksum invalid? Ignore.
+	 * We skip checking packets on the outgoing path
+	 * because the semantic of CHECKSUM_HW is different there 
+	 * and moreover root might send raw packets.
+	 */
+	/* FIXME: Source route IP option packets --RR */
+	if (((pf == PF_INET && hooknum == NF_IP_PRE_ROUTING) ||
+	     (pf == PF_INET6 && hooknum  == NF_IP6_PRE_ROUTING)) &&
+	    csum(skb, dataoff)) {
+		if (LOG_INVALID(IPPROTO_TCP))
+			nf_log_packet(pf, 0, skb, NULL, NULL, 
+				  "nf_ct_tcp: bad TCP checksum ");
+		return -NF_ACCEPT;
+	}
+
+	/* Check TCP flags. */
+	tcpflags = (((u_int8_t *)th)[13] & ~(TH_ECE|TH_CWR));
+	if (!tcp_valid_flags[tcpflags]) {
+		if (LOG_INVALID(IPPROTO_TCP))
+			nf_log_packet(pf, 0, skb, NULL, NULL, 
+				  "nf_ct_tcp: invalid TCP flag combination ");
+		return -NF_ACCEPT;
+	}
+
+	return NF_ACCEPT;
+}
+
+static int csum4(const struct sk_buff *skb, unsigned int dataoff)
+{
+	return csum_tcpudp_magic(skb->nh.iph->saddr, skb->nh.iph->daddr,
+				 skb->len - dataoff, IPPROTO_TCP,
+			         skb->ip_summed == CHECKSUM_HW ? skb->csum
+			      	 : skb_checksum(skb, dataoff,
+						skb->len - dataoff, 0));
+}
+
+static int csum6(const struct sk_buff *skb, unsigned int dataoff)
+{
+	return csum_ipv6_magic(&skb->nh.ipv6h->saddr, &skb->nh.ipv6h->daddr,
+			       skb->len - dataoff, IPPROTO_TCP,
+			       skb->ip_summed == CHECKSUM_HW ? skb->csum
+			       : skb_checksum(skb, dataoff, skb->len - dataoff,
+					      0));
+}
+
+static int tcp_error4(struct sk_buff *skb,
+		      unsigned int dataoff,
+		      enum nf_conntrack_info *ctinfo,
+		      int pf,
+		      unsigned int hooknum)
+{
+	return tcp_error(skb, dataoff, ctinfo, pf, hooknum, csum4);
+}
+
+static int tcp_error6(struct sk_buff *skb,
+		      unsigned int dataoff,
+		      enum nf_conntrack_info *ctinfo,
+		      int pf,
+		      unsigned int hooknum)
+{
+	return tcp_error(skb, dataoff, ctinfo, pf, hooknum, csum6);
+}
+
 /* Returns verdict for packet, or -1 for invalid. */
 static int tcp_packet(struct nf_conn *conntrack,
 		      const struct sk_buff *skb,
 		      unsigned int dataoff,
 		      enum nf_conntrack_info ctinfo,
+		      int pf,
 		      unsigned int hooknum)
 {
-	enum tcp_conntrack newconntrack, oldtcpstate;
-	struct tcphdr tcph;
+	enum tcp_conntrack new_state, old_state;
+	enum nf_conntrack_dir dir;
+	struct tcphdr *th, _tcph;
+	unsigned long timeout;
+	unsigned int index;
 
-	if (skb_copy_bits(skb, dataoff, &tcph, sizeof(tcph)) != 0)
-		return -1;
-	if (skb->len < dataoff + tcph.doff * 4)
-		return -1;
+	th = skb_header_pointer(skb, dataoff, sizeof(_tcph), &_tcph);
+	BUG_ON(th == NULL);
 
-	/* If only reply is a RST, we can consider ourselves not to
-	   have an established connection: this is a fairly common
-	   problem case, so we can delete the conntrack
-	   immediately.  --RR */
-	if (!test_bit(NF_S_SEEN_REPLY_BIT, &conntrack->status) && tcph.rst) {
-		if (del_timer(&conntrack->timeout))
-			conntrack->timeout.function((unsigned long)conntrack);
+	WRITE_LOCK(&tcp_lock);
+	old_state = conntrack->proto.tcp.state;
+	dir = NFCTINFO2DIR(ctinfo);
+	index = get_conntrack_index(th);
+	new_state = tcp_conntracks[dir][index][old_state];
+
+	switch (new_state) {
+	case TCP_CONNTRACK_IGNORE:
+		/* Either SYN in ORIGINAL, or SYN/ACK in REPLY direction. */
+		if (index == TCP_SYNACK_SET
+		    && conntrack->proto.tcp.last_index == TCP_SYN_SET
+		    && conntrack->proto.tcp.last_dir != dir
+		    && after(ntohl(th->ack_seq),
+		    	     conntrack->proto.tcp.last_seq)) {
+			/* This SYN/ACK acknowledges a SYN that we earlier 
+			 * ignored as invalid. This means that the client and
+			 * the server are both in sync, while the firewall is
+			 * not. We kill this session and block the SYN/ACK so
+			 * that the client cannot but retransmit its SYN and 
+			 * thus initiate a clean new session.
+			 */
+		    	WRITE_UNLOCK(&tcp_lock);
+			if (LOG_INVALID(IPPROTO_TCP))
+				nf_log_packet(pf, 0, skb, NULL, NULL, 
+					  "nf_ct_tcp: killing out of sync session ");
+		    	if (del_timer(&conntrack->timeout))
+		    		conntrack->timeout.function((unsigned long)
+		    					    conntrack);
+		    	return -NF_DROP;
+		}
+		conntrack->proto.tcp.last_index = index;
+		conntrack->proto.tcp.last_dir = dir;
+		conntrack->proto.tcp.last_seq = ntohl(th->seq);
+
+		WRITE_UNLOCK(&tcp_lock);
+		if (LOG_INVALID(IPPROTO_TCP))
+			nf_log_packet(pf, 0, skb, NULL, NULL, 
+				  "nf_ct_tcp: invalid SYN (ignored) ");
 		return NF_ACCEPT;
+	case TCP_CONNTRACK_MAX:
+		/* Invalid packet */
+		DEBUGP("nf_ct_tcp: Invalid dir=%i index=%u ostate=%u\n",
+		       dir, get_conntrack_index(th),
+		       old_state);
+		WRITE_UNLOCK(&tcp_lock);
+		if (LOG_INVALID(IPPROTO_TCP))
+			nf_log_packet(pf, 0, skb, NULL, NULL, 
+				  "nf_ct_tcp: invalid state ");
+		return -NF_ACCEPT;
+	case TCP_CONNTRACK_SYN_SENT:
+		if (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;
+		}
+		break;
+	case TCP_CONNTRACK_CLOSE:
+		if (index == TCP_RST_SET
+		    && test_bit(NF_S_SEEN_REPLY_BIT, &conntrack->status)
+		    && conntrack->proto.tcp.last_index <= TCP_SYNACK_SET
+		    && after(ntohl(th->ack_seq),
+		    	     conntrack->proto.tcp.last_seq)) {
+			/* Ignore RST closing down invalid SYN 
+			   we had let trough. */ 
+		    	WRITE_UNLOCK(&tcp_lock);
+			if (LOG_INVALID(IPPROTO_TCP))
+				nf_log_packet(pf, 0, skb, NULL, NULL, 
+					  "nf_ct_tcp: invalid RST (ignored) ");
+			return NF_ACCEPT;
+		}
+		/* Just fall trough */
+	default:
+		/* Keep compilers happy. */
+		break;
 	}
 
-	WRITE_LOCK(&tcp_lock);
-	oldtcpstate = conntrack->proto.tcp.state;
-	newconntrack
-		= tcp_conntracks
-		[NFCTINFO2DIR(ctinfo)]
-		[get_conntrack_index(&tcph)][oldtcpstate];
-
-	/* Invalid */
-	if (newconntrack == TCP_CONNTRACK_MAX) {
-		DEBUGP("nf_conntrack_tcp: Invalid dir=%i index=%u conntrack=%u\n",
-		       NFCTINFO2DIR(ctinfo), get_conntrack_index(&tcph),
-		       conntrack->proto.tcp.state);
+	if (!tcp_in_window(&conntrack->proto.tcp, dir, &index, 
+			   skb, dataoff, th, pf)) {
 		WRITE_UNLOCK(&tcp_lock);
-		return -1;
+		return -NF_ACCEPT;
 	}
+	/* From now on we have got in-window packets */
 
-	conntrack->proto.tcp.state = newconntrack;
+	/* If FIN was trimmed off, we don't change state. */
+	conntrack->proto.tcp.last_index = index;
+	new_state = tcp_conntracks[dir][index][old_state];
 
-	/* Poor man's window tracking: record SYN/ACK for handshake check */
-	if (oldtcpstate == TCP_CONNTRACK_SYN_SENT
-	    && NFCTINFO2DIR(ctinfo) == NF_CT_DIR_REPLY
-	    && tcph.syn && tcph.ack) {
-		conntrack->proto.tcp.handshake_ack
-			= htonl(ntohl(tcph.seq) + 1);
-		goto out;
-	}
+	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(th->source),
+		NIPQUAD(iph->daddr), ntohs(th->dest),
+		(th->syn ? 1 : 0), (th->ack ? 1 : 0),
+		(th->fin ? 1 : 0), (th->rst ? 1 : 0),
+		old_state, new_state);
 
-	/* Set ASSURED if we see valid ack in ESTABLISHED after SYN_RECV */
-	if (oldtcpstate == TCP_CONNTRACK_SYN_RECV
-	    && NFCTINFO2DIR(ctinfo) == NF_CT_DIR_ORIGINAL
-	    && tcph.ack && !tcph.syn
-	    && tcph.ack_seq == conntrack->proto.tcp.handshake_ack)
-		set_bit(NF_S_ASSURED_BIT, &conntrack->status);
+	conntrack->proto.tcp.state = new_state;
+	timeout = conntrack->proto.tcp.retrans >= nf_ct_tcp_max_retrans
+		  && *tcp_timeouts[new_state] > nf_ct_tcp_timeout_max_retrans
+		  ? nf_ct_tcp_timeout_max_retrans : *tcp_timeouts[new_state];
+	WRITE_UNLOCK(&tcp_lock);
 
-out:	WRITE_UNLOCK(&tcp_lock);
-	nf_ct_refresh(conntrack, *tcp_timeouts[newconntrack]);
+	if (!test_bit(NF_S_SEEN_REPLY_BIT, &conntrack->status)) {
+		/* If only reply is a RST, we can consider ourselves not to
+		   have an established connection: this is a fairly common
+		   problem case, so we can delete the conntrack
+		   immediately.  --RR */
+		if (th->rst) {
+			if (del_timer(&conntrack->timeout))
+				conntrack->timeout.function((unsigned long)
+							    conntrack);
+			return NF_ACCEPT;
+		}
+	} else if (!test_bit(NF_S_ASSURED_BIT, &conntrack->status)
+		   && (old_state == TCP_CONNTRACK_SYN_RECV
+		       || old_state == TCP_CONNTRACK_ESTABLISHED)
+		   && new_state == TCP_CONNTRACK_ESTABLISHED) {
+		/* Set ASSURED if we see see valid ack in ESTABLISHED 
+		   after SYN_RECV or a valid answer for a picked up 
+		   connection. */
+			set_bit(NF_S_ASSURED_BIT, &conntrack->status);
+	}
+	nf_ct_refresh_acct(conntrack, ctinfo, skb, timeout);
 
 	return NF_ACCEPT;
 }
-
-/* Called when a new connection for this protocol found. */
-static int tcp_new(struct nf_conn *conntrack, const struct sk_buff *skb,
+ 
+  /* Called when a new connection for this protocol found. */
+static int tcp_new(struct nf_conn *conntrack,
+		   const struct sk_buff *skb,
 		   unsigned int dataoff)
 {
-	enum tcp_conntrack newconntrack;
-	struct tcphdr tcph;
+	enum tcp_conntrack new_state;
+	struct tcphdr *th, _tcph;
+#ifdef DEBUGP_VARS
+	struct nf_ct_tcp_state *sender = &conntrack->proto.tcp.seen[0];
+	struct nf_ct_tcp_state *receiver = &conntrack->proto.tcp.seen[1];
+#endif
 
-	if (skb_copy_bits(skb, dataoff, &tcph, sizeof(tcph)) != 0)
-		return -1;
+	th = skb_header_pointer(skb, dataoff, sizeof(_tcph), &_tcph);
+	BUG_ON(th == NULL);
 
 	/* Don't need lock here: this conntrack not in circulation yet */
-	newconntrack
-		= tcp_conntracks[0][get_conntrack_index(&tcph)]
+	new_state
+		= tcp_conntracks[0][get_conntrack_index(th)]
 		[TCP_CONNTRACK_NONE];
 
 	/* Invalid: delete conntrack */
-	if (newconntrack == TCP_CONNTRACK_MAX) {
-		DEBUGP("nf_conntrack_tcp: invalid new deleting.\n");
+	if (new_state >= TCP_CONNTRACK_MAX) {
+		DEBUGP("nf_ct_tcp: invalid new deleting.\n");
 		return 0;
 	}
 
-	conntrack->proto.tcp.state = newconntrack;
+	if (new_state == TCP_CONNTRACK_SYN_SENT) {
+		/* SYN packet */
+		conntrack->proto.tcp.seen[0].td_end =
+			segment_seq_plus_len(ntohl(th->seq), skb->len,
+					     dataoff, th);
+		conntrack->proto.tcp.seen[0].td_maxwin = ntohs(th->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;
+
+		tcp_options(skb, dataoff, th, &conntrack->proto.tcp.seen[0]);
+		conntrack->proto.tcp.seen[1].flags = 0;
+		conntrack->proto.tcp.seen[0].loose = 
+		conntrack->proto.tcp.seen[1].loose = 0;
+	} else if (nf_ct_tcp_loose == 0) {
+		/* Don't try to pick up connections. */
+		return 0;
+	} 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(th->seq), skb->len,
+					     dataoff, th);
+		conntrack->proto.tcp.seen[0].td_maxwin = ntohs(th->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;
+
+		/* We assume SACK. Should we assume window scaling too? */
+		conntrack->proto.tcp.seen[0].flags =
+		conntrack->proto.tcp.seen[1].flags = NF_CT_TCP_FLAG_SACK_PERM;
+		conntrack->proto.tcp.seen[0].loose = 
+		conntrack->proto.tcp.seen[1].loose = nf_ct_tcp_loose;
+	}
+    
+	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;      
+
+	/* tcp_packet will set them */
+	conntrack->proto.tcp.state = TCP_CONNTRACK_NONE;
+	conntrack->proto.tcp.last_index = TCP_NONE_SET;
+	 
+	DEBUGP("tcp_new: 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);
 	return 1;
 }
-
+  
 static int tcp_exp_matches_pkt(struct nf_conntrack_expect *exp,
-			       const struct sk_buff *skb, unsigned int dataoff)
+			       const struct sk_buff *skb,
+			       unsigned dataoff)
 {
-	struct tcphdr tcph;
+	struct tcphdr *th, _tcph;
 	unsigned int datalen;
 
-	if (skb_copy_bits(skb, dataoff, &tcph, sizeof(tcph)) != 0)
+	th = skb_header_pointer(skb, dataoff, sizeof(_tcph), &_tcph);
+	if (th == NULL)
 		return 0;
-	datalen = skb->len - dataoff - tcph.doff*4;
+	datalen = skb->len - dataoff - th->doff*4;
 
-	return between(exp->seq, ntohl(tcph.seq), ntohl(tcph.seq) + datalen);
+	return between(exp->seq, ntohl(th->seq), ntohl(th->seq) + datalen);
 }
 
-struct nf_conntrack_protocol nf_conntrack_protocol_tcp
-= { { NULL, NULL }, PF_UNSPEC, IPPROTO_TCP, "tcp",
-    tcp_pkt_to_tuple, tcp_invert_tuple, tcp_print_tuple, tcp_print_conntrack,
-    tcp_packet, tcp_new, NULL, tcp_exp_matches_pkt, NULL };
+struct nf_conntrack_protocol nf_conntrack_protocol_tcp4 =
+{
+	.l3proto		= PF_INET,
+	.proto 			= IPPROTO_TCP,
+	.name 			= "tcp",
+	.pkt_to_tuple 		= tcp_pkt_to_tuple,
+	.invert_tuple 		= tcp_invert_tuple,
+	.print_tuple 		= tcp_print_tuple,
+	.print_conntrack 	= tcp_print_conntrack,
+	.packet 		= tcp_packet,
+	.new 			= tcp_new,
+	.exp_matches_pkt	= tcp_exp_matches_pkt,
+	.error			= tcp_error4,
+};
 
-EXPORT_SYMBOL(nf_conntrack_protocol_tcp);
+struct nf_conntrack_protocol nf_conntrack_protocol_tcp6 =
+{
+	.l3proto		= PF_INET6,
+	.proto 			= IPPROTO_TCP,
+	.name 			= "tcp",
+	.pkt_to_tuple 		= tcp_pkt_to_tuple,
+	.invert_tuple 		= tcp_invert_tuple,
+	.print_tuple 		= tcp_print_tuple,
+	.print_conntrack 	= tcp_print_conntrack,
+	.packet 		= tcp_packet,
+	.new 			= tcp_new,
+	.exp_matches_pkt	= tcp_exp_matches_pkt,
+	.error			= tcp_error6,
+};
+
+EXPORT_SYMBOL(nf_conntrack_protocol_tcp4);
+EXPORT_SYMBOL(nf_conntrack_protocol_tcp6);

Modified: trunk/patch-o-matic-ng/nf_conntrack/linux-2.6/net/netfilter/nf_conntrack_proto_udp.c
===================================================================
--- trunk/patch-o-matic-ng/nf_conntrack/linux-2.6/net/netfilter/nf_conntrack_proto_udp.c	2004-10-24 22:27:31 UTC (rev 3259)
+++ trunk/patch-o-matic-ng/nf_conntrack/linux-2.6/net/netfilter/nf_conntrack_proto_udp.c	2004-11-01 07:38:59 UTC (rev 3260)
@@ -18,6 +18,12 @@
 #include <linux/module.h>
 #include <linux/netfilter.h>
 #include <linux/udp.h>
+#include <linux/seq_file.h>
+#include <net/ip6_checksum.h>
+#include <net/checksum.h>
+#include <linux/netfilter.h>
+#include <linux/netfilter_ipv4.h>
+#include <linux/netfilter_ipv6.h>
 #include <linux/netfilter/nf_conntrack_protocol.h>
 
 unsigned long nf_ct_udp_timeout = 30*HZ;
@@ -27,14 +33,15 @@
 			     unsigned int dataoff,
 			     struct nf_conntrack_tuple *tuple)
 {
-	struct udphdr hdr;
+	struct udphdr _hdr, *hp;
 
 	/* Actually only need first 8 bytes. */
-	if (skb_copy_bits(skb, dataoff, &hdr, 8) != 0)
+	hp = skb_header_pointer(skb, dataoff, sizeof(_hdr), &_hdr);
+	if (hp == NULL)
 		return 0;
 
-	tuple->src.u.udp.port = hdr.source;
-	tuple->dst.u.udp.port = hdr.dest;
+	tuple->src.u.udp.port = hp->source;
+	tuple->dst.u.udp.port = hp->dest;
 
 	return 1;
 }
@@ -48,17 +55,17 @@
 }
 
 /* Print out the per-protocol part of the tuple. */
-static unsigned int udp_print_tuple(char *buffer,
-				    const struct nf_conntrack_tuple *tuple)
+static int udp_print_tuple(struct seq_file *s,
+			   const struct nf_conntrack_tuple *tuple)
 {
-	return sprintf(buffer, "sport=%hu dport=%hu ",
-		       ntohs(tuple->src.u.udp.port),
-		       ntohs(tuple->dst.u.udp.port));
+	return seq_printf(s, "sport=%hu dport=%hu ",
+			  ntohs(tuple->src.u.udp.port),
+			  ntohs(tuple->dst.u.udp.port));
 }
 
 /* Print out the private part of the conntrack. */
-static unsigned int udp_print_conntrack(char *buffer,
-					const struct nf_conn *conntrack)
+static int udp_print_conntrack(struct seq_file *s,
+			       const struct nf_conn *conntrack)
 {
 	return 0;
 }
@@ -67,21 +74,106 @@
 static int udp_packet(struct nf_conn *conntrack,
 		      const struct sk_buff *skb,
 		      unsigned int dataoff,
-		      enum nf_conntrack_info conntrackinfo,
+		      enum nf_conntrack_info ctinfo,
+		      int pf,
 		      unsigned int hooknum)
 {
 	/* If we've seen traffic both ways, this is some kind of UDP
 	   stream.  Extend timeout. */
 	if (test_bit(NF_S_SEEN_REPLY_BIT, &conntrack->status)) {
-		nf_ct_refresh(conntrack, nf_ct_udp_timeout_stream);
+		nf_ct_refresh_acct(conntrack, ctinfo, skb,
+				   nf_ct_udp_timeout_stream);
 		/* Also, more likely to be important, and not a probe */
 		set_bit(NF_S_ASSURED_BIT, &conntrack->status);
 	} else
-		nf_ct_refresh(conntrack, nf_ct_udp_timeout);
+		nf_ct_refresh_acct(conntrack, ctinfo, skb, nf_ct_udp_timeout);
 
 	return NF_ACCEPT;
 }
 
+static int udp_error(struct sk_buff *skb, unsigned int dataoff,
+		     enum nf_conntrack_info *ctinfo,
+		     int pf,
+		     unsigned int hooknum,
+		     int (*csum)(const struct sk_buff *, unsigned int))
+{
+	unsigned int udplen = skb->len - dataoff;
+	struct udphdr _hdr, *hdr;
+
+	/* Header is too small? */
+	hdr = skb_header_pointer(skb, dataoff, sizeof(_hdr), &_hdr);
+	if (hdr == NULL) {
+		if (LOG_INVALID(IPPROTO_UDP))
+			nf_log_packet(pf, 0, skb, NULL, NULL,
+				      "nf_ct_udp: short packet ");
+		return -NF_ACCEPT;
+	}
+
+	/* Truncated/malformed packets */
+	if (ntohs(hdr->len) > udplen || ntohs(hdr->len) < sizeof(*hdr)) {
+		if (LOG_INVALID(IPPROTO_UDP))
+			nf_log_packet(pf, 0, skb, NULL, NULL,
+				"nf_ct_udp: truncated/malformed packet ");
+		return -NF_ACCEPT;
+	}
+
+	/* Packet with no checksum */
+	if (!hdr->check)
+		return NF_ACCEPT;
+
+	/* Checksum invalid? Ignore.
+	 * We skip checking packets on the outgoing path
+	 * because the semantic of CHECKSUM_HW is different there
+	 * and moreover root might send raw packets.
+	 * FIXME: Source route IP option packets --RR */
+	if (((pf == PF_INET && hooknum == NF_IP_PRE_ROUTING) ||
+	     (pf == PF_INET6 && hooknum == NF_IP6_PRE_ROUTING))
+	    && csum(skb, dataoff)) {
+		if (LOG_INVALID(IPPROTO_UDP))
+			nf_log_packet(pf, 0, skb, NULL, NULL,
+				"nf_ct_udp: bad UDP checksum ");
+		return -NF_ACCEPT;
+	}
+
+	return NF_ACCEPT;
+}
+
+static int csum4(const struct sk_buff *skb, unsigned int dataoff)
+{
+	return csum_tcpudp_magic(skb->nh.iph->saddr, skb->nh.iph->daddr,
+				 skb->len - dataoff, IPPROTO_UDP,
+				 skb->ip_summed == CHECKSUM_HW ? skb->csum
+				 : skb_checksum(skb, dataoff,
+						skb->len - dataoff, 0));
+}
+
+static int csum6(const struct sk_buff *skb, unsigned int dataoff)
+{
+	return csum_ipv6_magic(&skb->nh.ipv6h->saddr, &skb->nh.ipv6h->daddr,
+			       skb->len - dataoff, IPPROTO_UDP,
+			       skb->ip_summed == CHECKSUM_HW ? skb->csum
+			       : skb_checksum(skb, dataoff, skb->len - dataoff,
+					      0));
+}
+
+static int udp_error4(struct sk_buff *skb,
+		      unsigned int dataoff,
+		      enum nf_conntrack_info *ctinfo,
+		      int pf,
+		      unsigned int hooknum)
+{
+	return udp_error(skb, dataoff, ctinfo, pf, hooknum, csum4);
+}
+
+static int udp_error6(struct sk_buff *skb,
+		      unsigned int dataoff,
+		      enum nf_conntrack_info *ctinfo,
+		      int pf,
+		      unsigned int hooknum)
+{
+	return udp_error(skb, dataoff, ctinfo, pf, hooknum, csum6);
+}
+
 /* Called when a new connection for this protocol found. */
 static int udp_new(struct nf_conn *conntrack, const struct sk_buff *skb,
 		   unsigned int dataoff)
@@ -89,9 +181,33 @@
 	return 1;
 }
 
-struct nf_conntrack_protocol nf_conntrack_protocol_udp
-= { { NULL, NULL }, PF_UNSPEC, IPPROTO_UDP, "udp",
-    udp_pkt_to_tuple, udp_invert_tuple, udp_print_tuple, udp_print_conntrack,
-    udp_packet, udp_new, NULL, NULL, NULL };
+struct nf_conntrack_protocol nf_conntrack_protocol_udp4 =
+{
+	.l3proto		= PF_INET,
+	.proto			= IPPROTO_UDP,
+	.name			= "udp",
+	.pkt_to_tuple		= udp_pkt_to_tuple,
+	.invert_tuple		= udp_invert_tuple,
+	.print_tuple		= udp_print_tuple,
+	.print_conntrack	= udp_print_conntrack,
+	.packet			= udp_packet,
+	.new			= udp_new,
+	.error			= udp_error4,
+};
 
-EXPORT_SYMBOL(nf_conntrack_protocol_udp);
+struct nf_conntrack_protocol nf_conntrack_protocol_udp6 =
+{
+	.l3proto		= PF_INET6,
+	.proto			= IPPROTO_UDP,
+	.name			= "udp",
+	.pkt_to_tuple		= udp_pkt_to_tuple,
+	.invert_tuple		= udp_invert_tuple,
+	.print_tuple		= udp_print_tuple,
+	.print_conntrack	= udp_print_conntrack,
+	.packet			= udp_packet,
+	.new			= udp_new,
+	.error			= udp_error6,
+};
+
+EXPORT_SYMBOL(nf_conntrack_protocol_udp4);
+EXPORT_SYMBOL(nf_conntrack_protocol_udp6);

Modified: trunk/patch-o-matic-ng/nf_conntrack/linux-2.6/net/netfilter/nf_conntrack_standalone.c
===================================================================
--- trunk/patch-o-matic-ng/nf_conntrack/linux-2.6/net/netfilter/nf_conntrack_standalone.c	2004-10-24 22:27:31 UTC (rev 3259)
+++ trunk/patch-o-matic-ng/nf_conntrack/linux-2.6/net/netfilter/nf_conntrack_standalone.c	2004-11-01 07:38:59 UTC (rev 3260)
@@ -24,6 +24,8 @@
 #include <linux/module.h>
 #include <linux/skbuff.h>
 #include <linux/proc_fs.h>
+#include <linux/seq_file.h>
+#include <linux/percpu.h>
 #include <linux/netdevice.h>
 #ifdef CONFIG_SYSCTL
 #include <linux/sysctl.h>
@@ -33,10 +35,10 @@
 #define ASSERT_WRITE_LOCK(x) MUST_BE_WRITE_LOCKED(&nf_conntrack_lock)
 
 #include <linux/netfilter/nf_conntrack.h>
+#include <linux/netfilter/nf_conntrack_l3proto.h>
 #include <linux/netfilter/nf_conntrack_protocol.h>
 #include <linux/netfilter/nf_conntrack_core.h>
 #include <linux/netfilter/nf_conntrack_helper.h>
-#include <linux/netfilter/nf_conntrack_l3proto.h>
 #include <linux/netfilter_ipv4/listhelp.h>
 
 #if 0
@@ -47,172 +49,339 @@
 
 MODULE_LICENSE("GPL");
 
+extern atomic_t nf_conntrack_count;
+DECLARE_PER_CPU(struct nf_conntrack_stat, nf_conntrack_stat);
+
 static int kill_l3proto(const struct nf_conn *i, void *data)
 {
+	struct nf_conntrack_l3proto *l3proto;
+	l3proto = (struct nf_conntrack_l3proto *)data;
 	return (i->tuplehash[NF_CT_DIR_ORIGINAL].tuple.src.l3num == 
-			*((u_int8_t *) data));
+			l3proto->l3proto);
 }
 
 static int kill_proto(const struct nf_conn *i, void *data)
 {
+	struct nf_conntrack_protocol *proto;
+	proto = (struct nf_conntrack_protocol *)data;
 	return (i->tuplehash[NF_CT_DIR_ORIGINAL].tuple.dst.protonum == 
-			*((u_int8_t *) data));
+			proto->proto) &&
+	       (i->tuplehash[NF_CT_DIR_ORIGINAL].tuple.src.l3num ==
+			proto->l3proto);
 }
 
-static unsigned int
-print_tuple(char *buffer, const struct nf_conntrack_tuple *tuple,
+#ifdef CONFIG_PROC_FS
+static int
+print_tuple(struct seq_file *s, const struct nf_conntrack_tuple *tuple,
 	    struct nf_conntrack_l3proto *l3proto,
 	    struct nf_conntrack_protocol *proto)
 {
-	unsigned int len;
-
-	len = l3proto->print_tuple(buffer, tuple);
-	len += proto->print_tuple(buffer + len, tuple);
-
-	return len;
+	return l3proto->print_tuple(s, tuple) || proto->print_tuple(s, tuple);
 }
-
-/* FIXME: Don't print source proto part. --RR */
+#ifdef CONFIG_NF_CT_ACCT
 static unsigned int
-print_expect(char *buffer, const struct nf_conntrack_expect *expect)
+seq_print_counters(struct seq_file *s, struct nf_conntrack_counter *counter)
 {
-	unsigned int len;
+	return seq_printf(s, "packets=%llu bytes=%llu ",
+			  (unsigned long long)counter->packets,
+			  (unsigned long long)counter->bytes);
+}
+#else
+#define seq_print_counters(x, y)	0
+#endif
 
-	if (expect->expectant->helper->timeout)
-		len = sprintf(buffer, "EXPECTING: %lu ",
-			      timer_pending(&expect->timeout)
-			      ? (expect->timeout.expires - jiffies)/HZ : 0);
-	else
-		len = sprintf(buffer, "EXPECTING: - ");
-	len += sprintf(buffer + len, "use=%u proto=%u ",
-		      atomic_read(&expect->use), expect->tuple.dst.protonum);
-	len += print_tuple(buffer + len, &expect->tuple,
-			   __nf_ct_find_l3proto(expect->tuple.src.l3num),
-			   __nf_ct_find_proto(expect->tuple.src.l3num,
-					      expect->tuple.dst.protonum));
-	len += sprintf(buffer + len, "\n");
-	return len;
+static void *ct_seq_start(struct seq_file *s, loff_t *pos)
+{
+	if (*pos >= nf_conntrack_htable_size)
+		return NULL;
+	return &nf_conntrack_hash[*pos];
 }
 
-static unsigned int
-print_conntrack(char *buffer, struct nf_conn *conntrack)
+static void ct_seq_stop(struct seq_file *s, void *v)
 {
-	unsigned int len;
-	struct nf_conntrack_l3proto *l3proto
-		= __nf_ct_find_l3proto(conntrack->tuplehash[NF_CT_DIR_ORIGINAL]
-				       .tuple.src.l3num);
-	struct nf_conntrack_protocol *proto
-		= __nf_ct_find_proto(conntrack->tuplehash[NF_CT_DIR_ORIGINAL]
-				     .tuple.src.l3num,
-				     conntrack->tuplehash[NF_CT_DIR_ORIGINAL]
-				     .tuple.dst.protonum);
+}
 
-	if (l3proto == NULL) {
-		DEBUGP("Can't find l3proto. pf == %d\n",
-			conntrack->tuplehash[NF_CT_DIR_ORIGINAL].tuple.src.l3num);
-		return 0;
-	}
-
-	len = sprintf(buffer, "%-8s %u %-8s %u %lu ",
-		      l3proto->name,
-		      conntrack->tuplehash[NF_CT_DIR_ORIGINAL]
-		      .tuple.src.l3num,
-		      proto->name,
-		      conntrack->tuplehash[NF_CT_DIR_ORIGINAL]
-		      .tuple.dst.protonum,
-		      timer_pending(&conntrack->timeout)
-		      ? (conntrack->timeout.expires - jiffies)/HZ : 0);
-
-	len += l3proto->print_conntrack(buffer + len, conntrack);
-	len += proto->print_conntrack(buffer + len, conntrack);
-	len += print_tuple(buffer + len,
-			   &conntrack->tuplehash[NF_CT_DIR_ORIGINAL].tuple,
-			   l3proto, proto);
-	if (!(test_bit(NF_S_SEEN_REPLY_BIT, &conntrack->status)))
-		len += sprintf(buffer + len, "[UNREPLIED] ");
-	len += print_tuple(buffer + len,
-			   &conntrack->tuplehash[NF_CT_DIR_REPLY].tuple,
-			   l3proto, proto);
-	if (test_bit(NF_S_ASSURED_BIT, &conntrack->status))
-		len += sprintf(buffer + len, "[ASSURED] ");
-	len += sprintf(buffer + len, "use=%u ",
-		       atomic_read(&conntrack->ct_general.use));
-	len += sprintf(buffer + len, "\n");
-
-	return len;
+static void *ct_seq_next(struct seq_file *s, void *v, loff_t *pos)
+{
+	(*pos)++;
+	if (*pos >= nf_conntrack_htable_size)
+		return NULL;
+	return &nf_conntrack_hash[*pos];
 }
 
-/* Returns true when finished. */
-static inline int
-conntrack_iterate(const struct nf_conntrack_tuple_hash *hash,
-		  char *buffer, off_t offset, off_t *upto,
-		  unsigned int *len, unsigned int maxlen)
+/* return 0 on success, 1 in case of error */
+static int ct_seq_real_show(const struct nf_conntrack_tuple_hash *hash,
+			    struct seq_file *s)
 {
-	unsigned int newlen;
-	NF_CT_ASSERT(hash->ctrack);
+	struct nf_conn *conntrack = hash->ctrack;
+	struct nf_conntrack_l3proto *l3proto;
+	struct nf_conntrack_protocol *proto;
 
 	MUST_BE_READ_LOCKED(&nf_conntrack_lock);
 
-	/* Only count originals */
+	NF_CT_ASSERT(conntrack);
+
+	/* we only want to print DIR_ORIGINAL */
 	if (NF_CT_DIRECTION(hash))
 		return 0;
 
-	if ((*upto)++ < offset)
-		return 0;
+	l3proto = nf_ct_find_l3proto(conntrack->tuplehash[NF_CT_DIR_ORIGINAL]
+				       .tuple.src.l3num);
 
-	newlen = print_conntrack(buffer + *len, hash->ctrack);
-	if (*len + newlen > maxlen)
+	NF_CT_ASSERT(l3proto);
+	proto = nf_ct_find_proto(conntrack->tuplehash[NF_CT_DIR_ORIGINAL]
+				   .tuple.src.l3num,
+				   conntrack->tuplehash[NF_CT_DIR_ORIGINAL]
+				   .tuple.dst.protonum);
+	NF_CT_ASSERT(proto);
+
+	if (seq_printf(s, "%-8s %u %-8s %u %lu ",
+		       l3proto->name,
+		       conntrack->tuplehash[NF_CT_DIR_ORIGINAL].tuple.src.l3num,
+		       proto->name,
+		       conntrack->tuplehash[NF_CT_DIR_ORIGINAL].tuple.dst.protonum,
+		       timer_pending(&conntrack->timeout)
+		       ? (conntrack->timeout.expires - jiffies)/HZ : 0) != 0)
 		return 1;
-	else *len += newlen;
 
+	if (l3proto->print_conntrack(s, conntrack))
+		return 1;
+
+	if (proto->print_conntrack(s, conntrack))
+		return 1;
+
+	if (print_tuple(s, &conntrack->tuplehash[NF_CT_DIR_ORIGINAL].tuple,
+			l3proto, proto))
+		return 1;
+
+	if (seq_print_counters(s, &conntrack->counters[NF_CT_DIR_ORIGINAL]))
+		return 1;
+
+	if (!(test_bit(NF_S_SEEN_REPLY_BIT, &conntrack->status)))
+		if (seq_printf(s, "[UNREPLIED] "))
+			return 1;
+
+	if (print_tuple(s, &conntrack->tuplehash[NF_CT_DIR_REPLY].tuple,
+			l3proto, proto))
+		return 1;
+
+	if (seq_print_counters(s, &conntrack->counters[NF_CT_DIR_REPLY]))
+		return 1;
+
+	if (test_bit(NF_S_ASSURED_BIT, &conntrack->status))
+		if (seq_printf(s, "[ASSURED] "))
+			return 1;
+	
+	if (seq_printf(s, "use=%u\n", atomic_read(&conntrack->ct_general.use)))
+		return 1;
+	
 	return 0;
 }
 
-static int
-list_conntracks(char *buffer, char **start, off_t offset, int length)
+static int ct_seq_show(struct seq_file *s, void *v)
 {
-	unsigned int i;
-	unsigned int len = 0;
-	off_t upto = 0;
-	struct list_head *e;
+	struct list_head *list = v;
+	int ret = 0;
 
+	/* FIXME: Simply truncates if hash chain too long. */
 	READ_LOCK(&nf_conntrack_lock);
-	/* Traverse hash; print originals then reply. */
-	for (i = 0; i < nf_conntrack_htable_size; i++) {
-		if (LIST_FIND(&nf_conntrack_hash[i], conntrack_iterate,
-			      struct nf_conntrack_tuple_hash *,
-			      buffer, offset, &upto, &len, length))
-			goto finished;
-	}
+	if (LIST_FIND(list, ct_seq_real_show,
+		      struct nf_conntrack_tuple_hash *, s))
+		ret = -ENOSPC;
+	READ_UNLOCK(&nf_conntrack_lock);
+	return ret;
+}
 
-	/* Now iterate through expecteds. */
+static struct seq_operations ct_seq_ops = {
+	.start = ct_seq_start,
+	.next  = ct_seq_next,
+	.stop  = ct_seq_stop,
+	.show  = ct_seq_show
+};
+
+static int ct_open(struct inode *inode, struct file *file)
+{
+	return seq_open(file, &ct_seq_ops);
+}
+
+static struct file_operations ct_file_ops = {
+	.owner   = THIS_MODULE,
+	.open    = ct_open,
+	.read    = seq_read,
+	.llseek  = seq_lseek,
+	.release = seq_release
+};
+
+/* expects */
+static void *exp_seq_start(struct seq_file *s, loff_t *pos)
+{
+	struct list_head *e = &nf_conntrack_expect_list;
+	loff_t i;
+
+	/* strange seq_file api calls stop even if we fail,
+	 * thus we need to grab lock since stop unlocks */
+	READ_LOCK(&nf_conntrack_lock);
 	READ_LOCK(&nf_conntrack_expect_tuple_lock);
-	list_for_each(e, &nf_conntrack_expect_list) {
-		unsigned int last_len;
-		struct nf_conntrack_expect *expect
-			= (struct nf_conntrack_expect *)e;
-		if (upto++ < offset) continue;
 
-		last_len = len;
-		len += print_expect(buffer + len, expect);
-		if (len > length) {
-			len = last_len;
-			goto finished_expects;
-		}
+	if (list_empty(e))
+		return NULL;
+
+	for (i = 0; i <= *pos; i++) {
+		e = e->next;
+		if (e == &nf_conntrack_expect_list)
+			return NULL;
 	}
+	return e;
+}
 
- finished_expects:
+static void *exp_seq_next(struct seq_file *s, void *v, loff_t *pos)
+{
+	struct list_head *e = v;
+
+	e = e->next;
+
+	if (e == &nf_conntrack_expect_list)
+		return NULL;
+
+	return e;
+}
+
+static void exp_seq_stop(struct seq_file *s, void *v)
+{
 	READ_UNLOCK(&nf_conntrack_expect_tuple_lock);
- finished:
 	READ_UNLOCK(&nf_conntrack_lock);
+}
 
-	/* `start' hack - see fs/proc/generic.c line ~165 */
-	*start = (char *)((unsigned int)upto - offset);
-	return len;
+static int exp_seq_show(struct seq_file *s, void *v)
+{
+	struct nf_conntrack_expect *expect = v;
+
+	if (expect->expectant->helper->timeout)
+		seq_printf(s, "%lu ", timer_pending(&expect->timeout)
+			   ? (expect->timeout.expires - jiffies)/HZ : 0);
+	else
+		seq_printf(s, "- ");
+	seq_printf(s, "use=%u l3proto = %u proto=%u ",
+		   atomic_read(&expect->use),
+		   expect->tuple.src.l3num,
+		   expect->tuple.dst.protonum);
+	print_tuple(s, &expect->tuple,
+		    nf_ct_find_l3proto(expect->tuple.src.l3num),
+		    nf_ct_find_proto(expect->tuple.src.l3num,
+				     expect->tuple.dst.protonum));
+	return seq_putc(s, '\n');
 }
 
+static struct seq_operations exp_seq_ops = {
+	.start = exp_seq_start,
+	.next = exp_seq_next,
+	.stop = exp_seq_stop,
+	.show = exp_seq_show
+};
+
+static int exp_open(struct inode *inode, struct file *file)
+{
+	return seq_open(file, &exp_seq_ops);
+}
+
+static struct file_operations exp_file_ops = {
+	.owner   = THIS_MODULE,
+	.open    = exp_open,
+	.read    = seq_read,
+	.llseek  = seq_lseek,
+	.release = seq_release
+};
+
+static void *ct_cpu_seq_start(struct seq_file *seq, loff_t *pos)
+{
+	int cpu;
+
+	if (*pos == 0)
+		return SEQ_START_TOKEN;
+
+	for (cpu = *pos-1; cpu < NR_CPUS; ++cpu) {
+		if (!cpu_possible(cpu))
+			continue;
+		*pos = cpu + 1;
+		return &per_cpu(nf_conntrack_stat, cpu);
+	}
+
+	return NULL;
+}
+
+static void *ct_cpu_seq_next(struct seq_file *seq, void *v, loff_t *pos)
+{
+	int cpu;
+
+	for (cpu = *pos; cpu < NR_CPUS; ++cpu) {
+		if (!cpu_possible(cpu))
+			continue;
+		*pos = cpu + 1;
+		return &per_cpu(nf_conntrack_stat, cpu);
+	}
+
+	return NULL;
+}
+
+static void ct_cpu_seq_stop(struct seq_file *seq, void *v)
+{
+}
+
+static int ct_cpu_seq_show(struct seq_file *seq, void *v)
+{
+	unsigned int nr_conntracks = atomic_read(&nf_conntrack_count);
+	struct nf_conntrack_stat *st = v;
+
+	if (v == SEQ_START_TOKEN) {
+		seq_printf(seq, "entries  searched found new invalid ignore delete delete_list insert insert_failed drop early_drop icmp_error  expect_new expect_create expect_delete\n");
+		return 0;
+	}
+
+	seq_printf(seq, "%08x  %08x %08x %08x %08x %08x %08x %08x "
+			"%08x %08x %08x %08x %08x  %08x %08x %08x \n",
+		   nr_conntracks,
+		   st->searched,
+		   st->found,
+		   st->new,
+		   st->invalid,
+		   st->ignore,
+		   st->delete,
+		   st->delete_list,
+		   st->insert,
+		   st->insert_failed,
+		   st->drop,
+		   st->early_drop,
+		   st->error,
+
+		   st->expect_new,
+		   st->expect_create,
+		   st->expect_delete
+		);
+	return 0;
+}
+
+static struct seq_operations ct_cpu_seq_ops = {
+	.start	= ct_cpu_seq_start,
+	.next	= ct_cpu_seq_next,
+	.stop	= ct_cpu_seq_stop,
+	.show	= ct_cpu_seq_show,
+};
+
+static int ct_cpu_seq_open(struct inode *inode, struct file *file)
+{
+	return seq_open(file, &ct_cpu_seq_ops);
+}
+
+static struct file_operations ct_cpu_seq_fops = {
+	.owner	 = THIS_MODULE,
+	.open	 = ct_cpu_seq_open,
+	.read	 = seq_read,
+	.llseek	 = seq_lseek,
+	.release = seq_release_private,
+};
+#endif /* CONFIG_PROC_FS */
+
 /* Sysctl support */
-
 #ifdef CONFIG_SYSCTL
 
 /* From nf_conntrack_core.c */
@@ -228,6 +397,10 @@
 extern unsigned long nf_ct_tcp_timeout_last_ack;
 extern unsigned long nf_ct_tcp_timeout_time_wait;
 extern unsigned long nf_ct_tcp_timeout_close;
+extern unsigned long nf_ct_tcp_timeout_max_retrans;
+extern int nf_ct_tcp_loose;
+extern int nf_ct_tcp_be_liberal;
+extern int nf_ct_tcp_max_retrans;
 
 /* From nf_conntrack_proto_udp.c */
 extern unsigned long nf_ct_udp_timeout;
@@ -236,6 +409,10 @@
 /* From nf_conntrack_proto_generic.c */
 extern unsigned long nf_ct_generic_timeout;
 
+/* Log invalid packets of a given protocol */
+static int log_invalid_proto_min = 0;
+static int log_invalid_proto_max = 255;
+
 static struct ctl_table_header *nf_ct_sysctl_header;
 
 static ctl_table nf_ct_sysctl_table[] = {
@@ -248,6 +425,14 @@
 		.proc_handler	= &proc_dointvec,
 	},
 	{
+		.ctl_name	= NET_NF_CONNTRACK_COUNT,
+		.procname	= "nf_conntrack_count",
+		.data		= &nf_conntrack_count,
+		.maxlen		= sizeof(int),
+		.mode		= 0444,
+		.proc_handler	= &proc_dointvec,
+	},
+	{
 		.ctl_name       = NET_NF_CONNTRACK_BUCKETS,
 		.procname       = "nf_conntrack_buckets",
 		.data           = &nf_conntrack_htable_size,
@@ -343,6 +528,50 @@
 		.mode		= 0644,
 		.proc_handler	= &proc_dointvec_jiffies,
 	},
+	{
+		.ctl_name	= NET_NF_CONNTRACK_LOG_INVALID,
+		.procname	= "nf_conntrack_log_invalid",
+		.data		= &nf_ct_log_invalid,
+		.maxlen		= sizeof(unsigned int),
+		.mode		= 0644,
+		.proc_handler	= &proc_dointvec_minmax,
+		.strategy	= &sysctl_intvec,
+		.extra1		= &log_invalid_proto_min,
+		.extra2		= &log_invalid_proto_max,
+	},
+	{
+		.ctl_name	= NET_NF_CONNTRACK_TCP_TIMEOUT_MAX_RETRANS,
+		.procname	= "nf_conntrack_tcp_timeout_max_retrans",
+		.data		= &nf_ct_tcp_timeout_max_retrans,
+		.maxlen		= sizeof(unsigned int),
+		.mode		= 0644,
+		.proc_handler	= &proc_dointvec_jiffies,
+	},
+	{
+		.ctl_name	= NET_NF_CONNTRACK_TCP_LOOSE,
+		.procname	= "nf_conntrack_tcp_loose",
+		.data		= &nf_ct_tcp_loose,
+		.maxlen		= sizeof(unsigned int),
+		.mode		= 0644,
+		.proc_handler	= &proc_dointvec,
+	},
+	{
+		.ctl_name	= NET_NF_CONNTRACK_TCP_BE_LIBERAL,
+		.procname       = "nf_conntrack_tcp_be_liberal",
+		.data           = &nf_ct_tcp_be_liberal,
+		.maxlen         = sizeof(unsigned int),
+		.mode           = 0644,
+		.proc_handler   = &proc_dointvec,
+	},
+	{
+		.ctl_name	= NET_NF_CONNTRACK_TCP_MAX_RETRANS,
+		.procname	= "nf_conntrack_tcp_max_retrans",
+		.data		= &nf_ct_tcp_max_retrans,
+		.maxlen		= sizeof(unsigned int),
+		.mode		= 0644,
+		.proc_handler	= &proc_dointvec,
+	},
+
 	{ .ctl_name = 0 }
 };
 
@@ -375,10 +604,14 @@
 	},
 	{ .ctl_name = 0 }
 };
-#endif
+EXPORT_SYMBOL(nf_ct_log_invalid);
+#endif /* CONFIG_SYSCTL */
+
 static int init_or_cleanup(int init)
 {
-	struct proc_dir_entry *proc;
+#ifdef CONFIG_PROC_FS
+	struct proc_dir_entry *proc, *proc_exp, *proc_stat;
+#endif
 	int ret = 0;
 
 	if (!init) goto cleanup;
@@ -387,16 +620,27 @@
 	if (ret < 0)
 		goto cleanup_nothing;
 
-	proc = proc_net_create("nf_conntrack",0,list_conntracks);
+#ifdef CONFIG_PROC_FS
+	proc = proc_net_fops_create("nf_conntrack", 0440, &ct_file_ops);
 	if (!proc) goto cleanup_init;
-	proc->owner = THIS_MODULE;
 
+	proc_exp = proc_net_fops_create("nf_conntrack_expect", 0440,
+					&exp_file_ops);
+	if (!proc_exp) goto cleanup_proc;
+
+	proc_stat = create_proc_entry("nf_conntrack", S_IRUGO, proc_net_stat);
+	if (!proc_stat)
+		goto cleanup_proc_exp;
+
+	proc_stat->proc_fops = &ct_cpu_seq_fops;
+	proc_stat->owner = THIS_MODULE;
+#endif
 #ifdef CONFIG_SYSCTL
 	nf_ct_sysctl_header = register_sysctl_table(nf_ct_net_table, 0);
 	if (nf_ct_sysctl_header == NULL) {
 		printk("nf_conntrack: can't register to sysctl.\n");
 		ret = -ENOMEM;
-		goto cleanup_proc;
+		goto cleanup_proc_stat;
 	}
 #endif
 
@@ -405,10 +649,16 @@
  cleanup:
 #ifdef CONFIG_SYSCTL
  	unregister_sysctl_table(nf_ct_sysctl_header);
+ cleanup_proc_stat:
+#endif
+#ifdef CONFIG_PROC_FS
+	proc_net_remove("nf_conntrack_stat");
+ cleanup_proc_exp:
+	proc_net_remove("nf_conntrack_expect");
  cleanup_proc:
-#endif
 	proc_net_remove("nf_conntrack");
  cleanup_init:
+#endif /* CNFIG_PROC_FS */
 	nf_conntrack_cleanup();
  cleanup_nothing:
 	return ret;
@@ -417,41 +667,30 @@
 int nf_conntrack_l3proto_register(struct nf_conntrack_l3proto *proto)
 {
 	int ret = 0;
-	struct list_head *i;
 
 	WRITE_LOCK(&nf_conntrack_lock);
-	list_for_each(i, &l3proto_list) {
-		if (((struct nf_conntrack_l3proto *)i)->l3proto
-		    == proto->l3proto) {
-			ret = -EBUSY;
-			WRITE_UNLOCK(&nf_conntrack_lock);
-			goto out;
-		}
+	if (nf_ct_l3protos[proto->l3proto] != &nf_conntrack_generic_l3proto) {
+		ret = -EBUSY;
+		goto out;
 	}
-
-	list_prepend(&l3proto_list, proto);
+	nf_ct_l3protos[proto->l3proto] = proto;
+out:
 	WRITE_UNLOCK(&nf_conntrack_lock);
 
-	ret = proto->init();
- out:
 	return ret;
 }
 
 void nf_conntrack_l3proto_unregister(struct nf_conntrack_l3proto *proto)
 {
-	proto->fini();
 	WRITE_LOCK(&nf_conntrack_lock);
-
-	/* nf_ct_find_proto() returns proto_generic in case there is no protocol 
-	 * helper. So this should be enough - HW */
-	LIST_DELETE(&l3proto_list, proto);
+	nf_ct_l3protos[proto->l3proto] = &nf_conntrack_generic_l3proto;
 	WRITE_UNLOCK(&nf_conntrack_lock);
 	
 	/* Somebody could be still looking at the proto in bh. */
 	synchronize_net();
 
 	/* Remove all contrack entries for this protocol */
-	nf_ct_selective_cleanup(kill_l3proto, &proto->l3proto);
+	nf_ct_selective_cleanup(kill_l3proto, proto);
 }
 
 /* FIXME: Allow NULL functions and sub in pointers to generic for
@@ -459,40 +698,70 @@
 int nf_conntrack_protocol_register(struct nf_conntrack_protocol *proto)
 {
 	int ret = 0;
-	struct list_head *i;
 
+retry:
 	WRITE_LOCK(&nf_conntrack_lock);
-	list_for_each(i, &protocol_list) {
-		if (((struct nf_conntrack_protocol *)i)->proto
-		    == proto->proto &&
-		   ((struct nf_conntrack_protocol *)i)->l3proto
-		    == proto->l3proto) {
+	if (nf_ct_protos[proto->l3proto]) {
+		if (nf_ct_protos[proto->l3proto][proto->proto]
+				!= &nf_conntrack_generic_protocol) {
 			ret = -EBUSY;
+			goto out_unlock;
+		}
+	} else {
+		/* l3proto may be loaded latter. */
+		struct nf_conntrack_protocol **proto_array;
+		int i;
+
+		WRITE_UNLOCK(&nf_conntrack_lock);
+
+		proto_array = (struct nf_conntrack_protocol **)
+				kmalloc(MAX_NF_CT_PROTO *
+					 sizeof(struct nf_conntrack_protocol *),
+					GFP_KERNEL);
+		if (proto_array == NULL) {
+			ret = -ENOMEM;
 			goto out;
 		}
+		for (i = 0; i < MAX_NF_CT_PROTO; i++)
+			proto_array[i] = &nf_conntrack_generic_protocol;
+
+		WRITE_LOCK(&nf_conntrack_lock);
+		if (nf_ct_protos[proto->l3proto]) {
+			/* bad timing, but no problem */
+			WRITE_UNLOCK(&nf_conntrack_lock);
+			kfree(proto_array);
+		} else {
+			nf_ct_protos[proto->l3proto] = proto_array;
+			WRITE_UNLOCK(&nf_conntrack_lock);
+		}
+
+		/*
+		 * Just once because array is never freed until unloading
+		 * nf_conntrack.ko
+		 */
+		goto retry;
 	}
 
-	list_prepend(&protocol_list, proto);
+	nf_ct_protos[proto->l3proto][proto->proto] = proto;
 
- out:
+out_unlock:
 	WRITE_UNLOCK(&nf_conntrack_lock);
+out:
 	return ret;
 }
 
 void nf_conntrack_protocol_unregister(struct nf_conntrack_protocol *proto)
 {
 	WRITE_LOCK(&nf_conntrack_lock);
-
-	/* nf_ct_find_proto() returns proto_generic in case there is no protocol 
-	 * helper. So this should be enough - HW */
-	LIST_DELETE(&protocol_list, proto);
+	nf_ct_protos[proto->l3proto][proto->proto]
+		= &nf_conntrack_generic_protocol;
 	WRITE_UNLOCK(&nf_conntrack_lock);
 	
 	/* Somebody could be still looking at the proto in bh. */
 	synchronize_net();
 
 	/* Remove all contrack entries for this protocol */
-	nf_ct_selective_cleanup(kill_proto, &proto->proto);
+	nf_ct_selective_cleanup(kill_proto, proto);
 }
 
 static int __init init(void)
@@ -514,19 +783,21 @@
 {
 }
 
+EXPORT_SYMBOL(nf_conntrack_l3proto_register);
+EXPORT_SYMBOL(nf_conntrack_l3proto_unregister);
 EXPORT_SYMBOL(nf_conntrack_protocol_register);
 EXPORT_SYMBOL(nf_conntrack_protocol_unregister);
 EXPORT_SYMBOL(nf_ct_invert_tuplepr);
 EXPORT_SYMBOL(nf_conntrack_alter_reply);
 EXPORT_SYMBOL(nf_conntrack_destroyed);
-EXPORT_SYMBOL(nf_ct_get);
 EXPORT_SYMBOL(need_nf_conntrack);
 EXPORT_SYMBOL(nf_conntrack_helper_register);
 EXPORT_SYMBOL(nf_conntrack_helper_unregister);
 EXPORT_SYMBOL(nf_ct_selective_cleanup);
-EXPORT_SYMBOL(nf_ct_refresh);
+EXPORT_SYMBOL(nf_ct_refresh_acct);
+EXPORT_SYMBOL(nf_ct_protos);
 EXPORT_SYMBOL(nf_ct_find_proto);
-EXPORT_SYMBOL(__nf_ct_find_proto);
+EXPORT_SYMBOL(nf_ct_l3protos);
 EXPORT_SYMBOL(nf_ct_find_helper);
 EXPORT_SYMBOL(nf_conntrack_expect_alloc);
 EXPORT_SYMBOL(nf_conntrack_expect_related);
@@ -539,12 +810,13 @@
 EXPORT_SYMBOL(nf_conntrack_expect_list);
 EXPORT_SYMBOL(nf_conntrack_lock);
 EXPORT_SYMBOL(nf_conntrack_hash);
+EXPORT_SYMBOL(nf_conntrack_untracked);
 EXPORT_SYMBOL_GPL(nf_conntrack_find_get);
 EXPORT_SYMBOL_GPL(nf_ct_put);
-EXPORT_SYMBOL(nf_conntrack_untracked);
-EXPORT_SYMBOL(nf_conntrack_l3proto_register);
-EXPORT_SYMBOL(nf_conntrack_l3proto_unregister);
+#ifdef CONFIG_IP_NF_NAT_NEEDED
+EXPORT_SYMBOL(nf_conntrack_tcp_update);
+#endif
+EXPORT_SYMBOL(__nf_conntrack_confirm);
 EXPORT_SYMBOL(nf_ct_get_tuple);
 EXPORT_SYMBOL(nf_ct_invert_tuple);
-EXPORT_SYMBOL(nf_conntrack_confirm);
 EXPORT_SYMBOL(nf_conntrack_in);

Modified: trunk/patch-o-matic-ng/nf_conntrack/linux-2.6.patch
===================================================================
--- trunk/patch-o-matic-ng/nf_conntrack/linux-2.6.patch	2004-10-24 22:27:31 UTC (rev 3259)
+++ trunk/patch-o-matic-ng/nf_conntrack/linux-2.6.patch	2004-11-01 07:38:59 UTC (rev 3260)
@@ -1,18 +1,164 @@
-diff -Nur linux-2.6.7/include/linux/netfilter.h linux-2.6.7-nfct/include/linux/netfilter.h
---- linux-2.6.7/include/linux/netfilter.h	2004-06-16 14:19:23.000000000 +0900
-+++ linux-2.6.7-nfct/include/linux/netfilter.h	2004-06-30 17:50:38.000000000 +0900
-@@ -178,6 +178,7 @@
+--- linux-2.6.10-rc1/./net/Makefile	2004-09-14 02:13:55.000000000 +0900
++++ linux-2.6.10-rc1-nfct/./net/Makefile	2004-10-27 11:28:17.429989192 +0900
+@@ -41,6 +41,7 @@ obj-$(CONFIG_DECNET)		+= decnet/
+ obj-$(CONFIG_ECONET)		+= econet/
+ obj-$(CONFIG_VLAN_8021Q)	+= 8021q/
+ obj-$(CONFIG_IP_SCTP)		+= sctp/
++obj-$(CONFIG_NETFILTER)		+= netfilter/
+ 
+ ifeq ($(CONFIG_NET),y)
+ obj-$(CONFIG_SYSCTL)		+= sysctl_net.o
+--- linux-2.6.10-rc1/./net/core/skbuff.c	2004-10-27 10:41:07.748166216 +0900
++++ linux-2.6.10-rc1-nfct/./net/core/skbuff.c	2004-10-27 11:28:17.430989040 +0900
+@@ -240,6 +240,7 @@ void __kfree_skb(struct sk_buff *skb)
+ 	}
+ #ifdef CONFIG_NETFILTER
+ 	nf_conntrack_put(skb->nfct);
++	nf_conntrack_put_reasm(skb->reasm);
+ #ifdef CONFIG_BRIDGE_NETFILTER
+ 	nf_bridge_put(skb->nf_bridge);
+ #endif
+@@ -312,6 +313,8 @@ struct sk_buff *skb_clone(struct sk_buff
+ 	C(nfct);
+ 	nf_conntrack_get(skb->nfct);
+ 	C(nfctinfo);
++	C(reasm);
++	nf_conntrack_get_reasm(skb->reasm);
+ #ifdef CONFIG_NETFILTER_DEBUG
+ 	C(nf_debug);
+ #endif
+@@ -379,6 +382,8 @@ static void copy_skb_header(struct sk_bu
+ 	new->nfct	= old->nfct;
+ 	nf_conntrack_get(old->nfct);
+ 	new->nfctinfo	= old->nfctinfo;
++	nf_conntrack_get_reasm(old->reasm);
++	new->reasm	= old->reasm;
+ #ifdef CONFIG_NETFILTER_DEBUG
+ 	new->nf_debug	= old->nf_debug;
+ #endif
+--- linux-2.6.10-rc1/./net/core/netfilter.c	2004-09-30 21:04:21.000000000 +0900
++++ linux-2.6.10-rc1-nfct/./net/core/netfilter.c	2004-10-27 11:28:17.431988888 +0900
+@@ -807,6 +807,7 @@ EXPORT_SYMBOL(nf_log_packet);
+    and hence manufactured ICMP or RST packets will not be associated
+    with it. */
+ void (*ip_ct_attach)(struct sk_buff *, struct sk_buff *);
++void (*nf_ct_attach)(struct sk_buff *, struct sk_buff *);
+ 
+ void __init netfilter_init(void)
+ {
+@@ -819,6 +820,7 @@ void __init netfilter_init(void)
+ }
+ 
+ EXPORT_SYMBOL(ip_ct_attach);
++EXPORT_SYMBOL(nf_ct_attach);
+ EXPORT_SYMBOL(ip_route_me_harder);
+ EXPORT_SYMBOL(nf_getsockopt);
+ EXPORT_SYMBOL(nf_hook_slow);
+--- linux-2.6.10-rc1/./net/ipv6/ip6_output.c	2004-09-30 21:04:27.000000000 +0900
++++ linux-2.6.10-rc1-nfct/./net/ipv6/ip6_output.c	2004-10-27 11:28:17.432988736 +0900
+@@ -478,6 +478,9 @@ static void ip6_copy_metadata(struct sk_
+ 	to->nfct = from->nfct;
+ 	nf_conntrack_get(to->nfct);
+ 	to->nfctinfo = from->nfctinfo;
++	nf_conntrack_put_reasm(from->reasm);
++	nf_conntrack_get_reasm(to->reasm);
++	to->reasm = from->reasm;
+ #ifdef CONFIG_BRIDGE_NETFILTER
+ 	nf_bridge_put(to->nf_bridge);
+ 	to->nf_bridge = from->nf_bridge;
+--- linux-2.6.10-rc1/./net/ipv6/netfilter/Makefile	2004-09-30 21:04:28.000000000 +0900
++++ linux-2.6.10-rc1-nfct/./net/ipv6/netfilter/Makefile	2004-10-27 11:28:17.432988736 +0900
+@@ -24,3 +24,9 @@ obj-$(CONFIG_IP6_NF_QUEUE) += ip6_queue.
+ obj-$(CONFIG_IP6_NF_TARGET_LOG) += ip6t_LOG.o
+ obj-$(CONFIG_IP6_NF_RAW) += ip6table_raw.o
+ obj-$(CONFIG_IP6_NF_MATCH_HL) += ip6t_hl.o
++
++# objects for l3 independent conntrack
++nf_conntrack_ipv6-objs  :=  nf_conntrack_l3proto_ipv6.o nf_conntrack_proto_icmpv6.o nf_conntrack_reasm.o
++
++# l3 independent conntrack
++obj-$(CONFIG_NF_CONNTRACK_IPV6) += nf_conntrack_ipv6.o
+--- linux-2.6.10-rc1/./net/ipv6/netfilter/Kconfig	2004-09-30 21:04:28.000000000 +0900
++++ linux-2.6.10-rc1-nfct/./net/ipv6/netfilter/Kconfig	2004-10-27 11:28:17.432988736 +0900
+@@ -239,5 +239,20 @@ config IP6_NF_RAW
+ 	  <file:Documentation/modules.txt>.  If unsure, say `N'.
+ 	  help
+ 
++config NF_CONNTRACK_IPV6
++	tristate "IPv6 support on new connection tracking (EXPERIMENTAL)"
++	depends on EXPERIMENTAL && ! IP6_NF_CONNTRACK
++	select NF_CONNTRACK
++	---help---
++	  Connection tracking keeps a record of what packets have passed
++	  through your machine, in order to figure out how they are related
++	  into connections.
++
++	  This is IPv6 support on Layer 3 independent connection tracking.
++	  Layer 3 independent connection tracking is experimental scheme
++	  which generalize ip_conntrack to support other layer 3 protocols.
++
++	  To compile it as a module, choose M here.  If unsure, say N.
++
+ endmenu
+ 
+--- linux-2.6.10-rc1/./net/ipv4/netfilter/Makefile	2004-10-27 10:41:07.757164848 +0900
++++ linux-2.6.10-rc1-nfct/./net/ipv4/netfilter/Makefile	2004-10-27 11:28:17.432988736 +0900
+@@ -101,3 +101,9 @@ obj-$(CONFIG_IP_NF_COMPAT_IPCHAINS) += i
+ obj-$(CONFIG_IP_NF_COMPAT_IPFWADM) += ipfwadm.o
+ 
+ obj-$(CONFIG_IP_NF_QUEUE) += ip_queue.o
++
++# objects for l3 independent conntrack
++nf_conntrack_ipv4-objs  :=  nf_conntrack_l3proto_ipv4.o nf_conntrack_proto_icmp.o
++
++# l3 independent conntrack
++obj-$(CONFIG_NF_CONNTRACK_IPV4) += nf_conntrack_ipv4.o
+--- linux-2.6.10-rc1/./net/ipv4/netfilter/Kconfig	2004-10-27 10:41:07.757164848 +0900
++++ linux-2.6.10-rc1-nfct/./net/ipv4/netfilter/Kconfig	2004-10-27 11:28:17.433988584 +0900
+@@ -732,5 +732,20 @@ config IP_NF_COMPAT_IPFWADM
+ 
+ 	  To compile it as a module, choose M here.  If unsure, say N.
+ 
++config NF_CONNTRACK_IPV4
++	tristate "IPv4 support on new connection tracking (EXPERIMENTAL)"
++	depends on EXPERIMENTAL && !IP_NF_CONNTRACK
++	select NF_CONNTRACK
++	---help---
++	  Connection tracking keeps a record of what packets have passed
++	  through your machine, in order to figure out how they are related
++	  into connections.
++
++	  This is IPv4 support on Layer 3 independent connection tracking.
++	  Layer 3 independent connection tracking is experimental scheme
++	  which generalize ip_conntrack to support other layer 3 protocols.
++
++	  To compile it as a module, choose M here.  If unsure, say N.
++
+ endmenu
+ 
+--- linux-2.6.10-rc1/./net/Kconfig	2004-08-25 08:16:03.000000000 +0900
++++ linux-2.6.10-rc1-nfct/./net/Kconfig	2004-10-27 11:28:17.433988584 +0900
+@@ -216,6 +216,7 @@ source "net/ipv4/netfilter/Kconfig"
+ source "net/ipv6/netfilter/Kconfig"
+ source "net/decnet/netfilter/Kconfig"
+ source "net/bridge/netfilter/Kconfig"
++source "net/netfilter/Kconfig"
+ 
+ endif
+ 
+--- linux-2.6.10-rc1/./include/linux/netfilter.h	2004-09-30 21:02:02.000000000 +0900
++++ linux-2.6.10-rc1-nfct/./include/linux/netfilter.h	2004-10-27 11:28:17.434988432 +0900
+@@ -179,6 +179,7 @@ ip6t_find_target_lock(const char *name, 
  extern inline struct arpt_target *
  arpt_find_target_lock(const char *name, int *error, struct semaphore *mutex);
- extern void (*ip_ct_attach)(struct sk_buff *, struct nf_ct_info *);
-+extern void (*nf_ct_attach)(struct sk_buff *, struct nf_ct_info *);
+ extern void (*ip_ct_attach)(struct sk_buff *, struct sk_buff *);
++extern void (*nf_ct_attach)(struct sk_buff *, struct sk_buff *);
  
  #ifdef CONFIG_NETFILTER_DEBUG
  extern void nf_dump_skb(int pf, struct sk_buff *skb);
-diff -Nur linux-2.6.7/include/linux/sysctl.h linux-2.6.7-nfct/include/linux/sysctl.h
---- linux-2.6.7/include/linux/sysctl.h	2004-06-16 14:19:35.000000000 +0900
-+++ linux-2.6.7-nfct/include/linux/sysctl.h	2004-06-30 17:45:50.000000000 +0900
-@@ -187,6 +187,7 @@
+--- linux-2.6.10-rc1/./include/linux/sysctl.h	2004-10-27 10:41:07.646181720 +0900
++++ linux-2.6.10-rc1-nfct/./include/linux/sysctl.h	2004-10-27 11:31:51.297476392 +0900
+@@ -191,6 +191,7 @@ enum
  	NET_DECNET=15,
  	NET_ECONET=16,
  	NET_SCTP=17, 
@@ -20,7 +166,7 @@
  };
  
  /* /proc/sys/kernel/random */
-@@ -251,6 +252,29 @@
+@@ -255,6 +256,42 @@ enum
  	NET_UNIX_MAX_DGRAM_QLEN=3,
  };
  
@@ -41,53 +187,75 @@
 +	NET_NF_CONNTRACK_ICMP_TIMEOUT=12,
 +	NET_NF_CONNTRACK_GENERIC_TIMEOUT=13,
 +	NET_NF_CONNTRACK_BUCKETS=14,
-+	NET_NF_CONNTRACK_ICMPV6_TIMEOUT=15,
-+	NET_NF_CONNTRACK_FRAG6_TIMEOUT=16,
-+	NET_NF_CONNTRACK_FRAG6_LOW_THRESH=17,
-+	NET_NF_CONNTRACK_FRAG6_HIGH_THRESH=18,
++	NET_NF_CONNTRACK_LOG_INVALID=15,
++	NET_NF_CONNTRACK_TCP_TIMEOUT_MAX_RETRANS=16,
++	NET_NF_CONNTRACK_TCP_LOOSE=17,
++	NET_NF_CONNTRACK_TCP_BE_LIBERAL=18,
++	NET_NF_CONNTRACK_TCP_MAX_RETRANS=19,
++	NET_NF_CONNTRACK_SCTP_TIMEOUT_CLOSED=20,
++	NET_NF_CONNTRACK_SCTP_TIMEOUT_COOKIE_WAIT=21,
++	NET_NF_CONNTRACK_SCTP_TIMEOUT_COOKIE_ECHOED=22,
++	NET_NF_CONNTRACK_SCTP_TIMEOUT_ESTABLISHED=23,
++	NET_NF_CONNTRACK_SCTP_TIMEOUT_SHUTDOWN_SENT=24,
++	NET_NF_CONNTRACK_SCTP_TIMEOUT_SHUTDOWN_RECD=25,
++	NET_NF_CONNTRACK_SCTP_TIMEOUT_SHUTDOWN_ACK_SENT=26,
++	NET_NF_CONNTRACK_COUNT=27,
++	NET_NF_CONNTRACK_ICMPV6_TIMEOUT=28,
++	NET_NF_CONNTRACK_FRAG6_TIMEOUT=29,
++	NET_NF_CONNTRACK_FRAG6_LOW_THRESH=30,
++	NET_NF_CONNTRACK_FRAG6_HIGH_THRESH=31,
 +};
 +
  /* /proc/sys/net/ipv4 */
  enum
  {
-diff -Nur linux-2.6.7/net/Kconfig linux-2.6.7-nfct/net/Kconfig
---- linux-2.6.7/net/Kconfig	2004-06-16 14:18:57.000000000 +0900
-+++ linux-2.6.7-nfct/net/Kconfig	2004-06-30 17:45:50.000000000 +0900
-@@ -214,6 +214,7 @@
- source "net/ipv6/netfilter/Kconfig"
- source "net/decnet/netfilter/Kconfig"
- source "net/bridge/netfilter/Kconfig"
-+source "net/netfilter/Kconfig"
+--- linux-2.6.10-rc1/./include/linux/skbuff.h	2004-10-12 09:28:58.000000000 +0900
++++ linux-2.6.10-rc1-nfct/./include/linux/skbuff.h	2004-10-27 11:28:17.435988280 +0900
+@@ -251,6 +251,7 @@ struct sk_buff {
+ 	__u32			nfcache;
+ 	__u32			nfctinfo;
+ 	struct nf_conntrack	*nfct;
++	struct sk_buff		*reasm;
+ #ifdef CONFIG_NETFILTER_DEBUG
+         unsigned int		nf_debug;
+ #endif
+@@ -1148,10 +1149,22 @@ static inline void nf_conntrack_get(stru
+ 	if (nfct)
+ 		atomic_inc(&nfct->use);
+ }
++static inline void nf_conntrack_get_reasm(struct sk_buff *skb)
++{
++	if (skb)
++		atomic_inc(&skb->users);
++}
++static inline void nf_conntrack_put_reasm(struct sk_buff *skb)
++{
++	if (skb)
++		kfree_skb(skb);
++}
+ static inline void nf_reset(struct sk_buff *skb)
+ {
+ 	nf_conntrack_put(skb->nfct);
+ 	skb->nfct = NULL;
++	nf_conntrack_put_reasm(skb->reasm);
++	skb->reasm = NULL;
+ #ifdef CONFIG_NETFILTER_DEBUG
+ 	skb->nf_debug = 0;
+ #endif
+--- linux-2.6.10-rc1/./include/linux/netfilter_ipv6.h	2004-09-14 02:11:32.000000000 +0900
++++ linux-2.6.10-rc1-nfct/./include/linux/netfilter_ipv6.h	2004-10-27 11:28:17.435988280 +0900
+@@ -56,6 +56,7 @@
  
- endif
+ enum nf_ip6_hook_priorities {
+ 	NF_IP6_PRI_FIRST = INT_MIN,
++	NF_IP6_PRI_CONNTRACK_DEFRAG = -400,
+ 	NF_IP6_PRI_SELINUX_FIRST = -225,
+ 	NF_IP6_PRI_CONNTRACK = -200,
+ 	NF_IP6_PRI_BRIDGE_SABOTAGE_FORWARD = -175,
+@@ -68,4 +69,6 @@ enum nf_ip6_hook_priorities {
+ 	NF_IP6_PRI_LAST = INT_MAX,
+ };
  
-diff -Nur linux-2.6.7/net/Makefile linux-2.6.7-nfct/net/Makefile
---- linux-2.6.7/net/Makefile	2004-06-16 14:19:22.000000000 +0900
-+++ linux-2.6.7-nfct/net/Makefile	2004-06-30 17:45:50.000000000 +0900
-@@ -40,6 +40,7 @@
- obj-$(CONFIG_ECONET)		+= econet/
- obj-$(CONFIG_VLAN_8021Q)	+= 8021q/
- obj-$(CONFIG_IP_SCTP)		+= sctp/
-+obj-$(CONFIG_NETFILTER)		+= netfilter/
- 
- ifeq ($(CONFIG_NET),y)
- obj-$(CONFIG_SYSCTL)		+= sysctl_net.o
-diff -Nur linux-2.6.7/net/core/netfilter.c linux-2.6.7-nfct/net/core/netfilter.c
---- linux-2.6.7/net/core/netfilter.c	2004-06-16 14:19:22.000000000 +0900
-+++ linux-2.6.7-nfct/net/core/netfilter.c	2004-06-30 17:46:47.000000000 +0900
-@@ -807,6 +807,7 @@
-    and hence manufactured ICMP or RST packets will not be associated
-    with it. */
- void (*ip_ct_attach)(struct sk_buff *, struct nf_ct_info *);
-+void (*nf_ct_attach)(struct sk_buff *, struct nf_ct_info *);
- 
- void __init netfilter_init(void)
- {
-@@ -819,6 +820,7 @@
- }
- 
- EXPORT_SYMBOL(ip_ct_attach);
-+EXPORT_SYMBOL(nf_ct_attach);
- EXPORT_SYMBOL(ip_route_me_harder);
- EXPORT_SYMBOL(nf_getsockopt);
- EXPORT_SYMBOL(nf_hook_slow);
++#define SO_ORIGINAL_DST 80
++
+ #endif /*__LINUX_IP6_NETFILTER_H*/




More information about the netfilter-cvslog mailing list