diff -ruN /virgin/linux-2.4.5-ac5/Documentation/Configure.help /usr/src/linux/Documentation/Configure.help
--- /virgin/linux-2.4.5-ac5/Documentation/Configure.help	Sat Jun 23 07:12:17 2001
+++ /usr/src/linux/Documentation/Configure.help	Tue Jun 12 02:55:07 2001
@@ -1923,6 +1923,38 @@
   If you want to compile it as a module, say M here and read
   Documentation/modules.txt.  If unsure, say `Y'.
 
+IP: Netfilter messages (EXPERIMENTAL)
+CONFIG_IP_NF_NETLINK
+  Netfilter has the ability to communicat IP table, connection
+  tracking and other various information to user space. The
+  nfnetlink software package (ftp://ftp.linux-sna.org/pub/netfilter/)
+  provides the nf application and nfnetlink library to read and
+  write data to NFNETLINK sockets.
+
+  If you want to compile it as a module, say M here and read
+  Documentation/modules.txt.  If unsure, say `N'.
+
+IP: IP tables message support (EXPERIMENTAL)
+CONFIG_IP_NF_IPTNETLINK
+  Netfilter has the ability to communicate IP table information
+  to user space. This feature allows the user to receive IP table
+  event notification and provides the ability to change IP table
+  information from user space through a NFNETLINK socket.
+
+  If you want to compile it as a module, say M here and read
+  Documentation/modules.txt.  If unsure, say `N'.
+
+IP: conntection tracking message support (EXPERIMENTAL)
+CONFIG_IP_NF_CTNETLINK
+  Netfilter has the ability to communicate connection tracking
+  information to user space. This feature allows the user to
+  receive connection tracking event notification and provides the
+  ability to change connection states from user space through a
+  NFNETLINK socket.
+
+  If you want to compile it as a module, say M here and read
+  Documentation/modules.txt.  If unsure, say `N'.
+
 IP: user space queueing via NETLINK (EXPERIMENTAL)
 CONFIG_IP_NF_QUEUE
   Netfilter has the ability to queue packets to user space: the
diff -ruN /virgin/linux-2.4.5-ac5/include/linux/netfilter_ipv4/ip_conntrack.h /usr/src/linux/include/linux/netfilter_ipv4/ip_conntrack.h
--- /virgin/linux-2.4.5-ac5/include/linux/netfilter_ipv4/ip_conntrack.h	Fri Apr 27 14:15:01 2001
+++ /usr/src/linux/include/linux/netfilter_ipv4/ip_conntrack.h	Tue Jun 19 01:40:54 2001
@@ -23,8 +23,26 @@
 	/* >= this indicates reply direction */
 	IP_CT_IS_REPLY,
 
-	/* Number of distinct IP_CT types (no NEW in reply dirn). */
-	IP_CT_NUMBER = IP_CT_IS_REPLY * 2 - 1
+	/* For ctnetlink only, when connection gets deleted. */
+        IP_CT_DELETE,
+
+	/* Number of distinct IP_CT types (no NEW in reply dirn, no DELETE). */
+	IP_CT_NUMBER = IP_CT_IS_REPLY * 2 - 1 
+};
+
+/* Bitset representing status of connection. */
+enum ip_conntrack_status {
+        /* It's an expected connection: bit 0 set.  This bit never changed */
+        IPS_EXPECTED_BIT = 0,
+        IPS_EXPECTED = (1 << IPS_EXPECTED_BIT),
+
+        /* We've seen packets both ways: bit 1 set.  Can be set, not unset. */
+        IPS_SEEN_REPLY_BIT = 1,
+        IPS_SEEN_REPLY = (1 << IPS_SEEN_REPLY_BIT),
+
+        /* Conntrack should never be early-expired. */
+        IPS_ASSURED_BIT = 2,
+        IPS_ASSURED = (1 << IPS_ASSURED_BIT),
 };
 
 #ifdef __KERNEL__
@@ -47,21 +65,6 @@
 #define IP_NF_ASSERT(x)
 #endif
 
-/* Bitset representing status of connection. */
-enum ip_conntrack_status {
-	/* It's an expected connection: bit 0 set.  This bit never changed */
-	IPS_EXPECTED_BIT = 0,
-	IPS_EXPECTED = (1 << IPS_EXPECTED_BIT),
-
-	/* We've seen packets both ways: bit 1 set.  Can be set, not unset. */
-	IPS_SEEN_REPLY_BIT = 1,
-	IPS_SEEN_REPLY = (1 << IPS_SEEN_REPLY_BIT),
-
-	/* Conntrack should never be early-expired. */
-	IPS_ASSURED_BIT = 2,
-	IPS_ASSURED = (1 << IPS_ASSURED_BIT),
-};
-
 struct ip_conntrack_expect
 {
 	/* Internal linked list */
@@ -163,9 +166,24 @@
 extern void ip_ct_refresh(struct ip_conntrack *ct,
 			  unsigned long extra_jiffies);
 
-/* These are for NAT.  Icky. */
-/* Call me when a conntrack is destroyed. */
-extern void (*ip_conntrack_destroyed)(struct ip_conntrack *conntrack);
+/* This is for the ip_conntrack_notify facilities. */
+struct ip_conntrack_notify
+{
+        /* Internal use. */
+        struct list_head list;
+
+	void (*destroyed)(struct ip_conntrack *conntrack);
+        void (*created)(struct ip_conntrack *conntrack,
+                enum ip_conntrack_info info,
+                const struct net_device *in, 
+		const struct net_device *out);
+};
+
+extern int ip_conntrack_notify_register(struct ip_conntrack_notify *nb);
+extern int ip_conntrack_notify_unregister(struct ip_conntrack_notify *nb);
+
+/* For ctnetlink. */
+extern void ip_conntrack_put(struct ip_conntrack *ct);
 
 /* Returns new sk_buff, or NULL */
 struct sk_buff *
diff -ruN /virgin/linux-2.4.5-ac5/include/linux/netfilter_ipv4/ip_conntrack_core.h /usr/src/linux/include/linux/netfilter_ipv4/ip_conntrack_core.h
--- /virgin/linux-2.4.5-ac5/include/linux/netfilter_ipv4/ip_conntrack_core.h	Fri Apr 27 14:15:01 2001
+++ /usr/src/linux/include/linux/netfilter_ipv4/ip_conntrack_core.h	Tue Jun 19 01:40:54 2001
@@ -29,7 +29,7 @@
 		     struct ip_conntrack_protocol *protocol);
 
 /* Find a connection corresponding to a tuple. */
-struct ip_conntrack_tuple_hash *
+extern struct ip_conntrack_tuple_hash *
 ip_conntrack_find_get(const struct ip_conntrack_tuple *tuple,
 		      const struct ip_conntrack *ignored_conntrack);
 
@@ -44,6 +44,7 @@
 	return NF_ACCEPT;
 }
 
+extern unsigned int ip_conntrack_htable_size;
 extern struct list_head *ip_conntrack_hash;
 extern struct list_head expect_list;
 DECLARE_RWLOCK_EXTERN(ip_conntrack_lock);
diff -ruN /virgin/linux-2.4.5-ac5/include/linux/netfilter_ipv4/ip_conntrack_ftp.h /usr/src/linux/include/linux/netfilter_ipv4/ip_conntrack_ftp.h
--- /virgin/linux-2.4.5-ac5/include/linux/netfilter_ipv4/ip_conntrack_ftp.h	Wed Apr 25 15:00:28 2001
+++ /usr/src/linux/include/linux/netfilter_ipv4/ip_conntrack_ftp.h	Tue Jun 19 01:40:54 2001
@@ -2,15 +2,6 @@
 #define _IP_CONNTRACK_FTP_H
 /* FTP tracking. */
 
-#ifndef __KERNEL__
-#error Only in kernel.
-#endif
-
-#include <linux/netfilter_ipv4/lockhelp.h>
-
-/* Protects ftp part of conntracks */
-DECLARE_LOCK_EXTERN(ip_ftp_lock);
-
 enum ip_ct_ftp_type
 {
 	/* PORT command from client */
@@ -41,4 +32,10 @@
 	int seq_aft_nl_set[IP_CT_DIR_MAX];
 };
 
+#ifdef __KERNEL__
+#include <linux/netfilter_ipv4/lockhelp.h>
+
+/* Protects ftp part of conntracks */
+DECLARE_LOCK_EXTERN(ip_ftp_lock);
+#endif /* __KERNEL__ */
 #endif /* _IP_CONNTRACK_FTP_H */
diff -ruN /virgin/linux-2.4.5-ac5/include/linux/netfilter_ipv4/ip_conntrack_protocol.h /usr/src/linux/include/linux/netfilter_ipv4/ip_conntrack_protocol.h
--- /virgin/linux-2.4.5-ac5/include/linux/netfilter_ipv4/ip_conntrack_protocol.h	Fri Apr 27 14:15:01 2001
+++ /usr/src/linux/include/linux/netfilter_ipv4/ip_conntrack_protocol.h	Tue Jun 19 01:40:54 2001
@@ -35,7 +35,7 @@
 	/* Returns verdict for packet, or -1 for invalid. */
 	int (*packet)(struct ip_conntrack *conntrack,
 		      struct iphdr *iph, size_t len,
-		      enum ip_conntrack_info ctinfo);
+		      enum ip_conntrack_info ctinfo, int *set_notify);
 
 	/* Called when a new connection for this protocol found;
 	 * returns TRUE if it's OK.  If so, packet() called next. */
diff -ruN /virgin/linux-2.4.5-ac5/include/linux/netfilter_ipv4/ip_conntrack_tcp.h /usr/src/linux/include/linux/netfilter_ipv4/ip_conntrack_tcp.h
--- /virgin/linux-2.4.5-ac5/include/linux/netfilter_ipv4/ip_conntrack_tcp.h	Fri Aug  4 13:07:24 2000
+++ /usr/src/linux/include/linux/netfilter_ipv4/ip_conntrack_tcp.h	Fri Jun  1 00:31:00 2001
@@ -2,10 +2,6 @@
 #define _IP_CONNTRACK_TCP_H
 /* TCP tracking. */
 
-#ifndef __KERNEL__
-#error Only in kernel.
-#endif
-
 enum tcp_conntrack {
 	TCP_CONNTRACK_NONE,
 	TCP_CONNTRACK_ESTABLISHED,
diff -ruN /virgin/linux-2.4.5-ac5/include/linux/netfilter_ipv4/ip_conntrack_tuple.h /usr/src/linux/include/linux/netfilter_ipv4/ip_conntrack_tuple.h
--- /virgin/linux-2.4.5-ac5/include/linux/netfilter_ipv4/ip_conntrack_tuple.h	Thu Aug 10 12:35:15 2000
+++ /usr/src/linux/include/linux/netfilter_ipv4/ip_conntrack_tuple.h	Fri Jun  1 00:31:00 2001
@@ -62,8 +62,6 @@
 	} dst;
 };
 
-#ifdef __KERNEL__
-
 #define DUMP_TUPLE(tp)						\
 DEBUGP("tuple %p: %u %u.%u.%u.%u:%hu -> %u.%u.%u.%u:%hu\n",	\
        (tp), (tp)->dst.protonum,				\
@@ -114,6 +112,8 @@
 		 || ((t->dst.protonum ^ tuple->dst.protonum)
 		     & mask->dst.protonum));
 }
+
+#ifdef __KERNEL__
 
 /* Connections have two entries in the hash table: one for each way */
 struct ip_conntrack_tuple_hash
diff -ruN /virgin/linux-2.4.5-ac5/include/linux/netfilter_ipv4/ip_nat.h /usr/src/linux/include/linux/netfilter_ipv4/ip_nat.h
--- /virgin/linux-2.4.5-ac5/include/linux/netfilter_ipv4/ip_nat.h	Wed Apr 25 15:00:28 2001
+++ /usr/src/linux/include/linux/netfilter_ipv4/ip_nat.h	Tue Jun 19 01:40:54 2001
@@ -55,22 +55,6 @@
 	struct ip_nat_range range[1];
 };
 
-#ifdef __KERNEL__
-#include <linux/list.h>
-#include <linux/netfilter_ipv4/lockhelp.h>
-
-/* Protects NAT hash tables, and NAT-private part of conntracks. */
-DECLARE_RWLOCK_EXTERN(ip_nat_lock);
-
-/* Hashes for by-source and IP/protocol. */
-struct ip_nat_hash
-{
-	struct list_head list;
-
-	/* conntrack we're embedded in: NULL if not in hash. */
-	struct ip_conntrack *conntrack;
-};
-
 /* Worst case: local-out manip + 1 post-routing, and reverse dirn. */
 #define IP_NAT_MAX_MANIPS (2*3)
 
@@ -88,6 +72,19 @@
 	/* Manipulations to occur at each conntrack in this dirn. */
 	struct ip_conntrack_manip manip;
 };
+
+#ifdef __KERNEL__
+#include <linux/netfilter_ipv4/lockhelp.h>
+#include <linux/list.h>
+
+/* Hashes for by-source and IP/protocol. */
+struct ip_nat_hash
+{
+        struct list_head list;
+
+        /* conntrack we're embedded in: NULL if not in hash. */
+        struct ip_conntrack *conntrack;
+};
 	
 /* The structure embedded in the conntrack structure. */
 struct ip_nat_info
@@ -110,6 +107,9 @@
 
 	struct ip_nat_seq seq[IP_CT_DIR_MAX];
 };
+
+/* Protects NAT hash tables, and NAT-private part of conntracks. */
+DECLARE_RWLOCK_EXTERN(ip_nat_lock);
 
 /* Set up the info structure to map into this range. */
 extern unsigned int ip_nat_setup_info(struct ip_conntrack *conntrack,
diff -ruN /virgin/linux-2.4.5-ac5/include/linux/netfilter_ipv4/ip_tables.h /usr/src/linux/include/linux/netfilter_ipv4/ip_tables.h
--- /virgin/linux-2.4.5-ac5/include/linux/netfilter_ipv4/ip_tables.h	Mon Dec 11 13:31:24 2000
+++ /usr/src/linux/include/linux/netfilter_ipv4/ip_tables.h	Tue Jun 19 01:40:54 2001
@@ -409,6 +409,22 @@
 extern int ipt_register_match(struct ipt_match *match);
 extern void ipt_unregister_match(struct ipt_match *match);
 
+/* The table itself */
+struct ipt_table_info
+{
+        /* Size per table */
+        unsigned int size;
+        /* Number of entries: FIXME. --RR */
+        unsigned int number;
+
+        /* Entry points and underflows */
+        unsigned int hook_entry[NF_IP_NUMHOOKS];
+        unsigned int underflow[NF_IP_NUMHOOKS];
+
+        /* ipt_entry tables: one per CPU */
+        char entries[0] __attribute__((aligned(SMP_CACHE_BYTES)));
+};
+
 /* Furniture shopping... */
 struct ipt_table
 {
@@ -440,5 +456,15 @@
 				 void *userdata);
 
 #define IPT_ALIGN(s) (((s) + (__alignof__(struct ipt_entry)-1)) & ~(__alignof__(struct ipt_entry)-1))
+
+extern struct semaphore ipt_mutex;
+
+extern inline struct ipt_table *
+ipt_find_table_lock(const char *name, int *error, struct semaphore *mutex);
+extern inline struct ipt_match *
+ipt_find_match_lock(const char *name, int *error, struct semaphore *mutex);
+extern inline struct ipt_target *
+ipt_find_target_lock(const char *name, int *error, struct semaphore *mutex);
+
 #endif /*__KERNEL__*/
 #endif /* _IPTABLES_H */
diff -ruN /virgin/linux-2.4.5-ac5/include/linux/netlink.h /usr/src/linux/include/linux/netlink.h
--- /virgin/linux-2.4.5-ac5/include/linux/netlink.h	Sun Nov 12 20:37:17 2000
+++ /usr/src/linux/include/linux/netlink.h	Tue Jun 12 02:07:20 2001
@@ -5,6 +5,7 @@
 #define NETLINK_SKIP		1	/* Reserved for ENskip  			*/
 #define NETLINK_USERSOCK	2	/* Reserved for user mode socket protocols 	*/
 #define NETLINK_FIREWALL	3	/* Firewalling hook				*/
+#define NETLINK_NETFILTER	5	/* Netfilter hook. */
 #define NETLINK_ARPD		8
 #define NETLINK_ROUTE6		11	/* af_inet6 route comm channel */
 #define NETLINK_IP6_FW		13
diff -ruN /virgin/linux-2.4.5-ac5/include/linux/nfnetlink.h /usr/src/linux/include/linux/nfnetlink.h
--- /virgin/linux-2.4.5-ac5/include/linux/nfnetlink.h	Wed Dec 31 16:00:00 1969
+++ /usr/src/linux/include/linux/nfnetlink.h	Thu Jun 21 05:47:40 2001
@@ -0,0 +1,203 @@
+#ifndef _NFNETLINK_H
+#define _NFNETLINK_H
+
+/* nfnetlink.h: structures and definitions for nfnetlink.
+ */
+
+/* netfilter netlink message types. 
+ */
+#define NFM_BASE		0x10
+
+#define NFM_GETCONNTRACK	(NFM_BASE + 0)
+#define NFM_DELCONNTRACK	(NFM_BASE + 1)
+#define NFM_NEWCONNTRACK	(NFM_BASE + 2)
+
+#define NFM_GETIPTABLE		(NFM_BASE + 3)
+#define NFM_DELIPTABLE		(NFM_BASE + 4)
+#define NFM_NEWIPTABLE		(NFM_BASE + 5)
+
+#define NFM_MAX         	(NFM_BASE + 6)
+
+/* Generic structure for encapsulation optional netfilter information.
+ * It is reminiscent of sockaddr, but with sa_family replaced
+ * with attribute type. 
+ * ! This should someday be put somewhere generic as now rtnetlink and
+ * ! nfnetlink use the same attributes methods. - J. Schulist.
+ */
+
+struct nfattr
+{
+        unsigned short  nfa_len;
+        unsigned short  nfa_type;
+};
+
+#define NFA_ALIGNTO     4
+#define NFA_ALIGN(len)	(((len) + NFA_ALIGNTO - 1) & ~(NFA_ALIGNTO - 1))
+#define NFA_OK(nfa,len)	((len) > 0 && (nfa)->nfa_len >= sizeof(struct nfattr) \
+	&& (nfa)->nfa_len <= (len))
+#define NFA_NEXT(nfa,attrlen)	((attrlen) -= NFA_ALIGN((nfa)->nfa_len), \
+	(struct nfattr *)(((char *)(nfa)) + NFA_ALIGN((nfa)->nfa_len)))
+#define NFA_LENGTH(len)	(NFA_ALIGN(sizeof(struct nfattr)) + (len))
+#define NFA_SPACE(len)	NFA_ALIGN(NFA_LENGTH(len))
+#define NFA_DATA(nfa)   ((void *)(((char *)(nfa)) + NFA_LENGTH(0)))
+#define NFA_PAYLOAD(nfa) ((int)((nfa)->nfa_len) - NFA_LENGTH(0))
+
+/* General form of address family dependent message.
+ */
+struct nfgenmsg {
+	unsigned char nfgen_family;
+};
+
+struct iptgenmsg {
+	unsigned char 	iptgen_family;
+	char 		iptgen_table[IPT_TABLE_MAXNAMELEN];
+};
+
+struct iptmsg {
+	unsigned char		iptm_family;
+	char			iptm_table[IPT_TABLE_MAXNAMELEN];
+	char			iptm_chain[IPT_FUNCTION_MAXNAMELEN];
+	unsigned int		iptm_entry_num;
+};
+
+enum iptattr_type_t
+{
+        IPTA_UNSPEC,	/* [none] I don't know (unspecified). */
+	IPTA_IP,	/* [ipt_ip] */
+	IPTA_NFCACHE,	/* [u_int] */
+	IPTA_COUNTERS,	/* [ipt_counters] */
+	IPTA_MATCH,	/* [ipt_info] */
+	IPTA_TARGET,	/* [ipt_info] */
+        IPTA_MAX = IPTA_TARGET
+};
+
+struct ipta_info {
+	u_int16_t 	size;
+	char 		name[IPT_FUNCTION_MAXNAMELEN];
+	unsigned char 	data[0];
+};
+
+#define NFM_IPTA(n)	((struct nfattr *)(((char *)(n)) \
+	+ NLMSG_ALIGN(sizeof(struct iptmsg))))
+
+/* Generic nfnetlink message header.
+ */
+struct ctmsg {
+        unsigned char   ctm_family;
+        unsigned char   ctm_orig_len;
+        unsigned char   ctm_rply_len;
+};
+
+#define NFM_NFA(n)      ((struct nfattr *)(((char *)(n)) \
+        + NLMSG_ALIGN(sizeof(struct ctmsg))))
+#define NFM_PAYLOAD(n)  NLMSG_PAYLOAD(n, sizeof(struct ctmsg))
+
+/* ctnetlink attribute types.
+ */
+enum ctattr_type_t
+{
+        CTA_UNSPEC,     /* [none] I don't know (unspecified). */
+        CTA_ORIG,       /* [ip_conntrack_tuple] Original tuple. */
+        CTA_RPLY,       /* [ip_conntrack_tuple] Reply tuple. */
+        CTA_IIF,        /* [char] Input interface name (ie eth0). */
+        CTA_OIF,        /* [char] Output interface name (ie eth1). */
+        CTA_STATUS,     /* [unsigned long] Status of connection. */
+        CTA_INFO,       /* [unsigned long] Information (ctinfo). */
+        CTA_PROTOINFO,  /* [rta_proto] Protocol specific ct information. */
+        CTA_HELPINFO,   /* [rta_help] Helper specific information. */
+        CTA_NATINFO,    /* [rta_nat] Any NAT transformations. */
+        CTA_MAX = CTA_NATINFO
+};
+
+/* Attribute specific data structures.
+ */
+
+#ifdef CONFIG_IP_NF_NAT_NEEDED
+struct cta_nat {
+	unsigned int num_manips;
+        struct ip_nat_info_manip manips[IP_NAT_MAX_MANIPS];
+};
+#endif /* CONFIG_IP_NF_NAT_NEEDED */
+
+struct cta_proto {
+	unsigned char num_proto;	/* Protocol number IPPROTO_X */
+	union {
+                struct ip_ct_tcp tcp;
+                struct ip_ct_icmp icmp;
+        } proto;
+};
+
+struct cta_help {
+	struct ip_conntrack_tuple tuple;
+        struct ip_conntrack_tuple mask;
+
+	union {
+                struct ip_ct_ftp ct_ftp_info;
+        } help;
+};
+
+/* ctnetlink multicast groups: reports any change of ctinfo,
+ * ctstatus, or protocol state change.
+ */
+#define NFGRP_IPV4_CT_TCP	0x01
+#define NFGRP_IPV4_CT_UDP	0x02
+#define NFGRP_IPV4_CT_ICMP	0x04
+
+#define NFGRP_IPV6_CT_TCP       0x10
+#define NFGRP_IPV6_CT_UDP       0x20
+#define NFGRP_IPV6_CT_ICMP      0x40
+
+#ifdef __KERNEL__
+extern void __nfa_fill(struct sk_buff *skb, int attrtype,
+        int attrlen, const void *data);
+#define NFA_PUT(skb, attrtype, attrlen, data) \
+({ if (skb_tailroom(skb) < (int)NFA_SPACE(attrlen)) goto nfattr_failure; \
+   __nfa_fill(skb, attrtype, attrlen, data); })
+
+extern struct semaphore nfnl_sem;
+#define nfnl_exlock()		do { } while(0)
+#define nfnl_exunlock()		do { } while(0)
+#define nfnl_exlock_nowait()	(0)
+
+#define nfnl_shlock()		down(&nfnl_sem)
+#define nfnl_shlock_nowait()	down_trylock(&nfnl_sem)
+
+#ifndef CONFIG_NF_NETLINK
+#define nfnl_shunlock()		up(&nfnl_sem)
+#else
+#define nfnl_shunlock()		do { up(&nfnl_sem); \
+                             		if(nfnl && nfnl->receive_queue.qlen) \
+                                     		nfnl->data_ready(nfnl, 0); \
+                        	} while(0)
+#endif
+
+extern void nfnl_lock(void);
+extern void nfnl_unlock(void);
+
+#define NF_MAX_CALLBACK		5
+struct nfnl_callback
+{
+	int type;
+	int (*call)(struct sock *nl, struct sk_buff *skb, 
+		struct nlmsghdr *nlh, int *errp);
+};
+
+struct nfnetlink_info
+{
+	/* Internal use. */
+        struct list_head list;
+
+	char name[32];
+	struct nfnl_callback cb[NF_MAX_CALLBACK];
+};
+
+extern int nfnetlink_register(struct nfnetlink_info *n);
+extern int nfnetlink_unregister(struct nfnetlink_info *n);
+
+extern int nfattr_parse(struct nfattr *tb[], int maxattr, 
+	struct nfattr *nfa, int len);
+extern int nfnetlink_send(struct sk_buff *skb, u32 pid, unsigned group, 
+	int echo);
+
+#endif	/* __KERNEL__ */
+#endif	/* _NFNETLINK_H */
diff -ruN /virgin/linux-2.4.5-ac5/net/ipv4/netfilter/Config.in /usr/src/linux/net/ipv4/netfilter/Config.in
--- /virgin/linux-2.4.5-ac5/net/ipv4/netfilter/Config.in	Tue Mar  6 22:44:16 2001
+++ /usr/src/linux/net/ipv4/netfilter/Config.in	Tue Jun 12 02:56:01 2001
@@ -4,8 +4,13 @@
 mainmenu_option next_comment
 comment '  IP: Netfilter Configuration'
 
+if [ "$CONFIG_NETLINK" = "y" ]; then
+  tristate 'Netfilter messages' CONFIG_IP_NF_NETLINK
+fi
+
 tristate 'Connection tracking (required for masq/NAT)' CONFIG_IP_NF_CONNTRACK
 if [ "$CONFIG_IP_NF_CONNTRACK" != "n" ]; then
+  dep_tristate '  Connection Tracking message support' CONFIG_IP_NF_CTNETLINK $CONFIG_IP_NF_NETLINK $CONFIG_IP_NF_CONNTRACK
   dep_tristate '  FTP protocol support' CONFIG_IP_NF_FTP $CONFIG_IP_NF_CONNTRACK
 fi
 
@@ -15,6 +20,7 @@
 tristate 'IP tables support (required for filtering/masq/NAT)' CONFIG_IP_NF_IPTABLES
 if [ "$CONFIG_IP_NF_IPTABLES" != "n" ]; then
 # The simple matches.
+  dep_tristate '  IP tables message support' CONFIG_IP_NF_IPTNETLINK $CONFIG_IP_NF_NETLINK $CONFIG_IP_NF_IPTABLES
   dep_tristate '  limit match support' CONFIG_IP_NF_MATCH_LIMIT $CONFIG_IP_NF_IPTABLES
   dep_tristate '  MAC address match support' CONFIG_IP_NF_MATCH_MAC $CONFIG_IP_NF_IPTABLES
   dep_tristate '  netfilter MARK match support' CONFIG_IP_NF_MATCH_MARK $CONFIG_IP_NF_IPTABLES
diff -ruN /virgin/linux-2.4.5-ac5/net/ipv4/netfilter/Makefile /usr/src/linux/net/ipv4/netfilter/Makefile
--- /virgin/linux-2.4.5-ac5/net/ipv4/netfilter/Makefile	Wed Apr 25 15:00:28 2001
+++ /usr/src/linux/net/ipv4/netfilter/Makefile	Wed Jun 13 02:11:30 2001
@@ -28,9 +28,15 @@
 ipfwadm-objs		:= $(ip_nf_compat-objs) ipfwadm_core.o
 ipchains-objs		:= $(ip_nf_compat-objs) ipchains_core.o
 
+# netfilter messages
+obj-$(CONFIG_IP_NF_NETLINK) += iptable_netlink.o
+
 # connection tracking
 obj-$(CONFIG_IP_NF_CONNTRACK) += ip_conntrack.o
 
+# connection tracking messages
+obj-$(CONFIG_IP_NF_CTNETLINK) += ip_netlink_conntrack.o
+
 # connection tracking helpers
 obj-$(CONFIG_IP_NF_FTP) += ip_conntrack_ftp.o
 
@@ -39,6 +45,9 @@
 
 # generic IP tables 
 obj-$(CONFIG_IP_NF_IPTABLES) += ip_tables.o
+
+# IP tables messages
+obj-$(CONFIG_IP_NF_IPTNETLINK) += ip_netlink_iptables.o
 
 # the three instances of ip_tables
 obj-$(CONFIG_IP_NF_FILTER) += iptable_filter.o
diff -ruN /virgin/linux-2.4.5-ac5/net/ipv4/netfilter/ip_conntrack_core.c /usr/src/linux/net/ipv4/netfilter/ip_conntrack_core.c
--- /virgin/linux-2.4.5-ac5/net/ipv4/netfilter/ip_conntrack_core.c	Fri Apr 27 14:15:01 2001
+++ /usr/src/linux/net/ipv4/netfilter/ip_conntrack_core.c	Fri Jun  1 00:31:00 2001
@@ -45,10 +45,10 @@
 
 DECLARE_RWLOCK(ip_conntrack_lock);
 
-void (*ip_conntrack_destroyed)(struct ip_conntrack *conntrack) = NULL;
 LIST_HEAD(expect_list);
 LIST_HEAD(protocol_list);
 static LIST_HEAD(helpers);
+static LIST_HEAD(notify_list);
 unsigned int ip_conntrack_htable_size = 0;
 static int ip_conntrack_max = 0;
 static atomic_t ip_conntrack_count = ATOMIC_INIT(0);
@@ -86,7 +86,7 @@
 	return p;
 }
 
-static inline void ip_conntrack_put(struct ip_conntrack *ct)
+inline void ip_conntrack_put(struct ip_conntrack *ct)
 {
 	IP_NF_ASSERT(ct);
 	IP_NF_ASSERT(ct->infos[0].master);
@@ -150,6 +150,55 @@
 	return protocol->invert_tuple(inverse, orig);
 }
 
+static inline void
+ip_conntrack_destroyed(struct ip_conntrack *ct)
+{
+        struct list_head *i;
+
+        for (i = notify_list.next; i != &notify_list; i = i->next)
+                if (((struct ip_conntrack_notify *)i)->destroyed)
+                        ((struct ip_conntrack_notify *)i)->destroyed(ct);
+        return;
+}
+
+static inline void
+ip_conntrack_created(struct ip_conntrack *ct,
+        enum ip_conntrack_info info, const struct net_device *in, 
+	const struct net_device *out)
+{
+        struct list_head *i;
+
+        for (i = notify_list.next; i != &notify_list; i = i->next)
+                if (((struct ip_conntrack_notify *)i)->created)
+                        ((struct ip_conntrack_notify *)i)->created(ct, 
+				info, in, out);
+        return;
+}
+
+int
+ip_conntrack_notify_register(struct ip_conntrack_notify *nb)
+{
+        MOD_INC_USE_COUNT;
+
+        WRITE_LOCK(&ip_conntrack_lock);
+        list_prepend(&notify_list, nb);
+        WRITE_UNLOCK(&ip_conntrack_lock);
+
+        return 0;
+}
+
+int
+ip_conntrack_notify_unregister(struct ip_conntrack_notify *nb)
+{
+        WRITE_LOCK(&ip_conntrack_lock);
+        LIST_DELETE(&notify_list, nb);
+        WRITE_UNLOCK(&ip_conntrack_lock);
+
+        MOD_DEC_USE_COUNT;
+
+        return 0;
+}
+
 static void
 clean_from_lists(struct ip_conntrack *ct)
 {
@@ -182,8 +231,7 @@
 	if (ct->master.master)
 		nf_conntrack_put(&ct->master);
 
-	if (ip_conntrack_destroyed)
-		ip_conntrack_destroyed(ct);
+	ip_conntrack_destroyed(ct);
 	kmem_cache_free(ip_conntrack_cachep, ct);
 	atomic_dec(&ip_conntrack_count);
 }
@@ -570,7 +618,7 @@
 static inline struct ip_conntrack *
 resolve_normal_ct(struct sk_buff *skb,
 		  struct ip_conntrack_protocol *proto,
-		  int *set_reply,
+		  int *set_reply, int *set_notify,
 		  unsigned int hooknum,
 		  enum ip_conntrack_info *ctinfo)
 {
@@ -611,6 +659,7 @@
 			DEBUGP("ip_conntrack_in: new packet for %p\n",
 			       h->ctrack);
 			*ctinfo = IP_CT_NEW;
+			*set_notify = 1;
 		}
 		*set_reply = 0;
 	}
@@ -628,7 +677,7 @@
 	struct ip_conntrack *ct;
 	enum ip_conntrack_info ctinfo;
 	struct ip_conntrack_protocol *proto;
-	int set_reply;
+	int set_reply, set_notify = 0;
 	int ret;
 
 	/* FIXME: Do this right please. --RR */
@@ -668,7 +717,8 @@
 	    && icmp_error_track(*pskb, &ctinfo, hooknum))
 		return NF_ACCEPT;
 
-	if (!(ct = resolve_normal_ct(*pskb, proto,&set_reply,hooknum,&ctinfo)))
+	if (!(ct = resolve_normal_ct(*pskb, proto, &set_reply, &set_notify,
+	    hooknum,&ctinfo)))
 		/* Not valid part of a connection */
 		return NF_ACCEPT;
 
@@ -678,7 +728,8 @@
 
 	IP_NF_ASSERT((*pskb)->nfct);
 
-	ret = proto->packet(ct, (*pskb)->nh.iph, (*pskb)->len, ctinfo);
+	ret = proto->packet(ct, (*pskb)->nh.iph, (*pskb)->len, ctinfo,
+	    &set_notify);
 	if (ret == -1) {
 		/* Invalid */
 		nf_conntrack_put((*pskb)->nfct);
@@ -698,7 +749,8 @@
 	}
 	if (set_reply)
 		set_bit(IPS_SEEN_REPLY_BIT, &ct->status);
-
+	if (set_notify)
+		ip_conntrack_created(ct, ctinfo, in, out);
 	return ret;
 }
 
@@ -1050,6 +1102,7 @@
 #ifdef CONFIG_SYSCTL
 	unregister_sysctl_table(ip_conntrack_sysctl_header);
 #endif
+
 	ip_ct_attach = NULL;
 	/* This makes sure all current packets have passed through
            netfilter framework.  Roll on, two-stage module
diff -ruN /virgin/linux-2.4.5-ac5/net/ipv4/netfilter/ip_conntrack_proto_generic.c /usr/src/linux/net/ipv4/netfilter/ip_conntrack_proto_generic.c
--- /virgin/linux-2.4.5-ac5/net/ipv4/netfilter/ip_conntrack_proto_generic.c	Fri Apr 27 14:15:01 2001
+++ /usr/src/linux/net/ipv4/netfilter/ip_conntrack_proto_generic.c	Fri Jun  1 00:31:00 2001
@@ -41,7 +41,7 @@
 /* Returns verdict for packet, or -1 for invalid. */
 static int established(struct ip_conntrack *conntrack,
 		       struct iphdr *iph, size_t len,
-		       enum ip_conntrack_info conntrackinfo)
+		       enum ip_conntrack_info conntrackinfo, int *set_notify)
 {
 	ip_ct_refresh(conntrack, GENERIC_TIMEOUT);
 	return NF_ACCEPT;
diff -ruN /virgin/linux-2.4.5-ac5/net/ipv4/netfilter/ip_conntrack_proto_icmp.c /usr/src/linux/net/ipv4/netfilter/ip_conntrack_proto_icmp.c
--- /virgin/linux-2.4.5-ac5/net/ipv4/netfilter/ip_conntrack_proto_icmp.c	Fri Apr 27 14:15:01 2001
+++ /usr/src/linux/net/ipv4/netfilter/ip_conntrack_proto_icmp.c	Fri Jun  1 00:31:00 2001
@@ -70,7 +70,7 @@
 /* Returns verdict for packet, or -1 for invalid. */
 static int icmp_packet(struct ip_conntrack *ct,
 		       struct iphdr *iph, size_t len,
-		       enum ip_conntrack_info ctinfo)
+		       enum ip_conntrack_info ctinfo, int *set_notify)
 {
 	/* Try to delete connection immediately after all replies:
            won't actually vanish as we still have skb, and del_timer
diff -ruN /virgin/linux-2.4.5-ac5/net/ipv4/netfilter/ip_conntrack_proto_tcp.c /usr/src/linux/net/ipv4/netfilter/ip_conntrack_proto_tcp.c
--- /virgin/linux-2.4.5-ac5/net/ipv4/netfilter/ip_conntrack_proto_tcp.c	Fri Apr 27 14:15:01 2001
+++ /usr/src/linux/net/ipv4/netfilter/ip_conntrack_proto_tcp.c	Fri Jun  1 00:31:00 2001
@@ -146,7 +146,7 @@
 /* Returns verdict for packet, or -1 for invalid. */
 static int tcp_packet(struct ip_conntrack *conntrack,
 		      struct iphdr *iph, size_t len,
-		      enum ip_conntrack_info ctinfo)
+		      enum ip_conntrack_info ctinfo, int *set_notify)
 {
 	enum tcp_conntrack newconntrack, oldtcpstate;
 	struct tcphdr *tcph = (struct tcphdr *)((u_int32_t *)iph + iph->ihl);
@@ -174,6 +174,9 @@
 		return -1;
 	}
 
+	if (oldtcpstate != newconntrack)
+		*set_notify = 1;
+
 	conntrack->proto.tcp.state = newconntrack;
 
 	/* Poor man's window tracking: record SYN/ACK for handshake check */
@@ -196,8 +199,10 @@
 		if (oldtcpstate == TCP_CONNTRACK_SYN_RECV
 		    && CTINFO2DIR(ctinfo) == IP_CT_DIR_ORIGINAL
 		    && tcph->ack && !tcph->syn
-		    && tcph->ack_seq == conntrack->proto.tcp.handshake_ack)
+		    && tcph->ack_seq == conntrack->proto.tcp.handshake_ack) {
 			set_bit(IPS_ASSURED_BIT, &conntrack->status);
+			*set_notify = 1;
+		}
 
 		ip_ct_refresh(conntrack, tcp_timeouts[newconntrack]);
 	}
diff -ruN /virgin/linux-2.4.5-ac5/net/ipv4/netfilter/ip_conntrack_proto_udp.c /usr/src/linux/net/ipv4/netfilter/ip_conntrack_proto_udp.c
--- /virgin/linux-2.4.5-ac5/net/ipv4/netfilter/ip_conntrack_proto_udp.c	Fri Apr 27 14:15:01 2001
+++ /usr/src/linux/net/ipv4/netfilter/ip_conntrack_proto_udp.c	Fri Jun  1 00:31:00 2001
@@ -47,14 +47,17 @@
 /* Returns verdict for packet, and may modify conntracktype */
 static int udp_packet(struct ip_conntrack *conntrack,
 		      struct iphdr *iph, size_t len,
-		      enum ip_conntrack_info conntrackinfo)
+		      enum ip_conntrack_info conntrackinfo, int *set_notify)
 {
 	/* If we've seen traffic both ways, this is some kind of UDP
 	   stream.  Extend timeout. */
 	if (conntrack->status & IPS_SEEN_REPLY) {
+		unsigned long oldstatus = conntrack->status;
 		ip_ct_refresh(conntrack, UDP_STREAM_TIMEOUT);
 		/* Also, more likely to be important, and not a probe */
 		set_bit(IPS_ASSURED_BIT, &conntrack->status);
+		if(oldstatus != conntrack->status)
+			*set_notify = 1;
 	} else
 		ip_ct_refresh(conntrack, UDP_TIMEOUT);
 
diff -ruN /virgin/linux-2.4.5-ac5/net/ipv4/netfilter/ip_conntrack_standalone.c /usr/src/linux/net/ipv4/netfilter/ip_conntrack_standalone.c
--- /virgin/linux-2.4.5-ac5/net/ipv4/netfilter/ip_conntrack_standalone.c	Fri Apr 27 14:15:01 2001
+++ /usr/src/linux/net/ipv4/netfilter/ip_conntrack_standalone.c	Fri Jun  1 00:31:00 2001
@@ -320,14 +320,19 @@
 EXPORT_SYMBOL(ip_conntrack_protocol_register);
 EXPORT_SYMBOL(invert_tuplepr);
 EXPORT_SYMBOL(ip_conntrack_alter_reply);
-EXPORT_SYMBOL(ip_conntrack_destroyed);
 EXPORT_SYMBOL(ip_conntrack_get);
 EXPORT_SYMBOL(ip_conntrack_module);
 EXPORT_SYMBOL(ip_conntrack_helper_register);
 EXPORT_SYMBOL(ip_conntrack_helper_unregister);
+EXPORT_SYMBOL(ip_conntrack_notify_register);
+EXPORT_SYMBOL(ip_conntrack_notify_unregister);
 EXPORT_SYMBOL(ip_ct_selective_cleanup);
 EXPORT_SYMBOL(ip_ct_refresh);
 EXPORT_SYMBOL(ip_conntrack_expect_related);
 EXPORT_SYMBOL(ip_conntrack_tuple_taken);
 EXPORT_SYMBOL(ip_ct_gather_frags);
 EXPORT_SYMBOL(ip_conntrack_htable_size);
+EXPORT_SYMBOL(ip_conntrack_hash);
+EXPORT_SYMBOL(ip_conntrack_lock);
+EXPORT_SYMBOL(ip_conntrack_put);
+EXPORT_SYMBOL(ip_conntrack_find_get);
diff -ruN /virgin/linux-2.4.5-ac5/net/ipv4/netfilter/ip_nat_core.c /usr/src/linux/net/ipv4/netfilter/ip_nat_core.c
--- /virgin/linux-2.4.5-ac5/net/ipv4/netfilter/ip_nat_core.c	Wed May 16 10:31:27 2001
+++ /usr/src/linux/net/ipv4/netfilter/ip_nat_core.c	Fri Jun  1 00:31:00 2001
@@ -856,6 +856,10 @@
 	return NF_ACCEPT;
 }
 
+static struct ip_conntrack_notify nat_notify = { { NULL, NULL },
+						ip_nat_cleanup_conntrack,
+						NULL };
+
 int __init ip_nat_init(void)
 {
 	size_t i;
@@ -882,9 +886,8 @@
 		INIT_LIST_HEAD(&byipsproto[i]);
 	}
 
-	/* FIXME: Man, this is a hack.  <SIGH> */
-	IP_NF_ASSERT(ip_conntrack_destroyed == NULL);
-	ip_conntrack_destroyed = &ip_nat_cleanup_conntrack;
+	if (ip_conntrack_notify_register(&nat_notify) < 0)
+		panic("ip_nat_init: cannot register notifier.\n");
 
 	return 0;
 }
@@ -900,5 +903,5 @@
 void ip_nat_cleanup(void)
 {
 	ip_ct_selective_cleanup(&clean_nat, NULL);
-	ip_conntrack_destroyed = NULL;
+	ip_conntrack_notify_unregister(&nat_notify);
 }
diff -ruN /virgin/linux-2.4.5-ac5/net/ipv4/netfilter/ip_netlink_conntrack.c /usr/src/linux/net/ipv4/netfilter/ip_netlink_conntrack.c
--- /virgin/linux-2.4.5-ac5/net/ipv4/netfilter/ip_netlink_conntrack.c	Wed Dec 31 16:00:00 1969
+++ /usr/src/linux/net/ipv4/netfilter/ip_netlink_conntrack.c	Wed Jun 13 05:30:10 2001
@@ -0,0 +1,427 @@
+/* Connection tracking via netlink socket. Allows for user space
+ * protocol helpers and general trouble making from userspace.
+ *
+ * Jay Schulist <jschlst@samba.org>, Copyright (c) 2001.
+ *
+ * Initial connection tracking via netlink development funded and 
+ * generally made possible by Network Robots, Inc. (www.networkrobots.com)
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/socket.h>
+#include <linux/kernel.h>
+#include <linux/major.h>
+#include <linux/sched.h>
+#include <linux/timer.h>
+#include <linux/string.h>
+#include <linux/sockios.h>
+#include <linux/net.h>
+#include <linux/fcntl.h>
+#include <linux/skbuff.h>
+#include <asm/uaccess.h>
+#include <asm/system.h>
+#include <net/sock.h>
+#include <linux/init.h>
+#include <linux/netlink.h>
+#include <linux/spinlock.h>
+#include <linux/rtnetlink.h>
+
+#include <linux/netfilter.h>
+#include <linux/netfilter_ipv4.h>
+#include <linux/netfilter_ipv4/ip_tables.h>
+#include <linux/netfilter_ipv4/ip_conntrack.h>
+#include <linux/netfilter_ipv4/ip_conntrack_protocol.h>
+#include <linux/netfilter_ipv4/ip_conntrack_helper.h>
+#include <linux/netfilter_ipv4/ip_conntrack_core.h>
+#include <linux/netfilter_ipv4/ip_conntrack_tcp.h>
+#include <linux/netfilter_ipv4/ip_conntrack_icmp.h>
+
+#include <linux/nfnetlink.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>
+
+char ctversion[] = "1.00";
+int ct_debug_level = 1;
+#define ct_debug(level, format, arg...) \
+	if(ct_debug_level > level)  \
+		printk(__FILE__ ": " format, ## arg)
+
+static int ctnetlink_fill_info(struct sk_buff *skb, u32 pid, u32 seq,
+	int event, int nowait, const struct ip_conntrack *ct, 
+	const enum ip_conntrack_info *ctinfo, unsigned char proto,
+	const struct net_device *in, const struct net_device *out)
+{
+	struct nlmsghdr *nlh;
+	struct ctmsg *msg;
+	unsigned long s;
+	unsigned char *b;
+
+	b = skb->tail;
+	nlh = NLMSG_PUT(skb, pid, seq, event, sizeof(struct ctmsg));
+        msg = NLMSG_DATA(nlh);
+        nlh->nlmsg_flags 	= (nowait && pid) ? NLM_F_MULTI : 0;
+        msg->ctm_family		= AF_INET;
+	msg->ctm_orig_len	= sizeof(struct ip_conntrack_tuple);
+	msg->ctm_rply_len	= sizeof(struct ip_conntrack_tuple);
+	NFA_PUT(skb, CTA_ORIG, sizeof(struct ip_conntrack_tuple), 
+		&ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple);
+	NFA_PUT(skb, CTA_RPLY, sizeof(struct ip_conntrack_tuple),
+		&ct->tuplehash[IP_CT_DIR_REPLY].tuple);
+	s = ct->status;
+	NFA_PUT(skb, CTA_STATUS, sizeof(unsigned long), &s);
+	if(in)
+		NFA_PUT(skb, CTA_IIF, IFNAMSIZ, in->name);
+        if(out)
+		NFA_PUT(skb, CTA_OIF, IFNAMSIZ, out->name);
+	if(ctinfo)
+		NFA_PUT(skb, CTA_INFO, sizeof(unsigned long), ctinfo);
+#ifdef CONFIG_IP_NF_NAT_NEEDED
+	if(ct->nat.info.initialized && ct->nat.info.num_manips)
+        {
+                const struct ip_nat_info *nat = &ct->nat.info;
+		struct cta_nat cn;
+
+		cn.num_manips = nat->num_manips;
+		memcpy(&cn.manips, &nat->manips, (nat->num_manips 
+			* sizeof(struct ip_nat_info_manip)));
+		NFA_PUT(skb, CTA_NATINFO, sizeof(struct cta_nat), &cn);
+        }
+#endif /* CONFIG_IP_NF_NAT_NEEDED */
+	if(ct->helper)
+	{
+		struct cta_help ch;
+
+		memcpy(&ch.tuple, &ct->helper->tuple, 
+			sizeof(struct ip_conntrack_tuple));
+		memcpy(&ch.mask, &ct->helper->mask,
+			sizeof(struct ip_conntrack_tuple));
+		memcpy(&ch.help.ct_ftp_info, &ct->help.ct_ftp_info, 
+			sizeof(struct ip_ct_ftp));
+		NFA_PUT(skb, CTA_HELPINFO, sizeof(struct cta_help), &ch);
+	}
+	if(proto == IPPROTO_TCP || proto == IPPROTO_UDP
+		 || proto == IPPROTO_ICMP)
+	{
+		struct cta_proto cp;
+
+	        cp.num_proto = proto;
+		if(proto == IPPROTO_TCP)
+			memcpy(&cp.proto.tcp, &ct->proto.tcp, 
+				sizeof(struct ip_ct_tcp));
+		if(proto == IPPROTO_ICMP)
+			memcpy(&cp.proto.icmp, &ct->proto.icmp,
+				sizeof(struct ip_ct_icmp));
+	        NFA_PUT(skb, CTA_PROTOINFO, sizeof(struct cta_proto), &cp);
+	}
+
+        nlh->nlmsg_len = skb->tail - b;
+        return (skb->len);
+
+nlmsg_failure:
+nfattr_failure:
+        skb_trim(skb, b - skb->data);
+        return (-1);
+}
+
+inline struct sk_buff *ctnetlink_event_build_msg(const struct ip_conntrack *ct, 
+	const enum ip_conntrack_info ctinfo, const unsigned char proto,
+	const struct net_device *in, const struct net_device *out)
+{
+	struct sk_buff *skb;
+	int err;
+
+        skb = alloc_skb(NLMSG_GOODSIZE, GFP_ATOMIC);
+        if(!skb)
+                return (NULL);
+
+	err = ctnetlink_fill_info(skb, 0, 0, NFM_NEWCONNTRACK, 1,
+		ct, &ctinfo, proto, in, out);
+	if(err <= 0)
+		goto nlmsg_failure;
+        return (skb);
+
+nlmsg_failure:
+        if(skb)
+                kfree_skb(skb);
+        return (NULL);
+}
+
+void ctnetlink_create(struct ip_conntrack *ct, enum ip_conntrack_info ctinfo,
+	const struct net_device *in, const struct net_device *out)
+{
+	u16 proto = ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.protonum;
+	struct sk_buff *skb;
+
+        skb = ctnetlink_event_build_msg(ct, ctinfo, proto, in, out);
+        if(!skb)
+                return;
+
+       	if (proto == IPPROTO_TCP) {
+		nfnetlink_send(skb, 0, NFGRP_IPV4_CT_TCP, 0);
+               	return;
+       	}
+       	if (proto == IPPROTO_UDP) {
+		nfnetlink_send(skb, 0, NFGRP_IPV4_CT_UDP, 0);
+               	return;
+       	}
+       	if (proto == IPPROTO_ICMP) {
+		nfnetlink_send(skb, 0, NFGRP_IPV4_CT_ICMP, 0);
+               	return;
+       	}
+
+	kfree_skb(skb);
+	return;
+}
+
+int ctnetlink_check_attributes(struct nlmsghdr *nlh, struct nfattr *cda[])
+{
+        int min_len;
+
+        /* check attribute lengths. */
+        min_len = sizeof(struct ctmsg);
+        if(nlh->nlmsg_len < min_len)
+                return (-EINVAL);
+
+        if(nlh->nlmsg_len > min_len)
+        {
+                struct nfattr *attr = NFM_NFA(NLMSG_DATA(nlh));
+                int attrlen = nlh->nlmsg_len - NLMSG_ALIGN(min_len);
+
+                while(NFA_OK(attr, attrlen))
+                {
+                        unsigned flavor = attr->nfa_type;
+                        if(flavor)
+                        {
+                                if(flavor > CTA_MAX)
+                                        return (-EINVAL);
+                                cda[flavor - 1] = attr;
+                        }
+                        attr = NFA_NEXT(attr, attrlen);
+                }
+        }
+        else
+                return (-EINVAL);
+
+        return (0);
+}
+
+void ctnetlink_destroy(struct ip_conntrack *ct)
+{
+        ctnetlink_create(ct, IP_CT_DELETE, NULL, NULL);
+}
+
+inline int ctnetlink_kill(const struct ip_conntrack *i, void *data)
+{
+	struct ip_conntrack *t = (struct ip_conntrack *)data;
+
+	if(!memcmp(&i->tuplehash[IP_CT_DIR_ORIGINAL], 
+		&t->tuplehash[IP_CT_DIR_ORIGINAL], 
+		sizeof(struct ip_conntrack_tuple_hash)))
+	{
+		ip_conntrack_put(t);
+		return (1);
+	}
+
+	return (0);
+}
+
+int ctnetlink_delete_conntrack(struct sock *ctnl, struct sk_buff *skb, 
+	struct nlmsghdr *nlh, int *errp)
+{
+        struct ip_conntrack_tuple_hash *h;
+	struct ip_conntrack_tuple *tuple;
+	struct nfattr *cda[CTA_MAX];
+
+	if(ctnetlink_check_attributes(nlh, cda) < 0)
+		return (-EINVAL);
+
+	if(cda[CTA_ORIG-1])
+		tuple = NFA_DATA(cda[CTA_ORIG-1]);
+	else
+	{
+		if(cda[CTA_RPLY-1])
+			tuple = NFA_DATA(cda[CTA_RPLY-1]);
+		else
+			return (-EINVAL);
+	}
+
+	h = ip_conntrack_find_get(tuple, NULL);
+        if(!h)
+		return (-ENOENT);
+	ip_ct_selective_cleanup(ctnetlink_kill, h->ctrack);
+
+	return (0);
+}
+
+int ctnetlink_done(struct netlink_callback *cb)
+{
+        return (0);
+}
+
+int ctnetlink_dump_build_msg(const struct ip_conntrack_tuple_hash *hash,
+        struct sk_buff *skb, u32 pid, u32 seq)
+{
+        struct ip_conntrack *ct;
+        int err, proto;
+
+        /* Only count originals */
+        if (DIRECTION(hash))
+                return (0);
+
+        ct = hash->ctrack;
+        if(!ct)
+                goto nlmsg_failure;
+
+        proto = ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.protonum;
+        err = ctnetlink_fill_info(skb, pid, seq, NFM_NEWCONNTRACK, 1,
+                ct, NULL, proto, NULL, NULL);
+        if(err <= 0)
+                goto nlmsg_failure;
+        return (0);
+
+nlmsg_failure:
+        if(skb)
+                kfree_skb(skb);
+        return (-1);
+}
+
+int ctnetlink_dump_table(struct sk_buff *skb, struct netlink_callback *cb)
+{
+        int i;
+        int idx;
+        int s_idx = cb->args[0];
+
+        /* Traverse hash; send originals then reply. */
+        READ_LOCK(&ip_conntrack_lock);
+        for(i = 0, idx = 0; i < ip_conntrack_htable_size; i++, idx++)
+        {
+                if(idx < s_idx)
+                        continue;
+                if(LIST_FIND(&ip_conntrack_hash[i], ctnetlink_dump_build_msg,
+                        struct ip_conntrack_tuple_hash *, skb,
+                        NETLINK_CB(cb->skb).pid, cb->nlh->nlmsg_seq))
+                        continue;
+        }
+        READ_UNLOCK(&ip_conntrack_lock);
+
+        cb->args[0] = idx;
+        return (skb->len);
+}
+
+int ctnetlink_get_conntrack(struct sock *ctnl, struct sk_buff *skb, 
+	struct nlmsghdr *nlh, int *errp)
+{
+        struct ip_conntrack_tuple_hash *h;
+        struct ip_conntrack_tuple *tuple;
+	struct nfattr *cda[CTA_MAX];
+	struct ip_conntrack *ct;
+	struct sk_buff *skb2 = NULL;
+        int err, proto;
+
+        if(nlh->nlmsg_flags & NLM_F_DUMP)
+        {
+                struct nfgenmsg *msg = NLMSG_DATA(nlh);
+                u32 rlen;
+
+                if(msg->nfgen_family != AF_INET)
+                        return (-EAFNOSUPPORT);
+
+                if((*errp = netlink_dump_start(ctnl, skb, nlh,
+                        ctnetlink_dump_table, ctnetlink_done)) != 0)
+			return (-EINVAL);
+                rlen = NLMSG_ALIGN(nlh->nlmsg_len);
+                if(rlen > skb->len)
+                        rlen = skb->len;
+                skb_pull(skb, rlen);
+		return (0);
+	}
+
+	if(ctnetlink_check_attributes(nlh, cda) < 0)
+		return (-EINVAL);
+
+        if(cda[CTA_ORIG-1])
+                tuple = NFA_DATA(cda[CTA_ORIG-1]);
+        else
+        {
+                if(cda[CTA_RPLY-1])
+                        tuple = NFA_DATA(cda[CTA_RPLY-1]);
+                else
+                        return (-EINVAL);
+        }
+
+	h = ip_conntrack_find_get(tuple, NULL);
+        if(!h)
+                return (-ENOENT);
+
+        ct = h->ctrack;
+        if(!ct)
+                goto nlmsg_failure;
+
+        skb2 = alloc_skb(NLMSG_GOODSIZE, GFP_ATOMIC);
+        if(!skb2)
+                return (-ENOMEM);
+	NETLINK_CB(skb2).dst_pid = NETLINK_CB(skb).pid;
+
+        proto = ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.protonum;
+        err = ctnetlink_fill_info(skb2, NETLINK_CB(skb).pid, nlh->nlmsg_seq, 
+		NFM_NEWCONNTRACK, 1, ct, NULL, proto, NULL, NULL);
+        if(err <= 0)
+                goto nlmsg_failure;
+
+	err = netlink_unicast(ctnl, skb2, NETLINK_CB(skb).pid, MSG_DONTWAIT);
+        if(err < 0)
+                return (err);
+        return (0);
+
+nlmsg_failure:
+        if(skb2)
+                kfree_skb(skb2);
+        return (-1);
+}
+
+/* Finish me: should support NLM_F_CREATE and NLM_F_REPLACE. */
+int ctnetlink_new_conntrack(struct sock *ctnl, struct sk_buff *skb, 
+	struct nlmsghdr *nlh, int *errp)
+{
+	return (-EOPNOTSUPP);
+}
+
+static struct ip_conntrack_notify ctnl_notify = { { NULL, NULL },
+						ctnetlink_destroy,
+						ctnetlink_create };
+
+static struct nfnetlink_info ctnl_info = { { NULL, NULL },
+	"conntrack",
+	{{ NFM_NEWCONNTRACK, ctnetlink_new_conntrack 	},
+	 { NFM_DELCONNTRACK, ctnetlink_delete_conntrack },
+	 { NFM_GETCONNTRACK, ctnetlink_get_conntrack 	}} 
+}; 
+
+void __exit ctnetlink_exit(void)
+{
+	printk("CTnetlink: unregistering with nfnetlink.\n");
+	ip_conntrack_notify_unregister(&ctnl_notify);
+	nfnetlink_unregister(&ctnl_info);
+	return;
+}
+
+int __init ctnetlink_init(void)
+{
+	printk("CTnetlink: registering with nfnetlink v%s.\n", ctversion);
+	if(nfnetlink_register(&ctnl_info) < 0)
+		panic("ctneltink_init: cannot register with nfnetlink.\n");
+	if(ip_conntrack_notify_register(&ctnl_notify) < 0)
+		panic("ctnetlink_init: cannot register notifier.\n");
+
+	return (0);
+}
+
+module_init(ctnetlink_init);
+module_exit(ctnetlink_exit);
diff -ruN /virgin/linux-2.4.5-ac5/net/ipv4/netfilter/ip_netlink_iptables.c /usr/src/linux/net/ipv4/netfilter/ip_netlink_iptables.c
--- /virgin/linux-2.4.5-ac5/net/ipv4/netfilter/ip_netlink_iptables.c	Wed Dec 31 16:00:00 1969
+++ /usr/src/linux/net/ipv4/netfilter/ip_netlink_iptables.c	Wed Jun 20 00:33:08 2001
@@ -0,0 +1,390 @@
+/* Connection tracking via netlink socket. Allows for user space
+ * protocol helpers and general trouble making from userspace.
+ *
+ * Jay Schulist <jschlst@samba.org>, Copyright (c) 2001.
+ *
+ * Initial connection tracking via netlink development funded and
+ * generally made possible by Network Robots, Inc. (www.networkrobots.com)
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/socket.h>
+#include <linux/kernel.h>
+#include <linux/major.h>
+#include <linux/sched.h>
+#include <linux/timer.h>
+#include <linux/string.h>
+#include <linux/sockios.h>
+#include <linux/net.h>
+#include <linux/fcntl.h>
+#include <linux/skbuff.h>
+#include <asm/uaccess.h>
+#include <asm/system.h>
+#include <net/sock.h>
+#include <linux/init.h>
+#include <linux/netlink.h>
+#include <linux/spinlock.h>
+#include <linux/rtnetlink.h>
+
+#include <linux/netfilter.h>
+#include <linux/netfilter_ipv4.h>
+#include <linux/netfilter_ipv4/ip_tables.h>
+#include <linux/netfilter_ipv4/ip_conntrack.h>
+#include <linux/netfilter_ipv4/ip_conntrack_protocol.h>
+#include <linux/netfilter_ipv4/ip_conntrack_helper.h>
+#include <linux/netfilter_ipv4/ip_conntrack_core.h>
+#include <linux/netfilter_ipv4/ip_conntrack_tcp.h>
+#include <linux/netfilter_ipv4/ip_conntrack_icmp.h>
+#include <linux/nfnetlink.h>
+
+char iptversion[] = "1.00";
+int ipt_debug_level = 1;
+#define ipt_debug(level, format, arg...) \
+        if(ipt_debug_level > level)  \
+                printk(__FILE__ ": " format, ## arg)
+
+int iptnetlink_check_attributes(struct nlmsghdr *nlh, struct nfattr *ida[])
+{
+        int min_len;
+
+        /* check attribute lengths. */
+        min_len = sizeof(struct iptmsg);
+        if(nlh->nlmsg_len < min_len)
+                return (-EINVAL);
+
+        if(nlh->nlmsg_len > min_len)
+        {
+                struct nfattr *attr = NFM_NFA(NLMSG_DATA(nlh));
+                int attrlen = nlh->nlmsg_len - NLMSG_ALIGN(min_len);
+
+                while(NFA_OK(attr, attrlen))
+                {
+                        unsigned flavor = attr->nfa_type;
+                        if(flavor)
+                        {
+                                if(flavor > IPTA_MAX)
+                                        return (-EINVAL);
+                                ida[flavor - 1] = attr;
+                        }
+                        attr = NFA_NEXT(attr, attrlen);
+                }
+        }
+        else
+                return (-EINVAL);
+
+        return (0);
+}
+
+int iptnetlink_check_and_kill_entry(struct ipt_entry *e, struct nlmsghdr *nlh,
+	struct nfattr *ipta[])
+{
+	struct ipt_entry_target *t;
+
+#ifdef NOT
+        /* Cleanup all matches */
+        IPT_MATCH_ITERATE(e, cleanup_match, NULL);
+
+	/* Cleanup all targets */
+        t = ipt_get_target(e);
+        if (t->u.kernel.target->destroy)
+                t->u.kernel.target->destroy(t->data,
+                                            t->u.target_size - sizeof(*t));
+        if (t->u.kernel.target->me)
+                __MOD_DEC_USE_COUNT(t->u.kernel.target->me);
+#endif
+
+	return (0);
+}
+
+int iptnetlink_delete_table(struct sock *iptnl, struct sk_buff *skb,
+        struct nlmsghdr *nlh, int *errp)
+{
+	struct nfattr *ipta[IPTA_MAX];
+	struct iptmsg *msg;
+	struct ipt_table *t;
+	struct ipt_entry *e;
+	struct ipt_ip *ip;
+	unsigned int i;
+	int ret;
+
+        if(iptnetlink_check_attributes(nlh, ipta) < 0)
+                return (-EINVAL);
+
+	if(!ipta[IPTA_IP])
+		return (-EINVAL);
+	ip = NFA_DATA(ipta[IPTA_IP]);
+
+#ifdef NOT
+	t = ipt_find_table_lock(msg->iptm_table_name, &ret, &ipt_mutex);
+        if(!t)
+		return (ret);
+
+	/* now find entry and delete it. */
+	IPT_ENTRY_ITERATE(t->private->entries, t->private->size, 
+		iptnetlink_check_and_kill_entry, nlh, ipta);
+
+	for(i = 0, i < t->private->size; i += e->next_offset)
+        {
+		int rm_size;
+
+        	e = (void *)(t->private->entries) + i;
+
+		if(memcmp(e->ip, ip, sizeof(struct ipt_ip)))
+			continue;
+
+		rm_size = e->next_offset;
+		memmove(e, (void *)(t->private->entries) + e->next_offset,
+			t->private->size - (i + e->next_offset));
+		t->private->size -= rm_size;
+		t->private->number--;
+	}
+
+	up(&ipt_mutex);
+#endif
+	return (0);
+}
+
+int iptnetlink_done(struct netlink_callback *cb)
+{
+        return (0);
+}
+
+static const char *hooknames[] = {
+	[NF_IP_PRE_ROUTING]  "PREROUTING",
+    	[NF_IP_LOCAL_IN]     "INPUT",
+    	[NF_IP_FORWARD]      "FORWARD",
+    	[NF_IP_LOCAL_OUT]    "OUTPUT",
+    	[NF_IP_POST_ROUTING] "POSTROUTING",
+};
+
+char *iptnetlink_chain_name(struct ipt_entry *e, struct ipt_table *t)
+{
+	struct ipt_entry_target *trgt;
+	static char name[IPT_FUNCTION_MAXNAMELEN];
+
+	trgt = ipt_get_target(e);
+
+	/* We know this is the start of a new chain if it's an ERROR
+         * target, or a hook entry point 
+	 */
+        if(!strcmp(trgt->u.kernel.target->name, IPT_ERROR_TARGET)) 
+		strcpy(name, (const char *)trgt->data);
+	else 
+	{
+		unsigned int i;
+
+        	for(i = 0; i < NF_IP_NUMHOOKS; i++) 
+                	if((t->valid_hooks & (1 << i))
+				&& ((struct ipt_entry *)(t->private->entries 
+				+ t->private->hook_entry[i])) == e)
+				strcpy(name, hooknames[i]);
+        }
+
+	return (name);
+}
+
+int iptnetlink_chain_number(struct ipt_entry *e, struct ipt_table *t, 
+	char *chain)
+{
+#ifdef NOT
+	struct ipt_entry_target *trgt;
+        static char name[IPT_FUNCTION_MAXNAMELEN];
+
+        trgt = ipt_get_target(e);
+
+        /* We know this is the start of a new chain if it's an ERROR
+         * target, or a hook entry point
+         */
+        if(!strcmp(trgt->u.kernel.target->name, IPT_ERROR_TARGET))
+                strcpy(name, (const char *)trgt->data);
+        else
+        {
+                unsigned int i;
+
+                for(i = 0; i < NF_IP_NUMHOOKS; i++)
+                        if((t->valid_hooks & (1 << i))
+                                && ((struct ipt_entry *)(t->private->entries
+                                + t->private->hook_entry[i])) == e)
+                                strcpy(name, hooknames[i]);
+        }
+#endif
+
+	return (0);
+}
+
+int iptnetlink_fill_entry_info(struct ipt_entry *e, struct ipt_table *t,
+	struct sk_buff *skb, u32 pid, u32 seq, int event, int nowait)
+{
+	struct nlmsghdr *nlh;
+        struct ipt_entry_match *mtch;
+        struct ipt_entry_target *trgt;
+	struct ipt_table_info *ti = t->private;
+        struct iptmsg *msg;
+        unsigned char *b;
+        int i;
+
+        b = skb->tail;
+        nlh = NLMSG_PUT(skb, pid, seq, event, sizeof(struct iptmsg));
+        msg = NLMSG_DATA(nlh);
+        nlh->nlmsg_flags  = (nowait && pid) ? NLM_F_MULTI : 0;
+        msg->iptm_family  = AF_INET;
+	strncpy(msg->iptm_table, t->name, IPT_TABLE_MAXNAMELEN);
+
+//	msg->iptm_entry_num = iptnetlink_get_chain(e, t, msg->iptm_chain);
+	strncpy(msg->iptm_chain, iptnetlink_chain_name(e, t), 
+		IPT_FUNCTION_MAXNAMELEN);
+	msg->iptm_entry_num = iptnetlink_chain_number(e, t);
+
+	/* copy the iptable entry data. */
+	NFA_PUT(skb, IPTA_IP, sizeof(struct ipt_ip), &e->ip);
+	NFA_PUT(skb, IPTA_NFCACHE, sizeof(unsigned int), &e->nfcache);
+	NFA_PUT(skb, IPTA_COUNTERS, sizeof(struct ipt_counters), &e->counters);
+
+	/* now copy any matches and targets over. */
+	for(i = sizeof(struct ipt_entry); i < e->target_offset; 
+		i += mtch->u.match_size)
+	{
+		struct ipta_info *minfo;
+		int msize;
+
+		mtch  = (void *)e + i;
+		msize = sizeof(struct ipta_info) + mtch->u.match_size;
+		minfo = (struct ipta_info *)kmalloc(msize, GFP_KERNEL);
+
+		strcpy(minfo->name, mtch->u.kernel.match->name);
+		minfo->size = mtch->u.match_size;
+		memcpy(minfo->data, mtch->data, minfo->size);
+
+		NFA_PUT(skb, IPTA_MATCH, msize, minfo);
+		kfree(minfo);
+	}
+
+	trgt = ipt_get_target(e);
+        if(trgt)
+	{
+		struct ipta_info *tinfo;
+		int tsize;
+
+		tsize = sizeof(struct ipta_info) 
+			+ (trgt->u.target_size-sizeof(struct ipt_entry_target));
+		tinfo = (struct ipta_info *)kmalloc(tsize, GFP_KERNEL);
+
+		memcpy(&tinfo->name, &trgt->u.kernel.target->name,
+			strlen(trgt->u.kernel.target->name) + 1);
+                tinfo->size = trgt->u.target_size-sizeof(struct ipt_entry_target);
+                memcpy(tinfo->data, trgt->data, tinfo->size);
+
+		NFA_PUT(skb, IPTA_TARGET, tsize, tinfo);
+                kfree(tinfo);
+	}
+
+	nlh->nlmsg_len = skb->tail - b;
+        return (skb->len);
+
+nlmsg_failure:
+nfattr_failure:
+        skb_trim(skb, b - skb->data);
+        return (-1);
+}
+
+int iptnetlink_dump_table(struct sk_buff *skb, struct netlink_callback *cb)
+{
+        int s_idx = cb->args[0];
+	struct iptgenmsg *msg = NLMSG_DATA(cb->nlh);
+        struct ipt_table *t;
+	int ret, idx = 0;
+
+        t = ipt_find_table_lock(msg->iptgen_table, &ret, &ipt_mutex);
+	if(t)
+	{
+		unsigned int i;
+		struct ipt_entry *e;
+
+		for(i = 0, idx = 0; i < t->private->size; idx++, 
+			i += e->next_offset)
+		{
+			e = (void *)(t->private->entries) + i;
+			if(idx < s_idx)
+                                continue;
+
+			ret = iptnetlink_fill_entry_info(e, t, skb,
+				NETLINK_CB(cb->skb).pid, cb->nlh->nlmsg_seq,
+				NFM_NEWIPTABLE, 1);
+			if(ret <= 0)
+				break;
+		}
+                up(&ipt_mutex);
+	}
+
+	cb->args[0] = idx;
+        return (skb->len);
+}
+
+int iptnetlink_get_table(struct sock *iptnl, struct sk_buff *skb,
+        struct nlmsghdr *nlh, int *errp)
+{
+	if(nlh->nlmsg_flags & NLM_F_DUMP)
+        {
+                struct iptgenmsg *msg = NLMSG_DATA(nlh);
+                u32 rlen;
+
+                if(msg->iptgen_family != AF_INET)
+                        return (-EAFNOSUPPORT);
+
+                if((*errp = netlink_dump_start(iptnl, skb, nlh,
+                        iptnetlink_dump_table, iptnetlink_done)) != 0)
+                        return (-EINVAL);
+                rlen = NLMSG_ALIGN(nlh->nlmsg_len);
+                if(rlen > skb->len)
+                        rlen = skb->len;
+                skb_pull(skb, rlen);
+                return (0);
+        }
+
+	/* Get a single table entry. */
+
+	return (-EOPNOTSUPP);
+}
+
+/* Finish me: should support NLM_F_CREATE and NLM_F_REPLACE. */
+int iptnetlink_new_table(struct sock *iptnl, struct sk_buff *skb,
+        struct nlmsghdr *nlh, int *errp)
+{
+	if(nlh->nlmsg_flags & NLM_F_REPLACE)
+        {
+
+	}
+
+        return (-EOPNOTSUPP);
+}
+
+static struct nfnetlink_info iptnl_info = { { NULL, NULL },
+        "iptables",
+        {{ NFM_NEWIPTABLE, iptnetlink_new_table 	},
+         { NFM_DELIPTABLE, iptnetlink_delete_table 	},
+         { NFM_GETIPTABLE, iptnetlink_get_table 	}}
+};
+
+void __exit iptnetlink_exit(void)
+{
+        printk("IPTnetlink: unregistering with nfnetlink.\n");
+        nfnetlink_unregister(&iptnl_info);
+        return;
+}
+
+int __init iptnetlink_init(void)
+{
+        printk("IPTnetlink: registering with nfnetlink v%s.\n", iptversion);
+        if(nfnetlink_register(&iptnl_info) < 0)
+                panic("iptneltink_init: cannot register with nfnetlink.\n");
+
+        return (0);
+}
+
+module_init(iptnetlink_init);
+module_exit(iptnetlink_exit);
diff -ruN /virgin/linux-2.4.5-ac5/net/ipv4/netfilter/ip_tables.c /usr/src/linux/net/ipv4/netfilter/ip_tables.c
--- /virgin/linux-2.4.5-ac5/net/ipv4/netfilter/ip_tables.c	Tue May 15 01:29:35 2001
+++ /usr/src/linux/net/ipv4/netfilter/ip_tables.c	Wed Jun 13 05:47:15 2001
@@ -48,7 +48,7 @@
 #define SMP_ALIGN(x) (((x) + SMP_CACHE_BYTES-1) & ~(SMP_CACHE_BYTES-1))
 
 /* Mutex protects lists (only traversed in user context). */
-static DECLARE_MUTEX(ipt_mutex);
+DECLARE_MUTEX(ipt_mutex);
 
 /* Must have mutex */
 #define ASSERT_READ_LOCK(x) IP_NF_ASSERT(down_trylock(&ipt_mutex) != 0)
@@ -77,22 +77,6 @@
 
    Hence the start of any table is given by get_table() below.  */
 
-/* The table itself */
-struct ipt_table_info
-{
-	/* Size per table */
-	unsigned int size;
-	/* Number of entries: FIXME. --RR */
-	unsigned int number;
-
-	/* Entry points and underflows */
-	unsigned int hook_entry[NF_IP_NUMHOOKS];
-	unsigned int underflow[NF_IP_NUMHOOKS];
-
-	/* ipt_entry tables: one per CPU */
-	char entries[0] __attribute__((aligned(SMP_CACHE_BYTES)));
-};
-
 static LIST_HEAD(ipt_target);
 static LIST_HEAD(ipt_match);
 static LIST_HEAD(ipt_tables);
@@ -456,20 +440,20 @@
 }
 #endif
 
-static inline struct ipt_table *
-find_table_lock(const char *name, int *error, struct semaphore *mutex)
+inline struct ipt_table *
+ipt_find_table_lock(const char *name, int *error, struct semaphore *mutex)
 {
 	return find_inlist_lock(&ipt_tables, name, "iptable_", error, mutex);
 }
 
-static inline struct ipt_match *
-find_match_lock(const char *name, int *error, struct semaphore *mutex)
+inline struct ipt_match *
+ipt_find_match_lock(const char *name, int *error, struct semaphore *mutex)
 {
 	return find_inlist_lock(&ipt_match, name, "ipt_", error, mutex);
 }
 
-static inline struct ipt_target *
-find_target_lock(const char *name, int *error, struct semaphore *mutex)
+inline struct ipt_target *
+ipt_find_target_lock(const char *name, int *error, struct semaphore *mutex)
 {
 	return find_inlist_lock(&ipt_target, name, "ipt_", error, mutex);
 }
@@ -639,7 +623,7 @@
 	int ret;
 	struct ipt_match *match;
 
-	match = find_match_lock(m->u.user.name, &ret, &ipt_mutex);
+	match = ipt_find_match_lock(m->u.user.name, &ret, &ipt_mutex);
 	if (!match) {
 		duprintf("check_match: `%s' not found\n", m->u.user.name);
 		return ret;
@@ -686,7 +670,7 @@
 		goto cleanup_matches;
 
 	t = ipt_get_target(e);
-	target = find_target_lock(t->u.user.name, &ret, &ipt_mutex);
+	target = ipt_find_target_lock(t->u.user.name, &ret, &ipt_mutex);
 	if (!target) {
 		duprintf("check_entry: `%s' not found\n", t->u.user.name);
 		goto cleanup_matches;
@@ -1022,7 +1006,7 @@
 	int ret;
 	struct ipt_table *t;
 
-	t = find_table_lock(entries->name, &ret, &ipt_mutex);
+	t = ipt_find_table_lock(entries->name, &ret, &ipt_mutex);
 	if (t) {
 		duprintf("t->private->number = %u\n",
 			 t->private->number);
@@ -1085,7 +1069,7 @@
 
 	duprintf("ip_tables: Translated table\n");
 
-	t = find_table_lock(tmp.name, &ret, &ipt_mutex);
+	t = ipt_find_table_lock(tmp.name, &ret, &ipt_mutex);
 	if (!t)
 		goto free_newinfo_counters_untrans;
 
@@ -1169,7 +1153,7 @@
 		goto free;
 	}
 
-	t = find_table_lock(tmp.name, &ret, &ipt_mutex);
+	t = ipt_find_table_lock(tmp.name, &ret, &ipt_mutex);
 	if (!t)
 		goto free;
 
@@ -1244,7 +1228,7 @@
 			break;
 		}
 		name[IPT_TABLE_MAXNAMELEN-1] = '\0';
-		t = find_table_lock(name, &ret, &ipt_mutex);
+		t = ipt_find_table_lock(name, &ret, &ipt_mutex);
 		if (t) {
 			struct ipt_getinfo info;
 
@@ -1755,6 +1739,10 @@
 EXPORT_SYMBOL(ipt_do_table);
 EXPORT_SYMBOL(ipt_register_target);
 EXPORT_SYMBOL(ipt_unregister_target);
+EXPORT_SYMBOL(ipt_find_table_lock);
+EXPORT_SYMBOL(ipt_find_match_lock);
+EXPORT_SYMBOL(ipt_find_target_lock);
+EXPORT_SYMBOL(ipt_mutex);
 
 module_init(init);
 module_exit(fini);
diff -ruN /virgin/linux-2.4.5-ac5/net/ipv4/netfilter/iptable_netlink.c /usr/src/linux/net/ipv4/netfilter/iptable_netlink.c
--- /virgin/linux-2.4.5-ac5/net/ipv4/netfilter/iptable_netlink.c	Wed Dec 31 16:00:00 1969
+++ /usr/src/linux/net/ipv4/netfilter/iptable_netlink.c	Wed Jun 13 05:29:58 2001
@@ -0,0 +1,251 @@
+/* Netfilter messages via netlink socket. Allows for user space
+ * protocol helpers and general trouble making from userspace.
+ *
+ * Jay Schulist <jschlst@samba.org>, Copyright (c) 2001.
+ *
+ * Initial netfilter messages via netlink development funded and
+ * generally made possible by Network Robots, Inc. (www.networkrobots.com)
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/socket.h>
+#include <linux/kernel.h>
+#include <linux/major.h>
+#include <linux/sched.h>
+#include <linux/timer.h>
+#include <linux/string.h>
+#include <linux/sockios.h>
+#include <linux/net.h>
+#include <linux/fcntl.h>
+#include <linux/skbuff.h>
+#include <asm/uaccess.h>
+#include <asm/system.h>
+#include <net/sock.h>
+#include <linux/init.h>
+#include <linux/spinlock.h>
+#include <linux/list.h>
+
+#include <linux/netfilter.h>
+#include <linux/netfilter_ipv4.h>
+#include <linux/netfilter_ipv4/ip_tables.h>
+#include <linux/netfilter_ipv4/ip_conntrack.h>
+#include <linux/netfilter_ipv4/ip_conntrack_protocol.h>
+#include <linux/netfilter_ipv4/ip_conntrack_helper.h>
+#include <linux/netfilter_ipv4/ip_conntrack_core.h>
+#include <linux/netfilter_ipv4/ip_conntrack_tcp.h>
+#include <linux/netfilter_ipv4/ip_conntrack_icmp.h>
+
+#include <linux/netlink.h>
+#include <linux/nfnetlink.h>
+
+char nfversion[] = "1.00pre01";
+int nf_debug_level = 1;
+#define nf_debug(level, format, arg...) \
+        if(nf_debug_level > level)  \
+                printk(__FILE__ ": " format, ## arg)
+
+static struct sock *nfnl = NULL;
+static LIST_HEAD(nfclient_list);
+DECLARE_MUTEX(nfnl_sem);
+
+void nfnl_lock(void)
+{
+        nfnl_shlock();
+        nfnl_exlock();
+}
+
+void nfnl_unlock(void)
+{
+        nfnl_exunlock();
+        nfnl_shunlock();
+}
+
+int nfnetlink_register(struct nfnetlink_info *n)
+{
+        MOD_INC_USE_COUNT;
+
+	nfnl_lock();
+	list_add((struct list_head *)n, &nfclient_list);
+	nfnl_unlock();
+
+        return (0);
+}
+
+int nfnetlink_unregister(struct nfnetlink_info *n)
+{
+	nfnl_lock();
+	list_del((struct list_head *)n);
+	nfnl_unlock();
+
+        MOD_DEC_USE_COUNT;
+
+        return (0);
+}
+
+struct nfnl_callback *nfnetlink_find_client(int type)
+{
+	struct list_head *i;
+	int n;
+
+        for(i = nfclient_list.next; i != &nfclient_list; i = i->next)
+		for(n = 0; n < NF_MAX_CALLBACK; n++)
+			if(((struct nfnetlink_info *)i)->cb[n].type == type)
+				return (&((struct nfnetlink_info *)i)->cb[n]);
+
+        return (NULL);
+}
+
+void __nfa_fill(struct sk_buff *skb, int attrtype, int attrlen,
+        const void *data)
+{
+        struct nfattr *nfa;
+        int size = NFA_LENGTH(attrlen);
+
+        nfa = (struct nfattr *)skb_put(skb, NFA_ALIGN(size));
+        nfa->nfa_type = attrtype;
+        nfa->nfa_len  = size;
+        memcpy(NFA_DATA(nfa), data, attrlen);
+}
+
+int nfattr_parse(struct nfattr *tb[], int maxattr, struct nfattr *nfa, int len)
+{
+        memset(tb, 0, sizeof(struct nfattr *)*maxattr);
+
+        while(NFA_OK(nfa, len)) 
+	{
+                unsigned flavor = nfa->nfa_type;
+                if(flavor && flavor <= maxattr)
+                        tb[flavor-1] = nfa;
+                nfa = NFA_NEXT(nfa, len);
+        }
+
+        return (0);
+}
+
+int nfnetlink_send(struct sk_buff *skb, u32 pid, unsigned group, int echo)
+{
+        int err = 0;
+
+        NETLINK_CB(skb).dst_groups = group;
+        if(echo)
+                atomic_inc(&skb->users);
+        netlink_broadcast(nfnl, skb, pid, group, GFP_KERNEL);
+        if(echo)
+                err = netlink_unicast(nfnl, skb, pid, MSG_DONTWAIT);
+
+        return (err);
+}
+
+/* Process one complete nfnetlink message. */
+static inline int nfnetlink_rcv_msg(struct sk_buff *skb,
+        struct nlmsghdr *nlh, int *errp)
+{
+	struct nfnl_callback *nc;
+	int type, err = 0;
+
+        /* Only requests are handled by kernel now. */
+        if(!(nlh->nlmsg_flags & NLM_F_REQUEST))
+                return (0);
+
+        /* Unknown message: reply with EINVAL */
+        type = nlh->nlmsg_type;
+        if(type > NFM_MAX)
+                goto err_inval;
+
+        /* All the messages must have at least 1 byte length */
+        if(nlh->nlmsg_len < NLMSG_LENGTH(sizeof(struct nfgenmsg)))
+                return (0);
+
+	nc = nfnetlink_find_client(type);
+	if(!nc)
+		goto err_inval;
+
+	err = nc->call(nfnl, skb, nlh, errp);
+        *errp = err;
+        return (err);
+
+err_inval:
+        *errp = -EINVAL;
+        return (-1);
+}
+
+/* Process one packet of messages. */
+static inline int nfnetlink_rcv_skb(struct sk_buff *skb)
+{
+        int err;
+        struct nlmsghdr *nlh;
+
+        while(skb->len >= NLMSG_SPACE(0))
+        {
+                u32 rlen;
+
+                nlh = (struct nlmsghdr *)skb->data;
+                if(nlh->nlmsg_len < sizeof(struct nlmsghdr)
+                        || skb->len < nlh->nlmsg_len)
+                        return (0);
+                rlen = NLMSG_ALIGN(nlh->nlmsg_len);
+                if(rlen > skb->len)
+                        rlen = skb->len;
+                if(nfnetlink_rcv_msg(skb, nlh, &err))
+                {
+                        if(err == 0)
+                                return (-1);
+                        netlink_ack(skb, nlh, err);
+                }
+                else
+                        if(nlh->nlmsg_flags & NLM_F_ACK)
+                                netlink_ack(skb, nlh, 0);
+                skb_pull(skb, rlen);
+        }
+
+        return (0);
+}
+
+static void nfnetlink_rcv(struct sock *sk, int len)
+{
+        do {
+                struct sk_buff *skb;
+
+                if(nfnl_shlock_nowait())
+                        return;
+
+                while((skb = skb_dequeue(&sk->receive_queue)) != NULL)
+                {
+                        if(nfnetlink_rcv_skb(skb))
+                        {
+                                if(skb->len)
+                                        skb_queue_head(&sk->receive_queue, skb);                                else
+                                        kfree_skb(skb);
+                                break;
+                        }
+                        kfree_skb(skb);
+                }
+
+                up(&nfnl_sem);
+        } while(nfnl && nfnl->receive_queue.qlen);
+}
+
+void __exit nfnetlink_exit(void)
+{
+        printk("Netfilter removing netlink socket.\n");
+        sock_release(nfnl->socket);
+        return;
+}
+
+int __init nfnetlink_init(void)
+{
+        printk("Netfilter messages via NETLINK v%s.\n", nfversion);
+        nfnl = netlink_kernel_create(NETLINK_NETFILTER, nfnetlink_rcv);
+        if(!nfnl)
+                panic("nfnetlink_init: cannot initialize nfnetlink.\n");
+
+        return (0);
+}
+
+module_init(nfnetlink_init);
+module_exit(nfnetlink_exit);
Binary files /virgin/linux-2.4.5-ac5/net/netlink/.af_netlink.c.swp and /usr/src/linux/net/netlink/.af_netlink.c.swp differ
diff -ruN /virgin/linux-2.4.5-ac5/net/netlink/netlink_dev.c /usr/src/linux/net/netlink/netlink_dev.c
--- /virgin/linux-2.4.5-ac5/net/netlink/netlink_dev.c	Fri Feb  9 11:29:44 2001
+++ /usr/src/linux/net/netlink/netlink_dev.c	Tue Jun 12 02:07:53 2001
@@ -200,6 +200,7 @@
 	make_devfs_entries ("skip", 1);
 	make_devfs_entries ("USERSOCK", 2);
 	make_devfs_entries ("fwmonitor", 3);
+	make_devfs_entries ("netfilter", 5);
 	make_devfs_entries ("ARPD", 8);
 	make_devfs_entries ("ROUTE6", 11);
 	make_devfs_entries ("IP6_FW", 13);
