diff -Nru --exclude .depend --exclude '*.o' --exclude '*.ko' --exclude '*.ver' --exclude '.*.flags' --exclude '*.orig' --exclude '*.rej' --exclude '*.cmd' --exclude '*.mod.c' --exclude '*~' linux-2.6.11-rc4/include/linux/netfilter_ipv4/ip_conntrack_core.h linux-2.6.11-rc4-helper_sysctl/include/linux/netfilter_ipv4/ip_conntrack_core.h
--- linux-2.6.11-rc4/include/linux/netfilter_ipv4/ip_conntrack_core.h	2005-02-13 13:52:05.000000000 +0100
+++ linux-2.6.11-rc4-helper_sysctl/include/linux/netfilter_ipv4/ip_conntrack_core.h	2005-02-13 16:15:56.000000000 +0100
@@ -48,5 +48,10 @@
 extern struct list_head *ip_conntrack_hash;
 extern struct list_head ip_conntrack_expect_list;
 DECLARE_RWLOCK_EXTERN(ip_conntrack_lock);
+extern struct list_head ip_conntrack_unconfirmed;
+
+extern void __ip_ct_destroy_expect(struct ip_conntrack_expect *exp);
+extern void __ip_ct_unlink_expect(struct ip_conntrack_expect *exp);
+
 #endif /* _IP_CONNTRACK_CORE_H */
 
diff -Nru --exclude .depend --exclude '*.o' --exclude '*.ko' --exclude '*.ver' --exclude '.*.flags' --exclude '*.orig' --exclude '*.rej' --exclude '*.cmd' --exclude '*.mod.c' --exclude '*~' linux-2.6.11-rc4/include/linux/netfilter_ipv4/ip_conntrack_helper.h linux-2.6.11-rc4-helper_sysctl/include/linux/netfilter_ipv4/ip_conntrack_helper.h
--- linux-2.6.11-rc4/include/linux/netfilter_ipv4/ip_conntrack_helper.h	2005-02-13 13:52:05.000000000 +0100
+++ linux-2.6.11-rc4-helper_sysctl/include/linux/netfilter_ipv4/ip_conntrack_helper.h	2005-02-13 16:44:16.000000000 +0100
@@ -15,6 +15,10 @@
 					 * expected connections */
 	unsigned int timeout;		/* timeout for expecteds */
 
+#ifdef CONFIG_PROC_FS
+	struct proc_dir_entry	*pde;	/* proc file for port changes */
+#endif
+
 	/* Mask of things we will help (compared against server response) */
 	struct ip_conntrack_tuple tuple;
 	struct ip_conntrack_tuple mask;
@@ -29,6 +33,11 @@
 extern int ip_conntrack_helper_register(struct ip_conntrack_helper *);
 extern void ip_conntrack_helper_unregister(struct ip_conntrack_helper *);
 
+extern int ip_conntrack_helper_add_port(struct ip_conntrack_helper *h,
+					u_int16_t port);
+extern int ip_conntrack_helper_del_port(struct ip_conntrack_helper *h,
+					u_int16_t port);
+
 /* Allocate space for an expectation: this is mandatory before calling 
    ip_conntrack_expect_related. */
 extern struct ip_conntrack_expect *ip_conntrack_expect_alloc(void);
@@ -38,4 +47,10 @@
 extern int ip_conntrack_expect_related(struct ip_conntrack_expect *exp);
 extern void ip_conntrack_unexpect_related(struct ip_conntrack_expect *exp);
 
+/* Find the helper for a for a particular tuple */
+extern struct ip_conntrack_helper *ip_ct_find_helper(const struct ip_conntrack_tuple *tuple);
+
+extern int ip_conntrack_helper_init(void);
+extern void ip_conntrack_helper_fini(void);
+
 #endif /*_IP_CONNTRACK_HELPER_H*/
diff -Nru --exclude .depend --exclude '*.o' --exclude '*.ko' --exclude '*.ver' --exclude '.*.flags' --exclude '*.orig' --exclude '*.rej' --exclude '*.cmd' --exclude '*.mod.c' --exclude '*~' linux-2.6.11-rc4/net/ipv4/netfilter/Makefile linux-2.6.11-rc4-helper_sysctl/net/ipv4/netfilter/Makefile
--- linux-2.6.11-rc4/net/ipv4/netfilter/Makefile	2005-02-13 13:52:06.000000000 +0100
+++ linux-2.6.11-rc4-helper_sysctl/net/ipv4/netfilter/Makefile	2005-02-13 13:59:13.000000000 +0100
@@ -3,7 +3,7 @@
 #
 
 # objects for the standalone - connection tracking / NAT
-ip_conntrack-objs	:= ip_conntrack_standalone.o ip_conntrack_core.o ip_conntrack_proto_generic.o ip_conntrack_proto_tcp.o ip_conntrack_proto_udp.o ip_conntrack_proto_icmp.o
+ip_conntrack-objs	:= ip_conntrack_standalone.o ip_conntrack_core.o ip_conntrack_helper.o ip_conntrack_proto_generic.o ip_conntrack_proto_tcp.o ip_conntrack_proto_udp.o ip_conntrack_proto_icmp.o
 iptable_nat-objs	:= ip_nat_standalone.o ip_nat_rule.o ip_nat_core.o ip_nat_helper.o ip_nat_proto_unknown.o ip_nat_proto_tcp.o ip_nat_proto_udp.o ip_nat_proto_icmp.o
 
 # connection tracking
diff -Nru --exclude .depend --exclude '*.o' --exclude '*.ko' --exclude '*.ver' --exclude '.*.flags' --exclude '*.orig' --exclude '*.rej' --exclude '*.cmd' --exclude '*.mod.c' --exclude '*~' linux-2.6.11-rc4/net/ipv4/netfilter/ip_conntrack_amanda.c linux-2.6.11-rc4-helper_sysctl/net/ipv4/netfilter/ip_conntrack_amanda.c
--- linux-2.6.11-rc4/net/ipv4/netfilter/ip_conntrack_amanda.c	2005-02-13 13:52:06.000000000 +0100
+++ linux-2.6.11-rc4-helper_sysctl/net/ipv4/netfilter/ip_conntrack_amanda.c	2005-02-13 17:41:38.000000000 +0100
@@ -160,7 +160,11 @@
 
 static int __init init(void)
 {
-	return ip_conntrack_helper_register(&amanda_helper);
+	int ret = ip_conntrack_helper_register(&amanda_helper);
+	if (ret)
+		return ret;
+	return ip_conntrack_helper_add_port(&amanda_helper,
+				ntohs(amanda_helper.tuple.src.u.tcp.port));
 }
 
 module_init(init);
diff -Nru --exclude .depend --exclude '*.o' --exclude '*.ko' --exclude '*.ver' --exclude '.*.flags' --exclude '*.orig' --exclude '*.rej' --exclude '*.cmd' --exclude '*.mod.c' --exclude '*~' linux-2.6.11-rc4/net/ipv4/netfilter/ip_conntrack_core.c linux-2.6.11-rc4-helper_sysctl/net/ipv4/netfilter/ip_conntrack_core.c
--- linux-2.6.11-rc4/net/ipv4/netfilter/ip_conntrack_core.c	2005-02-13 13:52:06.000000000 +0100
+++ linux-2.6.11-rc4-helper_sysctl/net/ipv4/netfilter/ip_conntrack_core.c	2005-02-13 17:01:40.000000000 +0100
@@ -49,7 +49,7 @@
 #include <linux/netfilter_ipv4/ip_conntrack_core.h>
 #include <linux/netfilter_ipv4/listhelp.h>
 
-#define IP_CONNTRACK_VERSION	"2.1"
+#define IP_CONNTRACK_VERSION	"2.2"
 
 #if 0
 #define DEBUGP printk
@@ -65,7 +65,6 @@
 void (*ip_conntrack_destroyed)(struct ip_conntrack *conntrack) = NULL;
 LIST_HEAD(ip_conntrack_expect_list);
 struct ip_conntrack_protocol *ip_ct_protos[MAX_IP_CT_PROTO];
-static LIST_HEAD(helpers);
 unsigned int ip_conntrack_htable_size = 0;
 int ip_conntrack_max;
 struct list_head *ip_conntrack_hash;
@@ -73,7 +72,7 @@
 static kmem_cache_t *ip_conntrack_expect_cachep;
 struct ip_conntrack ip_conntrack_untracked;
 unsigned int ip_ct_log_invalid;
-static LIST_HEAD(unconfirmed);
+LIST_HEAD(ip_conntrack_unconfirmed);
 static int ip_conntrack_vmalloc;
 
 DEFINE_PER_CPU(struct ip_conntrack_stat, ip_conntrack_stat);
@@ -137,7 +136,7 @@
 
 
 /* ip_conntrack_expect helper functions */
-static void destroy_expect(struct ip_conntrack_expect *exp)
+void __ip_ct_destroy_expect(struct ip_conntrack_expect *exp)
 {
 	ip_conntrack_put(exp->master);
 	IP_NF_ASSERT(!timer_pending(&exp->timeout));
@@ -145,7 +144,7 @@
 	CONNTRACK_STAT_INC(expect_delete);
 }
 
-static void unlink_expect(struct ip_conntrack_expect *exp)
+void __ip_ct_unlink_expect(struct ip_conntrack_expect *exp)
 {
 	MUST_BE_WRITE_LOCKED(&ip_conntrack_lock);
 	list_del(&exp->list);
@@ -158,9 +157,9 @@
 	struct ip_conntrack_expect *exp = (void *)ul_expect;
 
 	WRITE_LOCK(&ip_conntrack_lock);
-	unlink_expect(exp);
+	__ip_ct_unlink_expect(exp);
 	WRITE_UNLOCK(&ip_conntrack_lock);
-	destroy_expect(exp);
+	__ip_ct_destroy_expect(exp);
 }
 
 /* If an expectation for this connection is found, it gets delete from
@@ -179,7 +178,7 @@
 		if (ip_ct_tuple_mask_cmp(tuple, &i->tuple, &i->mask)
 		    && is_confirmed(i->master)
 		    && del_timer(&i->timeout)) {
-			unlink_expect(i);
+			__ip_ct_unlink_expect(i);
 			return i;
 		}
 	}
@@ -197,8 +196,8 @@
 
 	list_for_each_entry_safe(i, tmp, &ip_conntrack_expect_list, list) {
 		if (i->master == ct && del_timer(&i->timeout)) {
-			unlink_expect(i);
-			destroy_expect(i);
+			__ip_ct_unlink_expect(i);
+			__ip_ct_destroy_expect(i);
 		}
 	}
 }
@@ -439,19 +438,6 @@
 	return dropped;
 }
 
-static inline int helper_cmp(const struct ip_conntrack_helper *i,
-			     const struct ip_conntrack_tuple *rtuple)
-{
-	return ip_ct_tuple_mask_cmp(rtuple, &i->tuple, &i->mask);
-}
-
-static struct ip_conntrack_helper *ip_ct_find_helper(const struct ip_conntrack_tuple *tuple)
-{
-	return LIST_FIND(&helpers, helper_cmp,
-			 struct ip_conntrack_helper *,
-			 tuple);
-}
-
 /* Allocate a new conntrack: we return -ENOMEM if classification
    failed due to stress.  Otherwise it really is unclassifiable. */
 static struct ip_conntrack_tuple_hash *
@@ -529,7 +515,8 @@
 	}
 
 	/* Overload tuple linked list to put us in unconfirmed list. */
-	list_add(&conntrack->tuplehash[IP_CT_DIR_ORIGINAL].list, &unconfirmed);
+	list_add(&conntrack->tuplehash[IP_CT_DIR_ORIGINAL].list,
+		 &ip_conntrack_unconfirmed);
 
 	atomic_inc(&ip_conntrack_count);
 	WRITE_UNLOCK(&ip_conntrack_lock);
@@ -537,7 +524,7 @@
 	if (exp) {
 		if (exp->expectfn)
 			exp->expectfn(conntrack, exp);
-		destroy_expect(exp);
+		__ip_ct_destroy_expect(exp);
 	}
 
 	return &conntrack->tuplehash[IP_CT_DIR_ORIGINAL];
@@ -727,9 +714,9 @@
 	/* choose the the oldest expectation to evict */
 	list_for_each_entry_reverse(i, &ip_conntrack_expect_list, list) {
 		if (expect_matches(i, exp) && del_timer(&i->timeout)) {
-			unlink_expect(i);
+			__ip_ct_unlink_expect(i);
 			WRITE_UNLOCK(&ip_conntrack_lock);
-			destroy_expect(i);
+			__ip_ct_destroy_expect(i);
 			return;
 		}
 	}
@@ -781,8 +768,8 @@
 	list_for_each_entry_reverse(i, &ip_conntrack_expect_list, list) {
 		if (i->master == master) {
 			if (del_timer(&i->timeout)) {
-				unlink_expect(i);
-				destroy_expect(i);
+				__ip_ct_unlink_expect(i);
+				__ip_ct_destroy_expect(i);
 			}
 			break;
 		}
@@ -854,50 +841,6 @@
 	WRITE_UNLOCK(&ip_conntrack_lock);
 }
 
-int ip_conntrack_helper_register(struct ip_conntrack_helper *me)
-{
-	BUG_ON(me->timeout == 0);
-	WRITE_LOCK(&ip_conntrack_lock);
-	list_prepend(&helpers, me);
-	WRITE_UNLOCK(&ip_conntrack_lock);
-
-	return 0;
-}
-
-static inline int unhelp(struct ip_conntrack_tuple_hash *i,
-			 const struct ip_conntrack_helper *me)
-{
-	if (tuplehash_to_ctrack(i)->helper == me)
-		tuplehash_to_ctrack(i)->helper = NULL;
-	return 0;
-}
-
-void ip_conntrack_helper_unregister(struct ip_conntrack_helper *me)
-{
-	unsigned int i;
-	struct ip_conntrack_expect *exp, *tmp;
-
-	/* Need write lock here, to delete helper. */
-	WRITE_LOCK(&ip_conntrack_lock);
-	LIST_DELETE(&helpers, me);
-
-	/* Get rid of expectations */
-	list_for_each_entry_safe(exp, tmp, &ip_conntrack_expect_list, list) {
-		if (exp->master->helper == me && del_timer(&exp->timeout)) {
-			unlink_expect(exp);
-			destroy_expect(exp);
-		}
-	}
-	/* Get rid of expecteds, set helpers to NULL. */
-	LIST_FIND_W(&unconfirmed, unhelp, struct ip_conntrack_tuple_hash*, me);
-	for (i = 0; i < ip_conntrack_htable_size; i++)
-		LIST_FIND_W(&ip_conntrack_hash[i], unhelp,
-			    struct ip_conntrack_tuple_hash *, me);
-	WRITE_UNLOCK(&ip_conntrack_lock);
-
-	/* Someone could be still looking at the helper in a bh. */
-	synchronize_net();
-}
 
 static inline void ct_add_counters(struct ip_conntrack *ct,
 				   enum ip_conntrack_info ctinfo,
@@ -1017,7 +960,7 @@
 			break;
 	}
 	if (!h)
-		h = LIST_FIND_W(&unconfirmed, do_iter,
+		h = LIST_FIND_W(&ip_conntrack_unconfirmed, do_iter,
 				struct ip_conntrack_tuple_hash *, iter, data);
 	if (h)
 		atomic_inc(&tuplehash_to_ctrack(h)->ct_general.use);
@@ -1141,6 +1084,7 @@
 	kmem_cache_destroy(ip_conntrack_expect_cachep);
 	free_conntrack_hash();
 	nf_unregister_sockopt(&so_getorigdst);
+	ip_conntrack_helper_fini();
 }
 
 static int hashsize;
@@ -1234,8 +1178,14 @@
 	/*  - and look it like as a confirmed connection */
 	set_bit(IPS_CONFIRMED_BIT, &ip_conntrack_untracked.status);
 
+	ret = ip_conntrack_helper_init();
+	if (ret != 0)
+		goto err_free_exp_slab;
+
 	return ret;
 
+err_free_exp_slab:
+	kmem_cache_destroy(ip_conntrack_expect_cachep);
 err_free_conntrack_slab:
 	kmem_cache_destroy(ip_conntrack_cachep);
 err_free_hash:
diff -Nru --exclude .depend --exclude '*.o' --exclude '*.ko' --exclude '*.ver' --exclude '.*.flags' --exclude '*.orig' --exclude '*.rej' --exclude '*.cmd' --exclude '*.mod.c' --exclude '*~' linux-2.6.11-rc4/net/ipv4/netfilter/ip_conntrack_helper.c linux-2.6.11-rc4-helper_sysctl/net/ipv4/netfilter/ip_conntrack_helper.c
--- linux-2.6.11-rc4/net/ipv4/netfilter/ip_conntrack_helper.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.6.11-rc4-helper_sysctl/net/ipv4/netfilter/ip_conntrack_helper.c	2005-02-13 17:46:42.000000000 +0100
@@ -0,0 +1,338 @@
+/* Dynamic conntrack helper to port binding
+ * (C) 2005 by Harald Welte <laforge@netfilter.org>
+ *
+ * Portions (C) 1999-2001 Paul `Rusty' Russell  
+ * 	    (C) 2002-2005 Netfilter Core Team <coreteam@netfilter.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.
+ *
+ * 13 Feb 2005: Harald Welte <laforge@netfilter.org>
+ * 	- seperate out helper support from ip_conntrack_core
+ */
+
+#include <linux/module.h>
+#include <linux/config.h>
+#include <linux/proc_fs.h>
+#include <linux/seq_file.h>
+
+#include <linux/skbuff.h>
+#include <linux/netdevice.h>
+
+#define ASSERT_READ_LOCK(x) MUST_BE_READ_LOCKED(&ip_conntrack_lock)
+#define ASSERT_WRITE_LOCK(x) MUST_BE_WRITE_LOCKED(&ip_conntrack_lock)
+ 
+#include <linux/netfilter_ipv4/listhelp.h>
+#include <linux/netfilter_ipv4/ip_conntrack.h>
+#include <linux/netfilter_ipv4/ip_conntrack_core.h>
+#include <linux/netfilter_ipv4/ip_conntrack_helper.h>
+
+static LIST_HEAD(helpers);
+static LIST_HEAD(helper_ports);
+
+struct helper_port {
+	struct list_head		list;
+	struct ip_conntrack_helper	*helper;
+	struct ip_conntrack_tuple	tuple;
+};
+
+static struct helper_port *_hp_find_byport(struct ip_conntrack_helper *h, 
+					   u_int16_t port)
+{
+	struct helper_port *hp;
+	
+	/* Must be locked under ip_conntrack_lock */
+	list_for_each_entry(hp, &helper_ports, list) {
+		if (hp->helper == h && 
+		    hp->tuple.src.u.tcp.port == htons(port))
+			return hp;
+	}
+	return NULL;
+}
+
+int ip_conntrack_helper_add_port(struct ip_conntrack_helper *h, u_int16_t port)
+{
+	struct helper_port *hp;
+
+	hp = kmalloc(sizeof(*hp), GFP_KERNEL);
+	if (!hp)
+		return -ENOMEM;
+
+	/* Fill in the data */
+	hp->helper = h;
+	memcpy(&hp->tuple, &h->tuple, sizeof(struct ip_conntrack_tuple));
+	/* FIXME: this is a bit of a hack */
+	hp->tuple.src.u.tcp.port = htons(port);
+
+	WRITE_LOCK(&ip_conntrack_lock);
+
+	if (_hp_find_byport(h, port)) {
+		WRITE_UNLOCK(&ip_conntrack_lock);
+		kfree(hp);
+		return -EEXIST;
+	}
+
+	list_add(&hp->list, &helper_ports);
+
+	WRITE_UNLOCK(&ip_conntrack_lock);
+
+	return 0;
+}
+
+int ip_conntrack_helper_del_port(struct ip_conntrack_helper *h, u_int16_t port)
+{
+	struct helper_port *hp;
+
+	WRITE_LOCK(&ip_conntrack_lock);
+
+	hp = _hp_find_byport(h, port);
+
+	if (!hp) {
+		WRITE_UNLOCK(&ip_conntrack_lock);
+		return -ENODEV;
+	}
+
+	list_del(&hp->list);
+
+	WRITE_UNLOCK(&ip_conntrack_lock);
+
+	kfree(hp);
+
+	return 0;
+}
+
+#ifdef CONFIG_PROC_FS
+static struct proc_dir_entry *helper_procdir;
+
+static void *helper_seq_start(struct seq_file *s, loff_t *pos)
+{
+	struct proc_dir_entry *pde = s->private;
+	struct ip_conntrack_helper *h = pde->data;
+	struct helper_port *hp;
+	int i = 0;
+
+	/* strange seq_file aop calls stop even if start fails,
+	 * thus we need to unconditionally grab the lock */
+	READ_LOCK(&ip_conntrack_lock);
+
+	if (list_empty(&helper_ports))
+		return NULL;
+
+	list_for_each_entry(hp, &helper_ports, list) {
+		if (hp->helper == h) {
+			if (i == *pos) 
+				return hp;
+			i++;
+		}
+	}
+
+	return NULL;
+}
+
+static void *helper_seq_next(struct seq_file *s, void *v, loff_t *pos)
+{
+	struct proc_dir_entry *pde = s->private;
+	struct ip_conntrack_helper *h = pde->data;
+	struct helper_port *hp = v;
+
+	(*pos)++;
+	list_for_each_entry(hp, &hp->list, list) {
+		/* end traversal at list head */
+		if ((struct list_head *) hp == &helper_ports)
+			return NULL;
+		if (hp->helper == h) {
+			return hp;
+		}
+	}
+
+	return NULL;
+}
+
+static void helper_seq_stop(struct seq_file *s, void *v)
+{
+	READ_UNLOCK(&ip_conntrack_lock);
+}
+
+static int helper_seq_show(struct seq_file *s, void *v)
+{
+	struct helper_port *hp = v;
+
+	return seq_printf(s, "%u\n", ntohs(hp->tuple.src.u.tcp.port));
+}
+
+static struct seq_operations helper_seq_ops = {
+	.start	= helper_seq_start,
+	.next	= helper_seq_next,
+	.stop	= helper_seq_stop,
+	.show	= helper_seq_show,
+};
+
+static int helper_proc_open(struct inode *inode, struct file *file)
+{
+	int ret = seq_open(file, &helper_seq_ops);
+
+	if (!ret) {
+		struct seq_file *sf = file->private_data;
+		struct proc_dir_entry *pde = PDE(inode);
+
+		sf->private = pde;
+		/* FIXME: do we have to increase usage count ?*/
+	}
+
+	return ret;
+}
+
+static int helper_proc_release(struct inode *inode, struct file *file)
+{
+	int ret;
+
+	ret = seq_release(inode, file);
+
+	return ret;
+}
+
+static ssize_t helper_proc_write(struct file *file, const char __user *input,
+				 size_t size, loff_t *ofs)
+{
+#define PROC_WRITELEN	10
+	char buffer[PROC_WRITELEN+1];
+	struct proc_dir_entry *pde = PDE(file->f_dentry->d_inode);
+	struct ip_conntrack_helper *h = pde->data;
+	unsigned long port;
+
+	if (copy_from_user(buffer, input, PROC_WRITELEN))
+		return -EFAULT;
+
+	if (*buffer == '+') {
+		port = simple_strtoul(buffer+1, NULL, 10);
+		if (ip_conntrack_helper_add_port(h, port))
+			return -EINVAL;
+	} else if (*buffer == '-') {
+		port = simple_strtoul(buffer+1, NULL, 10);
+		if (ip_conntrack_helper_del_port(h, port))
+			return -EINVAL;
+	} else
+		return -EIO;
+
+	return size;
+}
+
+static struct file_operations helper_proc_fops = {
+	.owner	= THIS_MODULE,
+	.open	= helper_proc_open,
+	.read	= seq_read,
+	.write	= helper_proc_write,
+	.llseek	= seq_lseek,
+	.release = helper_proc_release,
+};
+
+#endif
+
+static inline int helper_cmp(const struct ip_conntrack_helper *i,
+			     const struct ip_conntrack_tuple *rtuple)
+{
+	return ip_ct_tuple_mask_cmp(rtuple, &i->tuple, &i->mask);
+}
+
+struct ip_conntrack_helper *ip_ct_find_helper(const struct ip_conntrack_tuple *tuple)
+{
+	return LIST_FIND(&helpers, helper_cmp,
+			 struct ip_conntrack_helper *,
+			 tuple);
+}
+
+
+int ip_conntrack_helper_register(struct ip_conntrack_helper *me)
+{
+	BUG_ON(me->timeout == 0);
+
+
+	WRITE_LOCK(&ip_conntrack_lock);
+	list_prepend(&helpers, me);
+	WRITE_UNLOCK(&ip_conntrack_lock);
+
+#ifdef CONFIG_PROC_FS
+	me->pde = create_proc_entry(me->name, S_IWUSR|S_IRUSR, helper_procdir);
+	if (!me->pde)
+		return 1;
+	me->pde->proc_fops = &helper_proc_fops;
+	me->pde->data = me;
+#endif
+
+	return 0;
+}
+
+static inline int unhelp(struct ip_conntrack_tuple_hash *i,
+			 const struct ip_conntrack_helper *me)
+{
+	if (tuplehash_to_ctrack(i)->helper == me)
+		tuplehash_to_ctrack(i)->helper = NULL;
+	return 0;
+}
+
+void ip_conntrack_helper_unregister(struct ip_conntrack_helper *me)
+{
+	unsigned int i;
+	struct ip_conntrack_expect *exp, *tmp;
+	struct helper_port *hp, *hp2;
+
+#ifdef CONFIG_PROC_FS
+	remove_proc_entry(me->pde->name, me->pde->parent);
+#endif
+
+	/* Need write lock here, to delete helper. */
+	WRITE_LOCK(&ip_conntrack_lock);
+
+	/* First delete us from global list of helpers */
+	LIST_DELETE(&helpers, me);
+
+	/* Then delete us from helper_ports */
+	list_for_each_entry_safe(hp, hp2, &helper_ports, list) {
+		if (hp->helper == me) {
+			list_del(&hp->list);
+			kfree(hp);
+		}
+	}
+
+	/* Get rid of expectations */
+	list_for_each_entry_safe(exp, tmp, &ip_conntrack_expect_list, list) {
+		if (exp->master->helper == me && del_timer(&exp->timeout)) {
+			__ip_ct_unlink_expect(exp);
+			__ip_ct_destroy_expect(exp);
+		}
+	}
+	/* Get rid of expecteds, set helpers to NULL. */
+	LIST_FIND_W(&ip_conntrack_unconfirmed, 
+		    unhelp, struct ip_conntrack_tuple_hash*, me);
+	for (i = 0; i < ip_conntrack_htable_size; i++)
+		LIST_FIND_W(&ip_conntrack_hash[i], unhelp,
+			    struct ip_conntrack_tuple_hash *, me);
+	WRITE_UNLOCK(&ip_conntrack_lock);
+
+	/* Someone could be still looking at the helper in a bh. */
+	synchronize_net();
+}
+
+int ip_conntrack_helper_init(void)
+{
+#ifdef CONFIG_PROC_FS
+	helper_procdir = proc_mkdir("ip_conntrack_helper", proc_net);
+	if (!helper_procdir) {
+		printk(KERN_ERR "ip_conntrack_helper: unable to create proc\n");
+		return -ENOMEM;
+	}
+#endif
+
+	return 0;
+}
+
+void ip_conntrack_helper_fini(void)
+{
+#ifdef CONFIG_PROC_FS
+	remove_proc_entry(helper_procdir->name, helper_procdir->parent);
+#endif
+}
+
+EXPORT_SYMBOL(ip_conntrack_helper_add_port);
+EXPORT_SYMBOL(ip_conntrack_helper_del_port);
diff -Nru --exclude .depend --exclude '*.o' --exclude '*.ko' --exclude '*.ver' --exclude '.*.flags' --exclude '*.orig' --exclude '*.rej' --exclude '*.cmd' --exclude '*.mod.c' --exclude '*~' linux-2.6.11-rc4/net/ipv4/netfilter/ip_conntrack_standalone.c linux-2.6.11-rc4-helper_sysctl/net/ipv4/netfilter/ip_conntrack_standalone.c
--- linux-2.6.11-rc4/net/ipv4/netfilter/ip_conntrack_standalone.c	2005-02-13 13:52:06.000000000 +0100
+++ linux-2.6.11-rc4-helper_sysctl/net/ipv4/netfilter/ip_conntrack_standalone.c	2005-02-13 16:14:26.000000000 +0100
@@ -918,3 +918,6 @@
 #ifdef CONFIG_IP_NF_NAT_NEEDED
 EXPORT_SYMBOL(ip_conntrack_tcp_update);
 #endif
+EXPORT_SYMBOL(__ip_ct_unlink_expect);
+EXPORT_SYMBOL(__ip_ct_destroy_expect);
+EXPORT_SYMBOL(ip_conntrack_unconfirmed);

