diff -urN -X /tmp/kerndiff.2QQi3B --minimal linux-2.4.0-test9-2/include/linux/netfilter_ipv4/ip_conntrack_rpc.h working-2.4.0-test9-2/include/linux/netfilter_ipv4/ip_conntrack_rpc.h
--- linux-2.4.0-test9-2/include/linux/netfilter_ipv4/ip_conntrack_rpc.h	Thu Jan  1 10:00:00 1970
+++ working-2.4.0-test9-2/include/linux/netfilter_ipv4/ip_conntrack_rpc.h	Thu Oct 12 10:40:35 2000
@@ -0,0 +1,71 @@
+/* RPC extension for IP connection tracking. */
+/* Marcelo Barbosa Lima - marcelo.lima@dcc.unicamp.br */
+
+#include <asm/param.h>
+#include <linux/sched.h>
+#include <linux/timer.h>
+#include <linux/stddef.h>
+#include <linux/list.h>
+
+#include <linux/netfilter_ipv4/ip_conntrack.h>
+#include <linux/netfilter_ipv4/ip_conntrack_helper.h>
+
+/* Data in RPC packet is encoded in XDR */
+#define IXDR_GET_INT32(buf) ((u_int32_t) ntohl((uint32_t)*buf))
+
+#if 0
+#define DEBUGRPC printk
+#else
+#define DEBUGRPC(format, args...)
+#endif
+
+/* Fast timeout, to deny DoS atacks */
+#define EXP (60 * HZ)
+
+/* Normal timeouts */
+#define EXPIRES (180 * HZ)
+
+ /* For future conections RPC, using client's cache bindings */
+ /* I'll use ip_conntrack_lock to lock these lists */
+
+/* This identifies each request and stores protocol */
+
+struct request_p {
+   struct list_head list;
+
+   u_int32_t xid;   
+   u_int32_t ip;
+   u_int16_t port;
+	
+   /* Protocol */
+   u_int16_t proto;
+
+   struct timer_list timeout;
+};
+
+struct expect_rpc {
+   /* list */
+   struct list_head list;
+
+   /* binding */
+   u_int32_t ip;
+   u_int16_t port;
+   u_int32_t ip_rmt;
+   u_int16_t proto;
+  
+   /* Expect RPC conection for long time, but this time expires */
+   struct timer_list timeout;  
+};
+
+inline int expect_rpc_cmp(const struct expect_rpc *l, u_int16_t port,
+			  u_int32_t ip, u_int32_t ip_rmt, u_int16_t proto)
+{
+	return (l->ip == ip && l->port == port &&
+		l->ip_rmt == ip_rmt && l->proto == proto);
+}
+
+inline int request_p_cmp(const struct request_p *p, u_int32_t xid, 
+			 u_int32_t ip, u_int32_t port) {
+	return(p->xid == xid && p->ip == ip && p->port);
+
+}
diff -urN -X /tmp/kerndiff.2QQi3B --minimal linux-2.4.0-test9-2/net/ipv4/netfilter/ip_conntrack_rpc_tcp.c working-2.4.0-test9-2/net/ipv4/netfilter/ip_conntrack_rpc_tcp.c
--- linux-2.4.0-test9-2/net/ipv4/netfilter/ip_conntrack_rpc_tcp.c	Thu Jan  1 10:00:00 1970
+++ working-2.4.0-test9-2/net/ipv4/netfilter/ip_conntrack_rpc_tcp.c	Thu Oct 12 10:39:05 2000
@@ -0,0 +1,336 @@
+/* RPC extension for IP connection tracking. */
+/* Marcelo Barbosa Lima - marcelo.lima@dcc.unicamp.br */
+/* Yes, my english is very poor :-) */
+
+#include <linux/module.h>
+#include <linux/netfilter.h>
+#include <linux/ip.h>
+#include <net/checksum.h>
+#include <net/tcp.h>
+#include <asm/param.h>
+#include <linux/sched.h>
+#include <linux/timer.h>
+#include <linux/stddef.h>
+#include <linux/list.h>
+#include <linux/netfilter_ipv4/ip_conntrack_rpc.h>
+#include <linux/netfilter_ipv4/ip_conntrack.h>
+#include <linux/netfilter_ipv4/ip_conntrack_helper.h>
+#include <linux/netfilter_ipv4/lockhelp.h>
+
+DECLARE_RWLOCK(ipct_rpc_tcp_lock);
+#define ASSERT_READ_LOCK(x) MUST_BE_READ_LOCKED(&ipct_rpc_tcp_lock)
+#define ASSERT_WRITE_LOCK(x) MUST_BE_WRITE_LOCKED(&ipct_rpc_tcp_lock)
+#include <linux/netfilter_ipv4/listhelp.h>
+
+ /* For future conections RPC, using client's cache bindings */
+
+LIST_HEAD(expect_rpc_list_tcp);
+LIST_HEAD(request_p_list_tcp);
+
+void delete_request_p(unsigned long request_p_ul) {
+	struct request_p *p = (void *)request_p_ul;
+	
+	WRITE_LOCK(&ipct_rpc_tcp_lock);
+	LIST_DELETE(&request_p_list_tcp, p);
+	WRITE_UNLOCK(&ipct_rpc_tcp_lock);
+	kfree(p);
+	return;
+
+}
+
+void delete_binding(unsigned long rpc_binding_ul) {
+	struct expect_rpc *l = (void *)rpc_binding_ul;
+
+	WRITE_LOCK(&ipct_rpc_tcp_lock);
+	LIST_DELETE(&expect_rpc_list_tcp, l);
+	WRITE_UNLOCK(&ipct_rpc_tcp_lock);
+	kfree(l);
+	return;
+}
+
+void exp_cl(struct expect_rpc * t) {
+	WRITE_LOCK(&ipct_rpc_tcp_lock);
+	del_timer(&t->timeout);
+	LIST_DELETE(&expect_rpc_list_tcp, t);
+	WRITE_UNLOCK(&ipct_rpc_tcp_lock);
+	kfree(t);
+	return;
+}
+
+
+/* Clean timers kernel list in unregister to deny crash */
+void clean_expect(struct list_head *list){
+	struct list_head *first = list->prev;
+	struct list_head *temp = list->next;
+	struct list_head *aux;
+
+	if(list_empty(list)) return;
+
+	    while(first != temp) {
+		aux = temp->next;
+		exp_cl((struct expect_rpc *)temp);
+		temp = aux;	
+	    }
+	exp_cl((struct expect_rpc *)temp);
+	return;	
+}
+
+void req_cl(struct request_p * r){
+	WRITE_LOCK(&ipct_rpc_tcp_lock);
+	del_timer(&r->timeout);
+	LIST_DELETE(&request_p_list_tcp, r);
+	WRITE_UNLOCK(&ipct_rpc_tcp_lock);
+	kfree(r);
+	return;
+}
+
+/* Clean timers kernel list in unregister to deny crash */
+void clean_request(struct list_head *list){
+	struct list_head *first = list->prev;
+	struct list_head *temp = list->next;
+	struct list_head *aux;
+
+	if(list_empty(list)) return;
+
+	    while(first != temp) {
+		aux = temp->next;
+		req_cl((struct request_p *)temp);
+		temp = aux;	
+	    }
+	req_cl((struct request_p *)temp);
+	return;
+}
+
+
+void alloc_request_p(u_int32_t xid, u_int16_t proto, u_int32_t ip,
+		     u_int16_t port){
+	struct request_p *req_p;
+        
+        /* Verifies if entry already exists */
+        WRITE_LOCK(&ipct_rpc_tcp_lock);
+        req_p = LIST_FIND(&request_p_list_tcp, request_p_cmp,
+		struct request_p *, xid, ip, port);
+
+        if(req_p){
+		/* Refresh timeout */
+		if (del_timer(&req_p->timeout)) {
+		   req_p->timeout.expires = jiffies + EXP;
+		   add_timer(&req_p->timeout);	
+	        } 
+		WRITE_UNLOCK(&ipct_rpc_tcp_lock);
+		return;	
+
+	}
+	WRITE_UNLOCK(&ipct_rpc_tcp_lock);
+	
+	/* Allocate new request_p */
+	req_p = (struct request_p *) kmalloc(sizeof(struct request_p), GFP_ATOMIC);
+        if(!req_p) {
+ 		DEBUGRPC("Can't allocate request_p\n");
+		return;			
+	}
+	*req_p = ((struct request_p) {{ NULL, NULL }, xid, ip, port, proto, 
+		{ { NULL, NULL }, jiffies + EXP, (unsigned long)req_p,
+			NULL }}); 
+      
+        /*Initialize timer */
+	init_timer(&req_p->timeout);
+	req_p->timeout.function = delete_request_p;
+        add_timer(&req_p->timeout); 
+
+	/* Put in list */
+	WRITE_LOCK(&ipct_rpc_tcp_lock);
+	list_prepend(&request_p_list_tcp, req_p);
+	WRITE_UNLOCK(&ipct_rpc_tcp_lock); 
+	return; 
+
+}
+
+/* Update Internal expect list */
+void binding(u_int32_t ip, u_int16_t port, u_int32_t ip_rmt, u_int16_t proto){
+	struct expect_rpc *rpc_binding;
+
+        /* Verifies if entry already exists */
+        WRITE_LOCK(&ipct_rpc_tcp_lock);
+        rpc_binding = LIST_FIND(&expect_rpc_list_tcp, expect_rpc_cmp,
+		struct expect_rpc *, port, ip, ip_rmt, proto);
+
+        if(rpc_binding){
+		/* Refresh timeout */
+		if (del_timer(&rpc_binding->timeout)) {
+		   rpc_binding->timeout.expires = jiffies + EXPIRES;
+		   add_timer(&rpc_binding->timeout);	
+	        } 
+		WRITE_UNLOCK(&ipct_rpc_tcp_lock);
+		return;	
+
+	}
+	WRITE_UNLOCK(&ipct_rpc_tcp_lock);
+	
+	/* Allocate new binding */
+	rpc_binding = (struct expect_rpc *) kmalloc(sizeof(struct expect_rpc), GFP_ATOMIC);
+        if(!rpc_binding) {
+ 		DEBUGRPC("Can't allocate rpc_binding\n");
+		return;			
+	}
+	*rpc_binding = ((struct expect_rpc) {{ NULL, NULL }, ip, port, ip_rmt, proto,
+		{ { NULL, NULL }, jiffies + EXPIRES, (unsigned long)rpc_binding,
+			NULL }}); 
+      
+        /*Initialize timer */
+	init_timer(&rpc_binding->timeout);
+	rpc_binding->timeout.function = delete_binding;
+        add_timer(&rpc_binding->timeout); 
+
+	/* Put in list */
+	WRITE_LOCK(&ipct_rpc_tcp_lock);
+	list_prepend(&expect_rpc_list_tcp, rpc_binding);
+	WRITE_UNLOCK(&ipct_rpc_tcp_lock); 
+	return; 
+
+}
+
+/* RPC using TCP - help */
+static int help(const struct iphdr *iph, size_t len,
+			 struct ip_conntrack *ct,
+			 enum ip_conntrack_info ctinfo)
+{
+	struct tcphdr *tcph = (void *)iph + iph->ihl * 4;
+	const u_int32_t *data = (const u_int32_t *)tcph + tcph->doff;
+
+        /* Translstion's buffer for XDR */
+        u_int16_t port_buf;
+
+	size_t tcplen = len - iph->ihl * 4;
+
+	/* Important for tracking */
+	int dir = CTINFO2DIR(ctinfo);
+	u_int32_t xid;
+	struct request_p *req_p;
+
+        /* This works for packets like handshake packets, ignore */
+        if (len == ((tcph->doff + iph->ihl) * 4)) return NF_ACCEPT;
+
+	/* Until there's been traffic both ways, don't look in packets. */
+	if (ctinfo != IP_CT_ESTABLISHED) {
+		DEBUGRPC("RPC_TCP: Conntrackinfo = %u\n", ctinfo);
+		return NF_ACCEPT;
+	}
+
+        /* Not whole TCP header? */
+        if (tcplen < sizeof(struct tcphdr) || tcplen < tcph->doff*4) {
+                DEBUGRPC("RPC: tcplen = %u\n", (unsigned)tcplen);
+                return NF_ACCEPT;
+        }
+
+        /* Checksum invalid?  Ignore. */
+        if (tcp_v4_check(tcph, tcplen, iph->saddr, iph->daddr,
+                         csum_partial((char *)tcph, tcplen, 0))) {
+                DEBUGRPC("RPC_help: bad csum: %p %u %u.%u.%u.%u %u.%u.%u.%u\n",
+                         tcph, tcplen, IP_PARTS(iph->saddr),
+                         IP_PARTS(iph->daddr));
+                return NF_ACCEPT;
+        }
+
+
+        /* If packet is client packet */
+        if(!dir){
+
+		/* Tests if packet len is ok */
+                if((tcplen - (tcph->doff * 4)) != 60)
+                        return NF_ACCEPT;
+
+	        /* Jump RPC Packet length in XDR */
+		data++;
+
+		/* Get XID */
+		xid = *data;
+
+		/* Jump until procedure number */
+		data = data + 5;
+		/* Is one get port procedure? */
+		if(IXDR_GET_INT32(data) == 3){ 
+		   data++;
+
+		   /* Jump Credentials and Verfifier */
+		   data = data + IXDR_GET_INT32(data) + 2;
+		   data = data + IXDR_GET_INT32(data) + 2;
+
+		   /* Get and store protocol in RPC client parameters */
+		   data = data + 2;
+		   alloc_request_p(xid, IXDR_GET_INT32(data), ct->tuplehash[dir].tuple.src.ip,
+				ct->tuplehash[dir].tuple.src.u.all);
+		}
+		return NF_ACCEPT;
+	}
+
+	/* Tests if packet len is ok */
+	if((tcplen - (tcph->doff * 4)) != 32)
+		return NF_ACCEPT;
+
+        /* Jump RPC Packet length in XDR */
+	data++;
+
+	/* XID is important */
+        req_p = LIST_FIND(&request_p_list_tcp, request_p_cmp,
+		struct request_p *, *data, ct->tuplehash[!dir].tuple.src.ip,
+		ct->tuplehash[!dir].tuple.src.u.all);
+
+	/* Drop packets for accurate tracking */
+	if(!req_p) return NF_DROP;
+        data = data++;
+
+        /* Verifies if packet is really a reply packet */
+	if(IXDR_GET_INT32(data) != 1) {
+		DEBUGRPC("RPC_TCP: Packet with problems");
+		return NF_ACCEPT;
+        }
+	data++;
+
+       	/* Is status accept? */
+        if(IXDR_GET_INT32(data)) return NF_ACCEPT;
+        data++;
+
+       /* Get Verifier lentgth. Jump verifier */
+       	data = data + IXDR_GET_INT32(data) + 2;
+
+       	/* Is accpet status "sucess"? */
+       	if(IXDR_GET_INT32(data)) return NF_ACCEPT;
+     	data++;
+
+       	/* Get server port number */          
+	port_buf = (u_int16_t) IXDR_GET_INT32(data);
+
+        /* All right. This binding must be in internal expect list */
+        /* If port == 0, service is not registred */
+        if(port_buf)
+         binding(ct->tuplehash[dir].tuple.src.ip, port_buf,
+	       ct->tuplehash[!dir].tuple.src.ip, req_p->proto); 
+	req_cl(req_p);
+        return NF_ACCEPT;
+}
+
+struct ip_conntrack_helper rpc_tcp = { { NULL, NULL },
+				       { { 0, { __constant_htons(111) } },
+					 { 0, { 0 }, IPPROTO_TCP } },
+				       { { 0, { 0xFFFF } },
+					 { 0, { 0 }, 0xFFFF } },
+				       help };
+
+int init_module(void)
+{
+	printk("ip_conntrack_rpc_tcp.o\n");
+	return ip_conntrack_helper_register(&rpc_tcp);
+}
+
+void cleanup_module(void)
+{
+	clean_expect(&expect_rpc_list_tcp);
+	clean_request(&request_p_list_tcp);
+	ip_conntrack_helper_unregister(&rpc_tcp);
+}
+
+struct module *ip_conntrack_rpc_tcp = THIS_MODULE;
+EXPORT_SYMBOL(expect_rpc_list_tcp);
+EXPORT_SYMBOL(ip_conntrack_rpc_tcp);
+EXPORT_SYMBOL(ipct_rpc_tcp_lock);
diff -urN -X /tmp/kerndiff.2QQi3B --minimal linux-2.4.0-test9-2/net/ipv4/netfilter/ip_conntrack_rpc_udp.c working-2.4.0-test9-2/net/ipv4/netfilter/ip_conntrack_rpc_udp.c
--- linux-2.4.0-test9-2/net/ipv4/netfilter/ip_conntrack_rpc_udp.c	Thu Jan  1 10:00:00 1970
+++ working-2.4.0-test9-2/net/ipv4/netfilter/ip_conntrack_rpc_udp.c	Thu Oct 12 10:39:28 2000
@@ -0,0 +1,329 @@
+/* RPC extension for IP connection tracking. */
+/* Marcelo Barbosa Lima - marcelo.lima@dcc.unicamp.br */
+/* Yes, my english is very poor :-) */
+
+#include <linux/module.h>
+#include <linux/netfilter.h>
+#include <linux/ip.h>
+#include <net/checksum.h>
+#include <net/udp.h>
+#include <asm/param.h>
+#include <linux/sched.h>
+#include <linux/timer.h>
+#include <linux/stddef.h>
+#include <linux/list.h>
+#include <linux/netfilter_ipv4/ip_conntrack_rpc.h>
+#include <linux/netfilter_ipv4/ip_conntrack.h>
+#include <linux/netfilter_ipv4/ip_conntrack_helper.h>
+#include <linux/netfilter_ipv4/lockhelp.h>
+
+DECLARE_RWLOCK(ipct_rpc_udp_lock);
+#define ASSERT_READ_LOCK(x) MUST_BE_READ_LOCKED(&ipct_rpc_udp_lock)
+#define ASSERT_WRITE_LOCK(x) MUST_BE_WRITE_LOCKED(&ipct_rpc_udp_lock)
+#include <linux/netfilter_ipv4/listhelp.h>
+
+ /* For future conections RPC, using client's cache bindings */
+LIST_HEAD(expect_rpc_list_udp);
+LIST_HEAD(request_p_list_udp);
+
+void delete_request_p(unsigned long request_p_ul) {
+	struct request_p *p = (void *)request_p_ul;
+	WRITE_LOCK(&ipct_rpc_udp_lock);
+	LIST_DELETE(&request_p_list_udp, p);
+	WRITE_UNLOCK(&ipct_rpc_udp_lock);
+	kfree(p);
+	return;
+
+}
+
+void delete_binding(unsigned long rpc_binding_ul) {
+	struct expect_rpc *l = (void *)rpc_binding_ul;
+	WRITE_LOCK(&ipct_rpc_udp_lock);
+	LIST_DELETE(&expect_rpc_list_udp, l);
+	WRITE_UNLOCK(&ipct_rpc_udp_lock);
+	kfree(l);
+	return;
+}
+
+void exp_cl(struct expect_rpc * t) {
+	WRITE_LOCK(&ipct_rpc_udp_lock);
+	del_timer(&t->timeout);
+	LIST_DELETE(&expect_rpc_list_udp, t);
+	WRITE_UNLOCK(&ipct_rpc_udp_lock);
+	kfree(t);
+	return;
+}
+
+
+/* Clean timers in unregister to deny crash */
+void clean_expect(struct list_head *list){
+	struct list_head *first = list->prev;
+	struct list_head *temp = list->next;
+	struct list_head *aux;
+
+	if(list_empty(list)) return;
+
+	    while(first != temp) {
+		aux = temp->next;
+		exp_cl((struct expect_rpc *)temp);
+		temp = aux;	
+	    }
+	exp_cl((struct expect_rpc *)temp);
+	return;	
+}
+
+void req_cl(struct request_p * r){
+	WRITE_LOCK(&ipct_rpc_udp_lock);
+	del_timer(&r->timeout);
+	LIST_DELETE(&request_p_list_udp, r);
+	WRITE_UNLOCK(&ipct_rpc_udp_lock);
+	kfree(r);
+	return;
+}
+
+/* Clean timers in unregister to deny crash */
+void clean_request(struct list_head *list){
+	struct list_head *first = list->prev;
+	struct list_head *temp = list->next;
+	struct list_head *aux;
+
+	if(list_empty(list)) return;
+
+	    while(first != temp) {
+		aux = temp->next;
+		req_cl((struct request_p *)temp);
+		temp = aux;	
+	    }
+	req_cl((struct request_p *)temp);
+	return;
+}
+
+
+void alloc_request_p(u_int32_t xid, u_int16_t proto, u_int32_t ip,
+		     u_int16_t port){
+	struct request_p *req_p;
+        
+        /* Verifies if entry already exists */
+        WRITE_LOCK(&ipct_rpc_udp_lock);
+        req_p = LIST_FIND(&request_p_list_udp, request_p_cmp,
+		struct request_p *, xid, ip, port);
+
+        if(req_p){
+		/* Refresh timeout */
+		if (del_timer(&req_p->timeout)) {
+		   req_p->timeout.expires = jiffies + EXP;
+		   add_timer(&req_p->timeout);	
+	        } 
+		WRITE_UNLOCK(&ipct_rpc_udp_lock);
+		return;	
+
+	}
+	WRITE_UNLOCK(&ipct_rpc_udp_lock);
+	
+	/* Allocate new request_p */
+	req_p = (struct request_p *) kmalloc(sizeof(struct request_p), GFP_ATOMIC);
+        if(!req_p) {
+ 		DEBUGRPC("Can't allocate request_p\n");
+		return;			
+	}
+	*req_p = ((struct request_p) {{ NULL, NULL }, xid, ip, port, proto, 
+		{ {NULL, NULL}, jiffies + EXP, (unsigned long)req_p,
+			NULL }}); 
+      
+        /*Initialize timer */
+	init_timer(&req_p->timeout);
+	req_p->timeout.function = delete_request_p;
+        add_timer(&req_p->timeout); 
+
+	/* Put in list */
+	WRITE_LOCK(&ipct_rpc_udp_lock);
+	list_prepend(&request_p_list_udp, req_p);
+	WRITE_UNLOCK(&ipct_rpc_udp_lock); 
+	return; 
+
+}
+
+/* Update Internal expect list */
+void binding(u_int32_t ip, u_int16_t port, u_int32_t ip_rmt, u_int16_t proto){
+	struct expect_rpc *rpc_binding;
+
+        /* Verifies if entry already exists */
+        WRITE_LOCK(&ipct_rpc_udp_lock);
+        rpc_binding = LIST_FIND(&expect_rpc_list_udp, expect_rpc_cmp,
+		struct expect_rpc *, port, ip, ip_rmt, proto);
+
+        if(rpc_binding){
+		/* Refresh timeout */
+		if (del_timer(&rpc_binding->timeout)) {
+		   rpc_binding->timeout.expires = jiffies + EXPIRES;
+		   add_timer(&rpc_binding->timeout);	
+	        } 
+		WRITE_UNLOCK(&ipct_rpc_udp_lock);
+		return;	
+
+	}
+	WRITE_UNLOCK(&ipct_rpc_udp_lock);
+	
+	/* Allocate new binding */
+	rpc_binding = (struct expect_rpc *) kmalloc(sizeof(struct expect_rpc), GFP_ATOMIC);
+        if(!rpc_binding) {
+ 		DEBUGRPC("Can't allocate rpc_binding\n");
+		return;			
+	}
+	*rpc_binding = ((struct expect_rpc) {{ NULL, NULL }, ip, port, ip_rmt, proto,
+		{ {NULL, NULL}, jiffies + EXPIRES, (unsigned long)rpc_binding,
+			NULL }}); 
+      
+        /*Initialize timer */
+	init_timer(&rpc_binding->timeout);
+	rpc_binding->timeout.function = delete_binding;
+        add_timer(&rpc_binding->timeout); 
+
+	/* Put in list */
+	WRITE_LOCK(&ipct_rpc_udp_lock);
+	list_prepend(&expect_rpc_list_udp, rpc_binding);
+	WRITE_UNLOCK(&ipct_rpc_udp_lock); 
+	return; 
+
+}
+
+
+/* RPC using UDP - help */
+static int help(const struct iphdr *iph, size_t len,
+			 struct ip_conntrack *ct,
+			 enum ip_conntrack_info ctinfo)
+{
+	struct udphdr *udph = (void *)iph + iph->ihl * 4;
+	const u_int32_t *data = (const u_int32_t *)udph + 2;
+
+	/* Checksum */
+	const u_int16_t *chsm = (const u_int16_t *)udph + 3;
+
+        /* Translstion's buffer for XDR */
+        u_int16_t port_buf;
+	
+	size_t udplen = len - iph->ihl * 4;
+
+	/* Important for tracking */
+	int dir = CTINFO2DIR(ctinfo);
+	u_int32_t xid;	
+	struct request_p *req_p;
+
+	/* Not whole UDP header */
+	if (udplen < sizeof(struct udphdr)) {
+		DEBUGRPC("RPC_UDP: udplen = %u\n", (unsigned)udplen);
+		return NF_ACCEPT;
+	}
+
+	/* Checksum is calculated? */
+         if(*chsm){
+	/* Checksum invalid?  Ignore. */
+		if (csum_tcpudp_magic(iph->saddr, iph->daddr, udplen, IPPROTO_UDP,
+		   csum_partial((char *)udph, udplen, 0))) {
+			DEBUGRPC("RPC_help_UDP: bad csum: %p %u%u.%u.%u.%u %u.%u.%u.%u\n",
+			 udph, udplen, IP_PARTS(iph->saddr),
+			 IP_PARTS(iph->daddr));
+			 return NF_ACCEPT;
+		   } 
+	}
+
+        /* If packet is client packet */
+        if (!dir) {
+		/* Test if packet len is ok */
+		if((udplen - sizeof(struct udphdr)) != 56) 
+			return NF_ACCEPT; 
+		/* Get XID */
+		xid = *data;
+		
+		/* Jump until procedure number */
+		data = data + 5;
+		/* Is one get port procedure? */
+		if(IXDR_GET_INT32(data) == 3){ 
+		   data++;
+
+		   /* Jump Credentials and Verfifier */
+		   data = data + IXDR_GET_INT32(data) + 2;
+		   data = data + IXDR_GET_INT32(data) + 2;		
+		
+		   /* Get and store protocol in RPC client parameters */
+		   data = data + 2;
+		   alloc_request_p(xid, IXDR_GET_INT32(data), ct->tuplehash[dir].tuple.src.ip,
+				ct->tuplehash[dir].tuple.src.u.all);
+		}
+		return NF_ACCEPT;		
+	}
+		
+	/* Until there's been traffic both ways, don't look in packets. */
+	if (ctinfo != IP_CT_ESTABLISHED) {
+		DEBUGRPC("RPC_UDP: Conntrackinfo = %u\n", ctinfo);
+		return NF_ACCEPT;
+	}
+
+	/* Test if packet len is ok */	
+	if((udplen - sizeof(struct udphdr)) != 28) return NF_ACCEPT;
+
+	/* XID is important */
+        req_p = LIST_FIND(&request_p_list_udp, request_p_cmp,
+		struct request_p *, *data, ct->tuplehash[!dir].tuple.src.ip,
+		ct->tuplehash[!dir].tuple.src.u.all);
+
+	/* Drop packets for accurate tracking */
+	if(!req_p) return NF_DROP;
+
+        data = data++;
+
+        /* Verifies if packet is really a reply packet */
+	if(IXDR_GET_INT32(data) != 1) {
+		DEBUGRPC("RPC_UDP: Packet with problems");
+		return NF_ACCEPT;
+        }         
+	data++;
+
+       	/* Is status accpet? */
+        if(IXDR_GET_INT32(data)) return NF_ACCEPT;
+        data++;
+	
+       /* Get Verifier lentgth. Jump verifier */
+       	data = data + IXDR_GET_INT32(data) + 2;
+
+       	/* Is accpet status "sucess"? */
+       	if(IXDR_GET_INT32(data)) return NF_ACCEPT;
+     	data++;
+
+       	/* Get server port number */          
+	port_buf = (u_int16_t) IXDR_GET_INT32(data);
+
+        /* All right. This binding must be in internal expect list */
+        /* If port == 0, service is not registred */
+        if(port_buf)
+          binding(ct->tuplehash[dir].tuple.src.ip, port_buf,
+	       ct->tuplehash[!dir].tuple.src.ip, req_p->proto); 
+	req_cl(req_p);
+       return NF_ACCEPT;
+}
+
+struct ip_conntrack_helper rpc_udp = { { NULL, NULL },
+				       { { 0, { __constant_htons(111) } },
+					 { 0, { 0 }, IPPROTO_UDP } },
+				       { { 0, { 0xFFFF } },
+					 { 0, { 0 }, 0xFFFF } },
+				       help };
+
+int init_module(void)
+{
+	printk("ip_conntrack_rpc_udp.o\n");
+	return ip_conntrack_helper_register(&rpc_udp);
+}
+
+void cleanup_module(void)
+{
+	clean_expect(&expect_rpc_list_udp);
+	clean_request(&request_p_list_udp);
+	ip_conntrack_helper_unregister(&rpc_udp);
+}
+
+struct module *ip_conntrack_rpc_udp = THIS_MODULE;
+EXPORT_SYMBOL(expect_rpc_list_udp);
+EXPORT_SYMBOL(ip_conntrack_rpc_udp);
+EXPORT_SYMBOL(ipct_rpc_udp_lock);
+
diff -urN -X /tmp/kerndiff.2QQi3B --minimal linux-2.4.0-test9-2/net/ipv4/netfilter/ipt_record_rpc.c working-2.4.0-test9-2/net/ipv4/netfilter/ipt_record_rpc.c
--- linux-2.4.0-test9-2/net/ipv4/netfilter/ipt_record_rpc.c	Thu Jan  1 10:00:00 1970
+++ working-2.4.0-test9-2/net/ipv4/netfilter/ipt_record_rpc.c	Thu Oct 12 10:51:40 2000
@@ -0,0 +1,169 @@
+/* Kernel module to match RPC connection tracking information. */
+/* Marcelo Barbosa Lima - marcelo.lima@dcc.unicamp.br */
+
+#include <linux/module.h>
+#include <linux/skbuff.h>
+#include <linux/list.h>
+#include <linux/udp.h>
+#include <linux/tcp.h>
+#include <linux/netfilter_ipv4/ip_conntrack.h>
+#include <linux/netfilter_ipv4/ip_tables.h>
+#include <linux/netfilter_ipv4/ip_conntrack_rpc.h>
+#include <linux/netfilter_ipv4/lockhelp.h>
+
+EXPORT_NO_SYMBOLS;
+
+extern struct list_head expect_rpc_list_tcp; 
+extern struct module *ip_conntrack_rpc_tcp;
+extern struct list_head expect_rpc_list_udp;
+extern struct module *ip_conntrack_rpc_udp;
+DECLARE_RWLOCK_EXTERN(ipct_rpc_tcp_lock);
+DECLARE_RWLOCK_EXTERN(ipct_rpc_udp_lock);
+
+#define ASSERT_READ_LOCK(x)					\
+do {								\
+	if (x == &expect_rpc_list_udp)				\
+		MUST_BE_READ_LOCKED(&ipct_rpc_udp_lock);	\
+	else if (x == &expect_rpc_list_tcp)			\
+		MUST_BE_READ_LOCKED(&ipct_rpc_tcp_lock);	\
+} while (0)
+			
+#define ASSERT_WRITE_LOCK(x)					\
+do {								\
+	if (x == &expect_rpc_list_udp)				\
+		MUST_BE_WRITE_LOCKED(&ipct_rpc_udp_lock);	\
+	else if (x == &expect_rpc_list_tcp)			\
+		MUST_BE_WRITE_LOCKED(&ipct_rpc_tcp_lock);	\
+} while (0)
+#include <linux/netfilter_ipv4/listhelp.h>
+
+
+static int
+match(const struct sk_buff *skb,
+      const struct net_device *in,
+      const struct net_device *out,
+      const void *matchinfo,
+      int offset,
+      const void *hdr,
+      u_int16_t datalen,
+      int *hotdrop)
+{
+	struct ip_conntrack *ct;
+	enum ip_conntrack_info ctinfo;
+	struct expect_rpc *rpc_bind;
+	const u_int32_t *data;
+	enum ip_conntrack_dir dir;
+	const struct tcphdr *tcp;
+	
+	ct = ip_conntrack_get((struct sk_buff *)skb, &ctinfo);
+	if (ct) {	
+         dir = CTINFO2DIR(ctinfo);
+
+	/* This does sanity check in UDP or TCP packets like their */
+	/* respective modules and permits only get port procedure in */
+	/* client communications with portmapper */
+	 switch(ct->tuplehash[0].tuple.dst.protonum){	
+	
+	   case IPPROTO_UDP:
+	 	if(offset == 0 && datalen < sizeof(struct udphdr)) {
+		    *hotdrop = 1;
+		    return 0;
+	 	}	
+		if(ntohs(ct->tuplehash[dir].tuple.dst.u.all) == 111){	
+
+	           if((datalen - sizeof(struct udphdr)) != 56)
+                        return 0;
+
+		   data = (const u_int32_t *)hdr + 7;
+		   if(IXDR_GET_INT32(data) != 3) 
+			 return 0;
+		   else  
+			return (1 && (!offset)); 
+		}
+		if(ntohs(ct->tuplehash[!dir].tuple.dst.u.all) == 111) 
+		   return (1 && (!offset));
+		
+	   case IPPROTO_TCP:
+		if(offset == 0 && datalen < sizeof(struct tcphdr)) {
+			*hotdrop = 1;
+			return 0;
+		}	
+		if(ntohs(ct->tuplehash[dir].tuple.dst.u.all) == 111) { 
+			tcp = hdr;
+			if(datalen == (tcp->doff * 4)) return (1 && (!offset));
+
+ 	                /* Tests if packet len is ok */
+                        if((datalen - (tcp->doff * 4)) != 60)
+                             return 0;
+			data = (const u_int32_t *)tcp + tcp->doff + 6;	
+
+                   if(IXDR_GET_INT32(data) != 3) 
+                         return 0;
+                   else
+                        return (1 && (!offset));
+		}
+		if(ntohs(ct->tuplehash[!dir].tuple.dst.u.all) == 111)
+                   return (1 && (!offset));
+	 }
+
+	/* Client using UDP in communication with portmapper */
+	WRITE_LOCK(&ipct_rpc_udp_lock);
+	rpc_bind = LIST_FIND(&expect_rpc_list_udp, expect_rpc_cmp, struct expect_rpc *, 
+ 			    ntohs(ct->tuplehash[0].tuple.dst.u.all),
+			    ct->tuplehash[1].tuple.src.ip, ct->tuplehash[0].tuple.src.ip,
+			    ct->tuplehash[0].tuple.dst.protonum);
+
+	if(rpc_bind) { 
+	   if(del_timer(&rpc_bind->timeout)) {
+	      rpc_bind->timeout.expires = jiffies + EXPIRES;
+	      add_timer(&rpc_bind->timeout);
+	   }
+	   WRITE_UNLOCK(&ipct_rpc_udp_lock);
+	/* Must not be a fragment */
+	return (1 && (!offset));
+	}
+	WRITE_UNLOCK(&ipct_rpc_udp_lock);
+
+	/* Client using TCP in communication with portmapper */
+	WRITE_LOCK(&ipct_rpc_tcp_lock);
+	rpc_bind = LIST_FIND(&expect_rpc_list_tcp, expect_rpc_cmp, struct expect_rpc *, 
+ 			    ntohs(ct->tuplehash[0].tuple.dst.u.all),
+			    ct->tuplehash[1].tuple.src.ip, ct->tuplehash[0].tuple.src.ip,
+			    ct->tuplehash[0].tuple.dst.protonum);
+
+	if (rpc_bind) {
+	   if (del_timer(&rpc_bind->timeout)) {
+	      rpc_bind->timeout.expires = jiffies + EXPIRES;
+	      add_timer(&rpc_bind->timeout);
+	   }
+	   WRITE_UNLOCK(&ipct_rpc_tcp_lock);
+	   return (1 && (!offset));
+	} 
+	   WRITE_UNLOCK(&ipct_rpc_tcp_lock);
+	return 0;
+	}
+	return 0;
+}
+
+
+static struct ipt_match record_rpc_match
+= { { NULL, NULL }, "record_rpc", &match, NULL, NULL, THIS_MODULE };
+
+int __init init(void)
+{
+	__MOD_INC_USE_COUNT(ip_conntrack_module);
+	__MOD_INC_USE_COUNT(ip_conntrack_rpc_udp);
+	__MOD_INC_USE_COUNT(ip_conntrack_rpc_tcp);
+	return ipt_register_match(&record_rpc_match);
+}
+
+void __exit cleanup(void)
+{
+	ipt_unregister_match(&record_rpc_match);
+	__MOD_DEC_USE_COUNT(ip_conntrack_module);
+	__MOD_DEC_USE_COUNT(ip_conntrack_rpc_tcp);
+	__MOD_DEC_USE_COUNT(ip_conntrack_rpc_udp);
+}
+
+module_init(init);
+module_exit(cleanup);
